]> git.donarmstrong.com Git - tmk_firmware.git/blob - protocol/lufa/lufa.c
Add power down mode sleep and watchdog interrupt
[tmk_firmware.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 }