]> git.gir.st - tmk_keyboard.git/blob - protocol/lufa/lufa.c
Add breathing sleep LED during USB suspend
[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 <avr/sleep.h>
40 #include <avr/wdt.h>
41 #include "report.h"
42 #include "host.h"
43 #include "host_driver.h"
44 #include "keyboard.h"
45 #include "action.h"
46 #include "matrix.h"
47 #include "led.h"
48 #include "sendchar.h"
49 #include "debug.h"
50 #ifdef SLEEP_LED_ENABLE
51 #include "sleep_led.h"
52 #endif
53
54 #include "descriptor.h"
55 #include "lufa.h"
56
57 static uint8_t idle_duration = 0;
58 static uint8_t protocol_report = 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 led_set(0x1f); // all on
154 }
155
156 void EVENT_USB_Device_Disconnect(void)
157 {
158 }
159
160 void EVENT_USB_Device_Reset(void)
161 {
162 }
163
164 void EVENT_USB_Device_Suspend()
165 {
166 #ifdef SLEEP_LED_ENABLE
167 sleep_led_enable();
168 #endif
169 }
170
171 void EVENT_USB_Device_WakeUp()
172 {
173 // initialize
174 matrix_init();
175 clear_keyboard();
176
177 #ifdef SLEEP_LED_ENABLE
178 sleep_led_disable();
179 #endif
180 led_set(host_keyboard_leds());
181 }
182
183 void EVENT_USB_Device_StartOfFrame(void)
184 {
185 Console_Task();
186 }
187
188 /** Event handler for the USB_ConfigurationChanged event.
189 * This is fired when the host sets the current configuration of the USB device after enumeration.
190 */
191 #if LUFA_VERSION_INTEGER < 0x120730
192 /* old API 120219 */
193 #define ENDPOINT_CONFIG(epnum, eptype, epdir, epsize, epbank) Endpoint_ConfigureEndpoint(epnum, eptype, epdir, epsize, epbank)
194 #else
195 /* new API >= 120730 */
196 #define ENDPOINT_BANK_SINGLE 1
197 #define ENDPOINT_BANK_DOUBLE 2
198 #define ENDPOINT_CONFIG(epnum, eptype, epdir, epsize, epbank) Endpoint_ConfigureEndpoint((epdir) | (epnum) , eptype, epsize, epbank)
199 #endif
200 void EVENT_USB_Device_ConfigurationChanged(void)
201 {
202 bool ConfigSuccess = true;
203
204 /* Setup Keyboard HID Report Endpoints */
205 ConfigSuccess &= ENDPOINT_CONFIG(KEYBOARD_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
206 KEYBOARD_EPSIZE, ENDPOINT_BANK_SINGLE);
207
208 #ifdef MOUSE_ENABLE
209 /* Setup Mouse HID Report Endpoint */
210 ConfigSuccess &= ENDPOINT_CONFIG(MOUSE_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
211 MOUSE_EPSIZE, ENDPOINT_BANK_SINGLE);
212 #endif
213
214 #ifdef EXTRAKEY_ENABLE
215 /* Setup Extra HID Report Endpoint */
216 ConfigSuccess &= ENDPOINT_CONFIG(EXTRAKEY_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
217 EXTRAKEY_EPSIZE, ENDPOINT_BANK_SINGLE);
218 #endif
219
220 #ifdef CONSOLE_ENABLE
221 /* Setup Console HID Report Endpoints */
222 ConfigSuccess &= ENDPOINT_CONFIG(CONSOLE_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
223 CONSOLE_EPSIZE, ENDPOINT_BANK_DOUBLE);
224 ConfigSuccess &= ENDPOINT_CONFIG(CONSOLE_OUT_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_OUT,
225 CONSOLE_EPSIZE, ENDPOINT_BANK_SINGLE);
226 #endif
227 }
228
229 /*
230 Appendix G: HID Request Support Requirements
231
232 The following table enumerates the requests that need to be supported by various types of HID class devices.
233
234 Device type GetReport SetReport GetIdle SetIdle GetProtocol SetProtocol
235 ------------------------------------------------------------------------------------------
236 Boot Mouse Required Optional Optional Optional Required Required
237 Non-Boot Mouse Required Optional Optional Optional Optional Optional
238 Boot Keyboard Required Optional Required Required Required Required
239 Non-Boot Keybrd Required Optional Required Required Optional Optional
240 Other Device Required Optional Optional Optional Optional Optional
241 */
242 /** Event handler for the USB_ControlRequest event.
243 * This is fired before passing along unhandled control requests to the library for processing internally.
244 */
245 void EVENT_USB_Device_ControlRequest(void)
246 {
247 uint8_t* ReportData = NULL;
248 uint8_t ReportSize = 0;
249
250 /* Handle HID Class specific requests */
251 switch (USB_ControlRequest.bRequest)
252 {
253 case HID_REQ_GetReport:
254 if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
255 {
256 Endpoint_ClearSETUP();
257
258 // Interface
259 switch (USB_ControlRequest.wIndex) {
260 case KEYBOARD_INTERFACE:
261 // TODO: test/check
262 ReportData = (uint8_t*)&keyboard_report_sent;
263 ReportSize = sizeof(keyboard_report_sent);
264 break;
265 }
266
267 /* Write the report data to the control endpoint */
268 Endpoint_Write_Control_Stream_LE(ReportData, ReportSize);
269 Endpoint_ClearOUT();
270 }
271
272 break;
273 case HID_REQ_SetReport:
274 if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
275 {
276
277 // Interface
278 switch (USB_ControlRequest.wIndex) {
279 case KEYBOARD_INTERFACE:
280 Endpoint_ClearSETUP();
281
282 while (!(Endpoint_IsOUTReceived())) {
283 if (USB_DeviceState == DEVICE_STATE_Unattached)
284 return;
285 }
286 keyboard_led_stats = Endpoint_Read_8();
287
288 Endpoint_ClearOUT();
289 Endpoint_ClearStatusStage();
290 break;
291 }
292
293 }
294
295 break;
296
297 case HID_REQ_GetProtocol:
298 if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
299 {
300 Endpoint_ClearSETUP();
301 while (!(Endpoint_IsINReady()));
302 Endpoint_Write_8(protocol_report);
303 Endpoint_ClearIN();
304 Endpoint_ClearStatusStage();
305 }
306
307 break;
308 case HID_REQ_SetProtocol:
309 if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
310 {
311 Endpoint_ClearSETUP();
312 Endpoint_ClearStatusStage();
313
314 protocol_report = ((USB_ControlRequest.wValue & 0xFF) != 0x00);
315 }
316
317 break;
318 case HID_REQ_SetIdle:
319 if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
320 {
321 Endpoint_ClearSETUP();
322 Endpoint_ClearStatusStage();
323
324 idle_duration = ((USB_ControlRequest.wValue & 0xFF00) >> 8);
325 }
326
327 break;
328 case HID_REQ_GetIdle:
329 if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
330 {
331 Endpoint_ClearSETUP();
332 while (!(Endpoint_IsINReady()));
333 Endpoint_Write_8(idle_duration);
334 Endpoint_ClearIN();
335 Endpoint_ClearStatusStage();
336 }
337
338 break;
339 }
340 }
341
342 /*******************************************************************************
343 * Host driver
344 ******************************************************************************/
345 static uint8_t keyboard_leds(void)
346 {
347 return keyboard_led_stats;
348 }
349
350 static void send_keyboard(report_keyboard_t *report)
351 {
352 uint8_t timeout = 0;
353
354 // TODO: handle NKRO report
355 /* Select the Keyboard Report Endpoint */
356 Endpoint_SelectEndpoint(KEYBOARD_IN_EPNUM);
357
358 /* Check if Keyboard Endpoint Ready for Read/Write */
359 while (--timeout && !Endpoint_IsReadWriteAllowed()) ;
360
361 /* Write Keyboard Report Data */
362 Endpoint_Write_Stream_LE(report, sizeof(report_keyboard_t), NULL);
363
364 /* Finalize the stream transfer to send the last packet */
365 Endpoint_ClearIN();
366
367 keyboard_report_sent = *report;
368 }
369
370 static void send_mouse(report_mouse_t *report)
371 {
372 #ifdef MOUSE_ENABLE
373 uint8_t timeout = 0;
374
375 /* Select the Mouse Report Endpoint */
376 Endpoint_SelectEndpoint(MOUSE_IN_EPNUM);
377
378 /* Check if Mouse Endpoint Ready for Read/Write */
379 while (--timeout && !Endpoint_IsReadWriteAllowed()) ;
380
381 /* Write Mouse Report Data */
382 Endpoint_Write_Stream_LE(report, sizeof(report_mouse_t), NULL);
383
384 /* Finalize the stream transfer to send the last packet */
385 Endpoint_ClearIN();
386 #endif
387 }
388
389 static void send_system(uint16_t data)
390 {
391 uint8_t timeout = 0;
392
393 report_extra_t r = {
394 .report_id = REPORT_ID_SYSTEM,
395 .usage = data
396 };
397 Endpoint_SelectEndpoint(EXTRAKEY_IN_EPNUM);
398 while (--timeout && !Endpoint_IsReadWriteAllowed()) ;
399 Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL);
400 Endpoint_ClearIN();
401 }
402
403 static void send_consumer(uint16_t data)
404 {
405 uint8_t timeout = 0;
406
407 report_extra_t r = {
408 .report_id = REPORT_ID_CONSUMER,
409 .usage = data
410 };
411 Endpoint_SelectEndpoint(EXTRAKEY_IN_EPNUM);
412 while (--timeout && !Endpoint_IsReadWriteAllowed()) ;
413 Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL);
414 Endpoint_ClearIN();
415 }
416
417
418 /*******************************************************************************
419 * sendchar
420 ******************************************************************************/
421 #ifdef CONSOLE_ENABLE
422 #define SEND_TIMEOUT 5
423 int8_t sendchar(uint8_t c)
424 {
425 // Not wait once timeouted.
426 // Because sendchar() is called so many times, waiting each call causes big lag.
427 static bool timeouted = false;
428
429 if (USB_DeviceState != DEVICE_STATE_Configured)
430 return -1;
431
432 uint8_t ep = Endpoint_GetCurrentEndpoint();
433 Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM);
434 if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {
435 Endpoint_SelectEndpoint(ep);
436 return -1;
437 }
438
439 if (timeouted && !Endpoint_IsReadWriteAllowed()) {
440 Endpoint_SelectEndpoint(ep);
441 return - 1;
442 }
443
444 timeouted = false;
445
446 uint8_t timeout = SEND_TIMEOUT;
447 uint16_t prevFN = USB_Device_GetFrameNumber();
448 while (!Endpoint_IsReadWriteAllowed()) {
449 switch (USB_DeviceState) {
450 case DEVICE_STATE_Unattached:
451 case DEVICE_STATE_Suspended:
452 return -1;
453 }
454 if (Endpoint_IsStalled()) {
455 Endpoint_SelectEndpoint(ep);
456 return -1;
457 }
458 if (prevFN != USB_Device_GetFrameNumber()) {
459 if (!(timeout--)) {
460 timeouted = true;
461 Endpoint_SelectEndpoint(ep);
462 return -1;
463 }
464 prevFN = USB_Device_GetFrameNumber();
465 }
466 }
467
468 Endpoint_Write_8(c);
469
470 // send when bank is full
471 if (!Endpoint_IsReadWriteAllowed())
472 Endpoint_ClearIN();
473
474 Endpoint_SelectEndpoint(ep);
475 return 0;
476 }
477 #else
478 int8_t sendchar(uint8_t c)
479 {
480 return 0;
481 }
482 #endif
483
484
485 /*******************************************************************************
486 * main
487 ******************************************************************************/
488 static void SetupHardware(void)
489 {
490 /* Disable watchdog if enabled by bootloader/fuses */
491 MCUSR &= ~(1 << WDRF);
492 wdt_disable();
493
494 /* Disable clock division */
495 clock_prescale_set(clock_div_1);
496
497 // Leonardo needs. Without this USB device is not recognized.
498 USB_Disable();
499
500 USB_Init();
501
502 // for Console_Task
503 USB_Device_EnableSOFEvents();
504 }
505
506
507 static bool wakeup_condition(void)
508 {
509 matrix_scan();
510 for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
511 if (matrix_get_row(r)) return true;
512 }
513 return false;
514 }
515
516 #define wdt_intr_enable(value) \
517 __asm__ __volatile__ ( \
518 "in __tmp_reg__,__SREG__" "\n\t" \
519 "cli" "\n\t" \
520 "wdr" "\n\t" \
521 "sts %0,%1" "\n\t" \
522 "out __SREG__,__tmp_reg__" "\n\t" \
523 "sts %0,%2" "\n\t" \
524 : /* no outputs */ \
525 : "M" (_SFR_MEM_ADDR(_WD_CONTROL_REG)), \
526 "r" (_BV(_WD_CHANGE_BIT) | _BV(WDE)), \
527 "r" ((uint8_t) ((value & 0x08 ? _WD_PS3_MASK : 0x00) | \
528 _BV(WDIE) | (value & 0x07)) ) \
529 : "r0" \
530 )
531
532 int main(void) __attribute__ ((weak));
533 int main(void)
534 {
535 SetupHardware();
536 keyboard_init();
537 host_set_driver(&lufa_driver);
538 #ifdef SLEEP_LED_ENABLE
539 sleep_led_init();
540 #endif
541 sei();
542
543 while (1) {
544 // while suspend
545 while (USB_DeviceState == DEVICE_STATE_Suspended) {
546 #ifndef NO_SUSPEND_POWER_DOWN
547 // Enable watchdog to wake from MCU sleep
548 cli();
549 wdt_reset();
550
551 // Watchdog Interrupt and System Reset Mode
552 //wdt_enable(WDTO_1S);
553 //WDTCSR |= _BV(WDIE);
554
555 // Watchdog Interrupt Mode
556 wdt_intr_enable(WDTO_120MS);
557
558 // TODO: more power saving
559 // See PicoPower application note
560 // - I/O port input with pullup
561 // - prescale clock
562 // - BOD disable
563 // - Power Reduction Register PRR
564 // sleep in power down mode
565 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
566 sleep_enable();
567 sei();
568 sleep_cpu();
569 sleep_disable();
570
571 // Disable watchdog after sleep
572 wdt_disable();
573 #endif
574
575 // Send request of USB Wakeup from Suspend to host
576 if (USB_Device_RemoteWakeupEnabled) {
577 if (wakeup_condition()) {
578 USB_Device_SendRemoteWakeup();
579 }
580 }
581 }
582
583 keyboard_task();
584
585 #if !defined(INTERRUPT_CONTROL_ENDPOINT)
586 USB_USBTask();
587 #endif
588 }
589 }
590
591 #ifndef NO_SUSPEND_POWER_DOWN
592 /* watchdog timeout */
593 ISR(WDT_vect)
594 {
595 /* wakeup from MCU sleep mode */
596 /*
597 // blink LED
598 static uint8_t led_state = 0;
599 static uint8_t led_count = 0;
600 led_count++;
601 if ((led_count & 0x07) == 0) {
602 led_set((led_state ^= (1<<USB_LED_CAPS_LOCK)));
603 }
604 */
605 }
606 #endif
Imprint / Impressum