Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make USBHIDKeyboard work at boot #6964

Merged
merged 23 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1201a79
Make USBHIDKeyboard work at boot
RefactorFactory Jul 10, 2022
849c62c
Merge branch 'master' into master
SuGlider Sep 5, 2022
a10e950
Merge branch 'master' into master
RefactorFactory Sep 17, 2022
0c12eec
Merge branch 'master' into master
RefactorFactory Nov 17, 2022
b7ac500
Merge branch 'master' into master
RefactorFactory Nov 30, 2022
6041393
Merge branch 'master' into master
RefactorFactory Dec 6, 2022
94eebf3
Merge branch 'master' into master
RefactorFactory Dec 12, 2022
5500ccd
Merge branch 'master' into master
RefactorFactory Dec 31, 2022
d06aa27
Merge branch 'master' into master
RefactorFactory Jan 5, 2023
2e67a32
Merge branch 'master' into master
RefactorFactory Jan 23, 2023
9f0bcaf
Merge branch 'master' into master
RefactorFactory Feb 15, 2023
a30d36c
Merge branch 'master' into master
RefactorFactory Feb 27, 2023
c28a6a6
Merge branch 'master' into master
RefactorFactory May 25, 2023
e0d2042
Merge branch 'espressif:master' into master
RefactorFactory Jun 10, 2023
e94a8c9
Merge branch 'master' into master
RefactorFactory Jul 1, 2023
1896f96
Merge branch 'master' into master
RefactorFactory Jul 21, 2023
16caa73
Merge branch 'master' into master
RefactorFactory Sep 4, 2023
ce9049c
Merge branch 'master' into master
RefactorFactory Sep 23, 2023
e3e612a
Merge branch 'master' into master
RefactorFactory Oct 14, 2023
d8a61e1
Merge branch 'master' into master
RefactorFactory Nov 4, 2023
b2643de
Merge branch 'master' into master
RefactorFactory Nov 25, 2023
6ab100c
Merge branch 'master' into master
me-no-dev Nov 29, 2023
aee11ba
Merge branch 'master' into master
P-R-O-C-H-Y Nov 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 12 additions & 1 deletion cores/esp32/esp32-hal-tinyusb.c
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,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 @@ -688,6 +692,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)){
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this reservation of endpoints IN 1, OUT 1, USBCDC will reserve OUT 3, IN 4, IN 5 (line 665), then when USBHID.cpp:tusb_hid_load_descriptor() calls tinyusb_get_free_in_endpoint()/tinyusb_get_free_out_endpoint(), those functions will return IN 3, OUT 4 because those functions prefer returning endpoints whose opposite is already used.

Some alternatives to calling tinyusb_reserve_out_endpoint()/tinyusb_reserve_in_endpoint() here to reserve IN 1, OUT 1:

  1. USBHID.cpp:tusb_hid_load_descriptor() could call tinyusb_get_free_duplex_endpoint() which would likely get endpoints IN 1, OUT 1 because USBHID.cpp:tusb_hid_load_descriptor() runs early enough.
  2. Change tinyusb_get_free_in_endpoint()/tinyusb_get_free_out_endpoint() not to prefer returning endpoints whose opposite is already used. Or add a flag parameter.

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 @@ -97,6 +97,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
22 changes: 15 additions & 7 deletions libraries/USB/src/USBHID.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ static SemaphoreHandle_t tinyusb_hid_device_input_sem = NULL;
static SemaphoreHandle_t 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 @@ -174,7 +175,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 @@ -202,14 +203,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 @@ -276,14 +278,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 Expand Up @@ -342,6 +345,11 @@ bool USBHID::SendReport(uint8_t id, const void* data, size_t len, uint32_t timeo
return false;
}

// If we're configured to support boot protocol, and the host has requested boot protocol, prevent
// sending of report ID, by passing report ID of 0 to tud_hid_n_report().
uint8_t effective_id = ((tinyusb_interface_protocol != HID_ITF_PROTOCOL_NONE) &&
(tud_hid_n_get_protocol(0) == HID_PROTOCOL_BOOT)) ? 0 : id;

bool res = ready();
if(!res){
log_e("not ready");
Expand All @@ -352,7 +360,7 @@ bool USBHID::SendReport(uint8_t id, const void* data, size_t len, uint32_t timeo
// we can wait for it to be given after calling tud_hid_n_report().
xSemaphoreTake(tinyusb_hid_device_input_sem, 0);

res = tud_hid_n_report(0, id, data, len);
res = tud_hid_n_report(0, effective_id, data, len);
if(!res){
log_e("report %u failed", id);
} else {
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 @@ -70,7 +70,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
23 changes: 19 additions & 4 deletions libraries/USB/src/USBHIDKeyboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ static const uint8_t report_descriptor[] = {
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(HID_REPORT_ID_KEYBOARD))
};

USBHIDKeyboard::USBHIDKeyboard(): hid(){
USBHIDKeyboard::USBHIDKeyboard(): hid(HID_ITF_PROTOCOL_KEYBOARD), shiftKeyReports(true){
static bool initialized = false;
if(!initialized){
initialized = true;
Expand Down Expand Up @@ -78,6 +78,11 @@ void USBHIDKeyboard::sendReport(KeyReport* keys)
hid.SendReport(HID_REPORT_ID_KEYBOARD, &report, sizeof(report));
}

void USBHIDKeyboard::setShiftKeyReports(bool set)
{
shiftKeyReports = set;
}

#define SHIFT 0x80
const uint8_t _asciimap[128] =
{
Expand Down Expand Up @@ -283,7 +288,12 @@ size_t USBHIDKeyboard::press(uint8_t k)
return 0;
}
if (k & 0x80) { // it's a capital letter or other character reached with shift
_keyReport.modifiers |= 0x02; // the left shift modifier
// At boot, some PCs need a separate report with the shift key down like a real keyboard.
if (shiftKeyReports) {
pressRaw(HID_KEY_SHIFT_LEFT);
} else {
_keyReport.modifiers |= 0x02; // the left shift modifier
}
k &= 0x7F;
}
}
Expand All @@ -306,8 +316,13 @@ size_t USBHIDKeyboard::release(uint8_t k)
return 0;
}
if (k & 0x80) { // it's a capital letter or other character reached with shift
_keyReport.modifiers &= ~(0x02); // the left shift modifier
k &= 0x7F;
if (shiftKeyReports) {
releaseRaw(k & 0x7F); // Release key without shift modifier
k = HID_KEY_SHIFT_LEFT; // Below, release shift modifier
} else {
_keyReport.modifiers &= ~(0x02); // the left shift modifier
k &= 0x7F;
}
}
}
return releaseRaw(k);
Expand Down
2 changes: 2 additions & 0 deletions libraries/USB/src/USBHIDKeyboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class USBHIDKeyboard: public USBHIDDevice, public Print
private:
USBHID hid;
KeyReport _keyReport;
bool shiftKeyReports;
public:
USBHIDKeyboard(void);
void begin(void);
Expand All @@ -129,6 +130,7 @@ class USBHIDKeyboard: public USBHIDDevice, public Print
size_t release(uint8_t k);
void releaseAll(void);
void sendReport(KeyReport* keys);
void setShiftKeyReports(bool set);

//raw functions work with TinyUSB's HID_KEY_* macros
size_t pressRaw(uint8_t k);
Expand Down