From cb7421eab7ab143af8ffbf025fce970eccb7bc7e Mon Sep 17 00:00:00 2001 From: kenji Date: Sat, 18 Oct 2025 22:38:23 -0400 Subject: [PATCH] add toggle button for USB and improve BLE polling rate --- README.md | 10 +++- blehid_config.h | 8 +++ bt_device.c | 35 ++++++++----- bt_device.h | 18 +++++++ hid_report.c | 104 ++++++++++++++++++++++++++----------- hid_report.h | 3 ++ main.c | 90 ++++++++++++++++++++++++++++++++ tusb_config.h | 9 ++-- usb_descriptors.c | 127 ++++++++++++++++++++++++---------------------- usb_descriptors.h | 4 +- usb_device.c | 6 +++ usb_device.h | 9 ++++ usb_host.c | 19 ++++--- usb_host.h | 2 +- 14 files changed, 323 insertions(+), 121 deletions(-) create mode 100644 blehid_config.h diff --git a/README.md b/README.md index fbc5f6c..6876f52 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ over BLE using HID over GATT Profile. You will need the following hardware to make the device: - Raspberry Pi Pico W - USB extension cable +- (Optional) Momentary push button ![Wiring schematic](schematic.svg) @@ -35,8 +36,10 @@ and the green cable to TP3. ![Wiring of USB connectors to Raspberry Pi Pico W](diagram.png) +Optionally, a momentary push button can be wired to GP5 and GND to switch between Bluetooth and USB passthrough mode. + The individual wires on the USB can be fragile, so hot glue or other strain -relief is a good idea. +relief is a good idea. ### Software @@ -64,6 +67,11 @@ with many LEDs, the sudden power draw when plugging in can cause issues, so it is recommended to turn off the LEDs before connecting to the Raspberry Pi Pico W. +If you connected a push button to GP5, then pressing the button will switch +between Bluetooth connection mode and USB passthrough mode. In USB passthrough +mode, the inputs will be passed through from the host to the USB connection made +through the test points or the Micro USB port. + In principle, the Pico BLE HID will work with peripherals plugged into a USB hub, but compatibility will vary with the specific hardware used, including the peripherals, USB hub, and operating system. This is because the diff --git a/blehid_config.h b/blehid_config.h new file mode 100644 index 0000000..f6e1117 --- /dev/null +++ b/blehid_config.h @@ -0,0 +1,8 @@ +#ifndef BLEHID_CONFIG_H_ +#define BLEHID_CONFIG_H_ + +#define BUTTON_PIN 5 +#define BUTTON_DEBOUNCE 20000 +#define BUTTON_LONG_PRESS 3000000 + +#endif diff --git a/bt_device.c b/bt_device.c index 24a283c..48a5928 100644 --- a/bt_device.c +++ b/bt_device.c @@ -28,6 +28,8 @@ static uint8_t battery = 100; hci_con_handle_t con_handle = HCI_CON_HANDLE_INVALID; uint16_t conn_interval=1000; static uint8_t protocol_mode = 1; +bt_state_t bt_state; +bt_device_command_t bt_device_command; static hids_device_report_t *dev_report_storage; @@ -109,14 +111,20 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack (void) size; - if (packet_type != HCI_EVENT_PACKET) return; + if (packet_type != HCI_EVENT_PACKET) { + cdc_count = sprintf(cdc_buf, "Unexpected packet type %02X\n", packet_type); + cdc_print_str(cdc_buf, cdc_count); + return; + } switch (hci_event_packet_get_type(packet)) { case HCI_EVENT_DISCONNECTION_COMPLETE: con_handle = HCI_CON_HANDLE_INVALID; cdc_print_msg("Disconnected\n"); - host_state=HOST_STOP_LISTEN; + if ( device_state == DEVICE_INACTIVE) { + host_state = HOST_STOP_LISTEN; + } cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0); break; case SM_EVENT_JUST_WORKS_REQUEST: @@ -133,6 +141,8 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack cdc_count = sprintf(cdc_buf, "Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet)); cdc_print_str(cdc_buf, cdc_count); break; + case SM_EVENT_IDENTITY_RESOLVING_STARTED: + break; case L2CAP_EVENT_CONNECTION_PARAMETER_UPDATE_RESPONSE: cdc_count = sprintf(cdc_buf, "L2CAP Connection Parameter Update Complete, response: %x\n", l2cap_event_connection_parameter_update_response_get_result(packet)); cdc_print_str(cdc_buf, cdc_count); @@ -160,6 +170,8 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack cdc_print_str(cdc_buf, cdc_count); break; default: + //cdc_count = sprintf(cdc_buf,"LE packet unknown %02X\n", hci_event_le_meta_get_subevent_code(packet)); + //cdc_print_str(cdc_buf, cdc_count); break; } break; @@ -170,13 +182,10 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack cdc_count = sprintf(cdc_buf, "Report Characteristic Subscribed %u\n", hids_subevent_input_report_enable_get_enable(packet)); cdc_print_str(cdc_buf, cdc_count); - host_state=HOST_START_LISTEN; + host_state = HOST_START_LISTEN; cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1); - // request connection param update via L2CAP following Apple Bluetooth Design Guidelines - // gap_request_connection_parameter_update(con_handle, 12, 12, 4, 100); // 15 ms, 4, 1s - - // directly update connection params via HCI following Apple Bluetooth Design Guidelines - // gap_update_connection_parameters(con_handle, 12, 12, 4, 100); // 60-75 ms, 4, 1s + // request update to max 125Hz polling + gap_request_connection_parameter_update(con_handle, 6,6,0,100); break; case HIDS_SUBEVENT_PROTOCOL_MODE: @@ -185,27 +194,29 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack cdc_print_str(cdc_buf, cdc_count); break; case HIDS_SUBEVENT_CAN_SEND_NOW: - //cdc_print_msg("HIDS_SUBEVENT_CAN_SEND_NOW\n"); send_report(); cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1-cyw43_arch_gpio_get(CYW43_WL_GPIO_LED_PIN)); break; default: + //cdc_count = sprintf(cdc_buf, "Unknown HID event %02X\n", hci_event_hids_meta_get_subevent_code(packet)); + //cdc_print_str(cdc_buf, cdc_count); break; } break; default: + //cdc_count = sprintf(cdc_buf, "Unknown HCI event %02X\n", hci_event_packet_get_type(packet)); + //cdc_print_str(cdc_buf, cdc_count); break; } } // called to update the report map aka HID report descriptor on the BT interface void update_desc_hid_report(void) { + con_handle = HCI_CON_HANDLE_INVALID; hci_power_control(HCI_POWER_OFF); - if (desc_hid_report_len>0) { - //cdc_print_msg("Update HID report descriptor: "); - //cdc_print_hex(desc_hid_report, desc_hid_report_len); + if (bt_state == BT_STATE_ACTIVE && desc_hid_report_len>0) { hids_device_init_with_storage(0, desc_hid_report, desc_hid_report_len, NUM_REPORT_IDS, dev_report_storage); hci_power_control(HCI_POWER_ON); } diff --git a/bt_device.h b/bt_device.h index 9253343..eb20873 100644 --- a/bt_device.h +++ b/bt_device.h @@ -1,6 +1,24 @@ #ifndef BT_DEVICE_H_ #define BT_DEVICE_H_ +#define BT_LOOP_POLL_INTERVAL 100 + +typedef enum { + BT_STATE_INACTIVE=0, + BT_STATE_STOP, + BT_STATE_START, + BT_STATE_ACTIVE +} bt_state_t; + +typedef enum { + BT_DEVICE_COMMAND_NONE=0, + BT_DEVICE_COMMAND_SWITCH, + BT_DEVICE_COMMAND_UNPAIR, + BT_DEVICE_COMMAND_UPDATE, +} bt_device_command_t; + +extern bt_state_t bt_state; +extern bt_device_command_t bt_device_command; extern uint16_t conn_interval; void btstack_main(void); diff --git a/hid_report.c b/hid_report.c index 118695a..c1f0ff0 100644 --- a/hid_report.c +++ b/hid_report.c @@ -19,6 +19,7 @@ extern hci_con_handle_t con_handle; uint8_t desc_hid_report[HID_DESCRIPTOR_SIZE]; uint16_t desc_hid_report_len=0; +uint8_t num_mounted=0; static uint8_t buffer_storage[REPORT_BUF_SIZE]; static btstack_ring_buffer_t report_buf; @@ -29,7 +30,7 @@ static struct report_desc *descriptors; 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 struct report_desc* report_desc_find(uint8_t dev_addr, uint8_t instance); +static struct report_desc * report_desc_find(uint8_t dev_addr, uint8_t instance); static struct report_dict* report_dict_alloc(struct report_desc *descriptor); static void report_dict_init(struct report_dict *mapping); static void report_dict_free(struct report_dict *mapping, struct report_desc *descriptor); @@ -74,7 +75,11 @@ bool stop_hid_reports_all(void) { } } - host_state = HOST_INACTIVE; + if (num_mounted > 0) { + host_state = HOST_MOUNTED; + } else { + host_state = HOST_INACTIVE; + } return true; } @@ -98,38 +103,51 @@ void send_report(){ // retrieve report from ring buffer btstack_ring_buffer_read(&report_buf, report->report, report->len, &num_bytes_read); - // find report id mapping - struct report_dict * mapping = find_mapping(report->dev_addr, report->instance, report->report[0]); + // send over BLE if connected + if (con_handle != HCI_CON_HANDLE_INVALID) { + // find report id mapping + struct report_dict * mapping = find_mapping(report->dev_addr, report->instance, report->report[0]); - cdc_count=sprintf(cdc_buf, "[%04x](%u)> ", con_handle, mapping->ble_id); - cdc_print_str(cdc_buf, cdc_count); + cdc_count=sprintf(cdc_buf, "[%04x](%u)> ", con_handle, mapping->ble_id); + cdc_print_str(cdc_buf, cdc_count); - // send hid report to ble interface - if (mapping != NULL) { - if (mapping->report_id==0) { - hids_device_send_input_report_for_id(con_handle, mapping->ble_id, report->report, report->len); - cdc_print_hex(report->report, report->len); - } else { - // replace report ID in original report before sending - hids_device_send_input_report_for_id(con_handle, mapping->ble_id, &report->report[1], report->len-1); - cdc_print_hex(&report->report[1], report->len-1); + // send hid report to ble interface + if (mapping != NULL) { + if (mapping->report_id==0) { + hids_device_send_input_report_for_id(con_handle, mapping->ble_id, report->report, report->len); + cdc_print_hex(report->report, report->len); + } else { + // replace report ID in original report before sending + hids_device_send_input_report_for_id(con_handle, mapping->ble_id, &report->report[1], report->len-1); + cdc_print_hex(&report->report[1], report->len-1); + } } + // request sending of next report + if (btstack_ring_buffer_bytes_available(&report_buf)) { + hids_device_request_can_send_now_event(con_handle); + } + } + + // send over USB if enabled + if ( device_state == DEVICE_ACTIVE ) { + struct report_desc *descriptor = report_desc_find(report->dev_addr, report->instance); + cdc_count=sprintf(cdc_buf, "[%u]> ", descriptor->dev_instance); + cdc_print_str(cdc_buf, cdc_count); + + forward_report(descriptor->dev_instance, report->report, report->len); + cdc_print_hex(report->report, report->len); } // set host to poll USB if (host_state == HOST_WAIT_POLL) { host_state = HOST_POLL; } - // request sending of next report - if (btstack_ring_buffer_bytes_available(&report_buf)) { - hids_device_request_can_send_now_event(con_handle); - } } } // add report to the BTstack ring buffer for sending void queue_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) { - if (con_handle != HCI_CON_HANDLE_INVALID) { + if (con_handle != HCI_CON_HANDLE_INVALID || device_state == DEVICE_ACTIVE ) { // convert len to array of two uint8_t from single uint16_t uint8_t len8[2]; memcpy(len8, &len, 2); @@ -142,8 +160,13 @@ void queue_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uin btstack_ring_buffer_write(&report_buf, report, len); } - // request send on BLE HID interface - hids_device_request_can_send_now_event(con_handle); + if (con_handle != HCI_CON_HANDLE_INVALID) { + // request send on BLE HID interface + hids_device_request_can_send_now_event(con_handle); + } else { + // BLE is not active, send over USB + send_report(); + } } // HID report on device has not been requested, flag so it can be polled @@ -215,6 +238,7 @@ bool add_descriptor(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_repo descriptor->desc_len = desc_len; descriptor->dev_addr = dev_addr; descriptor->instance = instance; + descriptor->dev_instance = instance; descriptor->listening = false; return true; @@ -226,7 +250,8 @@ bool generate_report_descriptor(void) { memset(desc_hid_report, 0, sizeof(desc_hid_report)); desc_hid_report_len=0; - uint8_t num_mounted = 0; + uint8_t id_counter = 0; + uint8_t instance_counter = 0; struct report_desc * current; for (current=descriptors; current != NULL; current=current->next) { @@ -251,11 +276,11 @@ bool generate_report_descriptor(void) { if (report_map == NULL) { return false; } - num_mounted++; + id_counter++; report_map->report_id = current->descriptor[i]; - report_map->ble_id = num_mounted; + report_map->ble_id = id_counter; - desc_hid_report[desc_hid_report_len+i]=num_mounted; + desc_hid_report[desc_hid_report_len+i]=id_counter; } } @@ -270,30 +295,49 @@ bool generate_report_descriptor(void) { } } desc_hid_report[desc_hid_report_len+col_pos+2] = 0x85; - desc_hid_report[desc_hid_report_len+col_pos+3] = num_mounted+1; + desc_hid_report[desc_hid_report_len+col_pos+3] = id_counter+1; memcpy(&desc_hid_report[desc_hid_report_len+col_pos+4], ¤t->descriptor[col_pos+2], current->desc_len-col_pos); desc_hid_report_len += current->desc_len+2; // store mapping with report ID of 0 struct report_dict * report_map = report_dict_alloc(current); - num_mounted++; + id_counter++; report_map->report_id = 0; - report_map->ble_id = num_mounted; + report_map->ble_id = id_counter; } else { desc_hid_report_len += current->desc_len; } + + // store mapping for host instance to device instance + current->dev_instance = instance_counter; + instance_counter++; } - if (num_mounted > NUM_REPORT_IDS) { + if (id_counter > NUM_REPORT_IDS) { cdc_print_msg("Error: too many report IDs\n"); return false; } + // update number of instances mounted + num_mounted = instance_counter; + btstack_ring_buffer_reset(&report_buf); return true; } +// find a return the report descriptor by dvice address and device instance +struct report_desc * get_report_desc(uint8_t dev_instance) { + struct report_desc *descriptor; + for (descriptor = descriptors; descriptor != NULL; descriptor = descriptor->next) { + if (descriptor->dev_instance==dev_instance) { + break; + } + } + + return descriptor; + +} // remove report descriptor for HID interface void remove_instance(uint8_t dev_addr, uint8_t instance) { struct report_desc *descriptor = report_desc_find(dev_addr, instance); diff --git a/hid_report.h b/hid_report.h index eb117e1..1acbd7d 100644 --- a/hid_report.h +++ b/hid_report.h @@ -12,6 +12,7 @@ struct report_desc { uint8_t instance; uint8_t descriptor[DESCRIPTOR_BUF_SIZE]; uint16_t desc_len; + uint8_t dev_instance; struct report_desc *next; struct report_dict *mappings; bool listening; @@ -36,6 +37,7 @@ struct report_data { extern uint8_t desc_hid_report[HID_DESCRIPTOR_SIZE]; extern uint16_t desc_hid_report_len; +extern uint8_t num_mounted; bool request_hid_reports_all(void); bool stop_hid_reports_all(void); @@ -45,5 +47,6 @@ void init_report_buf(void); bool add_descriptor(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len); void remove_instance(uint8_t dev_addr, uint8_t instance); bool generate_report_descriptor(void); +struct report_desc * get_report_desc(uint8_t dev_instance); #endif diff --git a/main.c b/main.c index 83ff115..0fef00c 100644 --- a/main.c +++ b/main.c @@ -2,6 +2,7 @@ #include "pico/multicore.h" #include "pico/bootrom.h" #include "hardware/clocks.h" +#include "hardware/gpio.h" #include "pio_usb.h" #include "tusb.h" @@ -12,8 +13,16 @@ #include "usb_device.h" #include "usb_host.h" +#include "blehid_config.h" + static void usb_main(void); +#ifdef BUTTON_PIN +static absolute_time_t last_button_time; +static uint32_t last_button_event; +static void gpio_callback(uint pin, uint32_t events); +#endif + // main loop int main(void) { // for PIO USB, we want clock speed to be multiple of 12MHz @@ -35,12 +44,24 @@ int main(void) { } static void usb_main(void) { + // setup GPIO button for pair selection +#ifdef BUTTON_PIN + gpio_init(BUTTON_PIN); + gpio_pull_up(BUTTON_PIN); + gpio_set_irq_enabled_with_callback(BUTTON_PIN, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &gpio_callback); +#endif + + // init and run usb host usb_host_init(); // init and run usb device usb_device_init(); + // set initial state + bt_state = BT_STATE_ACTIVE; + device_state = DEVICE_INACTIVE; + while (true) { switch ( host_state ) { case HOST_NEW_DESCRIPTOR: @@ -58,8 +79,77 @@ static void usb_main(void) { default: break; } + + switch ( device_state ) { + case DEVICE_STOP: + case DEVICE_START: + if ( tud_disconnect() ) { + sleep_ms(10); + tud_connect(); + } + + if ( device_state == DEVICE_STOP ) { + device_state = DEVICE_INACTIVE; + host_state = HOST_STOP_LISTEN; + } else if (device_state == DEVICE_START ) { + device_state = DEVICE_ACTIVE; + if ( num_mounted > 0 ) { + set_host_poll_interval(1); + host_state = HOST_START_LISTEN; + } + } + + break; + default: + break; + } + + switch ( bt_state ) { + case BT_STATE_STOP: + bt_state = BT_STATE_INACTIVE; + update_desc_hid_report(); + break; + case BT_STATE_START: + bt_state = BT_STATE_ACTIVE; + update_desc_hid_report(); + break; + default: + break; + } + tuh_task(); // tinyusb host task tud_task(); // tinyusb device task tud_cdc_write_flush(); } } + +#ifdef BUTTON_PIN +static void gpio_callback(uint pin, uint32_t events) { + uint64_t diff = absolute_time_diff_us(last_button_time, get_absolute_time()); + if (pin == BUTTON_PIN && diff >= BUTTON_DEBOUNCE) { + if (events & GPIO_IRQ_EDGE_FALL) { + // button pressed + last_button_time = get_absolute_time(); + } else if (events & GPIO_IRQ_EDGE_RISE) { + // button released + if (diff >= BUTTON_LONG_PRESS) { + //bt_device_command = BT_DEVICE_COMMAND_UNPAIR; + } else { + if ( bt_state == BT_STATE_ACTIVE ) { + bt_state = BT_STATE_STOP; + } else if ( bt_state == BT_STATE_INACTIVE ) { + bt_state = BT_STATE_START; + } + + if ( device_state == DEVICE_ACTIVE ) { + device_state = DEVICE_STOP; + } else if ( device_state == DEVICE_INACTIVE ) { + device_state = DEVICE_START; + } + //bt_device_command = BT_DEVICE_COMMAND_SWITCH; + } + last_button_time = get_absolute_time(); + } + } +} +#endif diff --git a/tusb_config.h b/tusb_config.h index 695e605..a2dfa1b 100644 --- a/tusb_config.h +++ b/tusb_config.h @@ -84,14 +84,15 @@ #endif //------------- Driver configuration -------------// -#define CFG_TUD_CDC 1 +#define CFG_TUD_CDC 1 +#define CFG_TUD_HID 8 // CDC FIFO size of TX and RX -#define CFG_TUD_CDC_RX_BUFSIZE 32 -#define CFG_TUD_CDC_TX_BUFSIZE 128 +#define CFG_TUD_CDC_RX_BUFSIZE 32 +#define CFG_TUD_CDC_TX_BUFSIZE 128 // CDC Endpoint transfer buffer size, more is faster -#define CFG_TUD_CDC_EP_BUFSIZE 128 +#define CFG_TUD_CDC_EP_BUFSIZE 128 //-------------------------------------------------------------------- // HOST CONFIGURATION diff --git a/usb_descriptors.c b/usb_descriptors.c index 1718c2f..7a91e33 100644 --- a/usb_descriptors.c +++ b/usb_descriptors.c @@ -1,37 +1,16 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2019 Ha Thach (tinyusb.org) - * sekigon-gonnoc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - #include "tusb.h" #include "bsp/board_api.h" +#include "hid_report.h" +#include "usb_device.h" + #include "usb_descriptors.h" -//--------------------------------------------------------------------+ -// Device Descriptors -//--------------------------------------------------------------------+ +// reserve space for HID descriptor +static uint8_t desc_configuration[DESC_CFG_MAX]; +static uint16_t _desc_str[32+1]; + +// USB device descriptor tusb_desc_device_t const desc_device = { .bLength = sizeof(tusb_desc_device_t), @@ -57,6 +36,17 @@ tusb_desc_device_t const desc_device = .bNumConfigurations = 0x01 }; +// string labels for device +char const* string_desc_arr [] = +{ + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "Raspberry Pi", // 1: Manufacturer + "Pico BLE HID", // 2: Product + NULL, // 3: Serials, should use chip ID + "Pico BLE HID CDC", // 4: CDC + "Pico USB HID", // 5: USB HID Device +}; + // Invoked when received GET DEVICE DESCRIPTOR // Application return pointer to descriptor uint8_t const * tud_descriptor_device_cb(void) @@ -64,47 +54,46 @@ uint8_t const * tud_descriptor_device_cb(void) return (uint8_t const *) &desc_device; } - -//--------------------------------------------------------------------+ -// Configuration Descriptor -//--------------------------------------------------------------------+ - -// full speed configuration -uint8_t const desc_fs_configuration[] = -{ - // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), - - // Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), -}; - // 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 - return desc_fs_configuration; + // set configuration descriptor and CDC descriptor + memset(desc_configuration, 0, sizeof(desc_configuration)); + + if ( device_state == DEVICE_ACTIVE ) { + uint8_t desc_initial[TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN+1] = { + TUD_CONFIG_DESCRIPTOR(1, 4+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); + } else { + uint8_t desc_initial[TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN+1] = { + TUD_CONFIG_DESCRIPTOR(1, 4, 0, TUD_CONFIG_DESC_LEN+TUD_CDC_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); + } + + // add a HID descriptor for each interface mounted on host + if ( device_state == DEVICE_ACTIVE ) { + struct report_desc * descriptor; + for (uint8_t i=0; idesc_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; } - -//--------------------------------------------------------------------+ -// String Descriptors -//--------------------------------------------------------------------+ - -// array of pointer to string descriptors -char const* string_desc_arr [] = -{ - (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) - "Raspberry Pi", // 1: Manufacturer - "Pico BLE HID", // 2: Product - NULL, // 3: Serials, should use chip ID - "Pico BLE HID CDC", // 4: Product -}; - -static uint16_t _desc_str[32+1]; - // 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) @@ -145,3 +134,17 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) return _desc_str; } + +// Invoked when received GET HID REPORT DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) +{ + // find HID report descriptor for indicated interface on the host and forward to device + struct report_desc * descriptor = get_report_desc(itf); + if ( descriptor != NULL ) { + return descriptor->descriptor; + } + + return NULL; +} diff --git a/usb_descriptors.h b/usb_descriptors.h index 65f1b76..8438c23 100644 --- a/usb_descriptors.h +++ b/usb_descriptors.h @@ -5,6 +5,7 @@ enum { ITF_NUM_CDC=0, ITF_NUM_CDC_DATA, + ITF_NUM_HID, ITF_NUM_TOTAL }; @@ -15,7 +16,8 @@ enum #define EPNUM_CDC_NOTIF 0x81 #define EPNUM_CDC_OUT 0x02 #define EPNUM_CDC_IN 0x82 +#define EPNUM_HID 0x83 -#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN) +#define DESC_CFG_MAX (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + CFG_TUD_HID*TUD_HID_DESC_LEN) #endif /* USB_DESCRIPTORS_H_ */ diff --git a/usb_device.c b/usb_device.c index 5a4c552..373dbfa 100644 --- a/usb_device.c +++ b/usb_device.c @@ -13,6 +13,8 @@ char cdc_buf[64]; uint16_t cdc_len; size_t cdc_count; +device_state_t device_state; + void usb_device_init(void) { // run TinyUSB device tusb_rhport_init_t dev_init = { @@ -46,6 +48,10 @@ uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_t return 0; } +bool forward_report(uint8_t instance, uint8_t const* report, uint16_t len) { + return tud_hid_n_report(instance, 0, report, len); +} + // print message to CDC in raw hex void cdc_print_hex(uint8_t const* msg, uint16_t msg_len) { (void) msg; diff --git a/usb_device.h b/usb_device.h index 2b0eca0..f7f3576 100644 --- a/usb_device.h +++ b/usb_device.h @@ -1,11 +1,20 @@ #ifndef USB_DEVICE_H_ #define USB_DEVICE_H_ +typedef enum { + DEVICE_INACTIVE=0, + DEVICE_START, + DEVICE_STOP, + DEVICE_ACTIVE +} device_state_t; + extern char cdc_buf[64]; extern uint16_t cdc_len; extern size_t cdc_count; +extern device_state_t device_state; void usb_device_init(void); +bool forward_report(uint8_t instance, uint8_t const* report, uint16_t len); void cdc_print_hex(uint8_t const* msg, uint16_t msg_len); void cdc_print_str(char const* msg, uint16_t msg_len); void cdc_print_msg(char const* msg); diff --git a/usb_host.c b/usb_host.c index 7701eb5..1619e9b 100644 --- a/usb_host.c +++ b/usb_host.c @@ -84,26 +84,25 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t cons last_report = get_absolute_time(); host_state = HOST_WAIT_POLL; queue_report(dev_addr, instance, report, len); - - // continue to request to receive report - /*if ( !tuh_hid_receive_report(dev_addr, instance) ) - { - cdc_print_msg("Error: cannot request report\r\n"); - }*/ } // get host ready by updating descriptors void host_ready(void) { if (absolute_time_diff_us(request_time, get_absolute_time()) >= 1000000){ - if(generate_report_descriptor()) { - if ( desc_hid_report_len > 0 ) { - host_state=HOST_MOUNTED; + if ( generate_report_descriptor() ) { + if ( num_mounted > 0 ) { + host_state = HOST_MOUNTED; cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1); } else { - host_state=HOST_INACTIVE; + host_state = HOST_INACTIVE; } + cdc_print_msg("Updating HID report map\n"); update_desc_hid_report(); + + if ( device_state == DEVICE_ACTIVE ) { + device_state = DEVICE_START; + } } } } diff --git a/usb_host.h b/usb_host.h index 2a6f5c5..977a328 100644 --- a/usb_host.h +++ b/usb_host.h @@ -1,7 +1,7 @@ #ifndef USB_HOST_H_ #define USB_HOST_H_ -#define HOST_POLL_INTERVAL 8000 +#define HOST_POLL_INTERVAL 7500 typedef enum { HOST_INACTIVE=0,