7 Commits

Author SHA1 Message Date
kenji e2e2246c5a fix USB descriptor interface count 2025-11-03 21:10:35 -05:00
kenji 16c743cb22 update README for Pico 2 W 2025-10-26 10:15:18 -04:00
kenji cb7421eab7 add toggle button for USB and improve BLE polling rate 2025-10-18 22:38:23 -04:00
kenji a1a0723e89 update CMake for Pico 2 W build 2025-10-18 10:35:33 -04:00
kenji c430a53755 add schematic 2025-09-11 09:42:11 -04:00
kenji 2e9159cb57 Fix preserve pairing on power cycle 2025-09-09 15:05:45 -04:00
kenji a1e1ff70fc improve memory management & polling 2025-08-20 21:30:22 -04:00
17 changed files with 14643 additions and 278 deletions
+7 -3
View File
@@ -2,7 +2,9 @@ set(PROJECT ble_hid)
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 3.13)
set(PICO_SDK_PATH /home/kenji/programming/pico/c/pico-sdk) set(PICO_SDK_PATH /home/kenji/programming/pico/c/pico-sdk)
set(PICO_PIO_USB_PATH /home/kenji/programming/pico/c/Pico-PIO-USB) set(PICO_PIO_USB_PATH /home/kenji/programming/pico/c/Pico-PIO-USB)
set(PICO_BOARD pico_w) if (NOT DEFINED PICO_BOARD)
set(PICO_BOARD pico_w)
endif()
include (${PICO_SDK_PATH}/external/pico_sdk_import.cmake) include (${PICO_SDK_PATH}/external/pico_sdk_import.cmake)
project(${PROJECT} C CXX ASM) project(${PROJECT} C CXX ASM)
@@ -15,7 +17,7 @@ target_sources(${PROJECT} PRIVATE
bt_device.c bt_device.c
hid_report.c hid_report.c
main.c main.c
#usb_descriptors.c usb_descriptors.c
usb_device.c usb_device.c
usb_host.c usb_host.c
) )
@@ -38,12 +40,14 @@ target_link_libraries(${PROJECT} PRIVATE
pico_pio_usb pico_pio_usb
pico_stdlib pico_stdlib
tinyusb_board tinyusb_board
#tinyusb_device tinyusb_device
tinyusb_host tinyusb_host
tinyusb_pico_pio_usb tinyusb_pico_pio_usb
) )
pico_btstack_make_gatt_header(ble_hid PRIVATE "${CMAKE_CURRENT_LIST_DIR}/ble_hid.gatt") pico_btstack_make_gatt_header(ble_hid PRIVATE "${CMAKE_CURRENT_LIST_DIR}/ble_hid.gatt")
set_target_properties(${PROJECT} PROPERTIES OUTPUT_NAME "${PROJECT}-${PICO_BOARD}")
pico_add_extra_outputs(${PROJECT}) pico_add_extra_outputs(${PROJECT})
+42 -31
View File
@@ -1,8 +1,8 @@
# Pico BLE HID # Pico BLE HID
This project uses a Raspberry Pi Pico W as a Bluetooth Low Energy adapter for This project uses a Raspberry Pi Pico W or Pico 2 W as a Bluetooth Low Energy
a wired USB HID input peripheral, allowing it to connect to a host device adapter for a wired USB HID input peripheral, allowing it to connect to a host
over BLE using HID over GATT Profile. device over BLE using HID over GATT Profile.
## Setup ## Setup
@@ -12,54 +12,65 @@ over BLE using HID over GATT Profile.
### Hardware ### Hardware
You will need the following hardware to make the device: You will need the following hardware to make the device:
- Raspberry Pi Pico W - Raspberry Pi Pico W or Raspberry Pi Pico 2 W
- USB extension cable - USB extension cable
- (Optional) Momentary push button
![Wiring schematic](schematic.svg)
You will need to cut the USB extension in half and connect the wires from the
female end to the Raspberry Pi Pico (2) W. The default configuration is to
attach the USB's green wire to pin 1/GP0 and USB's white wire to pin 2/GP1. You
will also need to connect the red to 5V VBUS/VSYS (since you'll be powering
from the USB connection, VBUS should be fine) and the black to any ground pin.
Pin 38 is the closest and most convenient, or the ground test point TP1 on the
back will also work. While you can connect the Raspberry Pi Pico (2) W to the
host device using the micro USB port and a micro USB cable, since you have
already sacrificed half of the USB extension cable, you might as well use the
male half to create a standard USB-A connection. If you wish to do so, then
simply connect the red and black wires of the male connector end to VBUS (5V)
and GND, respectively. For the data wires, you can solder them to the two test
points TP2 and TP3 on the back of the Raspberry Pi Pico (2) W. The white cable
goes to TP2 and the green cable to TP3.
![Wiring of USB connectors to Raspberry Pi Pico W](diagram.png) ![Wiring of USB connectors to Raspberry Pi Pico W](diagram.png)
You will need to cut the USB extension in half and connect the wires from the Optionally, a momentary push button can be wired to GP5 and GND to switch between Bluetooth and USB passthrough mode.
female end to the Raspberry Pi Pico W. The default configuration is to attach
the USB's green wire to pin 1/GP0 and USB's white wire to pin 2/GP1. You will
also need to connect the red to 5V VBUS/VSYS (since you'll be powering from the
USB connection, VBUS should be fine) and the black to any ground pin. Pin 38
is the closest and most convenient, or the ground test point TP1 on the back
will also work. While you can connect the Raspberry Pi Pico to the host device
using the micro USB port and a micro USB cable, since you have already
sacrificed half of the USB extension cable, you might as well use the male half
to create a standard USB-A connection. If you wish to do so, then simply
connect the red and black wires of the male connector end to VBUS (5V) and GND,
respectively. For the data wires, you can solder them to the two test points
TP2 and TP3 on the back of the Raspberry Pi Pico. The white cable goes to TP2
and the green cable to TP3.
The individual wires on the USB can be fragile, so hot glue or other strain 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 ### Software
Download the ble_hid.uf2 file from the latest Download the ble_hid-pico_w.uf2 (for Pico W) or ble_hid-pico2_w.uf2 (for Pico 2
[release](https://git.kkozai.com/kenji/pico_ble_hid/releases) and flash W) file from the latest
onto the Raspberry Pi Pico W by holding down the BOOTSEL button while plugging [release](https://git.kkozai.com/kenji/pico_ble_hid/releases) and flash onto
the Raspberry Pi Pico (2) W by holding down the BOOTSEL button while plugging
into your computer so that it appears as a USB drive, then transfer the into your computer so that it appears as a USB drive, then transfer the
firmware file onto the Pico W. After unmounting, the firmware install is firmware file onto the Pico (2) W. After unmounting, the firmware install is
complete. complete.
## Usage ## Usage
Connect the Raspberry Pi Pico W into any USB power source (a good, small power Connect the Raspberry Pi Pico (2) W into any USB power source (a good, small
bank will work), then plug in the desired peripheral into the female USB power bank will work), then plug in the desired peripheral into the female USB
socket. After about a second, the onboard LED will light up, indicating it is socket. After about a second, the onboard LED will light up, indicating it is
ready to be paired over Bluetooth LE. ready to be paired over Bluetooth LE.
From the host machine, go to your Bluetooth settings and pair with the From the host machine, go to your Bluetooth settings and pair with the
"Pico BLE HID" device. The LED should turn off when pairing begins, and it will "Pico BLE HID" device. The LED should turn off when pairing begins, and it will
turn back on when pairing is complete and the Raspberry Pi Pico W is turn back on when pairing is complete and the Raspberry Pi Pico (2) W is ready
ready to receive inputs. This will take a few seconds, so be patient. to receive inputs. This will take a few seconds, so be patient. Once the light
Once the light is back on, your device should work over the Bluetooth is back on, your device should work over the Bluetooth connection. When
connection. When connecting a power-hungry device such as a gaming keyboard connecting a power-hungry device such as a gaming keyboard with many LEDs, the
with many LEDs, the sudden power draw when plugging in can cause issues, so it sudden power draw when plugging in can cause issues, so it is recommended to
is recommended to turn off the LEDs before connecting to the Raspberry Pi Pico turn off the LEDs before connecting to the Raspberry Pi Pico (2) W.
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 In principle, the Pico BLE HID will work with peripherals plugged into
a USB hub, but compatibility will vary with the specific hardware used, a USB hub, but compatibility will vary with the specific hardware used,
+8
View File
@@ -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
+50 -29
View File
@@ -26,7 +26,10 @@ static btstack_packet_callback_registration_t l2cap_event_callback_registration;
static btstack_packet_callback_registration_t sm_event_callback_registration; static btstack_packet_callback_registration_t sm_event_callback_registration;
static uint8_t battery = 100; static uint8_t battery = 100;
hci_con_handle_t con_handle = HCI_CON_HANDLE_INVALID; hci_con_handle_t con_handle = HCI_CON_HANDLE_INVALID;
uint16_t conn_interval=1000;
static uint8_t protocol_mode = 1; 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; static hids_device_report_t *dev_report_storage;
@@ -50,8 +53,6 @@ void btstack_main(void){
bt_hid_setup(); bt_hid_setup();
init_report_buf(); init_report_buf();
hci_power_control(HCI_POWER_ON);
} }
static void bt_hid_setup(void) { static void bt_hid_setup(void) {
@@ -76,7 +77,7 @@ static void bt_hid_setup(void) {
// setup HID Device service // setup HID Device service
dev_report_storage = (hids_device_report_t *)malloc(sizeof(hids_device_report_t)*NUM_REPORT_IDS); dev_report_storage = (hids_device_report_t *)malloc(sizeof(hids_device_report_t)*NUM_REPORT_IDS);
hids_device_init_with_storage(0, get_desc_hid_report(), get_desc_hid_report_len(), NUM_REPORT_IDS, dev_report_storage); hids_device_init_with_storage(0, desc_hid_report, desc_hid_report_len, NUM_REPORT_IDS, dev_report_storage);
// setup advertisements // setup advertisements
uint16_t adv_int_min = 0x0030; uint16_t adv_int_min = 0x0030;
@@ -109,50 +110,68 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
(void) channel; (void) channel;
(void) size; (void) size;
uint16_t conn_interval;
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)) { switch (hci_event_packet_get_type(packet)) {
case HCI_EVENT_DISCONNECTION_COMPLETE: case HCI_EVENT_DISCONNECTION_COMPLETE:
con_handle = HCI_CON_HANDLE_INVALID; con_handle = HCI_CON_HANDLE_INVALID;
printf("Disconnected\n"); cdc_print_msg("Disconnected\n");
set_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); cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
break; break;
case SM_EVENT_JUST_WORKS_REQUEST: case SM_EVENT_JUST_WORKS_REQUEST:
printf("Just Works requested\n"); cdc_print_msg("Just Works requested\n");
sm_just_works_confirm(sm_event_just_works_request_get_handle(packet)); sm_just_works_confirm(sm_event_just_works_request_get_handle(packet));
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0); cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
break; break;
case SM_EVENT_NUMERIC_COMPARISON_REQUEST: case SM_EVENT_NUMERIC_COMPARISON_REQUEST:
printf("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet)); cdc_count = sprintf(cdc_buf, "Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet));
cdc_print_str(cdc_buf, cdc_count);
sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet)); sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet));
break; break;
case SM_EVENT_PASSKEY_DISPLAY_NUMBER: case SM_EVENT_PASSKEY_DISPLAY_NUMBER:
printf("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet)); 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; break;
case L2CAP_EVENT_CONNECTION_PARAMETER_UPDATE_RESPONSE: case L2CAP_EVENT_CONNECTION_PARAMETER_UPDATE_RESPONSE:
printf("L2CAP Connection Parameter Update Complete, response: %x\n", l2cap_event_connection_parameter_update_response_get_result(packet)); 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);
break; break;
case HCI_EVENT_LE_META: case HCI_EVENT_LE_META:
switch (hci_event_le_meta_get_subevent_code(packet)) { switch (hci_event_le_meta_get_subevent_code(packet)) {
case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: case HCI_SUBEVENT_LE_CONNECTION_COMPLETE:
// print connection parameters (without using float operations) // print connection parameters (without using float operations)
conn_interval = hci_subevent_le_connection_complete_get_conn_interval(packet); conn_interval = hci_subevent_le_connection_complete_get_conn_interval(packet);
printf("LE Connection Complete:\n"); set_host_poll_interval(conn_interval);
printf("- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3)); cdc_print_msg("LE Connection Complete:\n");
printf("- Connection Latency: %u\n", hci_subevent_le_connection_complete_get_conn_latency(packet)); cdc_count = sprintf(cdc_buf, "- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3));
cdc_print_str(cdc_buf, cdc_count);
cdc_count = sprintf(cdc_buf, "- Connection Latency: %u\n", hci_subevent_le_connection_complete_get_conn_latency(packet));
cdc_print_str(cdc_buf, cdc_count);
break; break;
case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE: case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE:
// print connection parameters (without using float operations) // print connection parameters (without using float operations)
conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet); conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet);
printf("LE Connection Update:\n"); set_host_poll_interval(conn_interval);
printf("- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3)); cdc_print_msg("LE Connection Update:\n");
printf("- Connection Latency: %u\n", hci_subevent_le_connection_update_complete_get_conn_latency(packet)); cdc_count = sprintf(cdc_buf, "- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3));
cdc_print_str(cdc_buf, cdc_count);
cdc_count = sprintf(cdc_buf, "- Connection Latency: %u\n", hci_subevent_le_connection_update_complete_get_conn_latency(packet));
cdc_print_str(cdc_buf, cdc_count);
break; break;
default: 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;
} }
break; break;
@@ -160,43 +179,45 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
switch (hci_event_hids_meta_get_subevent_code(packet)){ switch (hci_event_hids_meta_get_subevent_code(packet)){
case HIDS_SUBEVENT_INPUT_REPORT_ENABLE: case HIDS_SUBEVENT_INPUT_REPORT_ENABLE:
con_handle = hids_subevent_input_report_enable_get_con_handle(packet); con_handle = hids_subevent_input_report_enable_get_con_handle(packet);
printf("Report Characteristic Subscribed %u\n", hids_subevent_input_report_enable_get_enable(packet)); 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);
set_host_state(HOST_START_LISTEN); host_state = HOST_START_LISTEN;
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1); cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
// request connection param update via L2CAP following Apple Bluetooth Design Guidelines // request update to max 125Hz polling
// gap_request_connection_parameter_update(con_handle, 12, 12, 4, 100); // 15 ms, 4, 1s gap_request_connection_parameter_update(con_handle, 6,6,0,100);
// 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
break; break;
case HIDS_SUBEVENT_PROTOCOL_MODE: case HIDS_SUBEVENT_PROTOCOL_MODE:
protocol_mode = hids_subevent_protocol_mode_get_protocol_mode(packet); protocol_mode = hids_subevent_protocol_mode_get_protocol_mode(packet);
printf("Protocol Mode: %s mode\n", hids_subevent_protocol_mode_get_protocol_mode(packet) ? "Report" : "Boot"); cdc_count = sprintf(cdc_buf, "Protocol Mode: %s mode\n", hids_subevent_protocol_mode_get_protocol_mode(packet) ? "Report" : "Boot");
cdc_print_str(cdc_buf, cdc_count);
break; break;
case HIDS_SUBEVENT_CAN_SEND_NOW: case HIDS_SUBEVENT_CAN_SEND_NOW:
printf("HIDS_SUBEVENT_CAN_SEND_NOW\n");
send_report(); send_report();
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1-cyw43_arch_gpio_get(CYW43_WL_GPIO_LED_PIN)); cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1-cyw43_arch_gpio_get(CYW43_WL_GPIO_LED_PIN));
break; break;
default: 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;
} }
break; break;
default: 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; break;
} }
} }
// called to update the report map aka HID report descriptor on the BT interface // called to update the report map aka HID report descriptor on the BT interface
void update_desc_hid_report(void) { void update_desc_hid_report(void) {
con_handle = HCI_CON_HANDLE_INVALID;
hci_power_control(HCI_POWER_OFF); hci_power_control(HCI_POWER_OFF);
uint16_t len = get_desc_hid_report_len(); if (bt_state == BT_STATE_ACTIVE && desc_hid_report_len>0) {
if (len>0) { hids_device_init_with_storage(0, desc_hid_report, desc_hid_report_len, NUM_REPORT_IDS, dev_report_storage);
hids_device_init_with_storage(0, get_desc_hid_report(), len, NUM_REPORT_IDS, dev_report_storage);
hci_power_control(HCI_POWER_ON); hci_power_control(HCI_POWER_ON);
} }
} }
+20
View File
@@ -1,6 +1,26 @@
#ifndef BT_DEVICE_H_ #ifndef BT_DEVICE_H_
#define 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); void btstack_main(void);
void update_desc_hid_report(void); void update_desc_hid_report(void);
-3
View File
@@ -86,7 +86,4 @@
// To get the audio demos working even with HCI dump at 115200, this truncates long ACL packets // To get the audio demos working even with HCI dump at 115200, this truncates long ACL packets
//#define HCI_DUMP_STDOUT_MAX_SIZE_ACL 100 //#define HCI_DUMP_STDOUT_MAX_SIZE_ACL 100
// enable larger HID descriptor
#define MAX_ATTRIBUTE_VALUE_SIZE 512
#endif // _PICO_BTSTACK_BTSTACK_CONFIG_H #endif // _PICO_BTSTACK_BTSTACK_CONFIG_H
+106 -56
View File
@@ -11,27 +11,31 @@
#include "usb_device.h" #include "usb_device.h"
#include "usb_host.h" #include "usb_host.h"
#include "bt_device.h"
#include "hid_report.h" #include "hid_report.h"
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 uint8_t buffer_storage[REPORT_BUF_SIZE];
static btstack_ring_buffer_t report_buf; static btstack_ring_buffer_t report_buf;
static uint8_t desc_hid_report[HID_DESCRIPTOR_SIZE]; static struct report_data *report;
static uint16_t desc_hid_report_len=0;
static struct report_desc *descriptors; static struct report_desc *descriptors;
static struct report_desc* report_desc_alloc(void); static struct report_desc* report_desc_alloc(void);
static void report_desc_init(struct report_desc *descriptor); static void report_desc_init(struct report_desc *descriptor);
static void report_desc_free(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 struct report_dict* report_dict_alloc(struct report_desc *descriptor);
static void report_dict_init(struct report_dict *mapping); static void report_dict_init(struct report_dict *mapping);
static void report_dict_free(struct report_dict *mapping, struct report_desc *descriptor); static void report_dict_free(struct report_dict *mapping, struct report_desc *descriptor);
static struct report_dict* find_mapping(uint8_t dev_addr, uint8_t instance, uint8_t report_id); static struct report_dict* find_mapping(uint8_t dev_addr, uint8_t instance, uint8_t report_id);
extern hci_con_handle_t con_handle;
// start listening to HID input reports on all mounted devices // start listening to HID input reports on all mounted devices
bool request_hid_reports_all(void) { bool request_hid_reports_all(void) {
// send request to receive reports on all mounted devices // send request to receive reports on all mounted devices
@@ -39,15 +43,18 @@ bool request_hid_reports_all(void) {
for (current=descriptors; current != NULL; current=current->next) { for (current=descriptors; current != NULL; current=current->next) {
if (! current->listening) { if (! current->listening) {
if(request_hid_report(current->dev_addr, current->instance)) { if(request_hid_report(current->dev_addr, current->instance)) {
char tempbuf[40]; cdc_count = sprintf(cdc_buf, "Listening to input reports on [%u:%u]\n", current->dev_addr, current->instance);
size_t count = sprintf(tempbuf, "Listening to input reports on [%u:%u]\n", current->dev_addr, current->instance); cdc_print_str(cdc_buf, cdc_count);
cdc_print_str(tempbuf, count);
current->listening = true; current->listening = true;
} else { } else {
cdc_print_msg("Error listening to input report(s)\n");
stop_hid_reports_all();
return false; return false;
} }
} }
} }
host_state = HOST_LISTENING;
return true; return true;
} }
@@ -58,15 +65,21 @@ bool stop_hid_reports_all(void) {
for (current=descriptors; current != NULL; current=current->next) { for (current=descriptors; current != NULL; current=current->next) {
if (current->listening) { if (current->listening) {
if(stop_hid_report(current->dev_addr, current->instance)) { if(stop_hid_report(current->dev_addr, current->instance)) {
char tempbuf[40]; cdc_count = sprintf(cdc_buf, "Stopping input reports on [%u:%u]\n", current->dev_addr, current->instance);
size_t count = sprintf(tempbuf, "Stopping input reports on [%u:%u]\n", current->dev_addr, current->instance); cdc_print_str(cdc_buf, cdc_count);
cdc_print_str(tempbuf, count);
current->listening = false; current->listening = false;
} else { } else {
cdc_print_msg("Error stopping input report(s)\n");
return false; return false;
} }
} }
} }
if (num_mounted > 0) {
host_state = HOST_MOUNTED;
} else {
host_state = HOST_INACTIVE;
}
return true; return true;
} }
@@ -74,58 +87,72 @@ bool stop_hid_reports_all(void) {
void send_report(){ void send_report(){
// process queue and send next report // process queue and send next report
if (btstack_ring_buffer_bytes_available(&report_buf)) { if (btstack_ring_buffer_bytes_available(&report_buf)) {
uint8_t dev_addr;
uint8_t instance;
uint8_t len8[2]; uint8_t len8[2];
uint16_t len;
uint32_t num_bytes_read; uint32_t num_bytes_read;
// retrieve dev_addr from ring_buffer // retrieve dev_addr from ring_buffer
btstack_ring_buffer_read(&report_buf, &dev_addr, 1, &num_bytes_read); btstack_ring_buffer_read(&report_buf, &report->dev_addr, 1, &num_bytes_read);
// retrieve instance from ring buffer // retrieve instance from ring buffer
btstack_ring_buffer_read(&report_buf, &instance, 1, &num_bytes_read); btstack_ring_buffer_read(&report_buf, &report->instance, 1, &num_bytes_read);
// retrieve length as two uint8_t and turn into uint16_t // retrieve length as two uint8_t and turn into uint16_t
btstack_ring_buffer_read(&report_buf, len8, 2, &num_bytes_read); btstack_ring_buffer_read(&report_buf, len8, 2, &num_bytes_read);
memcpy(&len, len8, 2); memcpy(&report->len, len8, 2);
// retrieve report from ring buffer // retrieve report from ring buffer
uint8_t report[len]; btstack_ring_buffer_read(&report_buf, report->report, report->len, &num_bytes_read);
btstack_ring_buffer_read(&report_buf, report, len, &num_bytes_read);
// find report id mapping // send over BLE if connected
struct report_dict * mapping = find_mapping(dev_addr, instance, report[0]); 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]);
char tempbuf[16]; cdc_count=sprintf(cdc_buf, "[%04x](%u)> ", con_handle, mapping->ble_id);
size_t count=sprintf(tempbuf, "[%04x](%u) ", con_handle, mapping->ble_id); cdc_print_str(cdc_buf, cdc_count);
cdc_print_str(tempbuf, count);
// send hid report to ble interface // send hid report to ble interface
if (mapping != NULL) { if (mapping != NULL) {
if (mapping->report_id==0) { if (mapping->report_id==0) {
hids_device_send_input_report_for_id(con_handle, mapping->ble_id, report, len); hids_device_send_input_report_for_id(con_handle, mapping->ble_id, report->report, report->len);
cdc_print_hex(report, len); cdc_print_hex(report->report, report->len);
} else { } else {
// replace report ID in original report before sending // replace report ID in original report before sending
hids_device_send_input_report_for_id(con_handle, mapping->ble_id, &report[1], len-1); hids_device_send_input_report_for_id(con_handle, mapping->ble_id, &report->report[1], report->len-1);
cdc_print_hex(&report[1], 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);
} }
} }
// request send of next report // send over USB if enabled
hids_device_request_can_send_now_event(con_handle); 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;
}
} }
} }
// add report to the BTstack ring buffer for sending // 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) { 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 // convert len to array of two uint8_t from single uint16_t
uint8_t len8[2]; uint8_t len8[2];
memcpy(len8, &len, 2); memcpy(len8, &len, 2);
if (btstack_ring_buffer_bytes_free(&report_buf) >= len+4) { if (btstack_ring_buffer_bytes_free(&report_buf) >= len+5) {
// put instance, length, and report into ring buffer if space available // put instance, length, and report into ring buffer if space available
btstack_ring_buffer_write(&report_buf, &dev_addr, 1); btstack_ring_buffer_write(&report_buf, &dev_addr, 1);
btstack_ring_buffer_write(&report_buf, &instance, 1); btstack_ring_buffer_write(&report_buf, &instance, 1);
@@ -133,14 +160,25 @@ void queue_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uin
btstack_ring_buffer_write(&report_buf, report, len); btstack_ring_buffer_write(&report_buf, report, len);
} }
// request send on BLE HID interface if (con_handle != HCI_CON_HANDLE_INVALID) {
hids_device_request_can_send_now_event(con_handle); // 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
struct report_desc * descriptor;
descriptor = report_desc_find(dev_addr, instance);
descriptor->listening = false;
} }
// allocate memory for HID report ring buffer // allocate memory for HID report ring buffer
void init_report_buf(void) { void init_report_buf(void) {
btstack_ring_buffer_init(&report_buf, buffer_storage, sizeof(buffer_storage)); btstack_ring_buffer_init(&report_buf, buffer_storage, sizeof(buffer_storage));
report = REPORT_DATA_ALLOC();
} }
// allocate memory for USB interface report descriptor // allocate memory for USB interface report descriptor
@@ -200,6 +238,7 @@ bool add_descriptor(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_repo
descriptor->desc_len = desc_len; descriptor->desc_len = desc_len;
descriptor->dev_addr = dev_addr; descriptor->dev_addr = dev_addr;
descriptor->instance = instance; descriptor->instance = instance;
descriptor->dev_instance = instance;
descriptor->listening = false; descriptor->listening = false;
return true; return true;
@@ -211,7 +250,8 @@ bool generate_report_descriptor(void) {
memset(desc_hid_report, 0, sizeof(desc_hid_report)); memset(desc_hid_report, 0, sizeof(desc_hid_report));
desc_hid_report_len=0; desc_hid_report_len=0;
uint8_t num_mounted = 0; uint8_t id_counter = 0;
uint8_t instance_counter = 0;
struct report_desc * current; struct report_desc * current;
for (current=descriptors; current != NULL; current=current->next) { for (current=descriptors; current != NULL; current=current->next) {
@@ -236,11 +276,11 @@ bool generate_report_descriptor(void) {
if (report_map == NULL) { if (report_map == NULL) {
return false; return false;
} }
num_mounted++; id_counter++;
report_map->report_id = current->descriptor[i]; 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;
} }
} }
@@ -255,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+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], &current->descriptor[col_pos+2], current->desc_len-col_pos); memcpy(&desc_hid_report[desc_hid_report_len+col_pos+4], &current->descriptor[col_pos+2], current->desc_len-col_pos);
desc_hid_report_len += current->desc_len+2; desc_hid_report_len += current->desc_len+2;
// store mapping with report ID of 0 // store mapping with report ID of 0
struct report_dict * report_map = report_dict_alloc(current); struct report_dict * report_map = report_dict_alloc(current);
num_mounted++; id_counter++;
report_map->report_id = 0; report_map->report_id = 0;
report_map->ble_id = num_mounted; report_map->ble_id = id_counter;
} else { } else {
desc_hid_report_len += current->desc_len; 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"); cdc_print_msg("Error: too many report IDs\n");
return false; return false;
} }
// update number of instances mounted
num_mounted = instance_counter;
btstack_ring_buffer_reset(&report_buf); btstack_ring_buffer_reset(&report_buf);
return true; 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 // remove report descriptor for HID interface
void remove_instance(uint8_t dev_addr, uint8_t instance) { void remove_instance(uint8_t dev_addr, uint8_t instance) {
struct report_desc *descriptor = report_desc_find(dev_addr, instance); struct report_desc *descriptor = report_desc_find(dev_addr, instance);
@@ -358,12 +417,3 @@ static struct report_dict * find_mapping(uint8_t dev_addr, uint8_t instance, uin
return NULL; return NULL;
} }
uint16_t get_desc_hid_report_len(void) {
return desc_hid_report_len;
}
uint8_t const* get_desc_hid_report(void) {
return desc_hid_report;
}
+17 -4
View File
@@ -1,6 +1,7 @@
#ifndef HID_REPORT_H_ #ifndef HID_REPORT_H_
#define HID_REPORT_H_ #define HID_REPORT_H_
#define REPORT_MAX_SIZE 64
#define NUM_REPORT_IDS 16 #define NUM_REPORT_IDS 16
#define REPORT_BUF_SIZE 256 #define REPORT_BUF_SIZE 256
#define DESCRIPTOR_BUF_SIZE 256 #define DESCRIPTOR_BUF_SIZE 256
@@ -11,6 +12,7 @@ struct report_desc {
uint8_t instance; uint8_t instance;
uint8_t descriptor[DESCRIPTOR_BUF_SIZE]; uint8_t descriptor[DESCRIPTOR_BUF_SIZE];
uint16_t desc_len; uint16_t desc_len;
uint8_t dev_instance;
struct report_desc *next; struct report_desc *next;
struct report_dict *mappings; struct report_dict *mappings;
bool listening; bool listening;
@@ -22,8 +24,20 @@ struct report_dict {
struct report_dict *next; struct report_dict *next;
}; };
# define REPORT_DESC_ALLOC() (struct report_desc *)malloc(sizeof(struct report_desc)) struct report_data {
# define REPORT_DICT_ALLOC() (struct report_dict *)malloc(sizeof(struct report_dict)) 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_DICT_ALLOC() (struct report_dict *)malloc(sizeof(struct report_dict))
#define REPORT_DATA_ALLOC() (struct report_data *)malloc(sizeof(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 request_hid_reports_all(void);
bool stop_hid_reports_all(void); bool stop_hid_reports_all(void);
@@ -33,7 +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); 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); void remove_instance(uint8_t dev_addr, uint8_t instance);
bool generate_report_descriptor(void); bool generate_report_descriptor(void);
uint16_t get_desc_hid_report_len(void); struct report_desc * get_report_desc(uint8_t dev_instance);
uint8_t const* get_desc_hid_report(void);
#endif #endif
+115 -32
View File
@@ -2,6 +2,7 @@
#include "pico/multicore.h" #include "pico/multicore.h"
#include "pico/bootrom.h" #include "pico/bootrom.h"
#include "hardware/clocks.h" #include "hardware/clocks.h"
#include "hardware/gpio.h"
#include "pio_usb.h" #include "pio_usb.h"
#include "tusb.h" #include "tusb.h"
@@ -12,61 +13,143 @@
#include "usb_device.h" #include "usb_device.h"
#include "usb_host.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 // main loop
int main(void) { int main(void) {
// for PIO USB, we want clock speed to be multiple of 12MHz
set_sys_clock_khz(144000, true);
set_sys_clock_khz(192000, true); // need so BTstack can save pairing information to flash
flash_safe_execute_core_init();
sleep_ms(10); sleep_ms(10);
// setup BLE on core 1 // run BLE on core 1
multicore_reset_core1(); multicore_reset_core1();
multicore_launch_core1(btstack_main); multicore_launch_core1(btstack_main);
// run usb on core 0
usb_main();
return 0;
}
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 // init and run usb host
usb_host_init(); usb_host_init();
// init and run usb device // init and run usb device
//usb_device_init(); usb_device_init();
// set initial state
bt_state = BT_STATE_ACTIVE;
device_state = DEVICE_INACTIVE;
while (true) { while (true) {
switch ( get_host_state() ) { switch ( host_state ) {
case HOST_NEW_DESCRIPTOR: case HOST_NEW_DESCRIPTOR:
if ( host_ready() ) { host_ready();
if(generate_report_descriptor()) {
if ( get_desc_hid_report_len() > 0 ) {
set_host_state(HOST_MOUNTED);
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
} else {
set_host_state(HOST_INACTIVE);
}
cdc_print_msg("Updating HID report map\n");
update_desc_hid_report();
}
}
break; break;
case HOST_START_LISTEN: case HOST_START_LISTEN:
if (request_hid_reports_all()) { request_hid_reports_all();
set_host_state(HOST_LISTENING);
} else {
cdc_print_msg("Error listening to input report(s)\n");
set_host_state(HOST_INACTIVE);
}
break; break;
case HOST_STOP_LISTEN: case HOST_STOP_LISTEN:
if (stop_hid_reports_all()) { stop_hid_reports_all();
set_host_state(HOST_INACTIVE); break;
} else { case HOST_POLL:
cdc_print_msg("Error stopping input report(s)\n"); poll_devices();
}
break; break;
default: default:
break; break;
} }
tuh_task(); // tinyusb host task
//tud_task(); // tinyusb device task
//tud_cdc_write_flush();
}
return 0; 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
+14111
View File
File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 255 KiB

+8 -8
View File
@@ -39,8 +39,7 @@
#endif #endif
// Enable device stack // Enable device stack
//#define CFG_TUD_ENABLED 1 #define CFG_TUD_ENABLED 1
#define CFG_TUD_ENABLED 0
// Enable host stack // Enable host stack
#define CFG_TUH_ENABLED 1 #define CFG_TUH_ENABLED 1
@@ -85,14 +84,15 @@
#endif #endif
//------------- Driver configuration -------------// //------------- Driver configuration -------------//
#define CFG_TUD_CDC 1 #define CFG_TUD_CDC 1
#define CFG_TUD_HID 8
// CDC FIFO size of TX and RX // CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE 32 #define CFG_TUD_CDC_RX_BUFSIZE 32
#define CFG_TUD_CDC_TX_BUFSIZE 64 #define CFG_TUD_CDC_TX_BUFSIZE 128
// CDC Endpoint transfer buffer size, more is faster // CDC Endpoint transfer buffer size, more is faster
#define CFG_TUD_CDC_EP_BUFSIZE 64 #define CFG_TUD_CDC_EP_BUFSIZE 128
//-------------------------------------------------------------------- //--------------------------------------------------------------------
// HOST CONFIGURATION // HOST CONFIGURATION
@@ -122,8 +122,8 @@
#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB*3+1) // hub typically has 4 ports #define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB*3+1) // hub typically has 4 ports
#define CFG_TUH_HID (3*CFG_TUH_DEVICE_MAX+1) #define CFG_TUH_HID (3*CFG_TUH_DEVICE_MAX+1)
#define CFG_TUH_HID_EPIN_BUFSIZE 64 #define CFG_TUH_HID_EPIN_BUFSIZE 128
#define CFG_TUH_HID_EPOUT_BUFSIZE 64 #define CFG_TUH_HID_EPOUT_BUFSIZE 128
#ifdef __cplusplus #ifdef __cplusplus
} }
+65 -61
View File
@@ -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 "tusb.h"
#include "bsp/board_api.h" #include "bsp/board_api.h"
#include "hid_report.h"
#include "usb_device.h"
#include "usb_descriptors.h" #include "usb_descriptors.h"
//--------------------------------------------------------------------+ // reserve space for HID descriptor
// Device Descriptors 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 = tusb_desc_device_t const desc_device =
{ {
.bLength = sizeof(tusb_desc_device_t), .bLength = sizeof(tusb_desc_device_t),
@@ -57,6 +36,17 @@ tusb_desc_device_t const desc_device =
.bNumConfigurations = 0x01 .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 // Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor // Application return pointer to descriptor
uint8_t const * tud_descriptor_device_cb(void) uint8_t const * tud_descriptor_device_cb(void)
@@ -64,46 +54,46 @@ uint8_t const * tud_descriptor_device_cb(void)
return (uint8_t const *) &desc_device; 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 // Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor // Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete // Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_descriptor_configuration_cb(uint8_t index) uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{ {
(void) index; // for multiple configurations (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, 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);
} 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; i<num_mounted;i++) {
descriptor = get_report_desc(i);
if ( descriptor != NULL ) {
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;
} }
//--------------------------------------------------------------------+
// 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
};
static uint16_t _desc_str[32+1];
// Invoked when received GET STRING DESCRIPTOR request // Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete // 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) uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
@@ -144,3 +134,17 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
return _desc_str; 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;
}
+3 -1
View File
@@ -5,6 +5,7 @@ enum
{ {
ITF_NUM_CDC=0, ITF_NUM_CDC=0,
ITF_NUM_CDC_DATA, ITF_NUM_CDC_DATA,
ITF_NUM_HID,
ITF_NUM_TOTAL ITF_NUM_TOTAL
}; };
@@ -15,7 +16,8 @@ enum
#define EPNUM_CDC_NOTIF 0x81 #define EPNUM_CDC_NOTIF 0x81
#define EPNUM_CDC_OUT 0x02 #define EPNUM_CDC_OUT 0x02
#define EPNUM_CDC_IN 0x82 #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_ */ #endif /* USB_DESCRIPTORS_H_ */
+16 -8
View File
@@ -9,6 +9,12 @@
#include "usb_device.h" #include "usb_device.h"
char cdc_buf[64];
uint16_t cdc_len;
size_t cdc_count;
device_state_t device_state;
void usb_device_init(void) { void usb_device_init(void) {
// run TinyUSB device // run TinyUSB device
tusb_rhport_init_t dev_init = { tusb_rhport_init_t dev_init = {
@@ -42,27 +48,29 @@ uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_t
return 0; 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 // print message to CDC in raw hex
void cdc_print_hex(uint8_t const* msg, uint16_t msg_len) { void cdc_print_hex(uint8_t const* msg, uint16_t msg_len) {
(void) msg; (void) msg;
(void) msg_len; (void) msg_len;
/*char tempbuf[8];
size_t count;
for (int i=0; i<msg_len; i++) { for (int i=0; i<msg_len; i++) {
count=sprintf(tempbuf, "%02X ", msg[i]); cdc_count=sprintf(cdc_buf, "%02X ", msg[i]);
tud_cdc_write(tempbuf, count); tud_cdc_write(cdc_buf, cdc_count);
} }
tud_cdc_write_str("\n");*/ tud_cdc_write_str("\n");
} }
// print text message to CDC // print text message to CDC
void cdc_print_str(char const* msg, uint16_t msg_len) { void cdc_print_str(char const* msg, uint16_t msg_len) {
(void) msg; (void) msg;
(void) msg_len; (void) msg_len;
//tud_cdc_write(msg, msg_len); tud_cdc_write(msg, msg_len);
} }
void cdc_print_msg(char const* msg) { void cdc_print_msg(char const* msg) {
uint16_t msg_len = strlen(msg); cdc_len = strlen(msg);
cdc_print_str(msg, msg_len); cdc_print_str(msg, cdc_len);
} }
+15 -2
View File
@@ -1,7 +1,20 @@
#ifndef MAIN_DEVICE_H_ #ifndef USB_DEVICE_H_
#define MAIN_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); 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_hex(uint8_t const* msg, uint16_t msg_len);
void cdc_print_str(char const* msg, uint16_t msg_len); void cdc_print_str(char const* msg, uint16_t msg_len);
void cdc_print_msg(char const* msg); void cdc_print_msg(char const* msg);
+49 -34
View File
@@ -11,11 +11,14 @@
#include "hid_report.h" #include "hid_report.h"
#include "usb_device.h" #include "usb_device.h"
#include "bt_device.h"
#include "usb_host.h" #include "usb_host.h"
static host_state_t host_state; host_state_t host_state;
static absolute_time_t request_time; static absolute_time_t request_time;
static absolute_time_t last_report;
static uint16_t host_poll_interval=HOST_POLL_INTERVAL;
// initialize usb host // initialize usb host
void usb_host_init(void) { void usb_host_init(void) {
@@ -32,7 +35,7 @@ void usb_host_init(void) {
tuh_hid_set_default_protocol(HID_PROTOCOL_REPORT); tuh_hid_set_default_protocol(HID_PROTOCOL_REPORT);
tusb_init(BOARD_TUH_RHPORT, &host_init); tusb_init(BOARD_TUH_RHPORT, &host_init);
set_host_state(HOST_INACTIVE); host_state=HOST_INACTIVE;
} }
@@ -44,16 +47,12 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
/// send device vid:pid information to CDC for debugging /// send device vid:pid information to CDC for debugging
char tempbuf[CFG_TUD_CDC_TX_BUFSIZE]; cdc_count = sprintf(cdc_buf, "Mount: [%04x:%04x][%u:%u] Protocol = %u\n", vid, pid, dev_addr, instance, itf_protocol);
size_t count = sprintf(tempbuf, "Mount: [%04x:%04x][%u:%u] Protocol = %u\n", vid, pid, dev_addr, instance, itf_protocol); cdc_print_str(cdc_buf, cdc_count);
cdc_print_str(tempbuf, count);
// print descriptor
//cdc_print_hex(desc_report, desc_len);
// add to HID report descriptor // add to HID report descriptor
if ( add_descriptor(dev_addr, instance, desc_report, desc_len)) { if ( add_descriptor(dev_addr, instance, desc_report, desc_len)) {
set_host_state(HOST_NEW_DESCRIPTOR); host_state=HOST_NEW_DESCRIPTOR;
request_time=get_absolute_time(); request_time=get_absolute_time();
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0); cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
} }
@@ -63,58 +62,74 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
{ {
// send device address:instance to CDC for debugging // send device address:instance to CDC for debugging
char tempbuf[CFG_TUD_CDC_TX_BUFSIZE]; cdc_count = sprintf(cdc_buf, "Unmount: [%u:%u]\n", dev_addr, instance);
size_t count = sprintf(tempbuf, "Unmount: [%u:%u]\n", dev_addr, instance); cdc_print_str(cdc_buf, cdc_count);
cdc_print_str(tempbuf, count);
if (stop_hid_report(dev_addr, instance)) { if (stop_hid_report(dev_addr, instance)) {
cdc_print_msg("Successfully stopped receiving reports\n"); cdc_print_msg("Successfully stopped receiving reports\n");
} }
remove_instance(dev_addr, instance); remove_instance(dev_addr, instance);
set_host_state(HOST_NEW_DESCRIPTOR); host_state=HOST_NEW_DESCRIPTOR;
request_time=get_absolute_time(); request_time=get_absolute_time();
} }
// Invoked when received report from device via interrupt endpoint // 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) void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
{ {
char tempbuf[32]; cdc_count = sprintf(cdc_buf, "[%u:%u](%u)< ", dev_addr, instance, len);
size_t count = sprintf(tempbuf, "[%04x:%04x](%u) ", dev_addr, instance, len); cdc_print_str(cdc_buf,cdc_count);
cdc_print_str(tempbuf,count);
cdc_print_hex(report, len); cdc_print_hex(report, len);
last_report = get_absolute_time();
host_state = HOST_WAIT_POLL;
queue_report(dev_addr, instance, report, len); queue_report(dev_addr, instance, report, len);
}
// continue to request to receive report // get host ready by updating descriptors
if ( !tuh_hid_receive_report(dev_addr, instance) ) void host_ready(void) {
{ if (absolute_time_diff_us(request_time, get_absolute_time()) >= 1000000){
cdc_print_msg("Error: cannot request report\r\n"); 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;
}
cdc_print_msg("Updating HID report map\n");
update_desc_hid_report();
if ( device_state == DEVICE_ACTIVE ) {
device_state = DEVICE_START;
}
}
} }
} }
// set the state of the host // set devices to listening once polling interval has passed
void set_host_state(host_state_t new_host_state) { void poll_devices(void) {
host_state = new_host_state; if (absolute_time_diff_us(last_report, get_absolute_time()) >= host_poll_interval) {
request_hid_reports_all();
host_state = HOST_LISTENING;
}
} }
// get the current state of the host // set the USB host polling interval based on the BT connection interval
host_state_t get_host_state(void) { void set_host_poll_interval(uint16_t bt_conn_interval) {
return host_state; if (bt_conn_interval*1250 > HOST_POLL_INTERVAL) {
} host_poll_interval = bt_conn_interval*1250;
} else {
// indicate whether host is ready for updating descriptors host_poll_interval = HOST_POLL_INTERVAL;
bool host_ready(void) { }
return (absolute_time_diff_us(request_time, get_absolute_time()) > 1000000);
} }
// request HID input reports on specified device address and instance // request HID input reports on specified device address and instance
bool request_hid_report(uint8_t dev_addr, uint8_t instance) { bool request_hid_report(uint8_t dev_addr, uint8_t instance) {
// request to receive reports HID devices // request to receive reports HID devices
if ( !tuh_hid_receive_report(dev_addr, instance) ) { if ( !tuh_hid_receive_report(dev_addr, instance) ) {
char tempbuf[CFG_TUD_CDC_TX_BUFSIZE]; cdc_count = sprintf(cdc_buf, "Error: cannot request report on [%u:%u]\n", dev_addr, instance);
size_t count = sprintf(tempbuf, "Error: cannot request report on [%u:%u]\n", dev_addr, instance); cdc_print_str(cdc_buf, cdc_count);
cdc_print_str(tempbuf, count);
return false; return false;
} }
return true; return true;
+10 -5
View File
@@ -1,5 +1,7 @@
#ifndef MAIN_HOST_H_ #ifndef USB_HOST_H_
#define MAIN_HOST_H_ #define USB_HOST_H_
#define HOST_POLL_INTERVAL 7500
typedef enum { typedef enum {
HOST_INACTIVE=0, HOST_INACTIVE=0,
@@ -7,14 +9,17 @@ typedef enum {
HOST_MOUNTED, HOST_MOUNTED,
HOST_START_LISTEN, HOST_START_LISTEN,
HOST_LISTENING, HOST_LISTENING,
HOST_WAIT_POLL,
HOST_POLL,
HOST_STOP_LISTEN, HOST_STOP_LISTEN,
} host_state_t; } host_state_t;
extern host_state_t host_state;
void usb_host_init(void); void usb_host_init(void);
void set_host_state(host_state_t new_host_state); void host_ready(void);
host_state_t get_host_state(void); void poll_devices(void);
bool host_ready(void); void set_host_poll_interval(uint16_t bt_conn_interval);
bool request_hid_report(uint8_t dev_addr, uint8_t instance); bool request_hid_report(uint8_t dev_addr, uint8_t instance);
bool stop_hid_report(uint8_t dev_addr, uint8_t instance); bool stop_hid_report(uint8_t dev_addr, uint8_t instance);