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";
51 .bLength =
sizeof(tusb_desc_device_t),
52 .bDescriptorType = TUSB_DESC_DEVICE,
54 .bDeviceClass = TUSB_CLASS_MISC,
55 .bDeviceSubClass = MISC_SUBCLASS_COMMON,
56 .bDeviceProtocol = MISC_PROTOCOL_IAD,
57 .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
64 .bNumConfigurations = 1
74 uint8_t itf_count = 2;
75 uint16_t total_len = TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN;
76 bool has_ccid =
false;
77 uint16_t pref_vid = 0;
78 uint16_t pref_pid = 0;
85 const auto& def =
s_defs[i];
86 if (def.preferredVid != 0 && def.preferredPid != 0) {
87 pref_vid = def.preferredVid;
88 pref_pid = def.preferredPid;
90 if (def.cls == UsbInterfaceClass::Hid) {
92 total_len += def.hasOut ? TUD_HID_INOUT_DESC_LEN : TUD_HID_DESC_LEN;
94 }
else if (def.cls == UsbInterfaceClass::Ccid) {
103 if (pref_vid != 0 && pref_pid != 0) {
111 uint8_t cfg_desc[] = {
112 TUD_CONFIG_DESCRIPTOR(1, itf_count, 0, total_len, 0, 100),
114 memcpy(p, cfg_desc,
sizeof(cfg_desc));
115 p +=
sizeof(cfg_desc);
117 uint8_t cdc_desc[] = {
121 memcpy(p, cdc_desc,
sizeof(cdc_desc));
122 p +=
sizeof(cdc_desc);
124 uint8_t next_itf = 2;
125 uint8_t next_out = 0x03;
126 uint8_t next_in = 0x83;
129 const auto& def =
s_defs[i];
137 if (def.cls == UsbInterfaceClass::Hid) {
138 if (def.reportDesc ==
nullptr || def.reportDescLen == 0) {
139 LOG_W(
TAG,
"HID interface missing report descriptor");
143 const uint16_t ep_size = (def.epOutSize > def.epInSize) ? def.epOutSize : def.epInSize;
146 TUD_HID_INOUT_DESCRIPTOR(next_itf, str_idx, def.protocol,
148 next_out, next_in, ep_size, 5),
150 memcpy(p, desc,
sizeof(desc));
156 TUD_HID_DESCRIPTOR(next_itf, str_idx, def.protocol,
157 def.reportDescLen, next_in, def.epInSize, 10),
159 memcpy(p, desc,
sizeof(desc));
164 }
else if (def.cls == UsbInterfaceClass::Ccid) {
168 memcpy(p, desc,
sizeof(desc));
188 static bool inited =
false;
193 if (esp_read_mac(mac, ESP_MAC_WIFI_STA) == ESP_OK) {
195 "%02X%02X%02X%02X%02X%02X",
196 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
204 (
const char[]){0x09, 0x04},
244 static uint16_t str_desc[32];
247 const char* str =
nullptr;
256 if (!str)
return nullptr;
260 str_desc[0] = (TUSB_DESC_STRING << 8) | 4;
261 str_desc[1] = 0x0409;
266 uint8_t len = strlen(str);
267 if (len > 31) len = 31;
268 str_desc[0] = (TUSB_DESC_STRING << 8) | (2 + 2 * len);
269 for (uint8_t i = 0; i < len; i++) {
270 str_desc[1 + i] = str[i];
283 if (def_idx < 0)
return nullptr;
284 const auto& def =
s_defs[
static_cast<size_t>(def_idx)];
285 return def.reportDesc;
298 hid_report_type_t report_type,
299 uint8_t* buffer, uint16_t reqlen) {
302 if (def_idx < 0)
return 0;
303 const auto& def =
s_defs[
static_cast<size_t>(def_idx)];
304 if (!def.callbacks.onGetReport)
return 0;
305 return def.callbacks.onGetReport(report_id,
static_cast<uint8_t
>(report_type), buffer, reqlen);
317 hid_report_type_t report_type,
318 uint8_t
const* buffer, uint16_t bufsize) {
321 if (def_idx < 0)
return;
322 const auto& def =
s_defs[
static_cast<size_t>(def_idx)];
323 if (!def.callbacks.onSetReport)
return;
324 def.callbacks.onSetReport(report_id,
static_cast<uint8_t
>(report_type), buffer, bufsize);
336 if (def_idx < 0)
return;
337 const auto& def =
s_defs[
static_cast<size_t>(def_idx)];
338 if (!def.callbacks.onReportComplete)
return;
339 def.callbacks.onReportComplete(report, len);
368 if (needs_replug) *needs_replug =
false;
369 if (!defs && count > 0)
return false;
372 for (
size_t i = 0; i < count; i++) {
381 vTaskDelay(pdMS_TO_TICKS(20));
383 if (needs_replug) *needs_replug =
true;
403 return tud_hid_n_ready(instance);
414extern "C" bool usb_hid_send_report(uint8_t instance, uint8_t report_id,
const uint8_t* data, uint16_t len) {
415 if (!data || len == 0)
return false;
416 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.
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.