From 4d4f7684e684bec319f166121463a88cd4a62703 Mon Sep 17 00:00:00 2001 From: fredizzimo Date: Fri, 1 Jul 2016 17:04:53 +0300 Subject: Add ChibiOS support for QMK (#465) * Modularity and gcc warnings fixes. * Add ChibiOS support (USB stack + support files). * Make usb_main more USB_DRIVER #define independent. * Move chibios to tool. * Implement jump-to-bootloader. * Small updates. * Fix bootloader-jump compiling. * Move AVR specific sleep_led.c into avr. * Add basic sleep_led for chibios. * Update chibios README. * NKRO fixes. * Rename some Makefile defines. * Move STM32 bootloader address config to separate .h file. * Add ARM Teensies bootloader code. * Fix chibios/usb_main GET_REPORT handing. * Add missing #include to keymap.c. * Make bootmagic.c code portable (_delay_ms -> wait_ms). * Move declaration of keymap_config. Should really not declare variables in .h files - since it's included in different .c files, a proper linker then complains that the same variable is declared more than once (once for each .c file that the offending .h is included in). * Add eeprom support for chibios/kinetis. * Rename chibios example keyboard. * Move chibios/cortex selection to local Makefiles. * Chibios: use WFI in idle. WIP suspend stuff. * ChibiOS/kinetis: sending remote wakeup. * ChibiOS/STM32: send remote wakeup. * Fix report size of boot protocol. * Fix drop key stroke Keyboard report should be checked if its transfer finishs successfully. Otherwise key stroke can be missing when other key event occurs before the last report transfer is done. Boot protocol 10ms interval probably causes this problem in case it receives key events in a row within the period. NKRO protocol suffers less or nothing due to its interval 1ms. * Chibios/usb_main: rename a variable for clarity. * Add correct chibios/bootloader_jump for infinity KB. * ChibiOS: make reset request more CMSISy. * Chibios: Add breathing sleep LED on Kinetis MCUs. * ChibiOS: Update infinity bootloader code to match updated ChibiOS. * ChibiOS: prettify/document sleep_led code. * Chibios: Remove the wait in the main loop. * Add maple mini code. * Do timeout when writing to CONSOLE EP queue. Fixes TMK bug #266. * Chibios: add 'core/protocol' to the makefiles' search path. * Chibios: Update to new USB API. * Chibios: add more guards for transmitting (fix a deadlock bug). * Add update for chibios in README * Chibios: Fix a HardFault bug (wait after start). * Chibios: cleanup usb_main code. * Chibios: Revert common.mk change (fix AVR linking problem). * core: Fix chibios user compile options Compile options can be defined in project Makefile such as UDEFS, UADEFS, UINCDIR, ULIBDIR and ULIBS. * Sysv format for ChibiOS arm-none-eabi-size Some new patches to ChibiOS puts heap as it's own section. So the berkeley format is now useless, as the heap will be included in the BSS report. The sysv format displays the bss size correctly. * Fix hard-coded path of CHIBIOS * Add support for new version of ChibiOS and Contrib The Kinetis support has moved to a separate Contrib repository in the newest version of Chibios. There has also been some structure changes. So this adds support for those, while maintaining back- wards compability. * Update ChibiOS instructions * Chibios: implement sleep LED for STM32. * Chibios: Update the main chibios README. * Chibios: fix STM32_BOOTLOADER_ADDRESS name. * Chibios: make the default bootloader_jump redefinable (weak). * Chibios: disable LTO (link-time optimisation). With LTO enabled, sometimes things fail for mysterious reasons (e.g. bootloader jump on WF with LEDs enabled), just because the linker optimisation is too aggressive. * Chibios: add default location for chibios-contrib. * ChibiOS: update mk to match chibios/master. * ChibiOS: update instructions.md. * Add chibi_onekey example. * Add comments to chibi_onekey Makefile. * Rename some Makefile defines. * Move STM32 bootloader address config to separate .h file. * Rename chibios example keyboard. * Move chibios/cortex selection to local Makefiles. * Add Teensy LC onekey example. * Chibios: use WFI in idle. WIP suspend stuff. * Update chibi/teensy instructions. * Update chibios/Teensy instructions. * Add infinity_chibios * Add keymap_hasu.c * Infinity_chibios: select correct bootloader_jump. * Infinity_chibios: improve comments. * Add generic STM32F103C8T6 example. * Add maple mini code. * STM32F103x fixes. * Add maple mini pinout pic. * Chibios: updates for 3.0.4 git. * Chibios: rename example stm32_onekey -> stm32_f072_onekey. * Chibios: add makefiles for Teensy 3.x examples. * Chibios: update Teensy 3.x instructions. * Chibios: Tsy LC is cortex-m0plus. * Chibios: add more guards for transmitting (fix a deadlock bug). * Change README for chibios * Chibios: update examples to current chibios git. Match the changes in mainline chibios: - update chconf.h - update supplied ld scripts structure - update Teensy instructions (switch to official chibios and introduce contrib) * Add ChibiOS and ChibiOS-Contrib submodules Also fix the makefile path for them. * Moves chibios keyboards to keyboards folder * First version of ChibiOS compilation Only the stm32_f072_onkey keyboard is ported at the moment. It compiles, but still doesn't link. * More chibios fixes It now compiles without warnings and links * Move the teensy_lc_onekey to the keyboards folder * Clean up the make file rule structure * Remove keymap_fn_to_action * Update more ChibiOS keyboards to QMK Most of them does not compile at the moment though. * Use older version of Chibios libraries The newest ones have problems with compilation * Remove USB_UNCONFIGURED event It isn't present in the older version of ChibiOS * Fix the infinity_chibios compilation * Fix potentially uninitialized variable * Add missing include * Fix the ChibiOS makefile * Fix some Chibios keyboard compilation * Revert the rules.mk file back to master version * Combine the chibios and AVR makefiles With just the required overrides in the respective platform specific one. * Slight makefile restrucuring Platform specific compiler options * Move avr specific targets out of the main rules * Fix ChibiOS objcopy The ChibiOS objcopy needs different parameters, so the parameters are moved to the corresponding platform rule file * Fix the objcopy for real this time The comands were moved around, so chibios used avr and the ohter way around. Also change the objsize output format * Fix the thumb flags * Fix the infinity hasu keymap * Per platform cpp flags * Add gcc-arm-none-eabi package to travis * Add arm-none-eabi-newlib to travis * Fix the name of the libnewlib-arm-none-eabi lib * Fix the ChibiOS paths So that they are properly relative, and builds don't generate extra folders * Fix the board path of stm32_f103_onekey * Only consider folders with Makefiles as subproject --- tmk_core/protocol/chibios/usb_main.c | 1372 ++++++++++++++++++++++++++++++++++ 1 file changed, 1372 insertions(+) create mode 100644 tmk_core/protocol/chibios/usb_main.c (limited to 'tmk_core/protocol/chibios/usb_main.c') diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c new file mode 100644 index 0000000000..e2c9d9bf14 --- /dev/null +++ b/tmk_core/protocol/chibios/usb_main.c @@ -0,0 +1,1372 @@ +/* + * (c) 2015 flabberast + * + * Based on the following work: + * - Guillaume Duc's raw hid example (MIT License) + * https://github.com/guiduc/usb-hid-chibios-example + * - PJRC Teensy examples (MIT License) + * https://www.pjrc.com/teensy/usb_keyboard.html + * - hasu's TMK keyboard code (GPL v2 and some code Modified BSD) + * https://github.com/tmk/tmk_keyboard/ + * - ChibiOS demo code (Apache 2.0 License) + * http://www.chibios.org + * + * Since some GPL'd code is used, this work is licensed under + * GPL v2 or later. + */ + +#include "ch.h" +#include "hal.h" + +#include "usb_main.h" + +#include "host.h" +#include "debug.h" +#include "suspend.h" +#ifdef SLEEP_LED_ENABLE +#include "sleep_led.h" +#include "led.h" +#endif + +/* --------------------------------------------------------- + * Global interface variables and declarations + * --------------------------------------------------------- + */ + +uint8_t keyboard_idle __attribute__((aligned(2))) = 0; +uint8_t keyboard_protocol __attribute__((aligned(2))) = 1; +uint16_t keyboard_led_stats __attribute__((aligned(2))) = 0; +volatile uint16_t keyboard_idle_count = 0; +static virtual_timer_t keyboard_idle_timer; +static void keyboard_idle_timer_cb(void *arg); +#ifdef NKRO_ENABLE +extern bool keyboard_nkro; +#endif /* NKRO_ENABLE */ + +report_keyboard_t keyboard_report_sent = {{0}}; +#ifdef MOUSE_ENABLE +report_mouse_t mouse_report_blank = {0}; +#endif /* MOUSE_ENABLE */ +#ifdef EXTRAKEY_ENABLE +uint8_t extra_report_blank[3] = {0}; +#endif /* EXTRAKEY_ENABLE */ + +#ifdef CONSOLE_ENABLE +/* The emission buffers queue */ +output_buffers_queue_t console_buf_queue; +static uint8_t console_queue_buffer[BQ_BUFFER_SIZE(CONSOLE_QUEUE_CAPACITY, CONSOLE_EPSIZE)]; + +static virtual_timer_t console_flush_timer; +void console_queue_onotify(io_buffers_queue_t *bqp); +static void console_flush_cb(void *arg); +#endif /* CONSOLE_ENABLE */ + +/* --------------------------------------------------------- + * Descriptors and USB driver objects + * --------------------------------------------------------- + */ + +/* HID specific constants */ +#define USB_DESCRIPTOR_HID 0x21 +#define USB_DESCRIPTOR_HID_REPORT 0x22 +#define HID_GET_REPORT 0x01 +#define HID_GET_IDLE 0x02 +#define HID_GET_PROTOCOL 0x03 +#define HID_SET_REPORT 0x09 +#define HID_SET_IDLE 0x0A +#define HID_SET_PROTOCOL 0x0B + +/* USB Device Descriptor */ +static const uint8_t usb_device_descriptor_data[] = { + USB_DESC_DEVICE(0x0200, // bcdUSB (1.1) + 0, // bDeviceClass (defined in later in interface) + 0, // bDeviceSubClass + 0, // bDeviceProtocol + 64, // bMaxPacketSize (64 bytes) (the driver didn't work with 32) + VENDOR_ID, // idVendor + PRODUCT_ID, // idProduct + DEVICE_VER, // bcdDevice + 1, // iManufacturer + 2, // iProduct + 3, // iSerialNumber + 1) // bNumConfigurations +}; + +/* Device Descriptor wrapper */ +static const USBDescriptor usb_device_descriptor = { + sizeof usb_device_descriptor_data, + usb_device_descriptor_data +}; + +/* + * HID Report Descriptor + * + * See "Device Class Definition for Human Interface Devices (HID)" + * (http://www.usb.org/developers/hidpage/HID1_11.pdf) for the + * detailed descrition of all the fields + */ + +/* Keyboard Protocol 1, HID 1.11 spec, Appendix B, page 59-60 */ +static const uint8_t keyboard_hid_report_desc_data[] = { + 0x05, 0x01, // Usage Page (Generic Desktop), + 0x09, 0x06, // Usage (Keyboard), + 0xA1, 0x01, // Collection (Application), + 0x75, 0x01, // Report Size (1), + 0x95, 0x08, // Report Count (8), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0xE0, // Usage Minimum (224), + 0x29, 0xE7, // Usage Maximum (231), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x01, // Logical Maximum (1), + 0x81, 0x02, // Input (Data, Variable, Absolute), ;Modifier byte + 0x95, 0x01, // Report Count (1), + 0x75, 0x08, // Report Size (8), + 0x81, 0x03, // Input (Constant), ;Reserved byte + 0x95, 0x05, // Report Count (5), + 0x75, 0x01, // Report Size (1), + 0x05, 0x08, // Usage Page (LEDs), + 0x19, 0x01, // Usage Minimum (1), + 0x29, 0x05, // Usage Maximum (5), + 0x91, 0x02, // Output (Data, Variable, Absolute), ;LED report + 0x95, 0x01, // Report Count (1), + 0x75, 0x03, // Report Size (3), + 0x91, 0x03, // Output (Constant), ;LED report padding + 0x95, KBD_REPORT_KEYS, // Report Count (), + 0x75, 0x08, // Report Size (8), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0xFF, // Logical Maximum(255), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0x00, // Usage Minimum (0), + 0x29, 0xFF, // Usage Maximum (255), + 0x81, 0x00, // Input (Data, Array), + 0xc0 // End Collection +}; +/* wrapper */ +static const USBDescriptor keyboard_hid_report_descriptor = { + sizeof keyboard_hid_report_desc_data, + keyboard_hid_report_desc_data +}; + +#ifdef NKRO_ENABLE +static const uint8_t nkro_hid_report_desc_data[] = { + 0x05, 0x01, // Usage Page (Generic Desktop), + 0x09, 0x06, // Usage (Keyboard), + 0xA1, 0x01, // Collection (Application), + // bitmap of modifiers + 0x75, 0x01, // Report Size (1), + 0x95, 0x08, // Report Count (8), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0xE0, // Usage Minimum (224), + 0x29, 0xE7, // Usage Maximum (231), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x01, // Logical Maximum (1), + 0x81, 0x02, // Input (Data, Variable, Absolute), ;Modifier byte + // LED output report + 0x95, 0x05, // Report Count (5), + 0x75, 0x01, // Report Size (1), + 0x05, 0x08, // Usage Page (LEDs), + 0x19, 0x01, // Usage Minimum (1), + 0x29, 0x05, // Usage Maximum (5), + 0x91, 0x02, // Output (Data, Variable, Absolute), + 0x95, 0x01, // Report Count (1), + 0x75, 0x03, // Report Size (3), + 0x91, 0x03, // Output (Constant), + // bitmap of keys + 0x95, NKRO_REPORT_KEYS * 8, // Report Count (), + 0x75, 0x01, // Report Size (1), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x01, // Logical Maximum(1), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0x00, // Usage Minimum (0), + 0x29, NKRO_REPORT_KEYS * 8 - 1, // Usage Maximum (), + 0x81, 0x02, // Input (Data, Variable, Absolute), + 0xc0 // End Collection +}; +/* wrapper */ +static const USBDescriptor nkro_hid_report_descriptor = { + sizeof nkro_hid_report_desc_data, + nkro_hid_report_desc_data +}; +#endif /* NKRO_ENABLE */ + +#ifdef MOUSE_ENABLE +/* Mouse Protocol 1, HID 1.11 spec, Appendix B, page 59-60, with wheel extension + * http://www.microchip.com/forums/tm.aspx?high=&m=391435&mpage=1#391521 + * http://www.keil.com/forum/15671/ + * http://www.microsoft.com/whdc/device/input/wheel.mspx */ +static const uint8_t mouse_hid_report_desc_data[] = { + /* mouse */ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x02, // USAGE (Mouse) + 0xa1, 0x01, // COLLECTION (Application) + //0x85, REPORT_ID_MOUSE, // REPORT_ID (1) + 0x09, 0x01, // USAGE (Pointer) + 0xa1, 0x00, // COLLECTION (Physical) + // ---------------------------- Buttons + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x01, // USAGE_MINIMUM (Button 1) + 0x29, 0x05, // USAGE_MAXIMUM (Button 5) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x05, // REPORT_COUNT (5) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x75, 0x03, // REPORT_SIZE (3) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x03, // INPUT (Cnst,Var,Abs) + // ---------------------------- X,Y position + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x02, // REPORT_COUNT (2) + 0x81, 0x06, // INPUT (Data,Var,Rel) + // ---------------------------- Vertical wheel + 0x09, 0x38, // USAGE (Wheel) + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x35, 0x00, // PHYSICAL_MINIMUM (0) - reset physical + 0x45, 0x00, // PHYSICAL_MAXIMUM (0) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x06, // INPUT (Data,Var,Rel) + // ---------------------------- Horizontal wheel + 0x05, 0x0c, // USAGE_PAGE (Consumer Devices) + 0x0a, 0x38, 0x02, // USAGE (AC Pan) + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x06, // INPUT (Data,Var,Rel) + 0xc0, // END_COLLECTION + 0xc0, // END_COLLECTION +}; +/* wrapper */ +static const USBDescriptor mouse_hid_report_descriptor = { + sizeof mouse_hid_report_desc_data, + mouse_hid_report_desc_data +}; +#endif /* MOUSE_ENABLE */ + +#ifdef CONSOLE_ENABLE +static const uint8_t console_hid_report_desc_data[] = { + 0x06, 0x31, 0xFF, // Usage Page 0xFF31 (vendor defined) + 0x09, 0x74, // Usage 0x74 + 0xA1, 0x53, // Collection 0x53 + 0x75, 0x08, // report size = 8 bits + 0x15, 0x00, // logical minimum = 0 + 0x26, 0xFF, 0x00, // logical maximum = 255 + 0x95, CONSOLE_EPSIZE, // report count + 0x09, 0x75, // usage + 0x81, 0x02, // Input (array) + 0xC0 // end collection +}; +/* wrapper */ +static const USBDescriptor console_hid_report_descriptor = { + sizeof console_hid_report_desc_data, + console_hid_report_desc_data +}; +#endif /* CONSOLE_ENABLE */ + +#ifdef EXTRAKEY_ENABLE +/* audio controls & system controls + * http://www.microsoft.com/whdc/archive/w2kbd.mspx */ +static const uint8_t extra_hid_report_desc_data[] = { + /* system control */ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x80, // USAGE (System Control) + 0xa1, 0x01, // COLLECTION (Application) + 0x85, REPORT_ID_SYSTEM, // REPORT_ID (2) + 0x15, 0x01, // LOGICAL_MINIMUM (0x1) + 0x25, 0xb7, // LOGICAL_MAXIMUM (0xb7) + 0x19, 0x01, // USAGE_MINIMUM (0x1) + 0x29, 0xb7, // USAGE_MAXIMUM (0xb7) + 0x75, 0x10, // REPORT_SIZE (16) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x00, // INPUT (Data,Array,Abs) + 0xc0, // END_COLLECTION + /* consumer */ + 0x05, 0x0c, // USAGE_PAGE (Consumer Devices) + 0x09, 0x01, // USAGE (Consumer Control) + 0xa1, 0x01, // COLLECTION (Application) + 0x85, REPORT_ID_CONSUMER, // REPORT_ID (3) + 0x15, 0x01, // LOGICAL_MINIMUM (0x1) + 0x26, 0x9c, 0x02, // LOGICAL_MAXIMUM (0x29c) + 0x19, 0x01, // USAGE_MINIMUM (0x1) + 0x2a, 0x9c, 0x02, // USAGE_MAXIMUM (0x29c) + 0x75, 0x10, // REPORT_SIZE (16) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x00, // INPUT (Data,Array,Abs) + 0xc0, // END_COLLECTION +}; +/* wrapper */ +static const USBDescriptor extra_hid_report_descriptor = { + sizeof extra_hid_report_desc_data, + extra_hid_report_desc_data +}; +#endif /* EXTRAKEY_ENABLE */ + + +/* + * Configuration Descriptor tree for a HID device + * + * The HID Specifications version 1.11 require the following order: + * - Configuration Descriptor + * - Interface Descriptor + * - HID Descriptor + * - Endpoints Descriptors + */ +#define KBD_HID_DESC_NUM 0 +#define KBD_HID_DESC_OFFSET (9 + (9 + 9 + 7) * KBD_HID_DESC_NUM + 9) + +#ifdef MOUSE_ENABLE +# define MOUSE_HID_DESC_NUM (KBD_HID_DESC_NUM + 1) +# define MOUSE_HID_DESC_OFFSET (9 + (9 + 9 + 7) * MOUSE_HID_DESC_NUM + 9) +#else /* MOUSE_ENABLE */ +# define MOUSE_HID_DESC_NUM (KBD_HID_DESC_NUM + 0) +#endif /* MOUSE_ENABLE */ + +#ifdef CONSOLE_ENABLE +#define CONSOLE_HID_DESC_NUM (MOUSE_HID_DESC_NUM + 1) +#define CONSOLE_HID_DESC_OFFSET (9 + (9 + 9 + 7) * CONSOLE_HID_DESC_NUM + 9) +#else /* CONSOLE_ENABLE */ +# define CONSOLE_HID_DESC_NUM (MOUSE_HID_DESC_NUM + 0) +#endif /* CONSOLE_ENABLE */ + +#ifdef EXTRAKEY_ENABLE +# define EXTRA_HID_DESC_NUM (CONSOLE_HID_DESC_NUM + 1) +# define EXTRA_HID_DESC_OFFSET (9 + (9 + 9 + 7) * EXTRA_HID_DESC_NUM + 9) +#else /* EXTRAKEY_ENABLE */ +# define EXTRA_HID_DESC_NUM (CONSOLE_HID_DESC_NUM + 0) +#endif /* EXTRAKEY_ENABLE */ + +#ifdef NKRO_ENABLE +# define NKRO_HID_DESC_NUM (EXTRA_HID_DESC_NUM + 1) +# define NKRO_HID_DESC_OFFSET (9 + (9 + 9 + 7) * EXTRA_HID_DESC_NUM + 9) +#else /* NKRO_ENABLE */ +# define NKRO_HID_DESC_NUM (EXTRA_HID_DESC_NUM + 0) +#endif /* NKRO_ENABLE */ + +#define NUM_INTERFACES (NKRO_HID_DESC_NUM + 1) +#define CONFIG1_DESC_SIZE (9 + (9 + 9 + 7) * NUM_INTERFACES) + +static const uint8_t hid_configuration_descriptor_data[] = { + /* Configuration Descriptor (9 bytes) USB spec 9.6.3, page 264-266, Table 9-10 */ + USB_DESC_CONFIGURATION(CONFIG1_DESC_SIZE, // wTotalLength + NUM_INTERFACES, // bNumInterfaces + 1, // bConfigurationValue + 0, // iConfiguration + 0xA0, // bmAttributes (RESERVED|REMOTEWAKEUP) + 50), // bMaxPower (50mA) + + /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ + USB_DESC_INTERFACE(KBD_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass: HID + 0x01, // bInterfaceSubClass: Boot + 0x01, // bInterfaceProtocol: Keyboard + 0), // iInterface + + /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ + USB_DESC_BYTE(9), // bLength + USB_DESC_BYTE(0x21), // bDescriptorType (HID class) + USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 + USB_DESC_BYTE(0), // bCountryCode + USB_DESC_BYTE(1), // bNumDescriptors + USB_DESC_BYTE(0x22), // bDescriptorType (report desc) + USB_DESC_WORD(sizeof(keyboard_hid_report_desc_data)), // wDescriptorLength + + /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ + USB_DESC_ENDPOINT(KBD_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (Interrupt) + KBD_EPSIZE,// wMaxPacketSize + 10), // bInterval + + #ifdef MOUSE_ENABLE + /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ + USB_DESC_INTERFACE(MOUSE_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + // ThinkPad T23 BIOS doesn't work with boot mouse. + 0x00, // bInterfaceSubClass (0x01 = Boot) + 0x00, // bInterfaceProtocol (0x02 = Mouse) + /* + 0x01, // bInterfaceSubClass (0x01 = Boot) + 0x02, // bInterfaceProtocol (0x02 = Mouse) + */ + 0), // iInterface + + /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ + USB_DESC_BYTE(9), // bLength + USB_DESC_BYTE(0x21), // bDescriptorType (HID class) + USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 + USB_DESC_BYTE(0), // bCountryCode + USB_DESC_BYTE(1), // bNumDescriptors + USB_DESC_BYTE(0x22), // bDescriptorType (report desc) + USB_DESC_WORD(sizeof(mouse_hid_report_desc_data)), // wDescriptorLength + + /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ + USB_DESC_ENDPOINT(MOUSE_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (Interrupt) + MOUSE_EPSIZE, // wMaxPacketSize + 1), // bInterval + #endif /* MOUSE_ENABLE */ + + #ifdef CONSOLE_ENABLE + /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ + USB_DESC_INTERFACE(CONSOLE_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass: HID + 0x00, // bInterfaceSubClass: None + 0x00, // bInterfaceProtocol: None + 0), // iInterface + + /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ + USB_DESC_BYTE(9), // bLength + USB_DESC_BYTE(0x21), // bDescriptorType (HID class) + USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 + USB_DESC_BYTE(0), // bCountryCode + USB_DESC_BYTE(1), // bNumDescriptors + USB_DESC_BYTE(0x22), // bDescriptorType (report desc) + USB_DESC_WORD(sizeof(console_hid_report_desc_data)), // wDescriptorLength + + /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ + USB_DESC_ENDPOINT(CONSOLE_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (Interrupt) + CONSOLE_EPSIZE, // wMaxPacketSize + 1), // bInterval + #endif /* CONSOLE_ENABLE */ + + #ifdef EXTRAKEY_ENABLE + /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ + USB_DESC_INTERFACE(EXTRA_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass: HID + 0x00, // bInterfaceSubClass: None + 0x00, // bInterfaceProtocol: None + 0), // iInterface + + /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ + USB_DESC_BYTE(9), // bLength + USB_DESC_BYTE(0x21), // bDescriptorType (HID class) + USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 + USB_DESC_BYTE(0), // bCountryCode + USB_DESC_BYTE(1), // bNumDescriptors + USB_DESC_BYTE(0x22), // bDescriptorType (report desc) + USB_DESC_WORD(sizeof(extra_hid_report_desc_data)), // wDescriptorLength + + /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ + USB_DESC_ENDPOINT(EXTRA_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (Interrupt) + EXTRA_EPSIZE, // wMaxPacketSize + 10), // bInterval + #endif /* EXTRAKEY_ENABLE */ + + #ifdef NKRO_ENABLE + /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ + USB_DESC_INTERFACE(NKRO_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass: HID + 0x00, // bInterfaceSubClass: None + 0x00, // bInterfaceProtocol: None + 0), // iInterface + + /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ + USB_DESC_BYTE(9), // bLength + USB_DESC_BYTE(0x21), // bDescriptorType (HID class) + USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 + USB_DESC_BYTE(0), // bCountryCode + USB_DESC_BYTE(1), // bNumDescriptors + USB_DESC_BYTE(0x22), // bDescriptorType (report desc) + USB_DESC_WORD(sizeof(nkro_hid_report_desc_data)), // wDescriptorLength + + /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ + USB_DESC_ENDPOINT(NKRO_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (Interrupt) + NKRO_EPSIZE, // wMaxPacketSize + 1), // bInterval + #endif /* NKRO_ENABLE */ +}; + +/* Configuration Descriptor wrapper */ +static const USBDescriptor hid_configuration_descriptor = { + sizeof hid_configuration_descriptor_data, + hid_configuration_descriptor_data +}; + +/* wrappers */ +#define HID_DESCRIPTOR_SIZE 9 +static const USBDescriptor keyboard_hid_descriptor = { + HID_DESCRIPTOR_SIZE, + &hid_configuration_descriptor_data[KBD_HID_DESC_OFFSET] +}; +#ifdef MOUSE_ENABLE +static const USBDescriptor mouse_hid_descriptor = { + HID_DESCRIPTOR_SIZE, + &hid_configuration_descriptor_data[MOUSE_HID_DESC_OFFSET] +}; +#endif /* MOUSE_ENABLE */ +#ifdef CONSOLE_ENABLE +static const USBDescriptor console_hid_descriptor = { + HID_DESCRIPTOR_SIZE, + &hid_configuration_descriptor_data[CONSOLE_HID_DESC_OFFSET] +}; +#endif /* CONSOLE_ENABLE */ +#ifdef EXTRAKEY_ENABLE +static const USBDescriptor extra_hid_descriptor = { + HID_DESCRIPTOR_SIZE, + &hid_configuration_descriptor_data[EXTRA_HID_DESC_OFFSET] +}; +#endif /* EXTRAKEY_ENABLE */ +#ifdef NKRO_ENABLE +static const USBDescriptor nkro_hid_descriptor = { + HID_DESCRIPTOR_SIZE, + &hid_configuration_descriptor_data[NKRO_HID_DESC_OFFSET] +}; +#endif /* NKRO_ENABLE */ + + +/* U.S. English language identifier */ +static const uint8_t usb_string_langid[] = { + USB_DESC_BYTE(4), // bLength + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), // bDescriptorType + USB_DESC_WORD(0x0409) // wLANGID (U.S. English) +}; + +/* ugly ugly hack */ +#define PP_NARG(...) \ + PP_NARG_(__VA_ARGS__,PP_RSEQ_N()) +#define PP_NARG_(...) \ + PP_ARG_N(__VA_ARGS__) +#define PP_ARG_N( \ + _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ + _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ + _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ + _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ + _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ + _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ + _61,_62,_63,N,...) N +#define PP_RSEQ_N() \ + 63,62,61,60, \ + 59,58,57,56,55,54,53,52,51,50, \ + 49,48,47,46,45,44,43,42,41,40, \ + 39,38,37,36,35,34,33,32,31,30, \ + 29,28,27,26,25,24,23,22,21,20, \ + 19,18,17,16,15,14,13,12,11,10, \ + 9,8,7,6,5,4,3,2,1,0 + +/* Vendor string = manufacturer */ +static const uint8_t usb_string_vendor[] = { + USB_DESC_BYTE(PP_NARG(USBSTR_MANUFACTURER)+2), // bLength + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), // bDescriptorType + USBSTR_MANUFACTURER +}; + +/* Device Description string = product */ +static const uint8_t usb_string_description[] = { + USB_DESC_BYTE(PP_NARG(USBSTR_PRODUCT)+2), // bLength + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), // bDescriptorType + USBSTR_PRODUCT +}; + +/* Serial Number string (will be filled by the function init_usb_serial_string) */ +static uint8_t usb_string_serial[] = { + USB_DESC_BYTE(22), // bLength + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), // bDescriptorType + '0', 0, 'x', 0, 'D', 0, 'E', 0, 'A', 0, 'D', 0, 'B', 0, 'E', 0, 'E', 0, 'F', 0 +}; + +/* Strings wrappers array */ +static const USBDescriptor usb_strings[] = { + { sizeof usb_string_langid, usb_string_langid } + , + { sizeof usb_string_vendor, usb_string_vendor } + , + { sizeof usb_string_description, usb_string_description } + , + { sizeof usb_string_serial, usb_string_serial } +}; + +/* + * Handles the GET_DESCRIPTOR callback + * + * Returns the proper descriptor + */ +static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t lang) { + (void)usbp; + (void)lang; + switch(dtype) { + /* Generic descriptors */ + case USB_DESCRIPTOR_DEVICE: /* Device Descriptor */ + return &usb_device_descriptor; + + case USB_DESCRIPTOR_CONFIGURATION: /* Configuration Descriptor */ + return &hid_configuration_descriptor; + + case USB_DESCRIPTOR_STRING: /* Strings */ + if(dindex < 4) + return &usb_strings[dindex]; + break; + + /* HID specific descriptors */ + case USB_DESCRIPTOR_HID: /* HID Descriptors */ + switch(lang) { /* yea, poor label, it's actually wIndex from the setup packet */ + case KBD_INTERFACE: + return &keyboard_hid_descriptor; + +#ifdef MOUSE_ENABLE + case MOUSE_INTERFACE: + return &mouse_hid_descriptor; +#endif /* MOUSE_ENABLE */ +#ifdef CONSOLE_ENABLE + case CONSOLE_INTERFACE: + return &console_hid_descriptor; +#endif /* CONSOLE_ENABLE */ +#ifdef EXTRAKEY_ENABLE + case EXTRA_INTERFACE: + return &extra_hid_descriptor; +#endif /* EXTRAKEY_ENABLE */ +#ifdef NKRO_ENABLE + case NKRO_INTERFACE: + return &nkro_hid_descriptor; +#endif /* NKRO_ENABLE */ + } + + case USB_DESCRIPTOR_HID_REPORT: /* HID Report Descriptor */ + switch(lang) { + case KBD_INTERFACE: + return &keyboard_hid_report_descriptor; + +#ifdef MOUSE_ENABLE + case MOUSE_INTERFACE: + return &mouse_hid_report_descriptor; +#endif /* MOUSE_ENABLE */ +#ifdef CONSOLE_ENABLE + case CONSOLE_INTERFACE: + return &console_hid_report_descriptor; +#endif /* CONSOLE_ENABLE */ +#ifdef EXTRAKEY_ENABLE + case EXTRA_INTERFACE: + return &extra_hid_report_descriptor; +#endif /* EXTRAKEY_ENABLE */ +#ifdef NKRO_ENABLE + case NKRO_INTERFACE: + return &nkro_hid_report_descriptor; +#endif /* NKRO_ENABLE */ + } + } + return NULL; +} + +/* keyboard endpoint state structure */ +static USBInEndpointState kbd_ep_state; +/* keyboard endpoint initialization structure (IN) */ +static const USBEndpointConfig kbd_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + kbd_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + KBD_EPSIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &kbd_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; + +#ifdef MOUSE_ENABLE +/* mouse endpoint state structure */ +static USBInEndpointState mouse_ep_state; + +/* mouse endpoint initialization structure (IN) */ +static const USBEndpointConfig mouse_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + mouse_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + MOUSE_EPSIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &mouse_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif /* MOUSE_ENABLE */ + +#ifdef CONSOLE_ENABLE +/* console endpoint state structure */ +static USBInEndpointState console_ep_state; + +/* console endpoint initialization structure (IN) */ +static const USBEndpointConfig console_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + console_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + CONSOLE_EPSIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &console_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif /* CONSOLE_ENABLE */ + +#ifdef EXTRAKEY_ENABLE +/* extrakey endpoint state structure */ +static USBInEndpointState extra_ep_state; + +/* extrakey endpoint initialization structure (IN) */ +static const USBEndpointConfig extra_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + extra_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + EXTRA_EPSIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &extra_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif /* EXTRAKEY_ENABLE */ + +#ifdef NKRO_ENABLE +/* nkro endpoint state structure */ +static USBInEndpointState nkro_ep_state; + +/* nkro endpoint initialization structure (IN) */ +static const USBEndpointConfig nkro_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + nkro_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + NKRO_EPSIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &nkro_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif /* NKRO_ENABLE */ + +/* --------------------------------------------------------- + * USB driver functions + * --------------------------------------------------------- + */ + +/* Handles the USB driver global events + * TODO: maybe disable some things when connection is lost? */ +static void usb_event_cb(USBDriver *usbp, usbevent_t event) { + switch(event) { + case USB_EVENT_RESET: + //TODO: from ISR! print("[R]"); + return; + + case USB_EVENT_ADDRESS: + return; + + case USB_EVENT_CONFIGURED: + osalSysLockFromISR(); + /* Enable the endpoints specified into the configuration. */ + usbInitEndpointI(usbp, KBD_ENDPOINT, &kbd_ep_config); +#ifdef MOUSE_ENABLE + usbInitEndpointI(usbp, MOUSE_ENDPOINT, &mouse_ep_config); +#endif /* MOUSE_ENABLE */ +#ifdef CONSOLE_ENABLE + usbInitEndpointI(usbp, CONSOLE_ENDPOINT, &console_ep_config); + /* don't need to start the flush timer, it starts from console_in_cb automatically */ +#endif /* CONSOLE_ENABLE */ +#ifdef EXTRAKEY_ENABLE + usbInitEndpointI(usbp, EXTRA_ENDPOINT, &extra_ep_config); +#endif /* EXTRAKEY_ENABLE */ +#ifdef NKRO_ENABLE + usbInitEndpointI(usbp, NKRO_ENDPOINT, &nkro_ep_config); +#endif /* NKRO_ENABLE */ + osalSysUnlockFromISR(); + return; + + case USB_EVENT_SUSPEND: + //TODO: from ISR! print("[S]"); +#ifdef SLEEP_LED_ENABLE + sleep_led_enable(); +#endif /* SLEEP_LED_ENABLE */ + return; + + case USB_EVENT_WAKEUP: + //TODO: from ISR! print("[W]"); + suspend_wakeup_init(); +#ifdef SLEEP_LED_ENABLE + sleep_led_disable(); + // NOTE: converters may not accept this + led_set(host_keyboard_leds()); +#endif /* SLEEP_LED_ENABLE */ + return; + + case USB_EVENT_STALLED: + return; + } +} + +/* Function used locally in os/hal/src/usb.c for getting descriptors + * need it here for HID descriptor */ +static uint16_t get_hword(uint8_t *p) { + uint16_t hw; + + hw = (uint16_t)*p++; + hw |= (uint16_t)*p << 8U; + return hw; +} + +/* + * 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 + */ + +/* Callback for SETUP request on the endpoint 0 (control) */ +static bool usb_request_hook_cb(USBDriver *usbp) { + const USBDescriptor *dp; + + /* usbp->setup fields: + * 0: bmRequestType (bitmask) + * 1: bRequest + * 2,3: (LSB,MSB) wValue + * 4,5: (LSB,MSB) wIndex + * 6,7: (LSB,MSB) wLength (number of bytes to transfer if there is a data phase) */ + + /* Handle HID class specific requests */ + if(((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) && + ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) { + switch(usbp->setup[0] & USB_RTYPE_DIR_MASK) { + case USB_RTYPE_DIR_DEV2HOST: + switch(usbp->setup[1]) { /* bRequest */ + case HID_GET_REPORT: + switch(usbp->setup[4]) { /* LSB(wIndex) (check MSB==0?) */ + case KBD_INTERFACE: +#ifdef NKRO_ENABLE + case NKRO_INTERFACE: +#endif /* NKRO_ENABLE */ + usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent), NULL); + return TRUE; + break; + +#ifdef MOUSE_ENABLE + case MOUSE_INTERFACE: + usbSetupTransfer(usbp, (uint8_t *)&mouse_report_blank, sizeof(mouse_report_blank), NULL); + return TRUE; + break; +#endif /* MOUSE_ENABLE */ + +#ifdef CONSOLE_ENABLE + case CONSOLE_INTERFACE: + usbSetupTransfer(usbp, console_queue_buffer, CONSOLE_EPSIZE, NULL); + return TRUE; + break; +#endif /* CONSOLE_ENABLE */ + +#ifdef EXTRAKEY_ENABLE + case EXTRA_INTERFACE: + if(usbp->setup[3] == 1) { /* MSB(wValue) [Report Type] == 1 [Input Report] */ + switch(usbp->setup[2]) { /* LSB(wValue) [Report ID] */ + case REPORT_ID_SYSTEM: + extra_report_blank[0] = REPORT_ID_SYSTEM; + usbSetupTransfer(usbp, (uint8_t *)extra_report_blank, sizeof(extra_report_blank), NULL); + return TRUE; + break; + case REPORT_ID_CONSUMER: + extra_report_blank[0] = REPORT_ID_CONSUMER; + usbSetupTransfer(usbp, (uint8_t *)extra_report_blank, sizeof(extra_report_blank), NULL); + return TRUE; + break; + default: + return FALSE; + } + } else { + return FALSE; + } + break; +#endif /* EXTRAKEY_ENABLE */ + + default: + usbSetupTransfer(usbp, NULL, 0, NULL); + return TRUE; + break; + } + break; + + case HID_GET_PROTOCOL: + if((usbp->setup[4] == KBD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ + usbSetupTransfer(usbp, &keyboard_protocol, 1, NULL); + return TRUE; + } + break; + + case HID_GET_IDLE: + usbSetupTransfer(usbp, &keyboard_idle, 1, NULL); + return TRUE; + break; + } + break; + + case USB_RTYPE_DIR_HOST2DEV: + switch(usbp->setup[1]) { /* bRequest */ + case HID_SET_REPORT: + switch(usbp->setup[4]) { /* LSB(wIndex) (check MSB==0 and wLength==1?) */ + case KBD_INTERFACE: +#ifdef NKRO_ENABLE + case NKRO_INTERFACE: +#endif /* NKRO_ENABLE */ + /* keyboard_led_stats = + * keyboard_led_stats needs be word (or dword), otherwise we get an exception on F0 */ + usbSetupTransfer(usbp, (uint8_t *)&keyboard_led_stats, 1, NULL); + return TRUE; + break; + } + break; + + case HID_SET_PROTOCOL: + if((usbp->setup[4] == KBD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ + keyboard_protocol = ((usbp->setup[2]) != 0x00); /* LSB(wValue) */ +#ifdef NKRO_ENABLE + keyboard_nkro = !!keyboard_protocol; + if(!keyboard_nkro && keyboard_idle) { +#else /* NKRO_ENABLE */ + if(keyboard_idle) { +#endif /* NKRO_ENABLE */ + /* arm the idle timer if boot protocol & idle */ + osalSysLockFromISR(); + chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); + osalSysUnlockFromISR(); + } + } + usbSetupTransfer(usbp, NULL, 0, NULL); + return TRUE; + break; + + case HID_SET_IDLE: + keyboard_idle = usbp->setup[3]; /* MSB(wValue) */ + /* arm the timer */ +#ifdef NKRO_ENABLE + if(!keyboard_nkro && keyboard_idle) { +#else /* NKRO_ENABLE */ + if(keyboard_idle) { +#endif /* NKRO_ENABLE */ + osalSysLockFromISR(); + chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); + osalSysUnlockFromISR(); + } + usbSetupTransfer(usbp, NULL, 0, NULL); + return TRUE; + break; + } + break; + } + } + + /* Handle the Get_Descriptor Request for HID class (not handled by the default hook) */ + if((usbp->setup[0] == 0x81) && (usbp->setup[1] == USB_REQ_GET_DESCRIPTOR)) { + dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3], usbp->setup[2], get_hword(&usbp->setup[4])); + if(dp == NULL) + return FALSE; + usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL); + return TRUE; + } + + return FALSE; +} + +/* Start-of-frame callback */ +static void usb_sof_cb(USBDriver *usbp) { + kbd_sof_cb(usbp); +} + + +/* USB driver configuration */ +static const USBConfig usbcfg = { + usb_event_cb, /* USB events callback */ + usb_get_descriptor_cb, /* Device GET_DESCRIPTOR request callback */ + usb_request_hook_cb, /* Requests hook callback */ + usb_sof_cb /* Start Of Frame callback */ +}; + +/* + * Initialize the USB driver + */ +void init_usb_driver(USBDriver *usbp) { + /* + * Activates the USB driver and then the USB bus pull-up on D+. + * Note, a delay is inserted in order to not have to disconnect the cable + * after a reset. + */ + usbDisconnectBus(usbp); + chThdSleepMilliseconds(1500); + usbStart(usbp, &usbcfg); + usbConnectBus(usbp); + + chVTObjectInit(&keyboard_idle_timer); +#ifdef CONSOLE_ENABLE + obqObjectInit(&console_buf_queue, console_queue_buffer, CONSOLE_EPSIZE, CONSOLE_QUEUE_CAPACITY, console_queue_onotify, (void*)usbp); + chVTObjectInit(&console_flush_timer); +#endif +} + +/* + * Send remote wakeup packet + * Note: should not be called from ISR + */ +void send_remote_wakeup(USBDriver *usbp) { + (void)usbp; +#if defined(K20x) || defined(KL2x) +#if KINETIS_USB_USE_USB0 + USB0->CTL |= USBx_CTL_RESUME; + chThdSleepMilliseconds(15); + USB0->CTL &= ~USBx_CTL_RESUME; +#endif /* KINETIS_USB_USE_USB0 */ +#elif defined(STM32F0XX) || defined(STM32F1XX) /* K20x || KL2x */ + STM32_USB->CNTR |= CNTR_RESUME; + chThdSleepMilliseconds(15); + STM32_USB->CNTR &= ~CNTR_RESUME; +#else /* STM32F0XX || STM32F1XX */ +#warning Sending remote wakeup packet not implemented for your platform. +#endif /* K20x || KL2x */ +} + +/* --------------------------------------------------------- + * Keyboard functions + * --------------------------------------------------------- + */ + +/* keyboard IN callback hander (a kbd report has made it IN) */ +void kbd_in_cb(USBDriver *usbp, usbep_t ep) { + /* STUB */ + (void)usbp; + (void)ep; +} + +#ifdef NKRO_ENABLE +/* nkro IN callback hander (a nkro report has made it IN) */ +void nkro_in_cb(USBDriver *usbp, usbep_t ep) { + /* STUB */ + (void)usbp; + (void)ep; +} +#endif /* NKRO_ENABLE */ + +/* start-of-frame handler + * TODO: i guess it would be better to re-implement using timers, + * so that this is not going to have to be checked every 1ms */ +void kbd_sof_cb(USBDriver *usbp) { + (void)usbp; +} + +/* Idle requests timer code + * callback (called from ISR, unlocked state) */ +static void keyboard_idle_timer_cb(void *arg) { + USBDriver *usbp = (USBDriver *)arg; + + osalSysLockFromISR(); + + /* check that the states of things are as they're supposed to */ + if(usbGetDriverStateI(usbp) != USB_ACTIVE) { + /* do not rearm the timer, should be enabled on IDLE request */ + osalSysUnlockFromISR(); + return; + } + +#ifdef NKRO_ENABLE + if(!keyboard_nkro && keyboard_idle) { +#else /* NKRO_ENABLE */ + if(keyboard_idle) { +#endif /* NKRO_ENABLE */ + /* TODO: are we sure we want the KBD_ENDPOINT? */ + if(!usbGetTransmitStatusI(usbp, KBD_ENDPOINT)) { + usbStartTransmitI(usbp, KBD_ENDPOINT, (uint8_t *)&keyboard_report_sent, KBD_EPSIZE); + } + /* rearm the timer */ + chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); + } + + /* do not rearm the timer if the condition above fails + * it should be enabled again on either IDLE or SET_PROTOCOL requests */ + osalSysUnlockFromISR(); +} + +/* LED status */ +uint8_t keyboard_leds(void) { + return (uint8_t)(keyboard_led_stats & 0xFF); +} + +/* prepare and start sending a report IN + * not callable from ISR or locked state */ +void send_keyboard(report_keyboard_t *report) { + osalSysLock(); + if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return; + } + osalSysUnlock(); + +#ifdef NKRO_ENABLE + if(keyboard_nkro) { /* NKRO protocol */ + /* need to wait until the previous packet has made it through */ + /* can rewrite this using the synchronous API, then would wait + * until *after* the packet has been transmitted. I think + * this is more efficient */ + /* busy wait, should be short and not very common */ + osalSysLock(); + if(usbGetTransmitStatusI(&USB_DRIVER, NKRO_ENDPOINT)) { + /* Need to either suspend, or loop and call unlock/lock during + * every iteration - otherwise the system will remain locked, + * no interrupts served, so USB not going through as well. + * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ + osalThreadSuspendS(&(&USB_DRIVER)->epc[NKRO_ENDPOINT]->in_state->thread); + } + usbStartTransmitI(&USB_DRIVER, NKRO_ENDPOINT, (uint8_t *)report, sizeof(report_keyboard_t)); + osalSysUnlock(); + } else +#endif /* NKRO_ENABLE */ + { /* boot protocol */ + /* need to wait until the previous packet has made it through */ + /* busy wait, should be short and not very common */ + osalSysLock(); + if(usbGetTransmitStatusI(&USB_DRIVER, KBD_ENDPOINT)) { + /* Need to either suspend, or loop and call unlock/lock during + * every iteration - otherwise the system will remain locked, + * no interrupts served, so USB not going through as well. + * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ + osalThreadSuspendS(&(&USB_DRIVER)->epc[KBD_ENDPOINT]->in_state->thread); + } + usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT, (uint8_t *)report, KBD_EPSIZE); + osalSysUnlock(); + } + keyboard_report_sent = *report; +} + +/* --------------------------------------------------------- + * Mouse functions + * --------------------------------------------------------- + */ + +#ifdef MOUSE_ENABLE + +/* mouse IN callback hander (a mouse report has made it IN) */ +void mouse_in_cb(USBDriver *usbp, usbep_t ep) { + (void)usbp; + (void)ep; +} + +void send_mouse(report_mouse_t *report) { + osalSysLock(); + if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return; + } + osalSysUnlock(); + + /* TODO: LUFA manually waits for the endpoint to become ready + * for about 10ms for mouse, kbd, system; 1ms for nkro + * is this really needed? + */ + + osalSysLock(); + usbStartTransmitI(&USB_DRIVER, MOUSE_ENDPOINT, (uint8_t *)report, sizeof(report_mouse_t)); + osalSysUnlock(); +} + +#else /* MOUSE_ENABLE */ +void send_mouse(report_mouse_t *report) { + (void)report; +} +#endif /* MOUSE_ENABLE */ + +/* --------------------------------------------------------- + * Extrakey functions + * --------------------------------------------------------- + */ + +#ifdef EXTRAKEY_ENABLE + +/* extrakey IN callback hander */ +void extra_in_cb(USBDriver *usbp, usbep_t ep) { + /* STUB */ + (void)usbp; + (void)ep; +} + +static void send_extra_report(uint8_t report_id, uint16_t data) { + osalSysLock(); + if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return; + } + + report_extra_t report = { + .report_id = report_id, + .usage = data + }; + + usbStartTransmitI(&USB_DRIVER, EXTRA_ENDPOINT, (uint8_t *)&report, sizeof(report_extra_t)); + osalSysUnlock(); +} + +void send_system(uint16_t data) { + send_extra_report(REPORT_ID_SYSTEM, data); +} + +void send_consumer(uint16_t data) { + send_extra_report(REPORT_ID_CONSUMER, data); +} + +#else /* EXTRAKEY_ENABLE */ +void send_system(uint16_t data) { + (void)data; +} +void send_consumer(uint16_t data) { + (void)data; +} +#endif /* EXTRAKEY_ENABLE */ + +/* --------------------------------------------------------- + * Console functions + * --------------------------------------------------------- + */ + +#ifdef CONSOLE_ENABLE + +/* console IN callback hander */ +void console_in_cb(USBDriver *usbp, usbep_t ep) { + (void)ep; /* should have ep == CONSOLE_ENDPOINT, so use that to save time/space */ + uint8_t *buf; + size_t n; + + osalSysLockFromISR(); + + /* rearm the timer */ + chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); + + /* Freeing the buffer just transmitted, if it was not a zero size packet.*/ + if (usbp->epc[CONSOLE_ENDPOINT]->in_state->txsize > 0U) { + obqReleaseEmptyBufferI(&console_buf_queue); + } + + /* Checking if there is a buffer ready for transmission.*/ + buf = obqGetFullBufferI(&console_buf_queue, &n); + + if (buf != NULL) { + /* The endpoint cannot be busy, we are in the context of the callback, + so it is safe to transmit without a check.*/ + /* Should have n == CONSOLE_EPSIZE; check it? */ + usbStartTransmitI(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_EPSIZE); + } else { + /* Nothing to transmit.*/ + } + + osalSysUnlockFromISR(); +} + +/* Callback when data is inserted into the output queue + * Called from a locked state */ +void console_queue_onotify(io_buffers_queue_t *bqp) { + size_t n; + USBDriver *usbp = bqGetLinkX(bqp); + + if(usbGetDriverStateI(usbp) != USB_ACTIVE) + return; + + /* Checking if there is already a transaction ongoing on the endpoint.*/ + if (!usbGetTransmitStatusI(usbp, CONSOLE_ENDPOINT)) { + /* Trying to get a full buffer.*/ + uint8_t *buf = obqGetFullBufferI(&console_buf_queue, &n); + if (buf != NULL) { + /* Buffer found, starting a new transaction.*/ + /* Should have n == CONSOLE_EPSIZE; check this? */ + usbStartTransmitI(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_EPSIZE); + } + } +} + +/* Flush timer code + * callback (called from ISR, unlocked state) */ +static void console_flush_cb(void *arg) { + USBDriver *usbp = (USBDriver *)arg; + osalSysLockFromISR(); + + /* check that the states of things are as they're supposed to */ + if(usbGetDriverStateI(usbp) != USB_ACTIVE) { + /* rearm the timer */ + chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); + osalSysUnlockFromISR(); + return; + } + + /* If there is already a transaction ongoing then another one cannot be + started.*/ + if (usbGetTransmitStatusI(usbp, CONSOLE_ENDPOINT)) { + /* rearm the timer */ + chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); + osalSysUnlockFromISR(); + return; + } + + /* Checking if there only a buffer partially filled, if so then it is + enforced in the queue and transmitted.*/ + if(obqTryFlushI(&console_buf_queue)) { + size_t n,i; + uint8_t *buf = obqGetFullBufferI(&console_buf_queue, &n); + + osalDbgAssert(buf != NULL, "queue is empty"); + + /* zero the rest of the buffer (buf should point to allocated space) */ + for(i=n; i