summaryrefslogtreecommitdiff
path: root/tmk_core/protocol/lufa/lufa.c
diff options
context:
space:
mode:
Diffstat (limited to 'tmk_core/protocol/lufa/lufa.c')
-rw-r--r--tmk_core/protocol/lufa/lufa.c632
1 files changed, 632 insertions, 0 deletions
diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c
new file mode 100644
index 0000000000..345630aa90
--- /dev/null
+++ b/tmk_core/protocol/lufa/lufa.c
@@ -0,0 +1,632 @@
+/*
+ * Copyright 2012 Jun Wako <wakojun@gmail.com>
+ * This file is based on:
+ * LUFA-120219/Demos/Device/Lowlevel/KeyboardMouse
+ * LUFA-120219/Demos/Device/Lowlevel/GenericHID
+ */
+
+/*
+ LUFA Library
+ Copyright (C) Dean Camera, 2012.
+
+ dean [at] fourwalledcubicle [dot] com
+ www.lufa-lib.org
+*/
+
+/*
+ Copyright 2012 Dean Camera (dean [at] fourwalledcubicle [dot] com)
+ Copyright 2010 Denver Gingerich (denver [at] ossguy [dot] com)
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that the copyright notice and this
+ permission notice and warranty disclaimer appear in supporting
+ documentation, and that the name of the author not be used in
+ advertising or publicity pertaining to distribution of the
+ software without specific, written prior permission.
+
+ The author disclaim all warranties with regard to this
+ software, including all implied warranties of merchantability
+ and fitness. In no event shall the author be liable for any
+ special, indirect or consequential damages or any damages
+ whatsoever resulting from loss of use, data or profits, whether
+ in an action of contract, negligence or other tortious action,
+ arising out of or in connection with the use or performance of
+ this software.
+*/
+
+#include "report.h"
+#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"
+
+uint8_t keyboard_idle = 0;
+/* 0: Boot Protocol, 1: Report Protocol(default) */
+uint8_t keyboard_protocol = 1;
+static uint8_t keyboard_led_stats = 0;
+
+static report_keyboard_t keyboard_report_sent;
+
+
+/* Host driver */
+static uint8_t keyboard_leds(void);
+static void send_keyboard(report_keyboard_t *report);
+static void send_mouse(report_mouse_t *report);
+static void send_system(uint16_t data);
+static void send_consumer(uint16_t data);
+host_driver_t lufa_driver = {
+ keyboard_leds,
+ send_keyboard,
+ send_mouse,
+ send_system,
+ send_consumer
+};
+
+
+/*******************************************************************************
+ * Console
+ ******************************************************************************/
+#ifdef CONSOLE_ENABLE
+static void Console_Task(void)
+{
+ /* Device must be connected and configured for the task to run */
+ if (USB_DeviceState != DEVICE_STATE_Configured)
+ return;
+
+ uint8_t ep = Endpoint_GetCurrentEndpoint();
+
+#if 0
+ // 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();
+ }
+#endif
+
+ /* IN packet */
+ Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM);
+ if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {
+ Endpoint_SelectEndpoint(ep);
+ return;
+ }
+
+ // fill empty bank
+ while (Endpoint_IsReadWriteAllowed())
+ Endpoint_Write_8(0);
+
+ // flash senchar packet
+ if (Endpoint_IsINReady()) {
+ Endpoint_ClearIN();
+ }
+
+ Endpoint_SelectEndpoint(ep);
+}
+#else
+static void Console_Task(void)
+{
+}
+#endif
+
+
+/*******************************************************************************
+ * USB Events
+ ******************************************************************************/
+/*
+ * 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)
+{
+ print("[C]");
+ /* For battery powered device */
+ if (!USB_IsInitialized) {
+ USB_Disable();
+ USB_Init();
+ USB_Device_EnableSOFEvents();
+ }
+}
+
+void EVENT_USB_Device_Disconnect(void)
+{
+ print("[D]");
+ /* For battery powered device */
+ USB_IsInitialized = false;
+/* TODO: This doesn't work. After several plug in/outs can not be enumerated.
+ if (USB_IsInitialized) {
+ USB_Disable(); // Disable all interrupts
+ USB_Controller_Enable();
+ USB_INT_Enable(USB_INT_VBUSTI);
+ }
+*/
+}
+
+void EVENT_USB_Device_Reset(void)
+{
+ print("[R]");
+}
+
+void EVENT_USB_Device_Suspend()
+{
+ print("[S]");
+#ifdef SLEEP_LED_ENABLE
+ sleep_led_enable();
+#endif
+}
+
+void EVENT_USB_Device_WakeUp()
+{
+ print("[W]");
+ suspend_wakeup_init();
+
+#ifdef SLEEP_LED_ENABLE
+ sleep_led_disable();
+ // NOTE: converters may not accept this
+ led_set(host_keyboard_leds());
+#endif
+}
+
+#ifdef CONSOLE_ENABLE
+static bool console_flush = false;
+#define CONSOLE_FLUSH_SET(b) do { \
+ uint8_t sreg = SREG; cli(); console_flush = b; SREG = sreg; \
+} while (0)
+
+// called every 1ms
+void EVENT_USB_Device_StartOfFrame(void)
+{
+ static uint8_t count;
+ if (++count % 50) return;
+ count = 0;
+
+ if (!console_flush) return;
+ Console_Task();
+ console_flush = false;
+}
+#endif
+
+/** Event handler for the USB_ConfigurationChanged event.
+ * This is fired when the host sets the current configuration of the USB device after enumeration.
+ *
+ * ATMega32u2 supports dual bank(ping-pong mode) only on endpoint 3 and 4,
+ * it is safe to use singl bank for all endpoints.
+ */
+void EVENT_USB_Device_ConfigurationChanged(void)
+{
+ bool ConfigSuccess = true;
+
+ /* Setup Keyboard HID Report Endpoints */
+ 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_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_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_CONFIG(CONSOLE_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,
+ CONSOLE_EPSIZE, ENDPOINT_BANK_SINGLE);
+#if 0
+ ConfigSuccess &= ENDPOINT_CONFIG(CONSOLE_OUT_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_OUT,
+ CONSOLE_EPSIZE, ENDPOINT_BANK_SINGLE);
+#endif
+#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
+}
+
+/*
+Appendix G: HID Request Support Requirements
+
+The following table enumerates the requests that need to be supported by various types of HID class devices.
+
+Device type GetReport SetReport GetIdle SetIdle GetProtocol SetProtocol
+------------------------------------------------------------------------------------------
+Boot Mouse Required Optional Optional Optional Required Required
+Non-Boot Mouse Required Optional Optional Optional Optional Optional
+Boot Keyboard Required Optional Required Required Required Required
+Non-Boot Keybrd Required Optional Required Required Optional Optional
+Other Device Required Optional Optional Optional Optional Optional
+*/
+/** Event handler for the USB_ControlRequest event.
+ * This is fired before passing along unhandled control requests to the library for processing internally.
+ */
+void EVENT_USB_Device_ControlRequest(void)
+{
+ uint8_t* ReportData = NULL;
+ uint8_t ReportSize = 0;
+
+ /* Handle HID Class specific requests */
+ switch (USB_ControlRequest.bRequest)
+ {
+ case HID_REQ_GetReport:
+ if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
+ {
+ Endpoint_ClearSETUP();
+
+ // Interface
+ switch (USB_ControlRequest.wIndex) {
+ case KEYBOARD_INTERFACE:
+ // TODO: test/check
+ ReportData = (uint8_t*)&keyboard_report_sent;
+ ReportSize = sizeof(keyboard_report_sent);
+ break;
+ }
+
+ /* Write the report data to the control endpoint */
+ Endpoint_Write_Control_Stream_LE(ReportData, ReportSize);
+ Endpoint_ClearOUT();
+ }
+
+ break;
+ case HID_REQ_SetReport:
+ if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
+ {
+
+ // Interface
+ switch (USB_ControlRequest.wIndex) {
+ case KEYBOARD_INTERFACE:
+#ifdef NKRO_ENABLE
+ case NKRO_INTERFACE:
+#endif
+ Endpoint_ClearSETUP();
+
+ while (!(Endpoint_IsOUTReceived())) {
+ if (USB_DeviceState == DEVICE_STATE_Unattached)
+ return;
+ }
+ keyboard_led_stats = Endpoint_Read_8();
+
+ Endpoint_ClearOUT();
+ Endpoint_ClearStatusStage();
+ break;
+ }
+
+ }
+
+ break;
+
+ case HID_REQ_GetProtocol:
+ if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE))
+ {
+ if (USB_ControlRequest.wIndex == KEYBOARD_INTERFACE) {
+ Endpoint_ClearSETUP();
+ while (!(Endpoint_IsINReady()));
+ Endpoint_Write_8(keyboard_protocol);
+ Endpoint_ClearIN();
+ Endpoint_ClearStatusStage();
+ }
+ }
+
+ break;
+ case HID_REQ_SetProtocol:
+ if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
+ {
+ if (USB_ControlRequest.wIndex == KEYBOARD_INTERFACE) {
+ Endpoint_ClearSETUP();
+ Endpoint_ClearStatusStage();
+
+ keyboard_protocol = (USB_ControlRequest.wValue & 0xFF);
+ clear_keyboard();
+ }
+ }
+
+ break;
+ case HID_REQ_SetIdle:
+ if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE))
+ {
+ Endpoint_ClearSETUP();
+ Endpoint_ClearStatusStage();
+
+ keyboard_idle = ((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(keyboard_idle);
+ Endpoint_ClearIN();
+ Endpoint_ClearStatusStage();
+ }
+
+ break;
+ }
+}
+
+/*******************************************************************************
+ * Host driver
+ ******************************************************************************/
+static uint8_t keyboard_leds(void)
+{
+ return keyboard_led_stats;
+}
+
+static void send_keyboard(report_keyboard_t *report)
+{
+ uint8_t timeout = 255;
+
+ if (USB_DeviceState != DEVICE_STATE_Configured)
+ return;
+
+ /* Select the Keyboard Report Endpoint */
+#ifdef NKRO_ENABLE
+ if (keyboard_protocol && keyboard_nkro) {
+ /* Report protocol - NKRO */
+ Endpoint_SelectEndpoint(NKRO_IN_EPNUM);
+
+ /* Check if write ready for a polling interval around 1ms */
+ while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(4);
+ if (!Endpoint_IsReadWriteAllowed()) return;
+
+ /* Write Keyboard Report Data */
+ Endpoint_Write_Stream_LE(report, NKRO_EPSIZE, NULL);
+ }
+ else
+#endif
+ {
+ /* Boot protocol */
+ Endpoint_SelectEndpoint(KEYBOARD_IN_EPNUM);
+
+ /* Check if write ready for a polling interval around 10ms */
+ while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40);
+ if (!Endpoint_IsReadWriteAllowed()) return;
+
+ /* Write Keyboard Report Data */
+ 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 = 255;
+
+ if (USB_DeviceState != DEVICE_STATE_Configured)
+ return;
+
+ /* Select the Mouse Report Endpoint */
+ Endpoint_SelectEndpoint(MOUSE_IN_EPNUM);
+
+ /* Check if write ready for a polling interval around 10ms */
+ while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40);
+ if (!Endpoint_IsReadWriteAllowed()) return;
+
+ /* 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
+}
+
+static void send_system(uint16_t data)
+{
+ uint8_t timeout = 255;
+
+ if (USB_DeviceState != DEVICE_STATE_Configured)
+ return;
+
+ report_extra_t r = {
+ .report_id = REPORT_ID_SYSTEM,
+ .usage = data
+ };
+ Endpoint_SelectEndpoint(EXTRAKEY_IN_EPNUM);
+
+ /* Check if write ready for a polling interval around 10ms */
+ while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40);
+ if (!Endpoint_IsReadWriteAllowed()) return;
+
+ Endpoint_Write_Stream_LE(&r, sizeof(report_extra_t), NULL);
+ Endpoint_ClearIN();
+}
+
+static void send_consumer(uint16_t data)
+{
+ uint8_t timeout = 255;
+
+ if (USB_DeviceState != DEVICE_STATE_Configured)
+ return;
+
+ report_extra_t r = {
+ .report_id = REPORT_ID_CONSUMER,
+ .usage = data
+ };
+ Endpoint_SelectEndpoint(EXTRAKEY_IN_EPNUM);
+
+ /* Check if write ready for a polling interval around 10ms */
+ while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40);
+ if (!Endpoint_IsReadWriteAllowed()) return;
+
+ 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;
+
+ // prevents Console_Task() from running during sendchar() runs.
+ // or char will be lost. These two function is mutually exclusive.
+ CONSOLE_FLUSH_SET(false);
+
+ if (USB_DeviceState != DEVICE_STATE_Configured)
+ return -1;
+
+ uint8_t ep = Endpoint_GetCurrentEndpoint();
+ Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM);
+ if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {
+ goto ERROR_EXIT;
+ }
+
+ if (timeouted && !Endpoint_IsReadWriteAllowed()) {
+ goto ERROR_EXIT;
+ }
+
+ timeouted = false;
+
+ uint8_t timeout = SEND_TIMEOUT;
+ while (!Endpoint_IsReadWriteAllowed()) {
+ if (USB_DeviceState != DEVICE_STATE_Configured) {
+ goto ERROR_EXIT;
+ }
+ if (Endpoint_IsStalled()) {
+ goto ERROR_EXIT;
+ }
+ if (!(timeout--)) {
+ timeouted = true;
+ goto ERROR_EXIT;
+ }
+ _delay_ms(1);
+ }
+
+ Endpoint_Write_8(c);
+
+ // send when bank is full
+ if (!Endpoint_IsReadWriteAllowed()) {
+ while (!(Endpoint_IsINReady()));
+ Endpoint_ClearIN();
+ } else {
+ CONSOLE_FLUSH_SET(true);
+ }
+
+ Endpoint_SelectEndpoint(ep);
+ return 0;
+ERROR_EXIT:
+ Endpoint_SelectEndpoint(ep);
+ return -1;
+}
+#else
+int8_t sendchar(uint8_t c)
+{
+ return 0;
+}
+#endif
+
+
+/*******************************************************************************
+ * main
+ ******************************************************************************/
+static void setup_mcu(void)
+{
+ /* Disable watchdog if enabled by bootloader/fuses */
+ MCUSR &= ~(1 << WDRF);
+ wdt_disable();
+
+ /* Disable clock division */
+ clock_prescale_set(clock_div_1);
+}
+
+static void setup_usb(void)
+{
+ // Leonardo needs. Without this USB device is not recognized.
+ USB_Disable();
+
+ USB_Init();
+
+ // for Console_Task
+ USB_Device_EnableSOFEvents();
+ print_set_sendchar(sendchar);
+}
+
+int main(void) __attribute__ ((weak));
+int main(void)
+{
+ setup_mcu();
+ keyboard_setup();
+ setup_usb();
+ sei();
+
+ /* wait for USB startup & debug output */
+ while (USB_DeviceState != DEVICE_STATE_Configured) {
+#if defined(INTERRUPT_CONTROL_ENDPOINT)
+ ;
+#else
+ USB_USBTask();
+#endif
+ }
+ print("USB configured.\n");
+
+ /* init modules */
+ keyboard_init();
+ host_set_driver(&lufa_driver);
+#ifdef SLEEP_LED_ENABLE
+ sleep_led_init();
+#endif
+
+ print("Keyboard start.\n");
+ while (1) {
+ while (USB_DeviceState == DEVICE_STATE_Suspended) {
+ print("[s]");
+ suspend_power_down();
+ if (USB_Device_RemoteWakeupEnabled && suspend_wakeup_condition()) {
+ USB_Device_SendRemoteWakeup();
+ }
+ }
+
+ keyboard_task();
+
+#if !defined(INTERRUPT_CONTROL_ENDPOINT)
+ USB_USBTask();
+#endif
+ }
+}