#include "host.h"
#include "host_driver.h"
#include "keyboard.h"
+#include "action.h"
+#include "led.h"
#include "sendchar.h"
#include "debug.h"
+#ifdef SLEEP_LED_ENABLE
+#include "sleep_led.h"
+#endif
+#include "suspend.h"
#include "descriptor.h"
#include "lufa.h"
+static uint8_t idle_duration = 0;
+static uint8_t protocol_report = 1;
static uint8_t keyboard_led_stats = 0;
-// TODO: impl Control Request GET_REPORT
static report_keyboard_t keyboard_report_sent;
-#ifdef MOUSE_ENABLE
-static report_mouse_t mouse_report_sent;
-#endif
+
/* Host driver */
static uint8_t keyboard_leds(void);
static void send_mouse(report_mouse_t *report);
static void send_system(uint16_t data);
static void send_consumer(uint16_t data);
-static host_driver_t lufa_driver = {
+host_driver_t lufa_driver = {
keyboard_leds,
send_keyboard,
send_mouse,
};
-static void SetupHardware(void);
-static void Console_HID_Task(void);
-
-int main(void)
+/*******************************************************************************
+ * Console
+ ******************************************************************************/
+#ifdef CONSOLE_ENABLE
+static void Console_Task(void)
{
- SetupHardware();
- sei();
+ /* Device must be connected and configured for the task to run */
+ if (USB_DeviceState != DEVICE_STATE_Configured)
+ return;
- print_enable = true;
- debug_enable = true;
- debug_matrix = true;
- debug_keyboard = true;
- debug_mouse = true;
-
-/* TODO: can't print here
- _delay_ms(5000);
- USB_USBTask();
- print("abcdefg\n");
- USB_USBTask();
-*/
+ uint8_t ep = Endpoint_GetCurrentEndpoint();
- keyboard_init();
- host_set_driver(&lufa_driver);
- while (1) {
- keyboard_proc();
+#if 0
+ // TODO: impl receivechar()/recvchar()
+ Endpoint_SelectEndpoint(CONSOLE_OUT_EPNUM);
- Console_HID_Task();
- USB_USBTask();
+ /* Check to see if a packet has been sent from the host */
+ if (Endpoint_IsOUTReceived())
+ {
+ /* Check to see if the packet contains data */
+ if (Endpoint_IsReadWriteAllowed())
+ {
+ /* Create a temporary buffer to hold the read in report from the host */
+ uint8_t ConsoleData[CONSOLE_EPSIZE];
+
+ /* Read Console Report Data */
+ Endpoint_Read_Stream_LE(&ConsoleData, sizeof(ConsoleData), NULL);
+
+ /* Process Console Report Data */
+ //ProcessConsoleHIDReport(ConsoleData);
+ }
+
+ /* Finalize the stream transfer to send the last packet */
+ Endpoint_ClearOUT();
}
-}
+#endif
-void SetupHardware(void)
-{
- /* Disable watchdog if enabled by bootloader/fuses */
- MCUSR &= ~(1 << WDRF);
- wdt_disable();
+ /* IN packet */
+ Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM);
+ if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {
+ Endpoint_SelectEndpoint(ep);
+ return;
+ }
- /* Disable clock division */
- clock_prescale_set(clock_div_1);
+ // fill empty bank
+ while (Endpoint_IsReadWriteAllowed())
+ Endpoint_Write_8(0);
- USB_Init();
-}
+ // flash senchar packet
+ if (Endpoint_IsINReady()) {
+ Endpoint_ClearIN();
+ }
-static void Console_HID_Task(void)
+ Endpoint_SelectEndpoint(ep);
+}
+#else
+static void Console_Task(void)
{
- /* Device must be connected and configured for the task to run */
- if (USB_DeviceState != DEVICE_STATE_Configured)
- return;
-
- // TODO: impl receivechar()/recvchar()
- Endpoint_SelectEndpoint(CONSOLE_OUT_EPNUM);
-
- /* Check to see if a packet has been sent from the host */
- if (Endpoint_IsOUTReceived())
- {
- /* Check to see if the packet contains data */
- if (Endpoint_IsReadWriteAllowed())
- {
- /* Create a temporary buffer to hold the read in report from the host */
- uint8_t ConsoleData[CONSOLE_EPSIZE];
-
- /* Read Console Report Data */
- Endpoint_Read_Stream_LE(&ConsoleData, sizeof(ConsoleData), NULL);
-
- /* Process Console Report Data */
- //ProcessConsoleHIDReport(ConsoleData);
- }
-
- /* Finalize the stream transfer to send the last packet */
- Endpoint_ClearOUT();
- }
-
- /* IN packet */
- Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM);
- // send IN packet
- if (Endpoint_IsINReady())
- Endpoint_ClearIN();
}
+#endif
/*******************************************************************************
* USB Events
******************************************************************************/
-/** Event handler for the USB_Connect event. */
+/*
+ * Event Order of Plug in:
+ * 0) EVENT_USB_Device_Connect
+ * 1) EVENT_USB_Device_Suspend
+ * 2) EVENT_USB_Device_Reset
+ * 3) EVENT_USB_Device_Wake
+*/
void EVENT_USB_Device_Connect(void)
{
+ led_set(0x1f); // all on
}
-/** Event handler for the USB_Disconnect event. */
void EVENT_USB_Device_Disconnect(void)
{
}
+void EVENT_USB_Device_Reset(void)
+{
+}
+
+void EVENT_USB_Device_Suspend()
+{
+#ifdef SLEEP_LED_ENABLE
+ sleep_led_enable();
+#endif
+}
+
+void EVENT_USB_Device_WakeUp()
+{
+ suspend_wakeup_init();
+
+#ifdef SLEEP_LED_ENABLE
+ sleep_led_disable();
+#endif
+ led_set(host_keyboard_leds());
+}
+
+void EVENT_USB_Device_StartOfFrame(void)
+{
+ Console_Task();
+}
+
/** Event handler for the USB_ConfigurationChanged event.
* This is fired when the host sets the current configuration of the USB device after enumeration.
*/
+#if LUFA_VERSION_INTEGER < 0x120730
+ /* old API 120219 */
+ #define ENDPOINT_CONFIG(epnum, eptype, epdir, epsize, epbank) Endpoint_ConfigureEndpoint(epnum, eptype, epdir, epsize, epbank)
+#else
+ /* new API >= 120730 */
+ #define ENDPOINT_BANK_SINGLE 1
+ #define ENDPOINT_BANK_DOUBLE 2
+ #define ENDPOINT_CONFIG(epnum, eptype, epdir, epsize, epbank) Endpoint_ConfigureEndpoint((epdir) | (epnum) , eptype, epsize, epbank)
+#endif
void EVENT_USB_Device_ConfigurationChanged(void)
{
bool ConfigSuccess = true;
/* Setup Keyboard HID Report Endpoints */
- ConfigSuccess &= Endpoint_ConfigureEndpoint(KEYBOARD_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
- KEYBOARD_EPSIZE, ENDPOINT_BANK_SINGLE);
+ ConfigSuccess &= ENDPOINT_CONFIG(KEYBOARD_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
+ KEYBOARD_EPSIZE, ENDPOINT_BANK_SINGLE);
#ifdef MOUSE_ENABLE
/* Setup Mouse HID Report Endpoint */
- ConfigSuccess &= Endpoint_ConfigureEndpoint(MOUSE_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
- MOUSE_EPSIZE, ENDPOINT_BANK_SINGLE);
+ ConfigSuccess &= ENDPOINT_CONFIG(MOUSE_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
+ MOUSE_EPSIZE, ENDPOINT_BANK_SINGLE);
#endif
#ifdef EXTRAKEY_ENABLE
/* Setup Extra HID Report Endpoint */
- ConfigSuccess &= Endpoint_ConfigureEndpoint(EXTRA_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
- EXTRA_EPSIZE, ENDPOINT_BANK_SINGLE);
+ ConfigSuccess &= ENDPOINT_CONFIG(EXTRAKEY_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
+ EXTRAKEY_EPSIZE, ENDPOINT_BANK_SINGLE);
#endif
+#ifdef CONSOLE_ENABLE
/* Setup Console HID Report Endpoints */
- ConfigSuccess &= Endpoint_ConfigureEndpoint(CONSOLE_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
- CONSOLE_EPSIZE, ENDPOINT_BANK_SINGLE);
- ConfigSuccess &= Endpoint_ConfigureEndpoint(CONSOLE_OUT_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_OUT,
- CONSOLE_EPSIZE, ENDPOINT_BANK_SINGLE);
+ ConfigSuccess &= ENDPOINT_CONFIG(CONSOLE_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
+ CONSOLE_EPSIZE, ENDPOINT_BANK_DOUBLE);
+ ConfigSuccess &= ENDPOINT_CONFIG(CONSOLE_OUT_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_OUT,
+ CONSOLE_EPSIZE, ENDPOINT_BANK_SINGLE);
+#endif
+
+#ifdef NKRO_ENABLE
+ /* Setup NKRO HID Report Endpoints */
+ ConfigSuccess &= ENDPOINT_CONFIG(NKRO_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
+ NKRO_EPSIZE, ENDPOINT_BANK_SINGLE);
+#endif
}
/*
ReportData = (uint8_t*)&keyboard_report_sent;
ReportSize = sizeof(keyboard_report_sent);
break;
-#ifdef MOUSE_ENABLE
- case MOUSE_INTERFACE:
- // TODO: test/check
- ReportData = (uint8_t*)&mouse_report_sent;
- ReportSize = sizeof(mouse_report_sent);
- break;
-#endif
-#ifdef EXTRAKEY_ENABLE
- case EXTRA_INTERFACE:
- break;
-#endif
- case CONSOLE_INTERFACE:
- break;
}
/* Write the report data to the control endpoint */
case HID_REQ_SetReport:
if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
{
- Endpoint_ClearSETUP();
-
- /* Wait until the LED report has been sent by the host */
- while (!(Endpoint_IsOUTReceived()))
- {
- if (USB_DeviceState == DEVICE_STATE_Unattached)
- return;
- }
// Interface
switch (USB_ControlRequest.wIndex) {
case KEYBOARD_INTERFACE:
- // TODO: test/check
- /* Read in the LED report from the host */
+ Endpoint_ClearSETUP();
+
+ while (!(Endpoint_IsOUTReceived())) {
+ if (USB_DeviceState == DEVICE_STATE_Unattached)
+ return;
+ }
keyboard_led_stats = Endpoint_Read_8();
- break;
-#ifdef MOUSE_ENABLE
- case MOUSE_INTERFACE:
- break;
-#endif
-#ifdef EXTRAKEY_ENABLE
- case EXTRA_INTERFACE:
- break;
-#endif
- case CONSOLE_INTERFACE:
+
+ Endpoint_ClearOUT();
+ Endpoint_ClearStatusStage();
break;
}
- Endpoint_ClearOUT();
+ }
+
+ break;
+
+ case HID_REQ_GetProtocol:
+ if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
+ {
+ Endpoint_ClearSETUP();
+ while (!(Endpoint_IsINReady()));
+ Endpoint_Write_8(protocol_report);
+ Endpoint_ClearIN();
+ Endpoint_ClearStatusStage();
+ }
+
+ break;
+ case HID_REQ_SetProtocol:
+ if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
+ {
+ Endpoint_ClearSETUP();
+ Endpoint_ClearStatusStage();
+
+ protocol_report = ((USB_ControlRequest.wValue & 0xFF) != 0x00);
+ }
+
+ break;
+ case HID_REQ_SetIdle:
+ if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
+ {
+ Endpoint_ClearSETUP();
+ Endpoint_ClearStatusStage();
+
+ idle_duration = ((USB_ControlRequest.wValue & 0xFF00) >> 8);
+ }
+
+ break;
+ case HID_REQ_GetIdle:
+ if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
+ {
+ Endpoint_ClearSETUP();
+ while (!(Endpoint_IsINReady()));
+ Endpoint_Write_8(idle_duration);
+ Endpoint_ClearIN();
Endpoint_ClearStatusStage();
}
static void send_keyboard(report_keyboard_t *report)
{
- // TODO: handle NKRO report
+ uint8_t timeout = 0;
+
+ if (USB_DeviceState != DEVICE_STATE_Configured)
+ return;
+
/* Select the Keyboard Report Endpoint */
- Endpoint_SelectEndpoint(KEYBOARD_IN_EPNUM);
+#ifdef NKRO_ENABLE
+ if (keyboard_nkro) {
+ Endpoint_SelectEndpoint(NKRO_IN_EPNUM);
+ }
+ else
+#endif
+ {
+ Endpoint_SelectEndpoint(KEYBOARD_IN_EPNUM);
+ }
/* Check if Keyboard Endpoint Ready for Read/Write */
- if (Endpoint_IsReadWriteAllowed())
- {
- /* Write Keyboard Report Data */
- Endpoint_Write_Stream_LE(report, sizeof(report_keyboard_t), NULL);
+ while (--timeout && !Endpoint_IsReadWriteAllowed()) ;
- /* Finalize the stream transfer to send the last packet */
- Endpoint_ClearIN();
+ /* Write Keyboard Report Data */
+#ifdef NKRO_ENABLE
+ if (keyboard_nkro) {
+ Endpoint_Write_Stream_LE(report, NKRO_EPSIZE, NULL);
+ }
+ else
+#endif
+ {
+ /* boot mode */
+ Endpoint_Write_Stream_LE(report, KEYBOARD_EPSIZE, NULL);
}
+
+ /* Finalize the stream transfer to send the last packet */
+ Endpoint_ClearIN();
+
keyboard_report_sent = *report;
}
static void send_mouse(report_mouse_t *report)
{
#ifdef MOUSE_ENABLE
+ uint8_t timeout = 0;
+
+ if (USB_DeviceState != DEVICE_STATE_Configured)
+ return;
+
/* Select the Mouse Report Endpoint */
Endpoint_SelectEndpoint(MOUSE_IN_EPNUM);
/* Check if Mouse Endpoint Ready for Read/Write */
- if (Endpoint_IsReadWriteAllowed())
- {
- /* Write Mouse Report Data */
- Endpoint_Write_Stream_LE(report, sizeof(report_mouse_t), NULL);
+ while (--timeout && !Endpoint_IsReadWriteAllowed()) ;
- /* Finalize the stream transfer to send the last packet */
- Endpoint_ClearIN();
- }
- mouse_report_sent = *report;
+ /* Write Mouse Report Data */
+ Endpoint_Write_Stream_LE(report, sizeof(report_mouse_t), NULL);
+
+ /* Finalize the stream transfer to send the last packet */
+ Endpoint_ClearIN();
#endif
}
-typedef struct {
- uint8_t report_id;
- uint16_t usage;
-} __attribute__ ((packed)) report_extra_t;
-
static void send_system(uint16_t data)
{
- Endpoint_SelectEndpoint(EXTRA_IN_EPNUM);
- if (Endpoint_IsReadWriteAllowed()) {
- report_extra_t r = {
- .report_id = REPORT_ID_SYSTEM,
- .usage = data
- };
- Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL);
- Endpoint_ClearIN();
- }
+ uint8_t timeout = 0;
+
+ if (USB_DeviceState != DEVICE_STATE_Configured)
+ return;
+
+ report_extra_t r = {
+ .report_id = REPORT_ID_SYSTEM,
+ .usage = data
+ };
+ Endpoint_SelectEndpoint(EXTRAKEY_IN_EPNUM);
+ while (--timeout && !Endpoint_IsReadWriteAllowed()) ;
+ Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL);
+ Endpoint_ClearIN();
}
static void send_consumer(uint16_t data)
{
- Endpoint_SelectEndpoint(EXTRA_IN_EPNUM);
- if (Endpoint_IsReadWriteAllowed()) {
- report_extra_t r = {
- .report_id = REPORT_ID_CONSUMER,
- .usage = data
- };
- Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL);
- Endpoint_ClearIN();
- }
+ uint8_t timeout = 0;
+
+ if (USB_DeviceState != DEVICE_STATE_Configured)
+ return;
+
+ report_extra_t r = {
+ .report_id = REPORT_ID_CONSUMER,
+ .usage = data
+ };
+ Endpoint_SelectEndpoint(EXTRAKEY_IN_EPNUM);
+ while (--timeout && !Endpoint_IsReadWriteAllowed()) ;
+ Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL);
+ Endpoint_ClearIN();
}
/*******************************************************************************
* sendchar
******************************************************************************/
+#ifdef CONSOLE_ENABLE
+#define SEND_TIMEOUT 5
int8_t sendchar(uint8_t c)
{
+ // Not wait once timeouted.
+ // Because sendchar() is called so many times, waiting each call causes big lag.
+ static bool timeouted = false;
+
if (USB_DeviceState != DEVICE_STATE_Configured)
- return -1;
+ return -1;
+ uint8_t ep = Endpoint_GetCurrentEndpoint();
Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM);
+ if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {
+ Endpoint_SelectEndpoint(ep);
+ return -1;
+ }
+
+ if (timeouted && !Endpoint_IsReadWriteAllowed()) {
+ Endpoint_SelectEndpoint(ep);
+ return - 1;
+ }
+
+ timeouted = false;
- uint8_t timeout = 10;
+ uint8_t timeout = SEND_TIMEOUT;
uint16_t prevFN = USB_Device_GetFrameNumber();
- while (!Endpoint_IsINReady()) {
+ while (!Endpoint_IsReadWriteAllowed()) {
switch (USB_DeviceState) {
case DEVICE_STATE_Unattached:
case DEVICE_STATE_Suspended:
return -1;
}
- if (Endpoint_IsStalled())
+ if (Endpoint_IsStalled()) {
+ Endpoint_SelectEndpoint(ep);
return -1;
+ }
if (prevFN != USB_Device_GetFrameNumber()) {
- if (!(timeout--))
+ if (!(timeout--)) {
+ timeouted = true;
+ Endpoint_SelectEndpoint(ep);
return -1;
+ }
prevFN = USB_Device_GetFrameNumber();
}
}
Endpoint_Write_8(c);
- // send when packet is full
+ // send when bank is full
if (!Endpoint_IsReadWriteAllowed())
Endpoint_ClearIN();
+ Endpoint_SelectEndpoint(ep);
+ return 0;
+}
+#else
+int8_t sendchar(uint8_t c)
+{
return 0;
}
+#endif
+
+
+/*******************************************************************************
+ * main
+ ******************************************************************************/
+static void SetupHardware(void)
+{
+ /* Disable watchdog if enabled by bootloader/fuses */
+ MCUSR &= ~(1 << WDRF);
+ wdt_disable();
+
+ /* Disable clock division */
+ clock_prescale_set(clock_div_1);
+
+ // Leonardo needs. Without this USB device is not recognized.
+ USB_Disable();
+
+ USB_Init();
+
+ // for Console_Task
+ USB_Device_EnableSOFEvents();
+}
+
+int main(void) __attribute__ ((weak));
+int main(void)
+{
+ SetupHardware();
+ keyboard_init();
+ host_set_driver(&lufa_driver);
+#ifdef SLEEP_LED_ENABLE
+ sleep_led_init();
+#endif
+ sei();
+
+ while (1) {
+ while (USB_DeviceState == DEVICE_STATE_Suspended) {
+ suspend_power_down();
+ if (USB_Device_RemoteWakeupEnabled && suspend_wakeup_condition()) {
+ USB_Device_SendRemoteWakeup();
+ }
+ }
+
+ keyboard_task();
+
+#if !defined(INTERRUPT_CONTROL_ENDPOINT)
+ USB_USBTask();
+#endif
+ }
+}