Skip to content

Commit

Permalink
Make USBHIDKeyboard work at boot
Browse files Browse the repository at this point in the history
Add a boot_protocol parameter to the USBHIDKeyboard constructor. When true:

1. The USB interface descriptor will have an interface subclass of boot and
   and interface protocol of keyboard. This will cause some PC BIOS to send a
   a SET PROTOCOL BOOT request to the device.

2. The USB report descriptor will not have a report ID because boot protocol
   does not use report IDs.

3. When the host sends reports to the device, USBHIDKeyboard will accept a
   report ID of 0 instead of checking for HID_REPORT_ID_KEYBOARD(1).

4. When the device sends reports to the host, it will not send a report ID
   because boot protocol does not use report IDs.

5. Use endpoint address of 1 for input and output because some PC BIOS require
   it.
  • Loading branch information
RefactorFactory committed Jul 11, 2022
1 parent c93bf11 commit 490a075
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 15 deletions.
13 changes: 12 additions & 1 deletion cores/esp32/esp32-hal-tinyusb.c
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,11 @@ static void usb_device_task(void *param) {
#endif
static bool tinyusb_is_initialized = false;

esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb)
esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb){
return tinyusb_enable_interface2(interface, descriptor_len, cb, false);
}

esp_err_t tinyusb_enable_interface2(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb, bool reserve_endpoints)
{
if(tinyusb_is_initialized){
log_e("TinyUSB has already started! Interface %s not enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]);
Expand All @@ -661,6 +665,13 @@ esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descr
log_e("Interface %s invalid or already enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]);
return ESP_FAIL;
}
if(interface == USB_INTERFACE_HID && reserve_endpoints){
// Some simple PC BIOS requires specific endpoint addresses for keyboard at boot
if(!tinyusb_reserve_out_endpoint(1) ||!tinyusb_reserve_in_endpoint(1)){
log_e("HID Reserve Endpoints Failed");
return ESP_FAIL;
}
}
if(interface == USB_INTERFACE_CDC){
if(!tinyusb_reserve_out_endpoint(3) ||!tinyusb_reserve_in_endpoint(4) || !tinyusb_reserve_in_endpoint(5)){
log_e("CDC Reserve Endpoints Failed");
Expand Down
1 change: 1 addition & 0 deletions cores/esp32/esp32-hal-tinyusb.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ typedef enum {
typedef uint16_t (*tinyusb_descriptor_cb_t)(uint8_t * dst, uint8_t * itf);

esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb);
esp_err_t tinyusb_enable_interface2(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb, bool reserve_endpoints);
uint8_t tinyusb_add_string_descriptor(const char * str);
uint8_t tinyusb_get_free_duplex_endpoint(void);
uint8_t tinyusb_get_free_in_endpoint(void);
Expand Down
15 changes: 9 additions & 6 deletions libraries/USB/src/USBHID.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ static xSemaphoreHandle tinyusb_hid_device_input_sem = NULL;
static xSemaphoreHandle tinyusb_hid_device_input_mutex = NULL;

static bool tinyusb_hid_is_initialized = false;
static hid_interface_protocol_enum_t tinyusb_interface_protocol = HID_ITF_PROTOCOL_NONE;
static uint8_t tinyusb_loaded_hid_devices_num = 0;
static uint16_t tinyusb_hid_device_descriptor_len = 0;
static uint8_t * tinyusb_hid_device_descriptor = NULL;
Expand Down Expand Up @@ -173,7 +174,7 @@ static bool tinyusb_load_enabled_hid_devices(){

esp_hid_report_map_t *hid_report_map = esp_hid_parse_report_map(tinyusb_hid_device_descriptor, tinyusb_hid_device_descriptor_len);
if(hid_report_map){
log_d("Loaded HID Desriptor with the following reports:");
log_d("Loaded HID Descriptor with the following reports:");
for(uint8_t i=0; i<hid_report_map->reports_len; i++){
if(hid_report_map->reports[i].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT){
log_d(" ID: %3u, Type: %7s, Size: %2u, Usage: %8s",
Expand Down Expand Up @@ -201,14 +202,15 @@ extern "C" uint16_t tusb_hid_load_descriptor(uint8_t * dst, uint8_t * itf)
tinyusb_hid_is_initialized = true;

uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB HID");
uint8_t ep_in = tinyusb_get_free_in_endpoint();
// For keyboard boot protocol, we've already called tinyusb_enable_interface2(reserve_endpoints=true)
uint8_t ep_in = tinyusb_interface_protocol == HID_ITF_PROTOCOL_KEYBOARD ? 1 : tinyusb_get_free_in_endpoint();
TU_VERIFY (ep_in != 0);
uint8_t ep_out = tinyusb_get_free_out_endpoint();
uint8_t ep_out = tinyusb_interface_protocol == HID_ITF_PROTOCOL_KEYBOARD ? 1 : tinyusb_get_free_out_endpoint();
TU_VERIFY (ep_out != 0);
uint8_t descriptor[TUD_HID_INOUT_DESC_LEN] = {
// HID Input & Output descriptor
// Interface number, string index, protocol, report descriptor len, EP OUT & IN address, size & polling interval
TUD_HID_INOUT_DESCRIPTOR(*itf, str_index, HID_ITF_PROTOCOL_NONE, tinyusb_hid_device_descriptor_len, ep_out, (uint8_t)(0x80 | ep_in), 64, 1)
TUD_HID_INOUT_DESCRIPTOR(*itf, str_index, tinyusb_interface_protocol, tinyusb_hid_device_descriptor_len, ep_out, (uint8_t)(0x80 | ep_in), 64, 1)
};
*itf+=1;
memcpy(dst, descriptor, TUD_HID_INOUT_DESC_LEN);
Expand Down Expand Up @@ -275,14 +277,15 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_
}
}

USBHID::USBHID(){
USBHID::USBHID(hid_interface_protocol_enum_t itf_protocol){
if(!tinyusb_hid_devices_is_initialized){
tinyusb_hid_devices_is_initialized = true;
for(uint8_t i=0; i<USB_HID_DEVICES_MAX; i++){
memset(&tinyusb_hid_devices[i], 0, sizeof(tinyusb_hid_device_t));
}
tinyusb_hid_devices_num = 0;
tinyusb_enable_interface(USB_INTERFACE_HID, TUD_HID_INOUT_DESC_LEN, tusb_hid_load_descriptor);
tinyusb_interface_protocol = itf_protocol;
tinyusb_enable_interface2(USB_INTERFACE_HID, TUD_HID_INOUT_DESC_LEN, tusb_hid_load_descriptor, itf_protocol == HID_ITF_PROTOCOL_KEYBOARD);
}
}

Expand Down
2 changes: 1 addition & 1 deletion libraries/USB/src/USBHID.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class USBHIDDevice
class USBHID
{
public:
USBHID(void);
USBHID(hid_interface_protocol_enum_t itf_protocol = HID_ITF_PROTOCOL_NONE);
void begin(void);
void end(void);
bool ready(void);
Expand Down
20 changes: 14 additions & 6 deletions libraries/USB/src/USBHIDKeyboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,25 @@ static const uint8_t report_descriptor[] = {
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(HID_REPORT_ID_KEYBOARD))
};

USBHIDKeyboard::USBHIDKeyboard(): hid(){
// Boot protocol does not use a report ID
static const uint8_t boot_report_descriptor[] = {
TUD_HID_REPORT_DESC_KEYBOARD()
};

USBHIDKeyboard::USBHIDKeyboard(bool boot_protocol):
hid(boot_protocol ? HID_ITF_PROTOCOL_KEYBOARD : HID_ITF_PROTOCOL_NONE),
boot_protocol(boot_protocol){
static bool initialized = false;
if(!initialized){
initialized = true;
hid.addDevice(this, sizeof(report_descriptor));
hid.addDevice(this, boot_protocol ? sizeof(boot_report_descriptor) : sizeof(report_descriptor));
}
}

uint16_t USBHIDKeyboard::_onGetDescriptor(uint8_t* dst){
memcpy(dst, report_descriptor, sizeof(report_descriptor));
return sizeof(report_descriptor);
const size_t size = boot_protocol ? sizeof(boot_report_descriptor) : sizeof(report_descriptor);
memcpy(dst, boot_protocol ? boot_report_descriptor : report_descriptor, size);
return size;
}

void USBHIDKeyboard::begin(){
Expand All @@ -60,7 +68,7 @@ void USBHIDKeyboard::onEvent(arduino_usb_hid_keyboard_event_t event, esp_event_h
}

void USBHIDKeyboard::_onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len){
if(report_id == HID_REPORT_ID_KEYBOARD){
if((boot_protocol && report_id == 0) || report_id == HID_REPORT_ID_KEYBOARD){
arduino_usb_hid_keyboard_event_data_t p;
p.leds = buffer[0];
arduino_usb_event_post(ARDUINO_USB_HID_KEYBOARD_EVENTS, ARDUINO_USB_HID_KEYBOARD_LED_EVENT, &p, sizeof(arduino_usb_hid_keyboard_event_data_t), portMAX_DELAY);
Expand All @@ -77,7 +85,7 @@ void USBHIDKeyboard::sendReport(KeyReport* keys)
} else {
memset(report.keycode, 0, 6);
}
hid.SendReport(HID_REPORT_ID_KEYBOARD, &report, sizeof(report));
hid.SendReport(boot_protocol ? 0 : HID_REPORT_ID_KEYBOARD, &report, sizeof(report));
}

#define SHIFT 0x80
Expand Down
3 changes: 2 additions & 1 deletion libraries/USB/src/USBHIDKeyboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,9 @@ class USBHIDKeyboard: public USBHIDDevice, public Print
private:
USBHID hid;
KeyReport _keyReport;
bool boot_protocol;
public:
USBHIDKeyboard(void);
USBHIDKeyboard(bool boot_protocol = false);
void begin(void);
void end(void);
size_t write(uint8_t k);
Expand Down

0 comments on commit 490a075

Please sign in to comment.