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