Make USBHIDKeyboard work at boot (#6964)
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).
Co-authored-by: Rodrigo Garcia <rodrigo.garcia@espressif.com>
Co-authored-by: Me No Dev <me-no-dev@users.noreply.github.com>
Co-authored-by: Jan Procházka <90197375+P-R-O-C-H-Y@users.noreply.github.com>
This commit is contained in:
parent
89fd90d669
commit
9a9ec09f63
6 changed files with 50 additions and 13 deletions
|
|
@ -678,7 +678,11 @@ static void usb_device_task(void *param) {
|
||||||
#endif
|
#endif
|
||||||
static bool tinyusb_is_initialized = false;
|
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){
|
if(tinyusb_is_initialized){
|
||||||
log_e("TinyUSB has already started! Interface %s not enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]);
|
log_e("TinyUSB has already started! Interface %s not enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]);
|
||||||
|
|
@ -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]);
|
log_e("Interface %s invalid or already enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]);
|
||||||
return ESP_FAIL;
|
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(interface == USB_INTERFACE_CDC){
|
||||||
if(!tinyusb_reserve_out_endpoint(3) ||!tinyusb_reserve_in_endpoint(4) || !tinyusb_reserve_in_endpoint(5)){
|
if(!tinyusb_reserve_out_endpoint(3) ||!tinyusb_reserve_in_endpoint(4) || !tinyusb_reserve_in_endpoint(5)){
|
||||||
log_e("CDC Reserve Endpoints Failed");
|
log_e("CDC Reserve Endpoints Failed");
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ typedef enum {
|
||||||
typedef uint16_t (*tinyusb_descriptor_cb_t)(uint8_t * dst, uint8_t * itf);
|
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_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_add_string_descriptor(const char * str);
|
||||||
uint8_t tinyusb_get_free_duplex_endpoint(void);
|
uint8_t tinyusb_get_free_duplex_endpoint(void);
|
||||||
uint8_t tinyusb_get_free_in_endpoint(void);
|
uint8_t tinyusb_get_free_in_endpoint(void);
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ static SemaphoreHandle_t tinyusb_hid_device_input_sem = NULL;
|
||||||
static SemaphoreHandle_t tinyusb_hid_device_input_mutex = NULL;
|
static SemaphoreHandle_t tinyusb_hid_device_input_mutex = NULL;
|
||||||
|
|
||||||
static bool tinyusb_hid_is_initialized = false;
|
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 uint8_t tinyusb_loaded_hid_devices_num = 0;
|
||||||
static uint16_t tinyusb_hid_device_descriptor_len = 0;
|
static uint16_t tinyusb_hid_device_descriptor_len = 0;
|
||||||
static uint8_t * tinyusb_hid_device_descriptor = NULL;
|
static uint8_t * tinyusb_hid_device_descriptor = NULL;
|
||||||
|
|
@ -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);
|
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){
|
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++){
|
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){
|
if(hid_report_map->reports[i].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT){
|
||||||
log_d(" ID: %3u, Type: %7s, Size: %2u, Usage: %8s",
|
log_d(" ID: %3u, Type: %7s, Size: %2u, Usage: %8s",
|
||||||
|
|
@ -202,14 +203,15 @@ extern "C" uint16_t tusb_hid_load_descriptor(uint8_t * dst, uint8_t * itf)
|
||||||
tinyusb_hid_is_initialized = true;
|
tinyusb_hid_is_initialized = true;
|
||||||
|
|
||||||
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB HID");
|
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);
|
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);
|
TU_VERIFY (ep_out != 0);
|
||||||
uint8_t descriptor[TUD_HID_INOUT_DESC_LEN] = {
|
uint8_t descriptor[TUD_HID_INOUT_DESC_LEN] = {
|
||||||
// HID Input & Output descriptor
|
// HID Input & Output descriptor
|
||||||
// Interface number, string index, protocol, report descriptor len, EP OUT & IN address, size & polling interval
|
// 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;
|
*itf+=1;
|
||||||
memcpy(dst, descriptor, TUD_HID_INOUT_DESC_LEN);
|
memcpy(dst, descriptor, TUD_HID_INOUT_DESC_LEN);
|
||||||
|
|
@ -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){
|
if(!tinyusb_hid_devices_is_initialized){
|
||||||
tinyusb_hid_devices_is_initialized = true;
|
tinyusb_hid_devices_is_initialized = true;
|
||||||
for(uint8_t i=0; i<USB_HID_DEVICES_MAX; i++){
|
for(uint8_t i=0; i<USB_HID_DEVICES_MAX; i++){
|
||||||
memset(&tinyusb_hid_devices[i], 0, sizeof(tinyusb_hid_device_t));
|
memset(&tinyusb_hid_devices[i], 0, sizeof(tinyusb_hid_device_t));
|
||||||
}
|
}
|
||||||
tinyusb_hid_devices_num = 0;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -342,6 +345,11 @@ bool USBHID::SendReport(uint8_t id, const void* data, size_t len, uint32_t timeo
|
||||||
return false;
|
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();
|
bool res = ready();
|
||||||
if(!res){
|
if(!res){
|
||||||
log_e("not ready");
|
log_e("not ready");
|
||||||
|
|
@ -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().
|
// we can wait for it to be given after calling tud_hid_n_report().
|
||||||
xSemaphoreTake(tinyusb_hid_device_input_sem, 0);
|
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){
|
if(!res){
|
||||||
log_e("report %u failed", id);
|
log_e("report %u failed", id);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ public:
|
||||||
class USBHID
|
class USBHID
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
USBHID(void);
|
USBHID(hid_interface_protocol_enum_t itf_protocol = HID_ITF_PROTOCOL_NONE);
|
||||||
void begin(void);
|
void begin(void);
|
||||||
void end(void);
|
void end(void);
|
||||||
bool ready(void);
|
bool ready(void);
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ static const uint8_t report_descriptor[] = {
|
||||||
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(HID_REPORT_ID_KEYBOARD))
|
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;
|
static bool initialized = false;
|
||||||
if(!initialized){
|
if(!initialized){
|
||||||
initialized = true;
|
initialized = true;
|
||||||
|
|
@ -78,6 +78,11 @@ void USBHIDKeyboard::sendReport(KeyReport* keys)
|
||||||
hid.SendReport(HID_REPORT_ID_KEYBOARD, &report, sizeof(report));
|
hid.SendReport(HID_REPORT_ID_KEYBOARD, &report, sizeof(report));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void USBHIDKeyboard::setShiftKeyReports(bool set)
|
||||||
|
{
|
||||||
|
shiftKeyReports = set;
|
||||||
|
}
|
||||||
|
|
||||||
#define SHIFT 0x80
|
#define SHIFT 0x80
|
||||||
const uint8_t _asciimap[128] =
|
const uint8_t _asciimap[128] =
|
||||||
{
|
{
|
||||||
|
|
@ -283,7 +288,12 @@ size_t USBHIDKeyboard::press(uint8_t k)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (k & 0x80) { // it's a capital letter or other character reached with shift
|
if (k & 0x80) { // it's a capital letter or other character reached with shift
|
||||||
|
// 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
|
_keyReport.modifiers |= 0x02; // the left shift modifier
|
||||||
|
}
|
||||||
k &= 0x7F;
|
k &= 0x7F;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -306,10 +316,15 @@ size_t USBHIDKeyboard::release(uint8_t k)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (k & 0x80) { // it's a capital letter or other character reached with shift
|
if (k & 0x80) { // it's a capital letter or other character reached with shift
|
||||||
|
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
|
_keyReport.modifiers &= ~(0x02); // the left shift modifier
|
||||||
k &= 0x7F;
|
k &= 0x7F;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return releaseRaw(k);
|
return releaseRaw(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,7 @@ class USBHIDKeyboard: public USBHIDDevice, public Print
|
||||||
private:
|
private:
|
||||||
USBHID hid;
|
USBHID hid;
|
||||||
KeyReport _keyReport;
|
KeyReport _keyReport;
|
||||||
|
bool shiftKeyReports;
|
||||||
public:
|
public:
|
||||||
USBHIDKeyboard(void);
|
USBHIDKeyboard(void);
|
||||||
void begin(void);
|
void begin(void);
|
||||||
|
|
@ -129,6 +130,7 @@ public:
|
||||||
size_t release(uint8_t k);
|
size_t release(uint8_t k);
|
||||||
void releaseAll(void);
|
void releaseAll(void);
|
||||||
void sendReport(KeyReport* keys);
|
void sendReport(KeyReport* keys);
|
||||||
|
void setShiftKeyReports(bool set);
|
||||||
|
|
||||||
//raw functions work with TinyUSB's HID_KEY_* macros
|
//raw functions work with TinyUSB's HID_KEY_* macros
|
||||||
size_t pressRaw(uint8_t k);
|
size_t pressRaw(uint8_t k);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue