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