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
10 Copyright (C) Dean Camera, 2012.
12 dean [at] fourwalledcubicle [dot] com
17 Copyright 2012 Dean Camera (dean [at] fourwalledcubicle [dot] com)
18 Copyright 2010 Denver Gingerich (denver [at] ossguy [dot] com)
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.
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
41 #include "host_driver.h"
46 #include "descriptor.h"
49 static uint8_t idle_duration = 0;
50 static uint8_t protocol_report = 1;
51 static uint8_t keyboard_led_stats = 0;
53 static report_keyboard_t keyboard_report_sent;
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 = {
71 /*******************************************************************************
73 ******************************************************************************/
75 static void Console_Task(void)
77 /* Device must be connected and configured for the task to run */
78 if (USB_DeviceState != DEVICE_STATE_Configured)
81 uint8_t ep = Endpoint_GetCurrentEndpoint();
84 // TODO: impl receivechar()/recvchar()
85 Endpoint_SelectEndpoint(CONSOLE_OUT_EPNUM);
87 /* Check to see if a packet has been sent from the host */
88 if (Endpoint_IsOUTReceived())
90 /* Check to see if the packet contains data */
91 if (Endpoint_IsReadWriteAllowed())
93 /* Create a temporary buffer to hold the read in report from the host */
94 uint8_t ConsoleData[CONSOLE_EPSIZE];
96 /* Read Console Report Data */
97 Endpoint_Read_Stream_LE(&ConsoleData, sizeof(ConsoleData), NULL);
99 /* Process Console Report Data */
100 //ProcessConsoleHIDReport(ConsoleData);
103 /* Finalize the stream transfer to send the last packet */
109 Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM);
110 if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {
111 Endpoint_SelectEndpoint(ep);
116 while (Endpoint_IsReadWriteAllowed())
119 // flash senchar packet
120 if (Endpoint_IsINReady()) {
124 Endpoint_SelectEndpoint(ep);
127 static void Console_Task(void)
133 /*******************************************************************************
135 ******************************************************************************/
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
145 void EVENT_USB_Device_Connect(void)
149 void EVENT_USB_Device_Disconnect(void)
153 void EVENT_USB_Device_Reset(void)
157 void EVENT_USB_Device_Suspend()
162 void EVENT_USB_Device_WakeUp()
172 void EVENT_USB_Device_StartOfFrame(void)
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.
180 #if LUFA_VERSION_INTEGER < 0x120730
182 #define ENDPOINT_CONFIG(epnum, eptype, epdir, epsize, epbank) Endpoint_ConfigureEndpoint(epnum, eptype, epdir, epsize, epbank)
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)
189 void EVENT_USB_Device_ConfigurationChanged(void)
191 bool ConfigSuccess = true;
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);
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);
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);
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);
219 Appendix G: HID Request Support Requirements
221 The following table enumerates the requests that need to be supported by various types of HID class devices.
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
231 /** Event handler for the USB_ControlRequest event.
232 * This is fired before passing along unhandled control requests to the library for processing internally.
234 void EVENT_USB_Device_ControlRequest(void)
236 uint8_t* ReportData = NULL;
237 uint8_t ReportSize = 0;
239 /* Handle HID Class specific requests */
240 switch (USB_ControlRequest.bRequest)
242 case HID_REQ_GetReport:
243 if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
245 Endpoint_ClearSETUP();
248 switch (USB_ControlRequest.wIndex) {
249 case KEYBOARD_INTERFACE:
251 ReportData = (uint8_t*)&keyboard_report_sent;
252 ReportSize = sizeof(keyboard_report_sent);
256 /* Write the report data to the control endpoint */
257 Endpoint_Write_Control_Stream_LE(ReportData, ReportSize);
262 case HID_REQ_SetReport:
263 if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
267 switch (USB_ControlRequest.wIndex) {
268 case KEYBOARD_INTERFACE:
269 Endpoint_ClearSETUP();
271 while (!(Endpoint_IsOUTReceived())) {
272 if (USB_DeviceState == DEVICE_STATE_Unattached)
275 keyboard_led_stats = Endpoint_Read_8();
278 Endpoint_ClearStatusStage();
286 case HID_REQ_GetProtocol:
287 if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
289 Endpoint_ClearSETUP();
290 while (!(Endpoint_IsINReady()));
291 Endpoint_Write_8(protocol_report);
293 Endpoint_ClearStatusStage();
297 case HID_REQ_SetProtocol:
298 if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
300 Endpoint_ClearSETUP();
301 Endpoint_ClearStatusStage();
303 protocol_report = ((USB_ControlRequest.wValue & 0xFF) != 0x00);
307 case HID_REQ_SetIdle:
308 if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
310 Endpoint_ClearSETUP();
311 Endpoint_ClearStatusStage();
313 idle_duration = ((USB_ControlRequest.wValue & 0xFF00) >> 8);
317 case HID_REQ_GetIdle:
318 if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
320 Endpoint_ClearSETUP();
321 while (!(Endpoint_IsINReady()));
322 Endpoint_Write_8(idle_duration);
324 Endpoint_ClearStatusStage();
331 /*******************************************************************************
333 ******************************************************************************/
334 static uint8_t keyboard_leds(void)
336 return keyboard_led_stats;
339 static void send_keyboard(report_keyboard_t *report)
343 // TODO: handle NKRO report
344 /* Select the Keyboard Report Endpoint */
345 Endpoint_SelectEndpoint(KEYBOARD_IN_EPNUM);
347 /* Check if Keyboard Endpoint Ready for Read/Write */
348 while (--timeout && !Endpoint_IsReadWriteAllowed()) ;
350 /* Write Keyboard Report Data */
351 Endpoint_Write_Stream_LE(report, sizeof(report_keyboard_t), NULL);
353 /* Finalize the stream transfer to send the last packet */
356 keyboard_report_sent = *report;
359 static void send_mouse(report_mouse_t *report)
364 /* Select the Mouse Report Endpoint */
365 Endpoint_SelectEndpoint(MOUSE_IN_EPNUM);
367 /* Check if Mouse Endpoint Ready for Read/Write */
368 while (--timeout && !Endpoint_IsReadWriteAllowed()) ;
370 /* Write Mouse Report Data */
371 Endpoint_Write_Stream_LE(report, sizeof(report_mouse_t), NULL);
373 /* Finalize the stream transfer to send the last packet */
378 static void send_system(uint16_t data)
383 .report_id = REPORT_ID_SYSTEM,
386 Endpoint_SelectEndpoint(EXTRAKEY_IN_EPNUM);
387 while (--timeout && !Endpoint_IsReadWriteAllowed()) ;
388 Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL);
392 static void send_consumer(uint16_t data)
397 .report_id = REPORT_ID_CONSUMER,
400 Endpoint_SelectEndpoint(EXTRAKEY_IN_EPNUM);
401 while (--timeout && !Endpoint_IsReadWriteAllowed()) ;
402 Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL);
407 /*******************************************************************************
409 ******************************************************************************/
410 #ifdef CONSOLE_ENABLE
411 #define SEND_TIMEOUT 5
412 int8_t sendchar(uint8_t c)
414 // Not wait once timeouted.
415 // Because sendchar() is called so many times, waiting each call causes big lag.
416 static bool timeouted = false;
418 if (USB_DeviceState != DEVICE_STATE_Configured)
421 uint8_t ep = Endpoint_GetCurrentEndpoint();
422 Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM);
423 if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {
424 Endpoint_SelectEndpoint(ep);
428 if (timeouted && !Endpoint_IsReadWriteAllowed()) {
429 Endpoint_SelectEndpoint(ep);
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:
443 if (Endpoint_IsStalled()) {
444 Endpoint_SelectEndpoint(ep);
447 if (prevFN != USB_Device_GetFrameNumber()) {
450 Endpoint_SelectEndpoint(ep);
453 prevFN = USB_Device_GetFrameNumber();
459 // send when bank is full
460 if (!Endpoint_IsReadWriteAllowed())
463 Endpoint_SelectEndpoint(ep);
467 int8_t sendchar(uint8_t c)
474 /*******************************************************************************
476 ******************************************************************************/
477 static void SetupHardware(void)
479 /* Disable watchdog if enabled by bootloader/fuses */
480 MCUSR &= ~(1 << WDRF);
483 /* Disable clock division */
484 clock_prescale_set(clock_div_1);
486 // Leonardo needs. Without this USB device is not recognized.
492 USB_Device_EnableSOFEvents();
497 static bool wakeup_condition(void)
500 for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
501 if (matrix_get_row(r)) return true;
506 #include <avr/sleep.h>
508 #define wdt_intr_enable(value) \
509 __asm__ __volatile__ ( \
510 "in __tmp_reg__,__SREG__" "\n\t" \
514 "out __SREG__,__tmp_reg__" "\n\t" \
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)) ) \
524 int main(void) __attribute__ ((weak));
529 host_set_driver(&lufa_driver);
534 while (USB_DeviceState == DEVICE_STATE_Suspended) {
535 // Enable watchdog to wake from MCU sleep
539 // Watchdog Interrupt and System Reset Mode
540 //wdt_enable(WDTO_1S);
541 //WDTCSR |= _BV(WDIE);
543 // Watchdog Interrupt Mode
544 wdt_intr_enable(WDTO_120MS);
546 // TODO: more power saving
547 // See PicoPower application note
548 // - I/O port input with pullup
551 // - Power Reduction Register PRR
552 // sleep in power down mode
553 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
559 // Disable watchdog after sleep
562 // Send request of USB Wakeup from Suspend to host
563 if (USB_Device_RemoteWakeupEnabled) {
564 if (wakeup_condition()) {
565 USB_Device_SendRemoteWakeup();
572 #if !defined(INTERRUPT_CONTROL_ENDPOINT)
578 /* watchdog timeout during sleep */
582 static uint8_t led_state = 0;
583 static uint8_t led_count = 0;
585 if ((led_count & 0x07) == 0) {
586 led_set((led_state ^= (1<<USB_LED_CAPS_LOCK)));