/* * (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 #include "hook.h" /* TMK hooks */ __attribute__((weak)) void hook_usb_wakeup(void) { #ifdef SLEEP_LED_ENABLE sleep_led_disable(); // NOTE: converters may not accept this led_set(host_keyboard_leds()); #endif /* SLEEP_LED_ENABLE */ } __attribute__((weak)) void hook_usb_suspend_entry(void) { #ifdef SLEEP_LED_ENABLE sleep_led_enable(); #endif /* SLEEP_LED_ENABLE */ } /* --------------------------------------------------------- * 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), 0x26, 0xFF, 0x00, // 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) 0x26, 0xb7, 0x00, // 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]"); hook_usb_suspend_entry(); return; case USB_EVENT_WAKEUP: //TODO: from ISR! print("[W]"); suspend_wakeup_init(); hook_usb_wakeup(); 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