]> git.donarmstrong.com Git - qmk_firmware.git/blob - tmk_core/protocol/lufa/lufa.c
core: Fix for disabling NKRO in Boot protocol
[qmk_firmware.git] / tmk_core / protocol / lufa / lufa.c
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 uint8_t keyboard_idle = 0;
56 /* 0: Boot Protocol, 1: Report Protocol(default) */
57 uint8_t keyboard_protocol = 1;
58 static uint8_t keyboard_led_stats = 0;
59
60 static report_keyboard_t keyboard_report_sent;
61
62
63 /* Host driver */
64 static uint8_t keyboard_leds(void);
65 static void send_keyboard(report_keyboard_t *report);
66 static void send_mouse(report_mouse_t *report);
67 static void send_system(uint16_t data);
68 static void send_consumer(uint16_t data);
69 host_driver_t lufa_driver = {
70     keyboard_leds,
71     send_keyboard,
72     send_mouse,
73     send_system,
74     send_consumer
75 };
76
77
78 /*******************************************************************************
79  * Console
80  ******************************************************************************/
81 #ifdef CONSOLE_ENABLE
82 static void Console_Task(void)
83 {
84     /* Device must be connected and configured for the task to run */
85     if (USB_DeviceState != DEVICE_STATE_Configured)
86         return;
87
88     uint8_t ep = Endpoint_GetCurrentEndpoint();
89
90 #if 0
91     // TODO: impl receivechar()/recvchar()
92     Endpoint_SelectEndpoint(CONSOLE_OUT_EPNUM);
93
94     /* Check to see if a packet has been sent from the host */
95     if (Endpoint_IsOUTReceived())
96     {
97         /* Check to see if the packet contains data */
98         if (Endpoint_IsReadWriteAllowed())
99         {
100             /* Create a temporary buffer to hold the read in report from the host */
101             uint8_t ConsoleData[CONSOLE_EPSIZE];
102  
103             /* Read Console Report Data */
104             Endpoint_Read_Stream_LE(&ConsoleData, sizeof(ConsoleData), NULL);
105  
106             /* Process Console Report Data */
107             //ProcessConsoleHIDReport(ConsoleData);
108         }
109
110         /* Finalize the stream transfer to send the last packet */
111         Endpoint_ClearOUT();
112     }
113 #endif
114
115     /* IN packet */
116     Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM);
117     if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {
118         Endpoint_SelectEndpoint(ep);
119         return;
120     }
121
122     // fill empty bank
123     while (Endpoint_IsReadWriteAllowed())
124         Endpoint_Write_8(0);
125
126     // flash senchar packet
127     if (Endpoint_IsINReady()) {
128         Endpoint_ClearIN();
129     }
130
131     Endpoint_SelectEndpoint(ep);
132 }
133 #else
134 static void Console_Task(void)
135 {
136 }
137 #endif
138
139
140 /*******************************************************************************
141  * USB Events
142  ******************************************************************************/
143 /*
144  * Event Order of Plug in:
145  * 0) EVENT_USB_Device_Connect
146  * 1) EVENT_USB_Device_Suspend
147  * 2) EVENT_USB_Device_Reset
148  * 3) EVENT_USB_Device_Wake
149 */
150 void EVENT_USB_Device_Connect(void)
151 {
152     print("[C]");
153     /* For battery powered device */
154     if (!USB_IsInitialized) {
155         USB_Disable();
156         USB_Init();
157         USB_Device_EnableSOFEvents();
158     }
159 }
160
161 void EVENT_USB_Device_Disconnect(void)
162 {
163     print("[D]");
164     /* For battery powered device */
165     USB_IsInitialized = false;
166 /* TODO: This doesn't work. After several plug in/outs can not be enumerated. 
167     if (USB_IsInitialized) {
168         USB_Disable();  // Disable all interrupts
169         USB_Controller_Enable();
170         USB_INT_Enable(USB_INT_VBUSTI);
171     }
172 */
173 }
174
175 void EVENT_USB_Device_Reset(void)
176 {
177     print("[R]");
178 }
179
180 void EVENT_USB_Device_Suspend()
181 {
182     print("[S]");
183 #ifdef SLEEP_LED_ENABLE
184     sleep_led_enable();
185 #endif
186 }
187
188 void EVENT_USB_Device_WakeUp()
189 {
190     print("[W]");
191     suspend_wakeup_init();
192
193 #ifdef SLEEP_LED_ENABLE
194     sleep_led_disable();
195     // NOTE: converters may not accept this
196     led_set(host_keyboard_leds());
197 #endif
198 }
199
200 #ifdef CONSOLE_ENABLE
201 static bool console_flush = false;
202 #define CONSOLE_FLUSH_SET(b)   do { \
203     uint8_t sreg = SREG; cli(); console_flush = b; SREG = sreg; \
204 } while (0)
205
206 // called every 1ms
207 void EVENT_USB_Device_StartOfFrame(void)
208 {
209     static uint8_t count;
210     if (++count % 50) return;
211     count = 0;
212
213     if (!console_flush) return;
214     Console_Task();
215     console_flush = false;
216 }
217 #endif
218
219 /** Event handler for the USB_ConfigurationChanged event.
220  * This is fired when the host sets the current configuration of the USB device after enumeration.
221  *
222  * ATMega32u2 supports dual bank(ping-pong mode) only on endpoint 3 and 4,
223  * it is safe to use singl bank for all endpoints.
224  */
225 void EVENT_USB_Device_ConfigurationChanged(void)
226 {
227     bool ConfigSuccess = true;
228
229     /* Setup Keyboard HID Report Endpoints */
230     ConfigSuccess &= ENDPOINT_CONFIG(KEYBOARD_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
231                                      KEYBOARD_EPSIZE, ENDPOINT_BANK_SINGLE);
232
233 #ifdef MOUSE_ENABLE
234     /* Setup Mouse HID Report Endpoint */
235     ConfigSuccess &= ENDPOINT_CONFIG(MOUSE_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
236                                      MOUSE_EPSIZE, ENDPOINT_BANK_SINGLE);
237 #endif
238
239 #ifdef EXTRAKEY_ENABLE
240     /* Setup Extra HID Report Endpoint */
241     ConfigSuccess &= ENDPOINT_CONFIG(EXTRAKEY_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
242                                      EXTRAKEY_EPSIZE, ENDPOINT_BANK_SINGLE);
243 #endif
244
245 #ifdef CONSOLE_ENABLE
246     /* Setup Console HID Report Endpoints */
247     ConfigSuccess &= ENDPOINT_CONFIG(CONSOLE_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
248                                      CONSOLE_EPSIZE, ENDPOINT_BANK_SINGLE);
249 #if 0
250     ConfigSuccess &= ENDPOINT_CONFIG(CONSOLE_OUT_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_OUT,
251                                      CONSOLE_EPSIZE, ENDPOINT_BANK_SINGLE);
252 #endif
253 #endif
254
255 #ifdef NKRO_ENABLE
256     /* Setup NKRO HID Report Endpoints */
257     ConfigSuccess &= ENDPOINT_CONFIG(NKRO_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
258                                      NKRO_EPSIZE, ENDPOINT_BANK_SINGLE);
259 #endif
260 }
261
262 /*
263 Appendix G: HID Request Support Requirements
264
265 The following table enumerates the requests that need to be supported by various types of HID class devices.
266
267 Device type     GetReport   SetReport   GetIdle     SetIdle     GetProtocol SetProtocol
268 ------------------------------------------------------------------------------------------
269 Boot Mouse      Required    Optional    Optional    Optional    Required    Required
270 Non-Boot Mouse  Required    Optional    Optional    Optional    Optional    Optional
271 Boot Keyboard   Required    Optional    Required    Required    Required    Required
272 Non-Boot Keybrd Required    Optional    Required    Required    Optional    Optional
273 Other Device    Required    Optional    Optional    Optional    Optional    Optional
274 */
275 /** Event handler for the USB_ControlRequest event.
276  *  This is fired before passing along unhandled control requests to the library for processing internally.
277  */
278 void EVENT_USB_Device_ControlRequest(void)
279 {
280     uint8_t* ReportData = NULL;
281     uint8_t  ReportSize = 0;
282
283     /* Handle HID Class specific requests */
284     switch (USB_ControlRequest.bRequest)
285     {
286         case HID_REQ_GetReport:
287             if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
288             {
289                 Endpoint_ClearSETUP();
290
291                 // Interface
292                 switch (USB_ControlRequest.wIndex) {
293                 case KEYBOARD_INTERFACE:
294                     // TODO: test/check
295                     ReportData = (uint8_t*)&keyboard_report_sent;
296                     ReportSize = sizeof(keyboard_report_sent);
297                     break;
298                 }
299
300                 /* Write the report data to the control endpoint */
301                 Endpoint_Write_Control_Stream_LE(ReportData, ReportSize);
302                 Endpoint_ClearOUT();
303             }
304
305             break;
306         case HID_REQ_SetReport:
307             if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
308             {
309
310                 // Interface
311                 switch (USB_ControlRequest.wIndex) {
312                 case KEYBOARD_INTERFACE:
313 #ifdef NKRO_ENABLE
314                 case NKRO_INTERFACE:
315 #endif
316                     Endpoint_ClearSETUP();
317
318                     while (!(Endpoint_IsOUTReceived())) {
319                         if (USB_DeviceState == DEVICE_STATE_Unattached)
320                           return;
321                     }
322                     keyboard_led_stats = Endpoint_Read_8();
323
324                     Endpoint_ClearOUT();
325                     Endpoint_ClearStatusStage();
326                     break;
327                 }
328
329             }
330
331             break;
332
333         case HID_REQ_GetProtocol:
334             if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
335             {
336                 if (USB_ControlRequest.wIndex == KEYBOARD_INTERFACE) {
337                     Endpoint_ClearSETUP();
338                     while (!(Endpoint_IsINReady()));
339                     Endpoint_Write_8(keyboard_protocol);
340                     Endpoint_ClearIN();
341                     Endpoint_ClearStatusStage();
342                 }
343             }
344
345             break;
346         case HID_REQ_SetProtocol:
347             if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
348             {
349                 if (USB_ControlRequest.wIndex == KEYBOARD_INTERFACE) {
350                     Endpoint_ClearSETUP();
351                     Endpoint_ClearStatusStage();
352
353                     keyboard_protocol = (USB_ControlRequest.wValue & 0xFF);
354                     clear_keyboard();
355                 }
356             }
357
358             break;
359         case HID_REQ_SetIdle:
360             if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
361             {
362                 Endpoint_ClearSETUP();
363                 Endpoint_ClearStatusStage();
364
365                 keyboard_idle = ((USB_ControlRequest.wValue & 0xFF00) >> 8);
366             }
367
368             break;
369         case HID_REQ_GetIdle:
370             if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
371             {
372                 Endpoint_ClearSETUP();
373                 while (!(Endpoint_IsINReady()));
374                 Endpoint_Write_8(keyboard_idle);
375                 Endpoint_ClearIN();
376                 Endpoint_ClearStatusStage();
377             }
378
379             break;
380     }
381 }
382
383 /*******************************************************************************
384  * Host driver 
385  ******************************************************************************/
386 static uint8_t keyboard_leds(void)
387 {
388     return keyboard_led_stats;
389 }
390
391 static void send_keyboard(report_keyboard_t *report)
392 {
393     uint8_t timeout = 255;
394
395     if (USB_DeviceState != DEVICE_STATE_Configured)
396         return;
397
398     /* Select the Keyboard Report Endpoint */
399 #ifdef NKRO_ENABLE
400     if (keyboard_protocol && keyboard_nkro) {
401         /* Report protocol - NKRO */
402         Endpoint_SelectEndpoint(NKRO_IN_EPNUM);
403
404         /* Check if write ready for a polling interval around 1ms */
405         while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(4);
406         if (!Endpoint_IsReadWriteAllowed()) return;
407
408         /* Write Keyboard Report Data */
409         Endpoint_Write_Stream_LE(report, NKRO_EPSIZE, NULL);
410     }
411     else
412 #endif
413     {
414         /* Boot protocol */
415         Endpoint_SelectEndpoint(KEYBOARD_IN_EPNUM);
416
417         /* Check if write ready for a polling interval around 10ms */
418         while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40);
419         if (!Endpoint_IsReadWriteAllowed()) return;
420
421         /* Write Keyboard Report Data */
422         Endpoint_Write_Stream_LE(report, KEYBOARD_EPSIZE, NULL);
423     }
424
425     /* Finalize the stream transfer to send the last packet */
426     Endpoint_ClearIN();
427
428     keyboard_report_sent = *report;
429 }
430
431 static void send_mouse(report_mouse_t *report)
432 {
433 #ifdef MOUSE_ENABLE
434     uint8_t timeout = 255;
435
436     if (USB_DeviceState != DEVICE_STATE_Configured)
437         return;
438
439     /* Select the Mouse Report Endpoint */
440     Endpoint_SelectEndpoint(MOUSE_IN_EPNUM);
441
442     /* Check if write ready for a polling interval around 10ms */
443     while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40);
444     if (!Endpoint_IsReadWriteAllowed()) return;
445
446     /* Write Mouse Report Data */
447     Endpoint_Write_Stream_LE(report, sizeof(report_mouse_t), NULL);
448
449     /* Finalize the stream transfer to send the last packet */
450     Endpoint_ClearIN();
451 #endif
452 }
453
454 static void send_system(uint16_t data)
455 {
456     uint8_t timeout = 255;
457
458     if (USB_DeviceState != DEVICE_STATE_Configured)
459         return;
460
461     report_extra_t r = {
462         .report_id = REPORT_ID_SYSTEM,
463         .usage = data
464     };
465     Endpoint_SelectEndpoint(EXTRAKEY_IN_EPNUM);
466
467     /* Check if write ready for a polling interval around 10ms */
468     while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40);
469     if (!Endpoint_IsReadWriteAllowed()) return;
470
471     Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL);
472     Endpoint_ClearIN();
473 }
474
475 static void send_consumer(uint16_t data)
476 {
477     uint8_t timeout = 255;
478
479     if (USB_DeviceState != DEVICE_STATE_Configured)
480         return;
481
482     report_extra_t r = {
483         .report_id = REPORT_ID_CONSUMER,
484         .usage = data
485     };
486     Endpoint_SelectEndpoint(EXTRAKEY_IN_EPNUM);
487
488     /* Check if write ready for a polling interval around 10ms */
489     while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40);
490     if (!Endpoint_IsReadWriteAllowed()) return;
491
492     Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL);
493     Endpoint_ClearIN();
494 }
495
496
497 /*******************************************************************************
498  * sendchar
499  ******************************************************************************/
500 #ifdef CONSOLE_ENABLE
501 #define SEND_TIMEOUT 5
502 int8_t sendchar(uint8_t c)
503 {
504     // Not wait once timeouted.
505     // Because sendchar() is called so many times, waiting each call causes big lag.
506     static bool timeouted = false;
507
508     // prevents Console_Task() from running during sendchar() runs.
509     // or char will be lost. These two function is mutually exclusive.
510     CONSOLE_FLUSH_SET(false);
511
512     if (USB_DeviceState != DEVICE_STATE_Configured)
513         return -1;
514
515     uint8_t ep = Endpoint_GetCurrentEndpoint();
516     Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM);
517     if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {
518         goto ERROR_EXIT;
519     }
520
521     if (timeouted && !Endpoint_IsReadWriteAllowed()) {
522         goto ERROR_EXIT;
523     }
524
525     timeouted = false;
526
527     uint8_t timeout = SEND_TIMEOUT;
528     while (!Endpoint_IsReadWriteAllowed()) {
529         if (USB_DeviceState != DEVICE_STATE_Configured) {
530             goto ERROR_EXIT;
531         }
532         if (Endpoint_IsStalled()) {
533             goto ERROR_EXIT;
534         }
535         if (!(timeout--)) {
536             timeouted = true;
537             goto ERROR_EXIT;
538         }
539         _delay_ms(1);
540     }
541
542     Endpoint_Write_8(c);
543
544     // send when bank is full
545     if (!Endpoint_IsReadWriteAllowed()) {
546         while (!(Endpoint_IsINReady()));
547         Endpoint_ClearIN();
548     } else {
549         CONSOLE_FLUSH_SET(true);
550     }
551
552     Endpoint_SelectEndpoint(ep);
553     return 0;
554 ERROR_EXIT:
555     Endpoint_SelectEndpoint(ep);
556     return -1;
557 }
558 #else
559 int8_t sendchar(uint8_t c)
560 {
561     return 0;
562 }
563 #endif
564
565
566 /*******************************************************************************
567  * main
568  ******************************************************************************/
569 static void setup_mcu(void)
570 {
571     /* Disable watchdog if enabled by bootloader/fuses */
572     MCUSR &= ~(1 << WDRF);
573     wdt_disable();
574
575     /* Disable clock division */
576     clock_prescale_set(clock_div_1);
577 }
578
579 static void setup_usb(void)
580 {
581     // Leonardo needs. Without this USB device is not recognized.
582     USB_Disable();
583
584     USB_Init();
585
586     // for Console_Task
587     USB_Device_EnableSOFEvents();
588     print_set_sendchar(sendchar);
589 }
590
591 int main(void)  __attribute__ ((weak));
592 int main(void)
593 {
594     setup_mcu();
595     keyboard_setup();
596     setup_usb();
597     sei();
598
599     /* wait for USB startup & debug output */
600     while (USB_DeviceState != DEVICE_STATE_Configured) {
601 #if defined(INTERRUPT_CONTROL_ENDPOINT)
602         ;
603 #else
604         USB_USBTask();
605 #endif
606     }
607     print("USB configured.\n");
608
609     /* init modules */
610     keyboard_init();
611     host_set_driver(&lufa_driver);
612 #ifdef SLEEP_LED_ENABLE
613     sleep_led_init();
614 #endif
615
616     print("Keyboard start.\n");
617     while (1) {
618         while (USB_DeviceState == DEVICE_STATE_Suspended) {
619             print("[s]");
620             suspend_power_down();
621             if (USB_Device_RemoteWakeupEnabled && suspend_wakeup_condition()) {
622                     USB_Device_SendRemoteWakeup();
623             }
624         }
625
626         keyboard_task();
627
628 #if !defined(INTERRUPT_CONTROL_ENDPOINT)
629         USB_USBTask();
630 #endif
631     }
632 }