9#include "freertos/FreeRTOS.h"
10#include "freertos/task.h"
15#include "class/cdc/cdc.h"
16#include "class/hid/hid_device.h"
22static const char*
TAG =
"USB_HID";
55 .bLength =
sizeof(tusb_desc_device_t),
56 .bDescriptorType = TUSB_DESC_DEVICE,
58 .bDeviceClass = TUSB_CLASS_MISC,
59 .bDeviceSubClass = MISC_SUBCLASS_COMMON,
60 .bDeviceProtocol = MISC_PROTOCOL_IAD,
61 .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
68 .bNumConfigurations = 1
78 uint8_t itf_count = 2;
79 uint16_t total_len = TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN;
80 bool has_ccid =
false;
81 uint16_t pref_vid = 0;
82 uint16_t pref_pid = 0;
89 const auto& def =
s_defs[i];
90 if (def.preferredVid != 0 && def.preferredPid != 0) {
91 pref_vid = def.preferredVid;
92 pref_pid = def.preferredPid;
94 if (def.cls == UsbInterfaceClass::Hid) {
96 total_len += def.hasOut ? TUD_HID_INOUT_DESC_LEN : TUD_HID_DESC_LEN;
98 }
else if (def.cls == UsbInterfaceClass::Ccid) {
107 total_len += TUD_MSC_DESC_LEN;
112 if (pref_vid != 0 && pref_pid != 0) {
120 uint8_t cfg_desc[] = {
121 TUD_CONFIG_DESCRIPTOR(1, itf_count, 0, total_len, 0, 100),
123 memcpy(p, cfg_desc,
sizeof(cfg_desc));
124 p +=
sizeof(cfg_desc);
126 uint8_t cdc_desc[] = {
130 memcpy(p, cdc_desc,
sizeof(cdc_desc));
131 p +=
sizeof(cdc_desc);
133 uint8_t next_itf = 2;
134 uint8_t next_out = 0x03;
135 uint8_t next_in = 0x83;
138 const auto& def =
s_defs[i];
146 if (def.cls == UsbInterfaceClass::Hid) {
147 if (def.reportDesc ==
nullptr || def.reportDescLen == 0) {
148 LOG_W(
TAG,
"HID interface missing report descriptor");
152 const uint16_t ep_size = (def.epOutSize > def.epInSize) ? def.epOutSize : def.epInSize;
155 TUD_HID_INOUT_DESCRIPTOR(next_itf, str_idx, def.protocol,
157 next_out, next_in, ep_size, 5),
159 memcpy(p, desc,
sizeof(desc));
165 TUD_HID_DESCRIPTOR(next_itf, str_idx, def.protocol,
166 def.reportDescLen, next_in, def.epInSize, 10),
168 memcpy(p, desc,
sizeof(desc));
173 }
else if (def.cls == UsbInterfaceClass::Ccid) {
177 memcpy(p, desc,
sizeof(desc));
187 TUD_MSC_DESCRIPTOR(next_itf,
STR_MSC, next_out, next_in, 64),
189 memcpy(p, desc,
sizeof(desc));
208 static bool inited =
false;
213 if (esp_read_mac(mac, ESP_MAC_WIFI_STA) == ESP_OK) {
215 "%02X%02X%02X%02X%02X%02X",
216 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
224 (
const char[]){0x09, 0x04},
265 static uint16_t str_desc[32];
268 const char* str =
nullptr;
277 if (!str)
return nullptr;
281 str_desc[0] = (TUSB_DESC_STRING << 8) | 4;
282 str_desc[1] = 0x0409;
287 uint8_t len = strlen(str);
288 if (len > 31) len = 31;
289 str_desc[0] = (TUSB_DESC_STRING << 8) | (2 + 2 * len);
290 for (uint8_t i = 0; i < len; i++) {
291 str_desc[1 + i] = str[i];
304 if (def_idx < 0)
return nullptr;
305 const auto& def =
s_defs[
static_cast<size_t>(def_idx)];
306 return def.reportDesc;
319 hid_report_type_t report_type,
320 uint8_t* buffer, uint16_t reqlen) {
323 if (def_idx < 0)
return 0;
324 const auto& def =
s_defs[
static_cast<size_t>(def_idx)];
325 if (!def.callbacks.onGetReport)
return 0;
326 return def.callbacks.onGetReport(report_id,
static_cast<uint8_t
>(report_type), buffer, reqlen);
338 hid_report_type_t report_type,
339 uint8_t
const* buffer, uint16_t bufsize) {
342 if (def_idx < 0)
return;
343 const auto& def =
s_defs[
static_cast<size_t>(def_idx)];
344 if (!def.callbacks.onSetReport)
return;
345 def.callbacks.onSetReport(report_id,
static_cast<uint8_t
>(report_type), buffer, bufsize);
357 if (def_idx < 0)
return;
358 const auto& def =
s_defs[
static_cast<size_t>(def_idx)];
359 if (!def.callbacks.onReportComplete)
return;
360 def.callbacks.onReportComplete(report, len);
389 if (needs_replug) *needs_replug =
false;
390 if (!defs && count > 0)
return false;
393 for (
size_t i = 0; i < count; i++) {
402 vTaskDelay(pdMS_TO_TICKS(20));
404 if (needs_replug) *needs_replug =
true;
422 vTaskDelay(pdMS_TO_TICKS(20));
441 return tud_hid_n_ready(instance);
452extern "C" bool usb_hid_send_report(uint8_t instance, uint8_t report_id,
const uint8_t* data, uint16_t len) {
453 if (!data || len == 0)
return false;
454 return tud_hid_n_report(instance, report_id, data, len);
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
#define USB_VID_ESPRESSIF
#define TUD_CCID_TOTAL_LEN
#define EP_CDC_NOTIF_SIZE
#define TUD_CCID_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize)
static constexpr size_t FIXED_STRING_COUNT
bool usb_hid_init(void)
Public USB HID/composite configuration API implementation.
static size_t s_def_count
uint16_t const * tud_descriptor_string_cb(uint8_t index, uint16_t langid)
Returns UTF-16 string descriptors for fixed and dynamic strings.
uint8_t const * tud_descriptor_device_cb(void)
TinyUSB descriptor and HID report callbacks.
uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
Handles HID GET_REPORT requests by delegating to interface callbacks.
static const char * s_dynamic_strings[MAX_INTERFACES]
Dynamic module interface names generated during configuration apply.
static int8_t s_hid_map[MAX_INTERFACES]
bool usb_hid_ready(void)
Returns whether TinyUSB stack is ready.
static char s_serial_number[13]
USB serial number derived from ESP32 MAC address.
bool usb_hid_send_report(uint8_t instance, uint8_t report_id, const uint8_t *data, uint16_t len)
Sends one HID report on the selected interface instance.
static constexpr size_t MAX_INTERFACES
static const char * s_fixed_strings[]
Fixed USB string descriptors; dynamic interface strings are appended at runtime.
static size_t s_dynamic_string_count
static uint8_t s_config_descriptor[256]
static size_t s_hid_count
static bool s_hid_initialized
Global descriptor and interface registration state.
void usb_hid_set_msc(bool active)
Adds or removes the MSC interface and re-enumerates the device.
uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance)
Returns the HID report descriptor for a HID instance.
bool usb_hid_apply_config(const UsbInterfaceDef *defs, size_t count, bool *needs_replug)
Applies the runtime interface configuration and optionally triggers re-enumeration.
void tud_hid_report_complete_cb(uint8_t instance, uint8_t const *report, uint16_t len)
Notifies interface callbacks that a HID report transfer completed.
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
Returns the active configuration descriptor.
static UsbInterfaceDef s_defs[MAX_INTERFACES]
bool usb_hid_instance_ready(uint8_t instance)
Returns whether a specific HID instance endpoint is ready.
static uint16_t s_config_descriptor_len
void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize)
Handles HID SET_REPORT requests by delegating to interface callbacks.
static void build_config_descriptor(void)
Rebuilds the composite USB configuration descriptor from registered interfaces.
static tusb_desc_device_t device_descriptor
Descriptor storage used for runtime-generated TinyUSB configuration descriptors.
static void init_serial_number()
Initializes the serial-number string once from efuse MAC.