Skip to content

Commit 1201a79

Browse files
Make USBHIDKeyboard work at boot
1. Like a real keyboard, the USB interface descriptor will have an interface subclass of boot and an interface protocol of keyboard. This will cause some PC BIOS to send a SET PROTOCOL BOOT request to the device. 2. When the device sends reports to the host, if the host requested boot protocol, don't send a report ID because boot protocol does not use report IDs. 3. To work with some simple PC BIOS: a. Use endpoint address of 1 for input and output. b. Use separate reports for the shift key. These extra reports can be disabled by calling USBHIDKeyboard::setShiftKeyReports(false).
1 parent c93bf11 commit 1201a79

File tree

6 files changed

+50
-13
lines changed

6 files changed

+50
-13
lines changed

Diff for: cores/esp32/esp32-hal-tinyusb.c

+12-1
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,11 @@ static void usb_device_task(void *param) {
651651
#endif
652652
static bool tinyusb_is_initialized = false;
653653

654-
esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb)
654+
esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb){
655+
return tinyusb_enable_interface2(interface, descriptor_len, cb, false);
656+
}
657+
658+
esp_err_t tinyusb_enable_interface2(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb, bool reserve_endpoints)
655659
{
656660
if(tinyusb_is_initialized){
657661
log_e("TinyUSB has already started! Interface %s not enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]);
@@ -661,6 +665,13 @@ esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descr
661665
log_e("Interface %s invalid or already enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]);
662666
return ESP_FAIL;
663667
}
668+
if(interface == USB_INTERFACE_HID && reserve_endpoints){
669+
// Some simple PC BIOS requires specific endpoint addresses for keyboard at boot
670+
if(!tinyusb_reserve_out_endpoint(1) ||!tinyusb_reserve_in_endpoint(1)){
671+
log_e("HID Reserve Endpoints Failed");
672+
return ESP_FAIL;
673+
}
674+
}
664675
if(interface == USB_INTERFACE_CDC){
665676
if(!tinyusb_reserve_out_endpoint(3) ||!tinyusb_reserve_in_endpoint(4) || !tinyusb_reserve_in_endpoint(5)){
666677
log_e("CDC Reserve Endpoints Failed");

Diff for: cores/esp32/esp32-hal-tinyusb.h

+1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ typedef enum {
9494
typedef uint16_t (*tinyusb_descriptor_cb_t)(uint8_t * dst, uint8_t * itf);
9595

9696
esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb);
97+
esp_err_t tinyusb_enable_interface2(tinyusb_interface_t interface, uint16_t descriptor_len, tinyusb_descriptor_cb_t cb, bool reserve_endpoints);
9798
uint8_t tinyusb_add_string_descriptor(const char * str);
9899
uint8_t tinyusb_get_free_duplex_endpoint(void);
99100
uint8_t tinyusb_get_free_in_endpoint(void);

Diff for: libraries/USB/src/USBHID.cpp

+15-7
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ static xSemaphoreHandle tinyusb_hid_device_input_sem = NULL;
3939
static xSemaphoreHandle tinyusb_hid_device_input_mutex = NULL;
4040

4141
static bool tinyusb_hid_is_initialized = false;
42+
static hid_interface_protocol_enum_t tinyusb_interface_protocol = HID_ITF_PROTOCOL_NONE;
4243
static uint8_t tinyusb_loaded_hid_devices_num = 0;
4344
static uint16_t tinyusb_hid_device_descriptor_len = 0;
4445
static uint8_t * tinyusb_hid_device_descriptor = NULL;
@@ -173,7 +174,7 @@ static bool tinyusb_load_enabled_hid_devices(){
173174

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

203204
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB HID");
204-
uint8_t ep_in = tinyusb_get_free_in_endpoint();
205+
// For keyboard boot protocol, we've already called tinyusb_enable_interface2(reserve_endpoints=true)
206+
uint8_t ep_in = tinyusb_interface_protocol == HID_ITF_PROTOCOL_KEYBOARD ? 1 : tinyusb_get_free_in_endpoint();
205207
TU_VERIFY (ep_in != 0);
206-
uint8_t ep_out = tinyusb_get_free_out_endpoint();
208+
uint8_t ep_out = tinyusb_interface_protocol == HID_ITF_PROTOCOL_KEYBOARD ? 1 : tinyusb_get_free_out_endpoint();
207209
TU_VERIFY (ep_out != 0);
208210
uint8_t descriptor[TUD_HID_INOUT_DESC_LEN] = {
209211
// HID Input & Output descriptor
210212
// Interface number, string index, protocol, report descriptor len, EP OUT & IN address, size & polling interval
211-
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)
213+
TUD_HID_INOUT_DESCRIPTOR(*itf, str_index, tinyusb_interface_protocol, tinyusb_hid_device_descriptor_len, ep_out, (uint8_t)(0x80 | ep_in), 64, 1)
212214
};
213215
*itf+=1;
214216
memcpy(dst, descriptor, TUD_HID_INOUT_DESC_LEN);
@@ -275,14 +277,15 @@ void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_
275277
}
276278
}
277279

278-
USBHID::USBHID(){
280+
USBHID::USBHID(hid_interface_protocol_enum_t itf_protocol){
279281
if(!tinyusb_hid_devices_is_initialized){
280282
tinyusb_hid_devices_is_initialized = true;
281283
for(uint8_t i=0; i<USB_HID_DEVICES_MAX; i++){
282284
memset(&tinyusb_hid_devices[i], 0, sizeof(tinyusb_hid_device_t));
283285
}
284286
tinyusb_hid_devices_num = 0;
285-
tinyusb_enable_interface(USB_INTERFACE_HID, TUD_HID_INOUT_DESC_LEN, tusb_hid_load_descriptor);
287+
tinyusb_interface_protocol = itf_protocol;
288+
tinyusb_enable_interface2(USB_INTERFACE_HID, TUD_HID_INOUT_DESC_LEN, tusb_hid_load_descriptor, itf_protocol == HID_ITF_PROTOCOL_KEYBOARD);
286289
}
287290
}
288291

@@ -327,11 +330,16 @@ bool USBHID::SendReport(uint8_t id, const void* data, size_t len, uint32_t timeo
327330
return false;
328331
}
329332

333+
// If we're configured to support boot protocol, and the host has requested boot protocol, prevent
334+
// sending of report ID, by passing report ID of 0 to tud_hid_n_report().
335+
uint8_t effective_id = ((tinyusb_interface_protocol != HID_ITF_PROTOCOL_NONE) &&
336+
(tud_hid_n_get_protocol(0) == HID_PROTOCOL_BOOT)) ? 0 : id;
337+
330338
bool res = ready();
331339
if(!res){
332340
log_e("not ready");
333341
} else {
334-
res = tud_hid_n_report(0, id, data, len);
342+
res = tud_hid_n_report(0, effective_id, data, len);
335343
if(!res){
336344
log_e("report %u failed", id);
337345
} else {

Diff for: libraries/USB/src/USBHID.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class USBHIDDevice
6666
class USBHID
6767
{
6868
public:
69-
USBHID(void);
69+
USBHID(hid_interface_protocol_enum_t itf_protocol = HID_ITF_PROTOCOL_NONE);
7070
void begin(void);
7171
void end(void);
7272
bool ready(void);

Diff for: libraries/USB/src/USBHIDKeyboard.cpp

+19-4
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ static const uint8_t report_descriptor[] = {
3232
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(HID_REPORT_ID_KEYBOARD))
3333
};
3434

35-
USBHIDKeyboard::USBHIDKeyboard(): hid(){
35+
USBHIDKeyboard::USBHIDKeyboard(): hid(HID_ITF_PROTOCOL_KEYBOARD), shiftKeyReports(true){
3636
static bool initialized = false;
3737
if(!initialized){
3838
initialized = true;
@@ -80,6 +80,11 @@ void USBHIDKeyboard::sendReport(KeyReport* keys)
8080
hid.SendReport(HID_REPORT_ID_KEYBOARD, &report, sizeof(report));
8181
}
8282

83+
void USBHIDKeyboard::setShiftKeyReports(bool set)
84+
{
85+
shiftKeyReports = set;
86+
}
87+
8388
#define SHIFT 0x80
8489
const uint8_t _asciimap[128] =
8590
{
@@ -285,7 +290,12 @@ size_t USBHIDKeyboard::press(uint8_t k)
285290
return 0;
286291
}
287292
if (k & 0x80) { // it's a capital letter or other character reached with shift
288-
_keyReport.modifiers |= 0x02; // the left shift modifier
293+
// At boot, some PCs need a separate report with the shift key down like a real keyboard.
294+
if (shiftKeyReports) {
295+
pressRaw(HID_KEY_SHIFT_LEFT);
296+
} else {
297+
_keyReport.modifiers |= 0x02; // the left shift modifier
298+
}
289299
k &= 0x7F;
290300
}
291301
}
@@ -308,8 +318,13 @@ size_t USBHIDKeyboard::release(uint8_t k)
308318
return 0;
309319
}
310320
if (k & 0x80) { // it's a capital letter or other character reached with shift
311-
_keyReport.modifiers &= ~(0x02); // the left shift modifier
312-
k &= 0x7F;
321+
if (shiftKeyReports) {
322+
releaseRaw(k & 0x7F); // Release key without shift modifier
323+
k = HID_KEY_SHIFT_LEFT; // Below, release shift modifier
324+
} else {
325+
_keyReport.modifiers &= ~(0x02); // the left shift modifier
326+
k &= 0x7F;
327+
}
313328
}
314329
}
315330
return releaseRaw(k);

Diff for: libraries/USB/src/USBHIDKeyboard.h

+2
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ class USBHIDKeyboard: public USBHIDDevice, public Print
114114
private:
115115
USBHID hid;
116116
KeyReport _keyReport;
117+
bool shiftKeyReports;
117118
public:
118119
USBHIDKeyboard(void);
119120
void begin(void);
@@ -124,6 +125,7 @@ class USBHIDKeyboard: public USBHIDDevice, public Print
124125
size_t release(uint8_t k);
125126
void releaseAll(void);
126127
void sendReport(KeyReport* keys);
128+
void setShiftKeyReports(bool set);
127129

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

0 commit comments

Comments
 (0)