diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fed187..af7482d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,10 +11,10 @@ add_subdirectory(${PICO_PIO_USB_PATH} pico_pio_usb) add_executable(${PROJECT}) target_sources(${PROJECT} PRIVATE - main_device.c - main_host.c - usb_descriptors.c hyperx_elite2.c + main.c + usb_host.c + usb_device.c ) # print memory usage, enable all warnings diff --git a/build/hyperx_kb_rgb.uf2 b/build/hyperx_kb_rgb.uf2 index 1766647..fce9bd0 100644 Binary files a/build/hyperx_kb_rgb.uf2 and b/build/hyperx_kb_rgb.uf2 differ diff --git a/hyperx_elite2.c b/hyperx_elite2.c index 2ad202e..5103cdf 100644 --- a/hyperx_elite2.c +++ b/hyperx_elite2.c @@ -5,11 +5,13 @@ #include "hardware/adc.h" #include "hyperx_elite2.h" -#include "usb_descriptors.h" static bool sending = false; -static absolute_time_t lastTime; +static absolute_time_t lastSend; +static absolute_time_t lastRead; static bool backlight = false; +static uint16_t adc_value = 0; +static bool mute = false; static const unsigned int SKIP_INDICES[] = { 23, 29, 41, 47, 70, 71, 76, 87, 88, 93, 99, 100, 102, 108, 113 }; @@ -20,30 +22,23 @@ static uint8_t color_idx = 0; static uint8_t skipped = 0; const unsigned int* skip_idx = &SKIP_INDICES[0]; -static unsigned char nkro_buf[NKRO_BUF_SIZE]; -static unsigned char nkro_buf1[NKRO_BUF_SIZE]; -static unsigned char nkro_buf2[NKRO_BUF_SIZE]; -static unsigned char mediakeys[1] = {0}; -static unsigned char key_buf[KEY_BUF_SIZE]; -static uint8_t buf_start=0; -static uint8_t buf_end=0; -static uint8_t mediabit=0; -static uint8_t pos=0; -static bool mute=false; -static bool sendkeys=false; -static bool sendmedia=false; +static void send_color(uint8_t dev_addr, uint8_t red, uint8_t green, uint8_t blue); +static void send_initial(uint8_t dev_addr); void get_light() { - // get ADC reading from LDR + // get ADC reading from LDR every 500ms // if above threshold, set backlight to off - if (adc_read() > 400) { - backlight = false; - } else { - backlight = true; + if ( absolute_time_diff_us(lastRead, get_absolute_time()) >= 500000) { + adc_value = adc_read(); + if (backlight && adc_value >= LDR_OFF_THRESHOLD) { + backlight = false; + } else if (!backlight && adc_value <= LDR_ON_THRESHOLD) { + backlight = true; + } } } -void rgb_task(uint8_t dev_addr, uint8_t instance, uint8_t report_id) { +void rgb_task(uint8_t dev_addr) { // the RGB protocol used by HyperX sends individual key RGB data in // multiple packets // the code here will determine if we are in the middle of sending @@ -51,26 +46,26 @@ void rgb_task(uint8_t dev_addr, uint8_t instance, uint8_t report_id) { // packet if so at a rate of one packet every 20ms // otherwise, wait 1s before sending the next set of color packets if (sending) { - if ( absolute_time_diff_us(lastTime, get_absolute_time()) > 20000) { + if ( absolute_time_diff_us(lastSend, get_absolute_time()) >= 20000) { if( backlight) { - send_color(dev_addr, instance, report_id, 0x20, 0x20, 0x20); + send_color(dev_addr, 0x20, 0x20, 0x20); // send a dim white color (#202020) for all keys } else{ - send_color(dev_addr, instance, report_id, 0x00, 0x00, 0x00); + send_color(dev_addr, 0x00, 0x00, 0x00); // turn off all lighting by sending (#000000) for all keys } - lastTime = get_absolute_time(); + lastSend = get_absolute_time(); } } else { - if ( absolute_time_diff_us(lastTime, get_absolute_time()) > 500000) { - send_initial(dev_addr, instance, report_id); - lastTime = get_absolute_time(); + if ( absolute_time_diff_us(lastSend, get_absolute_time()) >= 500000) { + send_initial(dev_addr); + lastSend = get_absolute_time(); } } } // send an individual color packett with the desired RGB color -void send_color(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t red, uint8_t green, uint8_t blue) { +static void send_color(uint8_t dev_addr, uint8_t red, uint8_t green, uint8_t blue) { memset(buf, 0x00, BUF_SIZE); buf_idx = 0; @@ -129,7 +124,7 @@ void send_color(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t r buf_idx += 4; } - if(tuh_hid_set_report(dev_addr, instance, report_id, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE)) + if(tuh_hid_set_report(dev_addr, 0, 0, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE)) { // packet sent successfully, increment packets_sent++; @@ -140,7 +135,7 @@ void send_color(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t r // all keys have been sent, but the protocol expects NUM_PACKETS // packets to be sent in total; if we have not sent enough packets, // send extra packets of all 0x00 until done - if(tuh_hid_set_report(dev_addr, instance, report_id, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE)) + if(tuh_hid_set_report(dev_addr, 0, 0, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE)) { packets_sent++; } @@ -153,7 +148,7 @@ void send_color(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t r // send the special initialization packet that tells the keyboard to expect // color packets to follow -void send_initial(uint8_t dev_addr, uint8_t instance, uint8_t report_id) { +static void send_initial(uint8_t dev_addr) { memset(buf, 0x00, BUF_SIZE); color_idx = 0; @@ -164,7 +159,7 @@ void send_initial(uint8_t dev_addr, uint8_t instance, uint8_t report_id) { buf[0x00] = 0x04; buf[0x01] = 0xf2; - if (tuh_hid_set_report(dev_addr, instance, report_id, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE)) + if (tuh_hid_set_report(dev_addr, 0, 0, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE)) { // we have begun sending packets, so set flag to continue sending // packets at regular intervals @@ -177,148 +172,15 @@ void send_initial(uint8_t dev_addr, uint8_t instance, uint8_t report_id) { void startADC() { stdio_init_all(); adc_init(); - adc_gpio_init(28); - adc_select_input(2); + adc_gpio_init(LDR_PIN); + adc_select_input(LDR_ADC); } -// process media key presses and prepare HID Consumer Control byte -void send_media(uint8_t const* report, uint16_t len) { - (void) len; - if ( report[0]==0x05 ) { - switch(report[2]) { - case 0xB0: // next - mediabit = 0; - break; - case 0xB1: // prev - mediabit = 1; - break; - case 0xB3: // play/pause - mediabit = 2; - break; - case 0xB5: // mute - mediabit = 3; - if (report[3]) { - mute = !mute; - } - break; - default: - return; - } - if (report[3]) { - SET_KEYBIT(mediakeys, mediabit); - } else { - CLEAR_KEYBIT(mediakeys, mediabit); - } - // set flag to send media keys during the next sending round - sendmedia = true; - } else if ( report[0]==0x03 ) { - switch(report[1]) { - case 0xE9: // vol up - SET_KEYBIT(mediakeys, 4); - break; - case 0xEA: // vol down - SET_KEYBIT(mediakeys, 5); - break; - case 0x00: // none - CLEAR_KEYBIT(mediakeys, 4); - CLEAR_KEYBIT(mediakeys, 5); - break; - default: - return; - } - sendmedia = true; +// forward HID report after processing +bool forward_report(uint8_t instance, uint8_t const* report, uint16_t len) { + if (instance == 0x01 && report[0] == 0x03 && report[1] == 0xE2) { + mute = !mute; } + + return tud_hid_n_report(instance, 0, report, len); } - -// process new HID reports from the keyboard and set them up to forward to host -void process_report(uint8_t instance, uint8_t const* report, uint16_t len) { - if (instance==HYPERX_ITF_KEYBOARD || instance==HYPERX_ITF_NKRO) { - // received regular keyboard key events - if (sendkeys) { - // keyboard is currently sending to the host, add to queue - key_buf[buf_end]=len; - key_buf[buf_end+2]=instance; - memcpy(&key_buf[buf_end+2], report, len); - buf_end += (len + 2); - } else { - // immediately process keys and send new HID report to host - updatekeys(instance, report, len); - } - } else if (instance==HYPERX_ITF_KEYPRESS) { - // received media key events - send_media(report, len); - } -} - -// merge key states from boot keyboard and NKRO keyboard interfaces into a -// single packet to send to the host -void updatekeys(uint8_t instance, uint8_t const* report, uint16_t len) { - if(instance==HYPERX_ITF_KEYBOARD) { - boot2nkro(report, nkro_buf1, len); - sendkeys=true; - } else if (instance==HYPERX_ITF_NKRO) { - memcpy(nkro_buf2, report, len); - sendkeys=true; - } - merge_bitmap(nkro_buf, nkro_buf1, nkro_buf2); -} - -// set initial state of keyboard -void reset_keyboard() { - memset(nkro_buf, 0x00, NKRO_BUF_SIZE); - memset(nkro_buf1, 0x00, NKRO_BUF_SIZE); - memset(nkro_buf2, 0x00, NKRO_BUF_SIZE); - memset(key_buf, 0x00, KEY_BUF_SIZE); - buf_start=0; - buf_end=0; - sendkeys=false; - sendmedia=false; -} - -// check if there are any key events waiting to be sent to the host and send -void keyboard_task() { - if (sendkeys) { - // send the current keyboard state to the host - if (tud_hid_report(REPORT_ID_KEYBOARD, nkro_buf, NKRO_BUF_SIZE)) { - sendkeys=false; - } - } else if (sendmedia) { - // send the media keys state to the the host - if (tud_hid_report(REPORT_ID_CONSUMER_CONTROL, mediakeys, 1)) { - sendmedia=false; - } - } else if (buf_start != buf_end) { - // previous key state has been send, but a new key state is waiting - // in the buffer - // load from buffer and setup for sending in the next round - updatekeys(key_buf[buf_start+1], &key_buf[buf_start+2], key_buf[buf_start]); - buf_start += (key_buf[buf_start] + 2); - } else if (buf_start == buf_end) { - // the buffer has been cleared, so reset the buffer position to front - buf_start = 0; - buf_end = 0; - } -} - -// convert a boot keyboard packet into the NKRO bitmap format used by HyperX -void boot2nkro(uint8_t const* boot_report, uint8_t* nkro_report, uint16_t len) { - memset(nkro_report, 0x00, NKRO_BUF_SIZE); - (void) len; - // copy modifiers - nkro_report[0] = boot_report[0]; - - // set regular keyboard keys - for (pos=2; pos<8; pos++) { - if (boot_report[pos] > 0) { - SET_KEYBIT(nkro_report, boot_report[pos]+8); - } - } -} - -// merge two NKRO keyboard bitmaps into a single NKRO keyboard bitmap -void merge_bitmap(uint8_t* nkro_report, uint8_t const* nkro_report1, uint8_t const* nkro_report2){ - for (pos=0; pos < NKRO_BUF_SIZE; pos++) { - nkro_report[pos] = nkro_report1[pos] | nkro_report2[pos]; - } -} - diff --git a/hyperx_elite2.h b/hyperx_elite2.h index d125425..f5a30b6 100644 --- a/hyperx_elite2.h +++ b/hyperx_elite2.h @@ -1,6 +1,11 @@ #ifndef HYPERX_ELITE2_H_ #define HYPERX_ELITE2_H_ +#define LDR_PIN 28 +#define LDR_ADC 2 +#define LDR_OFF_THRESHOLD 500 +#define LDR_ON_THRESHOLD 400 + enum { HYPERX_KEYBOARD_VID = 0x0951, @@ -8,35 +13,11 @@ enum NUM_KEYS = 128, BUF_SIZE = 64, NUM_PACKETS = 10, - NKRO_BUF_SIZE = 15, - KEY_BUF_SIZE = 16*16, -}; - -enum -{ - HYPERX_ITF_KEYBOARD = 0, - HYPERX_ITF_KEYPRESS, - HYPERX_ITF_NKRO }; void get_light(); -void rgb_task(uint8_t dev_addr, uint8_t instance, uint8_t report_id); -void send_color(uint8_t dev_addr, uint8_t instance, uint8_t report_id, - uint8_t red, uint8_t green, uint8_t blue); -void send_initial(uint8_t dev_addr, uint8_t instance, uint8_t report_id); +void rgb_task(uint8_t dev_addr); void startADC(); -void send_media(uint8_t const* report, uint16_t len); -void keyboard_task(); -void reset_keyboard(); -void updatekeys(uint8_t instance, uint8_t const* report, uint16_t len); -void process_report(uint8_t instance, uint8_t const* report, uint16_t len); - - -void merge_bitmap(uint8_t* nkro_report, uint8_t const* nkro_report1, uint8_t const* nkro_report2); -void boot2nkro(uint8_t const* boot_report, uint8_t* nkro_report, uint16_t len); - -#define SET_KEYBIT(array, index) do { (array)[(index) / 8] |= 1 << ((index) % 8); } while(0) -#define CLEAR_KEYBIT(array, index) do { (array)[(index) / 8] &= ~(1 << ((index) % 8)); } while(0) - +bool forward_report(uint8_t instance, uint8_t const* report, uint16_t len); #endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..ec738be --- /dev/null +++ b/main.c @@ -0,0 +1,24 @@ +#include "pico/stdlib.h" +#include "pico/multicore.h" +#include "pico/bootrom.h" +#include "hardware/clocks.h" + +#include "usb_device.h" +#include "usb_host.h" + +// main loop +int main(void) { + // default 125MHz is not appropreate. Sysclock should be multiple of 12MHz. + set_sys_clock_khz(144000, true); + + sleep_ms(10); + + // run usb host on core 1 + multicore_reset_core1(); + multicore_launch_core1(usb_host_main); + + // run usb device on core 0 + usb_device_main(); + + return 0; +} diff --git a/main_device.c b/main_device.c deleted file mode 100644 index 06a7170..0000000 --- a/main_device.c +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include -#include - -#include "pico/stdlib.h" -#include "pico/multicore.h" -#include "pico/bootrom.h" -#include "hardware/clocks.h" - -#include "tusb.h" -#include "usb_descriptors.h" - -#include "hyperx_elite2.h" -#include "main_host.h" - -static absolute_time_t lastRead; - -extern void core1_main(); - -int main(void) { - // default 125MHz is not appropreate. Sysclock should be multiple of 12MHz. - set_sys_clock_khz(144000, true); - - // start ADC for reading LDR - startADC(); - - sleep_ms(10); - - multicore_reset_core1(); - // run TinyUSB host on core 1 - multicore_launch_core1(core1_main); - - // run TinyUSB device on core 0 - tud_init(0); - - while (true) { - if ( absolute_time_diff_us(lastRead, get_absolute_time()) > 500000) { - // read the ADC every 500ms to determine RGB lighting - get_light(); - } - // check keyboard packet queue and send to TinyUSB device - keyboard_task(); - // TinyUSB device task - tud_task(); - } - - return 0; -} - - -// Invoked when received SET_REPORT control request or -// received data on OUT endpoint ( Report ID = 0, Type = 0 ) - -void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) -{ - - - if (instance == 0 && report_type==HID_REPORT_TYPE_OUTPUT && report_id==REPORT_ID_KEYBOARD) { - // received keyboard indicator LED status from host, so update on - // the keyboard - set_indicator(buffer,bufsize); - } - - // echo the HID report to CDC for debugging - char tempbuf[128]; - size_t count; - if(bufsize>0) { - count = sprintf(tempbuf, "\nReceived device set report type %u on interface %u with ID %u (%u)\n", report_type, instance, report_id, bufsize); - tud_cdc_write(tempbuf, count); - for(int i=0;i -#include -#include - -#include "pico/stdlib.h" -#include "pico/multicore.h" -#include "pico/bootrom.h" - -#include "pio_usb.h" -#include "tusb.h" -#include "usb_descriptors.h" -#include "hyperx_elite2.h" - -#include "main_host.h" - -static bool enabled=false; -static uint8_t kb_addr=0; - -// main process for core 1 to handle USB host events -void core1_main() { - sleep_ms(10); - - // configure PIO USB for use as TinyUSB host - pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG; - pio_cfg.alarm_pool = (void*) alarm_pool_create(2,1); - tuh_configure(1, TUH_CFGID_RPI_PIO_USB_CONFIGURATION, &pio_cfg); - tuh_hid_set_default_protocol(HID_PROTOCOL_REPORT); - tuh_init(1); - - - while (true) { - if (enabled) { - // keyboard is plugged in, so send RGB states on regular intervals - rgb_task(kb_addr, HYPERX_ITF_KEYBOARD, REPORT_ID_KEYBOARD); - } - tuh_task(); // tinyusb host task - } -} - -// Invoked when device with hid interface is mounted -void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) -{ - (void)desc_report; - (void)desc_len; - - uint16_t vid, pid; - tuh_vid_pid_get(dev_addr, &vid, &pid); - const char* protocol_str[] = { "None", "Keyboard", "Mouse" }; - uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); - - // send device vid:pid information to CDC for debugging - char tempbuf[128]; - size_t count = sprintf(tempbuf, "[%04x:%04x][%u] HID Interface %u, Protocol = %s \n", vid, pid, dev_addr, instance, protocol_str[itf_protocol]); - tud_cdc_write(tempbuf, count); - tud_cdc_write_flush(); - - // request to receive report if mounted device matches HyperX Alloy Elite 2 - if (vid==HYPERX_KEYBOARD_VID && pid==HYPERX_ELITE2_PID) - { - enabled = true; - kb_addr = dev_addr; - reset_keyboard(); - if ( !tuh_hid_receive_report(dev_addr, instance) ) - { - //tud_cdc_write_str("Error: cannot request report\r\n"); - } - } - -} - -// Invoked when device with hid interface is un-mounted -void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) -{ - (void) instance; - - if(dev_addr==kb_addr){ - kb_addr=0; - enabled=false; - } -} - -// Invoked when received report from device via interrupt endpoint -void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) -{ - char tempbuf[64]; - size_t count; - if(len>0) { - // echo HID report to CDC for debugging - count = sprintf(tempbuf, "\n[%u] Received message on interface %u (%u)\n", dev_addr, instance, len); - tud_cdc_write(tempbuf, count); - for(int i=0;i 31 ) chr_count = 31; - - // Convert ASCII string into UTF-16 - for(uint8_t i=0; i +#include +#include +#include + +#include "pico/stdlib.h" +#include "bsp/board_api.h" +#include "tusb.h" + +#include "hyperx_elite2.h" +#include "usb_host.h" + +#include "usb_device.h" + +char cdc_buf[64]; +uint16_t cdc_len; +size_t cdc_count; +device_state_t device_state; + +static uint8_t desc_configuration[DESC_CFG_MAX]; +static uint16_t _desc_str[32+1]; + +static tusb_desc_device_t const desc_device = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + + // Use Interface Association Descriptor (IAD) for CDC + // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; +static char const* string_desc_arr [] = +{ + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "Raspberry Pi", // 1: Manufacturer + "Pico HyperX Elite 2 RGB Controller", // 2: Product + NULL, // 3: Serials, should use chip ID + "Pico HyperX Elite 2 CDC", // 4: CDC + "Pico HyperX Elite 2 HID", // 5: HID +}; + +static void usb_device_init(void); + +// main task for USB device +void usb_device_main(void) { + // start ADC for reading LDR + startADC(); + + // initialize TinyUSB device + device_state = DEVICE_INACTIVE; + usb_device_init(); + + while (true) { + switch ( device_state ) { + case DEVICE_ACTIVE: + break; + case DEVICE_INACTIVE: + break; + case DEVICE_RESTART: + if (tud_disconnect()) { + sleep_ms(100); + if (tud_connect()) { + if ( host_state == HOST_INACTIVE ) { + device_state = DEVICE_INACTIVE; + } else { + device_state = DEVICE_ACTIVE; + } + } + } + break; + default: + break; + } + get_light(); // get the ADC LDR reading + tud_task(); + tud_cdc_write_flush(); + } +} + +// initialize the TinyUSB device +static void usb_device_init(void) { + // run TinyUSB device + tusb_rhport_init_t dev_init = { + .role = TUSB_ROLE_DEVICE, + .speed = TUSB_SPEED_AUTO, + }; + tusb_init(BOARD_TUH_RHPORT, &dev_init); +} + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const * tud_descriptor_device_cb(void) +{ + return (uint8_t const *) &desc_device; +} + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) +{ + (void) index; // for multiple configurations + + memset(desc_configuration, 0, sizeof(desc_configuration)); + uint8_t desc_initial[TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN+1] = { + TUD_CONFIG_DESCRIPTOR(1, 2+num_mounted, 0, TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN+num_mounted*TUD_HID_DESC_LEN, 0x00, 100), + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64) + }; + memcpy(desc_configuration, desc_initial, TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN); + + if ( descriptors != NULL) { + struct report_desc *descriptor; + for (uint8_t i=0; idev_addr, i); + uint8_t hid_desc[TUD_HID_DESC_LEN+1] = { + TUD_HID_DESCRIPTOR(ITF_NUM_HID+i, 5, HID_ITF_PROTOCOL_NONE, descriptor->desc_len, EPNUM_HID+i, CFG_TUD_HID_EP_BUFSIZE, 1) + }; + memcpy(&desc_configuration[TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN+i*TUD_HID_DESC_LEN], hid_desc, TUD_HID_DESC_LEN); + } + } + + return desc_configuration; +} + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) +{ + (void) langid; + + uint8_t chr_count; + + switch (index) { + case 0: // langid + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + break; + case 3: // serial + chr_count = board_usb_get_serial(_desc_str+1, 32); + break; + default: + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; + + char* str = string_desc_arr[index]; + + // Cap at max char + chr_count = (uint8_t) strlen(str); + if ( chr_count > 31 ) chr_count = 31; + + // Convert ASCII string into UTF-16 + for(uint8_t i=0; idev_addr, itf); + if ( descriptor != NULL ) { + return descriptor->descriptor; + } + } + + return NULL; +} + + +// Invoked when received SET_REPORT control request or +// received data on OUT endpoint ( Report ID = 0, Type = 0 ) +void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { + // forward to device connected to host + if (descriptors != NULL) { + tuh_hid_set_report(descriptors->dev_addr, instance, report_id, report_type, buffer, bufsize); + } +} + +// Invoked when received GET_REPORT control request +// Application must fill buffer report's content and return its length. +// Return zero will cause the stack to STALL request +uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) +{ + (void) instance; + (void) report_id; + (void) report_type; + (void) buffer; + (void) reqlen; + + return 0; +} + +// print message to CDC in raw hex +void cdc_print_hex(uint8_t const* msg, uint16_t msg_len) { + (void) msg; + (void) msg_len; + for (int i=0; i +#include +#include +#include + +#include "pico/stdlib.h" +#include "pio_usb.h" +#include "tusb.h" + +#include "hyperx_elite2.h" +#include "usb_device.h" + +#include "usb_host.h" + +host_state_t host_state; +struct report_desc *descriptors; +uint8_t num_mounted=0; +static absolute_time_t request_time; +static struct report_data *report; +static bool enabled=false; +static uint8_t kb_addr=0; + +static void usb_host_init(void); +static void host_ready(void); +static bool request_hid_report(uint8_t dev_addr, uint8_t instance); +static bool stop_hid_report(uint8_t dev_addr, uint8_t instance); +static bool request_hid_reports_all(void); +static bool stop_hid_reports_all(void); +static struct report_desc* report_desc_alloc(void); +static void report_desc_init(struct report_desc *descriptor); +static void report_desc_free(struct report_desc *descriptor); +static bool add_descriptor(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len); +static void remove_instance(uint8_t dev_addr, uint8_t instance); + +// initialize usb host +static void usb_host_init(void) { + // configure PIO USB for TinyUSB host + pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG; + pio_cfg.alarm_pool = (void*) alarm_pool_create(2,1); + tuh_configure(1, TUH_CFGID_RPI_PIO_USB_CONFIGURATION, &pio_cfg); + + // run TinyUSB host + tusb_rhport_init_t host_init = { + .role = TUSB_ROLE_HOST, + .speed = TUSB_SPEED_AUTO, + }; + tuh_hid_set_default_protocol(HID_PROTOCOL_REPORT); + tusb_init(BOARD_TUH_RHPORT, &host_init); + + host_state=HOST_INACTIVE; +} + +void usb_host_main(void) { + usb_host_init(); + + while (true) { + switch ( host_state ) { + case HOST_MOUNTED: + device_state = DEVICE_RESTART; + host_ready(); + break; + case HOST_UNMOUNTED: + host_state = HOST_MOUNTED; + break; + case HOST_START_LISTEN: + request_hid_reports_all(); + break; + case HOST_STOP_LISTEN: + stop_hid_reports_all(); + break; + case HOST_LISTENING: + if (enabled) { + rgb_task(kb_addr); + } + break; + default: + break; + } + tuh_task(); + } +} + +// Invoked when device with hid interface is mounted +void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) +{ + uint16_t vid, pid; + tuh_vid_pid_get(dev_addr, &vid, &pid); + uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); + + /// send device vid:pid information to CDC for debugging + cdc_count = sprintf(cdc_buf, "Mount: [%04x:%04x][%u:%u] Protocol = %u\n", vid, pid, dev_addr, instance, itf_protocol); + tud_cdc_write(cdc_buf, cdc_count); + + + if (vid==HYPERX_KEYBOARD_VID && pid==HYPERX_ELITE2_PID) { + kb_addr = dev_addr; + enabled = true; + } + + // add to HID report descriptor + if ( add_descriptor(dev_addr, instance, desc_report, desc_len)) { + num_mounted++; + host_state=HOST_MOUNTED; + request_time=get_absolute_time(); + } +} + +// Invoked when device with hid interface is un-mounted +void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) +{ + // send device address:instance to CDC for debugging + cdc_count = sprintf(cdc_buf, "Unmount: [%u:%u]\n", dev_addr, instance); + tud_cdc_write(cdc_buf, cdc_count); + + if (stop_hid_report(dev_addr, instance)) { + tud_cdc_write_str("Successfully stopped receiving reports\n"); + } + + remove_instance(dev_addr, instance); + if (dev_addr == kb_addr) { + enabled = false; + } + num_mounted--; + host_state=HOST_UNMOUNTED; + request_time=get_absolute_time(); +} + +// Invoked when received report from device via interrupt endpoint +void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) +{ + if (len > 0 ) { + cdc_count = sprintf(cdc_buf, "[%u:%u](%u) ", dev_addr, instance, len); + tud_cdc_write(cdc_buf,cdc_count); + cdc_print_hex(report, len); + } + + forward_report(instance, report, len); + + // continue to request to receive report + if ( !tuh_hid_receive_report(dev_addr, instance) ) + { + tud_cdc_write_str("Error: cannot request report\r\n"); + } +} + +// start listening on host for HID events +static void host_ready(void) { + if (absolute_time_diff_us(request_time, get_absolute_time()) >= 500000){ + if( descriptors != NULL ) { + host_state = HOST_START_LISTEN; + } else { + host_state = HOST_INACTIVE; + } + } +} + +// request HID input reports on specified device address and instance +static bool request_hid_report(uint8_t dev_addr, uint8_t instance) { + // request to receive reports HID devices + if ( !tuh_hid_receive_report(dev_addr, instance) ) { + cdc_count = sprintf(cdc_buf, "Error: cannot request report on [%u:%u]\n", dev_addr, instance); + tud_cdc_write(cdc_buf, cdc_count); + return false; + } + return true; +} + +// stop receiving HID input reports on specified device address and instance +static bool stop_hid_report(uint8_t dev_addr, uint8_t instance) { + if (!tuh_hid_receive_abort(dev_addr, instance)) { + tud_cdc_write_str("Error: could not stop receiving reports\n"); + return false; + } + + return true; +} + +// start listening to HID input reports on all mounted devices +static bool request_hid_reports_all(void) { + // send request to receive reports on all mounted devices + struct report_desc * current; + for (current=descriptors; current != NULL; current=current->next) { + if (! current->listening) { + if(request_hid_report(current->dev_addr, current->instance)) { + cdc_count = sprintf(cdc_buf, "Listening to input reports on [%u:%u]\n", current->dev_addr, current->instance); + tud_cdc_write(cdc_buf, cdc_count); + current->listening = true; + } else { + tud_cdc_write_str("Error listening to input report(s)\n"); + stop_hid_reports_all(); + return false; + } + } + } + + host_state = HOST_LISTENING; + return true; +} + +// stop listening to HID input reports on all mounted devices +static bool stop_hid_reports_all(void) { + // send request to stop reports on all mounted devices + struct report_desc * current; + for (current=descriptors; current != NULL; current=current->next) { + if (current->listening) { + if(stop_hid_report(current->dev_addr, current->instance)) { + cdc_count = sprintf(cdc_buf, "Stopping input reports on [%u:%u]\n", current->dev_addr, current->instance); + tud_cdc_write(cdc_buf, cdc_count); + current->listening = false; + } else { + tud_cdc_write_str("Error stopping input report(s)\n"); + return false; + } + } + } + + host_state = HOST_INACTIVE; + return true; +} + +// allocate memory for USB interface report descriptor +static struct report_desc * report_desc_alloc(void) { + struct report_desc *ret = REPORT_DESC_ALLOC(); + + if (ret != NULL) { + report_desc_init(ret); + if (descriptors == NULL) { + descriptors = ret; + } else { + struct report_desc *last; + for (last = descriptors; last->next != NULL; last=last->next); + last->next = ret; + } + } + + return ret; +} + +// initialize report descriptor struct +static void report_desc_init(struct report_desc *descriptor) { + memset(descriptor, 0, sizeof(struct report_desc)); + descriptor->next = NULL; +} + +// free memory and teardown usb->bt report ID mappings for report descriptor struct +static void report_desc_free(struct report_desc *descriptor) { + if (descriptor != NULL) { + if (descriptors == descriptor) { + descriptors = descriptor->next; + } else { + struct report_desc *last; + for (last = descriptors; last->next != NULL; last = last->next) { + if ((last->next) == descriptor) { + last->next = descriptor->next; + break; + } + } + } + free(descriptor); + } +} + +// add report descriptor for new HID interface +static bool add_descriptor(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) { + struct report_desc * descriptor = report_desc_alloc(); + if (descriptor == NULL) { + return false; + } + + memcpy(descriptor->descriptor, desc_report, desc_len); + descriptor->desc_len = desc_len; + descriptor->dev_addr = dev_addr; + descriptor->instance = instance; + descriptor->listening = false; + + return true; + +} + +// remove report descriptor for HID interface +static void remove_instance(uint8_t dev_addr, uint8_t instance) { + struct report_desc *descriptor = report_desc_find(dev_addr, instance); + + if (descriptor != NULL) { + report_desc_free(descriptor); + } +} + +// find report descriptor by device address and instance +struct report_desc * report_desc_find(uint8_t dev_addr, uint8_t instance) { + struct report_desc *descriptor; + for (descriptor = descriptors; descriptor != NULL; descriptor = descriptor->next) { + if (descriptor->dev_addr==dev_addr && descriptor->instance==instance) { + break; + } + } + + return descriptor; +} + diff --git a/usb_host.h b/usb_host.h new file mode 100644 index 0000000..b37dba3 --- /dev/null +++ b/usb_host.h @@ -0,0 +1,45 @@ +#ifndef USB_HOST_H_ +#define USB_HOST_H_ + +#define REPORT_MAX_SIZE 64 +#define REPORT_BUF_SIZE 256 +#define DESCRIPTOR_MAX_SIZE 256 + +typedef enum { + HOST_INACTIVE=0, + HOST_MOUNTED, + HOST_UNMOUNTED, + HOST_START_LISTEN, + HOST_LISTENING, + HOST_STOP_LISTEN, +} host_state_t; + +struct report_desc { + uint8_t dev_addr; + uint8_t instance; + uint8_t descriptor[DESCRIPTOR_MAX_SIZE]; + uint16_t desc_len; + struct report_desc *next; + bool listening; +}; + +struct report_data { + uint8_t dev_addr; + uint8_t instance; + uint8_t report[REPORT_MAX_SIZE]; + uint16_t len; +}; + +#define REPORT_DESC_ALLOC() (struct report_desc *)malloc(sizeof(struct report_desc)) +#define REPORT_DATA_ALLOC() (struct report_data *)malloc(sizeof(struct report_data)) + +extern host_state_t host_state; +extern struct report_desc *descriptors; +extern uint8_t num_mounted; + +void usb_host_main(void); +struct report_desc * report_desc_find(uint8_t dev_addr, uint8_t instance); + + +#endif +