]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright 2012 Jun Wako <wakojun@gmail.com> | |
3 | * This file is based on: | |
4 | * LUFA-120219/Demos/Device/Lowlevel/KeyboardMouse | |
5 | * LUFA-120219/Demos/Device/Lowlevel/GenericHID | |
6 | */ | |
7 | ||
8 | /* | |
9 | LUFA Library | |
10 | Copyright (C) Dean Camera, 2012. | |
11 | ||
12 | dean [at] fourwalledcubicle [dot] com | |
13 | www.lufa-lib.org | |
14 | */ | |
15 | ||
16 | /* | |
17 | Copyright 2012 Dean Camera (dean [at] fourwalledcubicle [dot] com) | |
18 | Copyright 2010 Denver Gingerich (denver [at] ossguy [dot] com) | |
19 | ||
20 | Permission to use, copy, modify, distribute, and sell this | |
21 | software and its documentation for any purpose is hereby granted | |
22 | without fee, provided that the above copyright notice appear in | |
23 | all copies and that both that the copyright notice and this | |
24 | permission notice and warranty disclaimer appear in supporting | |
25 | documentation, and that the name of the author not be used in | |
26 | advertising or publicity pertaining to distribution of the | |
27 | software without specific, written prior permission. | |
28 | ||
29 | The author disclaim all warranties with regard to this | |
30 | software, including all implied warranties of merchantability | |
31 | and fitness. In no event shall the author be liable for any | |
32 | special, indirect or consequential damages or any damages | |
33 | whatsoever resulting from loss of use, data or profits, whether | |
34 | in an action of contract, negligence or other tortious action, | |
35 | arising out of or in connection with the use or performance of | |
36 | this software. | |
37 | */ | |
38 | ||
39 | #include "report.h" | |
40 | #include "host.h" | |
41 | #include "host_driver.h" | |
42 | #include "keyboard.h" | |
43 | #include "action.h" | |
44 | #include "led.h" | |
45 | #include "sendchar.h" | |
46 | #include "debug.h" | |
47 | #ifdef SLEEP_LED_ENABLE | |
48 | #include "sleep_led.h" | |
49 | #endif | |
50 | #include "suspend.h" | |
51 | ||
52 | #include "descriptor.h" | |
53 | #include "lufa.h" | |
54 | ||
55 | static uint8_t idle_duration = 0; | |
56 | static uint8_t protocol_report = 1; | |
57 | static uint8_t keyboard_led_stats = 0; | |
58 | ||
59 | static report_keyboard_t keyboard_report_sent; | |
60 | ||
61 | ||
62 | /* Host driver */ | |
63 | static uint8_t keyboard_leds(void); | |
64 | static void send_keyboard(report_keyboard_t *report); | |
65 | static void send_mouse(report_mouse_t *report); | |
66 | static void send_system(uint16_t data); | |
67 | static void send_consumer(uint16_t data); | |
68 | host_driver_t lufa_driver = { | |
69 | keyboard_leds, | |
70 | send_keyboard, | |
71 | send_mouse, | |
72 | send_system, | |
73 | send_consumer | |
74 | }; | |
75 | ||
76 | ||
77 | /******************************************************************************* | |
78 | * Console | |
79 | ******************************************************************************/ | |
80 | #ifdef CONSOLE_ENABLE | |
81 | static void Console_Task(void) | |
82 | { | |
83 | /* Device must be connected and configured for the task to run */ | |
84 | if (USB_DeviceState != DEVICE_STATE_Configured) | |
85 | return; | |
86 | ||
87 | uint8_t ep = Endpoint_GetCurrentEndpoint(); | |
88 | ||
89 | #if 0 | |
90 | // TODO: impl receivechar()/recvchar() | |
91 | Endpoint_SelectEndpoint(CONSOLE_OUT_EPNUM); | |
92 | ||
93 | /* Check to see if a packet has been sent from the host */ | |
94 | if (Endpoint_IsOUTReceived()) | |
95 | { | |
96 | /* Check to see if the packet contains data */ | |
97 | if (Endpoint_IsReadWriteAllowed()) | |
98 | { | |
99 | /* Create a temporary buffer to hold the read in report from the host */ | |
100 | uint8_t ConsoleData[CONSOLE_EPSIZE]; | |
101 | ||
102 | /* Read Console Report Data */ | |
103 | Endpoint_Read_Stream_LE(&ConsoleData, sizeof(ConsoleData), NULL); | |
104 | ||
105 | /* Process Console Report Data */ | |
106 | //ProcessConsoleHIDReport(ConsoleData); | |
107 | } | |
108 | ||
109 | /* Finalize the stream transfer to send the last packet */ | |
110 | Endpoint_ClearOUT(); | |
111 | } | |
112 | #endif | |
113 | ||
114 | /* IN packet */ | |
115 | Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM); | |
116 | if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) { | |
117 | Endpoint_SelectEndpoint(ep); | |
118 | return; | |
119 | } | |
120 | ||
121 | // fill empty bank | |
122 | while (Endpoint_IsReadWriteAllowed()) | |
123 | Endpoint_Write_8(0); | |
124 | ||
125 | // flash senchar packet | |
126 | if (Endpoint_IsINReady()) { | |
127 | Endpoint_ClearIN(); | |
128 | } | |
129 | ||
130 | Endpoint_SelectEndpoint(ep); | |
131 | } | |
132 | #else | |
133 | static void Console_Task(void) | |
134 | { | |
135 | } | |
136 | #endif | |
137 | ||
138 | ||
139 | /******************************************************************************* | |
140 | * USB Events | |
141 | ******************************************************************************/ | |
142 | /* | |
143 | * Event Order of Plug in: | |
144 | * 0) EVENT_USB_Device_Connect | |
145 | * 1) EVENT_USB_Device_Suspend | |
146 | * 2) EVENT_USB_Device_Reset | |
147 | * 3) EVENT_USB_Device_Wake | |
148 | */ | |
149 | void EVENT_USB_Device_Connect(void) | |
150 | { | |
151 | } | |
152 | ||
153 | void EVENT_USB_Device_Disconnect(void) | |
154 | { | |
155 | } | |
156 | ||
157 | void EVENT_USB_Device_Reset(void) | |
158 | { | |
159 | } | |
160 | ||
161 | void EVENT_USB_Device_Suspend() | |
162 | { | |
163 | #ifdef SLEEP_LED_ENABLE | |
164 | sleep_led_enable(); | |
165 | #endif | |
166 | } | |
167 | ||
168 | void EVENT_USB_Device_WakeUp() | |
169 | { | |
170 | suspend_wakeup_init(); | |
171 | ||
172 | #ifdef SLEEP_LED_ENABLE | |
173 | sleep_led_disable(); | |
174 | // NOTE: converters may not accept this | |
175 | led_set(host_keyboard_leds()); | |
176 | #endif | |
177 | } | |
178 | ||
179 | void EVENT_USB_Device_StartOfFrame(void) | |
180 | { | |
181 | Console_Task(); | |
182 | } | |
183 | ||
184 | /** Event handler for the USB_ConfigurationChanged event. | |
185 | * This is fired when the host sets the current configuration of the USB device after enumeration. | |
186 | */ | |
187 | #if LUFA_VERSION_INTEGER < 0x120730 | |
188 | /* old API 120219 */ | |
189 | #define ENDPOINT_CONFIG(epnum, eptype, epdir, epsize, epbank) Endpoint_ConfigureEndpoint(epnum, eptype, epdir, epsize, epbank) | |
190 | #else | |
191 | /* new API >= 120730 */ | |
192 | #define ENDPOINT_BANK_SINGLE 1 | |
193 | #define ENDPOINT_BANK_DOUBLE 2 | |
194 | #define ENDPOINT_CONFIG(epnum, eptype, epdir, epsize, epbank) Endpoint_ConfigureEndpoint((epdir) | (epnum) , eptype, epsize, epbank) | |
195 | #endif | |
196 | void EVENT_USB_Device_ConfigurationChanged(void) | |
197 | { | |
198 | bool ConfigSuccess = true; | |
199 | ||
200 | /* Setup Keyboard HID Report Endpoints */ | |
201 | ConfigSuccess &= ENDPOINT_CONFIG(KEYBOARD_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN, | |
202 | KEYBOARD_EPSIZE, ENDPOINT_BANK_SINGLE); | |
203 | ||
204 | #ifdef MOUSE_ENABLE | |
205 | /* Setup Mouse HID Report Endpoint */ | |
206 | ConfigSuccess &= ENDPOINT_CONFIG(MOUSE_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN, | |
207 | MOUSE_EPSIZE, ENDPOINT_BANK_SINGLE); | |
208 | #endif | |
209 | ||
210 | #ifdef EXTRAKEY_ENABLE | |
211 | /* Setup Extra HID Report Endpoint */ | |
212 | ConfigSuccess &= ENDPOINT_CONFIG(EXTRAKEY_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN, | |
213 | EXTRAKEY_EPSIZE, ENDPOINT_BANK_SINGLE); | |
214 | #endif | |
215 | ||
216 | #ifdef CONSOLE_ENABLE | |
217 | /* Setup Console HID Report Endpoints */ | |
218 | ConfigSuccess &= ENDPOINT_CONFIG(CONSOLE_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN, | |
219 | CONSOLE_EPSIZE, ENDPOINT_BANK_DOUBLE); | |
220 | ConfigSuccess &= ENDPOINT_CONFIG(CONSOLE_OUT_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_OUT, | |
221 | CONSOLE_EPSIZE, ENDPOINT_BANK_SINGLE); | |
222 | #endif | |
223 | ||
224 | #ifdef NKRO_ENABLE | |
225 | /* Setup NKRO HID Report Endpoints */ | |
226 | ConfigSuccess &= ENDPOINT_CONFIG(NKRO_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN, | |
227 | NKRO_EPSIZE, ENDPOINT_BANK_SINGLE); | |
228 | #endif | |
229 | } | |
230 | ||
231 | /* | |
232 | Appendix G: HID Request Support Requirements | |
233 | ||
234 | The following table enumerates the requests that need to be supported by various types of HID class devices. | |
235 | ||
236 | Device type GetReport SetReport GetIdle SetIdle GetProtocol SetProtocol | |
237 | ------------------------------------------------------------------------------------------ | |
238 | Boot Mouse Required Optional Optional Optional Required Required | |
239 | Non-Boot Mouse Required Optional Optional Optional Optional Optional | |
240 | Boot Keyboard Required Optional Required Required Required Required | |
241 | Non-Boot Keybrd Required Optional Required Required Optional Optional | |
242 | Other Device Required Optional Optional Optional Optional Optional | |
243 | */ | |
244 | /** Event handler for the USB_ControlRequest event. | |
245 | * This is fired before passing along unhandled control requests to the library for processing internally. | |
246 | */ | |
247 | void EVENT_USB_Device_ControlRequest(void) | |
248 | { | |
249 | uint8_t* ReportData = NULL; | |
250 | uint8_t ReportSize = 0; | |
251 | ||
252 | /* Handle HID Class specific requests */ | |
253 | switch (USB_ControlRequest.bRequest) | |
254 | { | |
255 | case HID_REQ_GetReport: | |
256 | if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE)) | |
257 | { | |
258 | Endpoint_ClearSETUP(); | |
259 | ||
260 | // Interface | |
261 | switch (USB_ControlRequest.wIndex) { | |
262 | case KEYBOARD_INTERFACE: | |
263 | // TODO: test/check | |
264 | ReportData = (uint8_t*)&keyboard_report_sent; | |
265 | ReportSize = sizeof(keyboard_report_sent); | |
266 | break; | |
267 | } | |
268 | ||
269 | /* Write the report data to the control endpoint */ | |
270 | Endpoint_Write_Control_Stream_LE(ReportData, ReportSize); | |
271 | Endpoint_ClearOUT(); | |
272 | } | |
273 | ||
274 | break; | |
275 | case HID_REQ_SetReport: | |
276 | if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE)) | |
277 | { | |
278 | ||
279 | // Interface | |
280 | switch (USB_ControlRequest.wIndex) { | |
281 | case KEYBOARD_INTERFACE: | |
282 | Endpoint_ClearSETUP(); | |
283 | ||
284 | while (!(Endpoint_IsOUTReceived())) { | |
285 | if (USB_DeviceState == DEVICE_STATE_Unattached) | |
286 | return; | |
287 | } | |
288 | keyboard_led_stats = Endpoint_Read_8(); | |
289 | ||
290 | Endpoint_ClearOUT(); | |
291 | Endpoint_ClearStatusStage(); | |
292 | break; | |
293 | } | |
294 | ||
295 | } | |
296 | ||
297 | break; | |
298 | ||
299 | case HID_REQ_GetProtocol: | |
300 | if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE)) | |
301 | { | |
302 | Endpoint_ClearSETUP(); | |
303 | while (!(Endpoint_IsINReady())); | |
304 | Endpoint_Write_8(protocol_report); | |
305 | Endpoint_ClearIN(); | |
306 | Endpoint_ClearStatusStage(); | |
307 | } | |
308 | ||
309 | break; | |
310 | case HID_REQ_SetProtocol: | |
311 | if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE)) | |
312 | { | |
313 | Endpoint_ClearSETUP(); | |
314 | Endpoint_ClearStatusStage(); | |
315 | ||
316 | protocol_report = ((USB_ControlRequest.wValue & 0xFF) != 0x00); | |
317 | } | |
318 | ||
319 | break; | |
320 | case HID_REQ_SetIdle: | |
321 | if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE)) | |
322 | { | |
323 | Endpoint_ClearSETUP(); | |
324 | Endpoint_ClearStatusStage(); | |
325 | ||
326 | idle_duration = ((USB_ControlRequest.wValue & 0xFF00) >> 8); | |
327 | } | |
328 | ||
329 | break; | |
330 | case HID_REQ_GetIdle: | |
331 | if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE)) | |
332 | { | |
333 | Endpoint_ClearSETUP(); | |
334 | while (!(Endpoint_IsINReady())); | |
335 | Endpoint_Write_8(idle_duration); | |
336 | Endpoint_ClearIN(); | |
337 | Endpoint_ClearStatusStage(); | |
338 | } | |
339 | ||
340 | break; | |
341 | } | |
342 | } | |
343 | ||
344 | /******************************************************************************* | |
345 | * Host driver | |
346 | ******************************************************************************/ | |
347 | static uint8_t keyboard_leds(void) | |
348 | { | |
349 | return keyboard_led_stats; | |
350 | } | |
351 | ||
352 | static void send_keyboard(report_keyboard_t *report) | |
353 | { | |
354 | uint8_t timeout = 0; | |
355 | ||
356 | if (USB_DeviceState != DEVICE_STATE_Configured) | |
357 | return; | |
358 | ||
359 | /* Select the Keyboard Report Endpoint */ | |
360 | #ifdef NKRO_ENABLE | |
361 | if (keyboard_nkro) { | |
362 | Endpoint_SelectEndpoint(NKRO_IN_EPNUM); | |
363 | } | |
364 | else | |
365 | #endif | |
366 | { | |
367 | Endpoint_SelectEndpoint(KEYBOARD_IN_EPNUM); | |
368 | } | |
369 | ||
370 | /* Check if Keyboard Endpoint Ready for Read/Write */ | |
371 | while (--timeout && !Endpoint_IsReadWriteAllowed()) ; | |
372 | ||
373 | /* Write Keyboard Report Data */ | |
374 | #ifdef NKRO_ENABLE | |
375 | if (keyboard_nkro) { | |
376 | Endpoint_Write_Stream_LE(report, NKRO_EPSIZE, NULL); | |
377 | } | |
378 | else | |
379 | #endif | |
380 | { | |
381 | /* boot mode */ | |
382 | Endpoint_Write_Stream_LE(report, KEYBOARD_EPSIZE, NULL); | |
383 | } | |
384 | ||
385 | /* Finalize the stream transfer to send the last packet */ | |
386 | Endpoint_ClearIN(); | |
387 | ||
388 | keyboard_report_sent = *report; | |
389 | } | |
390 | ||
391 | static void send_mouse(report_mouse_t *report) | |
392 | { | |
393 | #ifdef MOUSE_ENABLE | |
394 | uint8_t timeout = 0; | |
395 | ||
396 | if (USB_DeviceState != DEVICE_STATE_Configured) | |
397 | return; | |
398 | ||
399 | /* Select the Mouse Report Endpoint */ | |
400 | Endpoint_SelectEndpoint(MOUSE_IN_EPNUM); | |
401 | ||
402 | /* Check if Mouse Endpoint Ready for Read/Write */ | |
403 | while (--timeout && !Endpoint_IsReadWriteAllowed()) ; | |
404 | ||
405 | /* Write Mouse Report Data */ | |
406 | Endpoint_Write_Stream_LE(report, sizeof(report_mouse_t), NULL); | |
407 | ||
408 | /* Finalize the stream transfer to send the last packet */ | |
409 | Endpoint_ClearIN(); | |
410 | #endif | |
411 | } | |
412 | ||
413 | static void send_system(uint16_t data) | |
414 | { | |
415 | uint8_t timeout = 0; | |
416 | ||
417 | if (USB_DeviceState != DEVICE_STATE_Configured) | |
418 | return; | |
419 | ||
420 | report_extra_t r = { | |
421 | .report_id = REPORT_ID_SYSTEM, | |
422 | .usage = data | |
423 | }; | |
424 | Endpoint_SelectEndpoint(EXTRAKEY_IN_EPNUM); | |
425 | while (--timeout && !Endpoint_IsReadWriteAllowed()) ; | |
426 | Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL); | |
427 | Endpoint_ClearIN(); | |
428 | } | |
429 | ||
430 | static void send_consumer(uint16_t data) | |
431 | { | |
432 | uint8_t timeout = 0; | |
433 | ||
434 | if (USB_DeviceState != DEVICE_STATE_Configured) | |
435 | return; | |
436 | ||
437 | report_extra_t r = { | |
438 | .report_id = REPORT_ID_CONSUMER, | |
439 | .usage = data | |
440 | }; | |
441 | Endpoint_SelectEndpoint(EXTRAKEY_IN_EPNUM); | |
442 | while (--timeout && !Endpoint_IsReadWriteAllowed()) ; | |
443 | Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL); | |
444 | Endpoint_ClearIN(); | |
445 | } | |
446 | ||
447 | ||
448 | /******************************************************************************* | |
449 | * sendchar | |
450 | ******************************************************************************/ | |
451 | #ifdef CONSOLE_ENABLE | |
452 | #define SEND_TIMEOUT 5 | |
453 | int8_t sendchar(uint8_t c) | |
454 | { | |
455 | // Not wait once timeouted. | |
456 | // Because sendchar() is called so many times, waiting each call causes big lag. | |
457 | static bool timeouted = false; | |
458 | ||
459 | if (USB_DeviceState != DEVICE_STATE_Configured) | |
460 | return -1; | |
461 | ||
462 | uint8_t ep = Endpoint_GetCurrentEndpoint(); | |
463 | Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM); | |
464 | if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) { | |
465 | Endpoint_SelectEndpoint(ep); | |
466 | return -1; | |
467 | } | |
468 | ||
469 | if (timeouted && !Endpoint_IsReadWriteAllowed()) { | |
470 | Endpoint_SelectEndpoint(ep); | |
471 | return - 1; | |
472 | } | |
473 | ||
474 | timeouted = false; | |
475 | ||
476 | uint8_t timeout = SEND_TIMEOUT; | |
477 | uint16_t prevFN = USB_Device_GetFrameNumber(); | |
478 | while (!Endpoint_IsReadWriteAllowed()) { | |
479 | switch (USB_DeviceState) { | |
480 | case DEVICE_STATE_Unattached: | |
481 | case DEVICE_STATE_Suspended: | |
482 | return -1; | |
483 | } | |
484 | if (Endpoint_IsStalled()) { | |
485 | Endpoint_SelectEndpoint(ep); | |
486 | return -1; | |
487 | } | |
488 | if (prevFN != USB_Device_GetFrameNumber()) { | |
489 | if (!(timeout--)) { | |
490 | timeouted = true; | |
491 | Endpoint_SelectEndpoint(ep); | |
492 | return -1; | |
493 | } | |
494 | prevFN = USB_Device_GetFrameNumber(); | |
495 | } | |
496 | } | |
497 | ||
498 | Endpoint_Write_8(c); | |
499 | ||
500 | // send when bank is full | |
501 | if (!Endpoint_IsReadWriteAllowed()) | |
502 | Endpoint_ClearIN(); | |
503 | ||
504 | Endpoint_SelectEndpoint(ep); | |
505 | return 0; | |
506 | } | |
507 | #else | |
508 | int8_t sendchar(uint8_t c) | |
509 | { | |
510 | return 0; | |
511 | } | |
512 | #endif | |
513 | ||
514 | ||
515 | /******************************************************************************* | |
516 | * main | |
517 | ******************************************************************************/ | |
518 | static void SetupHardware(void) | |
519 | { | |
520 | /* Disable watchdog if enabled by bootloader/fuses */ | |
521 | MCUSR &= ~(1 << WDRF); | |
522 | wdt_disable(); | |
523 | ||
524 | /* Disable clock division */ | |
525 | clock_prescale_set(clock_div_1); | |
526 | ||
527 | // Leonardo needs. Without this USB device is not recognized. | |
528 | USB_Disable(); | |
529 | ||
530 | USB_Init(); | |
531 | ||
532 | // for Console_Task | |
533 | USB_Device_EnableSOFEvents(); | |
534 | print_set_sendchar(sendchar); | |
535 | } | |
536 | ||
537 | int main(void) __attribute__ ((weak)); | |
538 | int main(void) | |
539 | { | |
540 | SetupHardware(); | |
541 | sei(); | |
542 | ||
543 | /* wait for USB startup & debug output */ | |
544 | while (USB_DeviceState != DEVICE_STATE_Configured) { | |
545 | #if defined(INTERRUPT_CONTROL_ENDPOINT) | |
546 | ; | |
547 | #else | |
548 | USB_USBTask(); | |
549 | #endif | |
550 | } | |
551 | print("USB configured.\n"); | |
552 | ||
553 | /* init modules */ | |
554 | keyboard_init(); | |
555 | host_set_driver(&lufa_driver); | |
556 | #ifdef SLEEP_LED_ENABLE | |
557 | sleep_led_init(); | |
558 | #endif | |
559 | ||
560 | print("Keyboard start.\n"); | |
561 | while (1) { | |
562 | while (USB_DeviceState == DEVICE_STATE_Suspended) { | |
563 | suspend_power_down(); | |
564 | if (USB_Device_RemoteWakeupEnabled && suspend_wakeup_condition()) { | |
565 | USB_Device_SendRemoteWakeup(); | |
566 | } | |
567 | } | |
568 | ||
569 | keyboard_task(); | |
570 | ||
571 | #if !defined(INTERRUPT_CONTROL_ENDPOINT) | |
572 | USB_USBTask(); | |
573 | #endif | |
574 | } | |
575 | } |