diff --git a/hardware/teensy/cores/teensy3/WProgram.h b/hardware/teensy/cores/teensy3/WProgram.h index 6ead78f..469c90b 100644 --- a/hardware/teensy/cores/teensy3/WProgram.h +++ b/hardware/teensy/cores/teensy3/WProgram.h @@ -23,6 +23,7 @@ #include "usb_serial.h" #include "usb_seremu.h" #include "usb_keyboard.h" +#include "usb_nkro.h" #include "usb_mouse.h" #include "usb_joystick.h" #include "usb_midi.h" diff --git a/hardware/teensy/cores/teensy3/usb_desc.c b/hardware/teensy/cores/teensy3/usb_desc.c index 372b06a..c85e9c4 100644 --- a/hardware/teensy/cores/teensy3/usb_desc.c +++ b/hardware/teensy/cores/teensy3/usb_desc.c @@ -120,6 +120,33 @@ static uint8_t keyboard_report_desc[] = { }; #endif +#ifdef NKRO_INTERFACE +static uint8_t nkro_hid_report_desc[] = { + 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 + // 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 +}; +#endif + #ifdef MOUSE_INTERFACE // Mouse Protocol 1, HID 1.11 spec, Appendix B, page 59-60, with wheel extension static uint8_t mouse_report_desc[] = { @@ -632,6 +659,34 @@ static uint8_t config_descriptor[CONFIG_DESC_SIZE] = { JOYSTICK_INTERVAL, // bInterval #endif // JOYSTICK_INTERFACE +#ifdef NKRO_INTERFACE + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + NKRO_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x00, // bInterfaceSubClass (0x01 = Boot) + 0x00, // bInterfaceProtocol (0x01 = Keyboard) + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(nkro_hid_report_desc)), // wDescriptorLength + MSB(sizeof(nkro_hid_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + NKRO_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + NKRO_SIZE, 0, // wMaxPacketSize + NKRO_INTERVAL, // bInterval +#endif // NKRO_INTERFACE }; @@ -698,6 +753,10 @@ const usb_descriptor_list_t usb_descriptor_list[] = { {0x2200, JOYSTICK_INTERFACE, joystick_report_desc, sizeof(joystick_report_desc)}, {0x2100, JOYSTICK_INTERFACE, config_descriptor+JOYSTICK_DESC_OFFSET, 9}, #endif +#ifdef NKRO_INTERFACE + {0x2200, NKRO_INTERFACE, nkro_hid_report_desc, sizeof(nkro_hid_report_desc)}, + {0x2100, NKRO_INTERFACE, config_descriptor+NKRO_DESC_OFFSET, 9}, +#endif #ifdef RAWHID_INTERFACE {0x2200, RAWHID_INTERFACE, rawhid_report_desc, sizeof(rawhid_report_desc)}, {0x2100, RAWHID_INTERFACE, config_descriptor+RAWHID_DESC_OFFSET, 9}, diff --git a/hardware/teensy/cores/teensy3/usb_desc.h b/hardware/teensy/cores/teensy3/usb_desc.h index 00ca926..4ca9f25 100644 --- a/hardware/teensy/cores/teensy3/usb_desc.h +++ b/hardware/teensy/cores/teensy3/usb_desc.h @@ -84,9 +84,9 @@ let me know? http://forum.pjrc.com/forums/4-Suggestions-amp-Bug-Reports #define PRODUCT_NAME {'K','e','y','b','o','a','r','d','/','M','o','u','s','e','/','J','o','y','s','t','i','c','k'} #define PRODUCT_NAME_LEN 23 #define EP0_SIZE 64 - #define NUM_ENDPOINTS 5 + #define NUM_ENDPOINTS 6 #define NUM_USB_BUFFERS 24 - #define NUM_INTERFACE 4 + #define NUM_INTERFACE 5 #define SEREMU_INTERFACE 2 // Serial emulation #define SEREMU_TX_ENDPOINT 1 #define SEREMU_TX_SIZE 64 @@ -106,16 +106,23 @@ let me know? http://forum.pjrc.com/forums/4-Suggestions-amp-Bug-Reports #define JOYSTICK_ENDPOINT 4 #define JOYSTICK_SIZE 16 #define JOYSTICK_INTERVAL 2 + #define NKRO_INTERFACE 4 + #define NKRO_ENDPOINT 6 + #define NKRO_SIZE 32 + #define NKRO_REPORT_KEYS ( NKRO_SIZE - 1 ) + #define NKRO_INTERVAL 1 #define KEYBOARD_DESC_OFFSET (9 + 9) #define MOUSE_DESC_OFFSET (9 + 9+9+7 + 9) #define SEREMU_DESC_OFFSET (9 + 9+9+7 + 9+9+7 + 9) #define JOYSTICK_DESC_OFFSET (9 + 9+9+7 + 9+9+7 + 9+9+7+7 + 9) - #define CONFIG_DESC_SIZE (9 + 9+9+7 + 9+9+7 + 9+9+7+7 + 9+9+7) + #define NKRO_DESC_OFFSET (9 + 9+9+7 + 9+9+7 + 9+9+7+7 + 9+9+7 + 9) + #define CONFIG_DESC_SIZE (9 + 9+9+7 + 9+9+7 + 9+9+7+7 + 9+9+7 + 9+9+7) #define ENDPOINT1_CONFIG ENDPOINT_TRANSIMIT_ONLY #define ENDPOINT2_CONFIG ENDPOINT_RECEIVE_ONLY #define ENDPOINT3_CONFIG ENDPOINT_TRANSIMIT_ONLY #define ENDPOINT4_CONFIG ENDPOINT_TRANSIMIT_ONLY #define ENDPOINT5_CONFIG ENDPOINT_TRANSIMIT_ONLY + #define ENDPOINT6_CONFIG ENDPOINT_TRANSIMIT_ONLY #elif defined(USB_SERIAL_HID) #define VENDOR_ID 0x16C0 @@ -128,9 +135,9 @@ let me know? http://forum.pjrc.com/forums/4-Suggestions-amp-Bug-Reports #define PRODUCT_NAME {'S','e','r','i','a','l','/','K','e','y','b','o','a','r','d','/','M','o','u','s','e','/','J','o','y','s','t','i','c','k'} #define PRODUCT_NAME_LEN 30 #define EP0_SIZE 64 - #define NUM_ENDPOINTS 6 + #define NUM_ENDPOINTS 7 #define NUM_USB_BUFFERS 30 - #define NUM_INTERFACE 5 + #define NUM_INTERFACE 6 #define CDC_IAD_DESCRIPTOR 1 #define CDC_STATUS_INTERFACE 0 #define CDC_DATA_INTERFACE 1 // Serial @@ -152,16 +159,23 @@ let me know? http://forum.pjrc.com/forums/4-Suggestions-amp-Bug-Reports #define JOYSTICK_ENDPOINT 6 #define JOYSTICK_SIZE 16 #define JOYSTICK_INTERVAL 1 + #define NKRO_INTERFACE 5 // NKRO interface + #define NKRO_ENDPOINT 7 + #define NKRO_SIZE 32 + #define NKRO_REPORT_KEYS ( NKRO_SIZE - 1 ) + #define NKRO_INTERVAL 1 #define KEYBOARD_DESC_OFFSET (9+8 + 9+5+5+4+5+7+9+7+7 + 9) #define MOUSE_DESC_OFFSET (9+8 + 9+5+5+4+5+7+9+7+7 + 9+9+7 + 9) #define JOYSTICK_DESC_OFFSET (9+8 + 9+5+5+4+5+7+9+7+7 + 9+9+7 + 9+9+7 + 9) - #define CONFIG_DESC_SIZE (9+8 + 9+5+5+4+5+7+9+7+7 + 9+9+7 + 9+9+7 + 9+9+7) + #define NKRO_DESC_OFFSET (9+8 + 9+5+5+4+5+7+9+7+7 + 9+9+7 + 9+9+7 + 9+9+7 + 9) + #define CONFIG_DESC_SIZE (9+8 + 9+5+5+4+5+7+9+7+7 + 9+9+7 + 9+9+7 + 9+9+7 + 9+9+7) #define ENDPOINT1_CONFIG ENDPOINT_TRANSIMIT_ONLY #define ENDPOINT2_CONFIG ENDPOINT_TRANSIMIT_ONLY #define ENDPOINT3_CONFIG ENDPOINT_RECEIVE_ONLY #define ENDPOINT4_CONFIG ENDPOINT_TRANSIMIT_ONLY #define ENDPOINT5_CONFIG ENDPOINT_TRANSIMIT_ONLY #define ENDPOINT6_CONFIG ENDPOINT_TRANSIMIT_ONLY + #define ENDPOINT7_CONFIG ENDPOINT_TRANSIMIT_ONLY #elif defined(USB_MIDI) #define VENDOR_ID 0x16C0 diff --git a/hardware/teensy/cores/teensy3/usb_dev.h b/hardware/teensy/cores/teensy3/usb_dev.h index 693cb02..c70540a 100644 --- a/hardware/teensy/cores/teensy3/usb_dev.h +++ b/hardware/teensy/cores/teensy3/usb_dev.h @@ -45,6 +45,13 @@ extern uint8_t keyboard_idle_count; extern volatile uint8_t keyboard_leds; #endif +#ifdef NKRO_INTERFACE +extern uint8_t nkro_report_data[NKRO_SIZE]; +extern uint8_t nkro_protocol; +extern uint8_t nkro_idle_config; +extern uint8_t nkro_idle_count; +#endif + #ifdef MIDI_INTERFACE extern void usb_midi_flush_output(void); #endif diff --git a/hardware/teensy/cores/teensy3/usb_inst.cpp b/hardware/teensy/cores/teensy3/usb_inst.cpp index ce15aa9..99aa1af 100644 --- a/hardware/teensy/cores/teensy3/usb_inst.cpp +++ b/hardware/teensy/cores/teensy3/usb_inst.cpp @@ -10,6 +10,7 @@ usb_mouse_class Mouse; usb_joystick_class Joystick; uint8_t usb_joystick_class::manual_mode = 0; usb_seremu_class Serial; +usb_nkro_class Nkro; #endif #ifdef USB_SERIAL_HID @@ -18,6 +19,7 @@ usb_keyboard_class Keyboard; usb_mouse_class Mouse; usb_joystick_class Joystick; uint8_t usb_joystick_class::manual_mode = 0; +usb_nkro_class Nkro; #endif #ifdef USB_MIDI diff --git a/hardware/teensy/cores/teensy3/usb_nkro.c b/hardware/teensy/cores/teensy3/usb_nkro.c new file mode 100644 index 0000000..d0a0e24 --- /dev/null +++ b/hardware/teensy/cores/teensy3/usb_nkro.c @@ -0,0 +1,116 @@ +#include "usb_dev.h" +#include "usb_nkro.h" +#include "core_pins.h" // for yield() +#include "keylayouts.h" +//#include "HardwareSerial.h" +#include // for memcpy() + +#ifdef NKRO_INTERFACE // defined by usb_dev.h -> usb_desc.h + +// byte0: which modifier keys are currently pressed +// 1=left ctrl, 2=left shift, 4=left alt, 8=left gui +// 16=right ctrl, 32=right shift, 64=right alt, 128=right gui +// bytes1-NKRO_SIZE: which keys are currently pressed, one bit +// per key for codes 0 to ((8*NKRO_REPORT_SIZE) - 1) +uint8_t nkro_report_data[NKRO_SIZE]; + +// protocol setting from the host. We use exactly the same report +// either way, so this variable only stores the setting since we +// are required to be able to report which setting is in use. +uint8_t nkro_protocol=1; + +// the idle configuration, how often we send the report to the +// host (ms * 4) even when it hasn't changed +uint8_t nkro_idle_config=125; + +// count until idle timeout +uint8_t nkro_idle_count=0; + + +// NKRO Keyboard +void usb_nkro_reset_keys() +{ + uint8_t index; + for (index=0; index < NKRO_SIZE; index++) + { + nkro_report_data[index] = 0; + } +} + +void usb_nkro_reset_key(uint8_t usb_keycode) +{ + uint8_t bit = usb_keycode % 8; + uint8_t byte = (usb_keycode / 8) + 1; + + if (usb_keycode >= 240 && usb_keycode <= 247) + { + // Reset a modifier key + nkro_report_data[0] &= ~(1<< bit); + } + else if (byte > 0 && byte <= NKRO_REPORT_KEYS) + { + nkro_report_data[byte] &= ~(1 << bit); + } +} + +void usb_nkro_set_key(uint8_t usb_keycode) +{ + uint8_t bit = usb_keycode % 8; + uint8_t byte = (usb_keycode / 8) + 1; + + if (usb_keycode >= 240 && usb_keycode <= 247) + { + // Reset a modifier key + nkro_report_data[0] |= (1 << bit); + } + else if (byte > 0 && byte <= NKRO_REPORT_KEYS) + { + nkro_report_data[byte] |= (1 << bit); + } +} + +// Maximum number of transmit packets to queue so we don't starve other endpoints for memory +#define TX_PACKET_LIMIT 4 + +static uint8_t transmit_previous_timeout=0; + +// When the PC isn't listening, how long do we wait before discarding data? +#define TX_TIMEOUT_MSEC 50 + +#if F_CPU == 96000000 + #define TX_TIMEOUT (TX_TIMEOUT_MSEC * 596) +#elif F_CPU == 48000000 + #define TX_TIMEOUT (TX_TIMEOUT_MSEC * 428) +#elif F_CPU == 24000000 + #define TX_TIMEOUT (TX_TIMEOUT_MSEC * 262) +#endif + + +int usb_nkro_send_nkro_now(void) +{ + uint32_t wait_count=0; + usb_packet_t *tx_packet; + + while (1) { + if (!usb_configuration) { + return -1; + } + if (usb_tx_packet_count(NKRO_ENDPOINT) < TX_PACKET_LIMIT) { + tx_packet = usb_malloc(); + if (tx_packet) break; + } + if (++wait_count > TX_TIMEOUT || transmit_previous_timeout) { + transmit_previous_timeout = 1; + return -1; + } + yield(); + } + memcpy(tx_packet->buf, nkro_report_data, NKRO_SIZE); + tx_packet->len = NKRO_SIZE; + usb_tx(NKRO_ENDPOINT, tx_packet); + + return 0; + +} + +#endif // NKRO_INTERFACE diff --git a/hardware/teensy/cores/teensy3/usb_nkro.h b/hardware/teensy/cores/teensy3/usb_nkro.h new file mode 100644 index 0000000..7ab0b79 --- /dev/null +++ b/hardware/teensy/cores/teensy3/usb_nkro.h @@ -0,0 +1,42 @@ +#ifndef USBnkro_h_ +#define USBnkro_h_ + +#include "keylayouts.h" + +#if defined(USB_HID) || defined(USB_SERIAL_HID) + +#include + +// C language implementation +#ifdef __cplusplus +extern "C" { +#endif +void usb_nkro_reset_keys(); +void usb_nkro_reset_key(uint8_t key); +void usb_nkro_set_key(uint8_t key); +int usb_nkro_send_nkro_now(); +#ifdef __cplusplus +} +#endif + + + +// C++ interface +#ifdef __cplusplus +#include "Stream.h" + +class usb_nkro_class +{ + public: + void reset_keys() { usb_nkro_reset_keys(); } + void reset_key(uint8_t key) { usb_nkro_reset_key(key); } + void set_key(uint8_t key) { usb_nkro_set_key(key); } + int send_nkro_now(void) { return usb_nkro_send_nkro_now(); } +}; + +extern usb_nkro_class Nkro; + +#endif // __cplusplus + +#endif // USB_HID || USB_SERIAL_HID +#endif // USBnkro_h_