2#if defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || \
3 defined(USE_ESP32_VARIANT_ESP32S31) || defined(USE_ESP32_VARIANT_ESP32H4)
18#pragma GCC diagnostic ignored "-Wparentheses"
20using namespace bytebuffer;
22#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
23static void print_ep_desc(
const usb_ep_desc_t *ep_desc) {
24 const char *ep_type_str;
25 int type = ep_desc->bmAttributes & USB_BM_ATTRIBUTES_XFERTYPE_MASK;
28 case USB_BM_ATTRIBUTES_XFER_CONTROL:
31 case USB_BM_ATTRIBUTES_XFER_ISOC:
34 case USB_BM_ATTRIBUTES_XFER_BULK:
37 case USB_BM_ATTRIBUTES_XFER_INT:
46 "\t\t*** Endpoint descriptor ***\n"
48 "\t\tbDescriptorType %d\n"
49 "\t\tbEndpointAddress 0x%x\tEP %d %s\n"
50 "\t\tbmAttributes 0x%x\t%s\n"
51 "\t\twMaxPacketSize %d\n"
53 ep_desc->bLength, ep_desc->bDescriptorType, ep_desc->bEndpointAddress, USB_EP_DESC_GET_EP_NUM(ep_desc),
54 USB_EP_DESC_GET_EP_DIR(ep_desc) ?
"IN" :
"OUT", ep_desc->bmAttributes, ep_type_str, ep_desc->wMaxPacketSize,
58static void usbh_print_intf_desc(
const usb_intf_desc_t *intf_desc) {
60 "\t*** Interface descriptor ***\n"
62 "\tbDescriptorType %d\n"
63 "\tbInterfaceNumber %d\n"
64 "\tbAlternateSetting %d\n"
65 "\tbNumEndpoints %d\n"
66 "\tbInterfaceClass 0x%x\n"
68 intf_desc->bLength, intf_desc->bDescriptorType, intf_desc->bInterfaceNumber, intf_desc->bAlternateSetting,
69 intf_desc->bNumEndpoints, intf_desc->bInterfaceProtocol, intf_desc->iInterface);
72static void usbh_print_cfg_desc(
const usb_config_desc_t *cfg_desc) {
74 "*** Configuration descriptor ***\n"
76 " bDescriptorType %d\n"
78 " bNumInterfaces %d\n"
79 " bConfigurationValue %d\n"
80 " iConfiguration %d\n"
81 " bmAttributes 0x%x\n"
83 cfg_desc->bLength, cfg_desc->bDescriptorType, cfg_desc->wTotalLength, cfg_desc->bNumInterfaces,
84 cfg_desc->bConfigurationValue, cfg_desc->iConfiguration, cfg_desc->bmAttributes, cfg_desc->bMaxPower * 2);
87static void usb_client_print_device_descriptor(
const usb_device_desc_t *devc_desc) {
88 if (devc_desc == NULL) {
93 "*** Device descriptor ***\n"
95 " bDescriptorType %d\n"
97 " bDeviceClass 0x%x\n"
98 " bDeviceSubClass 0x%x\n"
99 " bDeviceProtocol 0x%x\n"
100 " bMaxPacketSize0 %d\n"
103 " bcdDevice %d.%d0\n"
104 " iManufacturer %d\n"
106 " iSerialNumber %d\n"
107 " bNumConfigurations %d",
108 devc_desc->bLength, devc_desc->bDescriptorType, ((devc_desc->bcdUSB >> 8) & 0xF),
109 ((devc_desc->bcdUSB >> 4) & 0xF), devc_desc->bDeviceClass, devc_desc->bDeviceSubClass,
110 devc_desc->bDeviceProtocol, devc_desc->bMaxPacketSize0, devc_desc->idVendor, devc_desc->idProduct,
111 ((devc_desc->bcdDevice >> 8) & 0xF), ((devc_desc->bcdDevice >> 4) & 0xF), devc_desc->iManufacturer,
112 devc_desc->iProduct, devc_desc->iSerialNumber, devc_desc->bNumConfigurations);
115static void usb_client_print_config_descriptor(
const usb_config_desc_t *cfg_desc,
116 print_class_descriptor_cb class_specific_cb) {
117 if (cfg_desc ==
nullptr) {
122 uint16_t w_total_length = cfg_desc->wTotalLength;
123 const usb_standard_desc_t *next_desc = (
const usb_standard_desc_t *) cfg_desc;
126 switch (next_desc->bDescriptorType) {
127 case USB_W_VALUE_DT_CONFIG:
128 usbh_print_cfg_desc((
const usb_config_desc_t *) next_desc);
130 case USB_W_VALUE_DT_INTERFACE:
131 usbh_print_intf_desc((
const usb_intf_desc_t *) next_desc);
133 case USB_W_VALUE_DT_ENDPOINT:
134 print_ep_desc((
const usb_ep_desc_t *) next_desc);
137 if (class_specific_cb) {
138 class_specific_cb(next_desc);
143 next_desc = usb_parse_next_descriptor(next_desc, w_total_length, &offset);
145 }
while (next_desc != NULL);
152 if (desc ==
nullptr || desc->bLength < 2)
154 int char_count = (desc->bLength - 2) / 2;
157 for (
int i = 0; i != char_count && p <
end; i++) {
158 auto c = desc->wData[i];
164 *p++ =
static_cast<char>(c);
172static const char *utf16_to_latin1(
const T *data,
size_t count, std::span<char, DESC_STRING_BUF_SIZE>
buffer) {
175 for (
size_t i = 0; i !=
count && p <
end; i++) {
177 *p++ =
static_cast<char>(data[i]);
183static const char *get_descriptor_string(
const usb_str_desc_t *desc, std::span<char, DESC_STRING_BUF_SIZE>
buffer) {
184 if (desc ==
nullptr || desc->bLength < 2)
185 return "(unspecified)";
186 return utf16_to_latin1(desc->wData, (desc->bLength - 2) / 2,
buffer);
189static const char *filter_string(
const char16_t *filter, std::span<char, DESC_STRING_BUF_SIZE>
buffer) {
190 return utf16_to_latin1(filter, std::char_traits<char16_t>::length(filter),
buffer);
195static bool descriptor_string_equals(
const usb_str_desc_t *desc,
const char16_t *expected) {
196 const int char_count = (desc ==
nullptr || desc->bLength < 2) ? 0 : (desc->bLength - 2) / 2;
197 for (
int i = 0; i != char_count; i++) {
198 if (expected[i] == u
'\0' || desc->wData[i] != expected[i])
201 return expected[char_count] == u
'\0';
207 const usb_device_desc_t *desc;
208 esp_err_t err = usb_host_get_device_descriptor(this->
device_handle_, &desc);
210 ESP_LOGW(TAG,
"Device descriptor query failed: %s", esp_err_to_name(err));
216 usb_device_info_t dev_info;
219 ESP_LOGW(TAG,
"Device info query failed: %s", esp_err_to_name(err));
223 ESP_LOGW(TAG,
"Manufacturer string descriptor is not ASCII");
226 ESP_LOGW(TAG,
"Product string descriptor is not ASCII");
229 ESP_LOGW(TAG,
"Serial number string descriptor is not ASCII");
235static void client_event_cb(
const usb_host_client_event_msg_t *event_msg,
void *ptr) {
236 auto *client =
static_cast<USBClient *
>(ptr);
239 UsbEvent *
event = client->event_pool.allocate();
240 if (event ==
nullptr) {
242 client->event_queue.increment_dropped_count();
247 switch (event_msg->event) {
248 case USB_HOST_CLIENT_EVENT_NEW_DEV: {
249 ESP_LOGD(TAG,
"New device %d", event_msg->new_dev.address);
251 event->data.device_new.address = event_msg->new_dev.address;
254 case USB_HOST_CLIENT_EVENT_DEV_GONE: {
255 ESP_LOGD(TAG,
"Device gone");
257 event->data.device_gone.handle = event_msg->dev_gone.dev_hdl;
261 ESP_LOGD(TAG,
"Unknown event %d", event_msg->event);
262 client->event_pool.release(event);
268 client->event_queue.push(event);
271 client->enable_loop_soon_any_context();
277 usb_host_client_config_t config{.is_synchronous =
false,
278 .max_num_event_msg = 5,
279 .async = {.client_event_callback = client_event_cb, .callback_arg =
this}};
280 auto err = usb_host_client_register(&config, &this->
handle_);
282 ESP_LOGE(TAG,
"client register failed: %s", esp_err_to_name(err));
290 usb_host_transfer_alloc(USB_MAX_PACKET_SIZE, 0, &request.transfer);
291 request.client =
this;
302 ESP_LOGE(TAG,
"Failed to create USB task");
308 auto *client =
static_cast<USBClient *
>(arg);
313 usb_host_client_handle_events(this->
handle_, portMAX_DELAY);
318 bool had_work =
false;
322 while ((event = this->
event_queue.pop()) !=
nullptr) {
324 switch (event->
type) {
337 uint16_t dropped = this->
event_queue.get_and_reset_dropped_count();
339 ESP_LOGW(TAG,
"Dropped %u USB events due to queue overflow", dropped);
361 ESP_LOGW(TAG,
"Device open failed: %s", esp_err_to_name(err));
365 ESP_LOGD(TAG,
"Get descriptor device %d", this->
device_addr_);
366 const usb_device_desc_t *desc;
367 err = usb_host_get_device_descriptor(this->
device_handle_, &desc);
369 ESP_LOGW(TAG,
"Device get_desc failed: %s", esp_err_to_name(err));
373 ESP_LOGD(TAG,
"Device descriptor: vid %X pid %X", desc->idVendor, desc->idProduct);
374 if ((this->
vid_ != 0 && desc->idVendor != this->vid_) || (this->pid_ != 0 && desc->idProduct != this->pid_)) {
375 ESP_LOGD(TAG,
"Not our device, closing");
379 usb_device_info_t dev_info;
382 ESP_LOGW(TAG,
"Device info failed: %s", esp_err_to_name(err));
386 char buf_manuf[DESC_STRING_BUF_SIZE];
387 char buf_product[DESC_STRING_BUF_SIZE];
388 const bool manufacturer_matches =
390 descriptor_string_equals(dev_info.str_desc_manufacturer, this->manufacturer_filter_);
391 const bool product_matches =
392 this->
product_filter_ ==
nullptr || descriptor_string_equals(dev_info.str_desc_product, this->product_filter_);
393 if (!manufacturer_matches || !product_matches) {
394 ESP_LOGD(TAG,
"Device does not match filter, closing. Manuf: %s; Prod: %s",
395 get_descriptor_string(dev_info.str_desc_manufacturer, buf_manuf),
396 get_descriptor_string(dev_info.str_desc_product, buf_product));
401 char buf_serial[DESC_STRING_BUF_SIZE];
402 ESP_LOGD(TAG,
"Device connected: Manuf: %s; Prod: %s; Serial: %s",
403 get_descriptor_string(dev_info.str_desc_manufacturer, buf_manuf),
404 get_descriptor_string(dev_info.str_desc_product, buf_product),
405 get_descriptor_string(dev_info.str_desc_serial_num, buf_serial));
407#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
408 const usb_device_desc_t *device_desc;
409 err = usb_host_get_device_descriptor(this->
device_handle_, &device_desc);
411 usb_client_print_device_descriptor(device_desc);
412 const usb_config_desc_t *config_desc;
413 err = usb_host_get_active_config_descriptor(this->
device_handle_, &config_desc);
415 usb_client_print_config_descriptor(config_desc,
nullptr);
445static void control_callback(
const usb_transfer_t *xfer) {
448 trq->status.success = xfer->status == USB_TRANSFER_STATUS_COMPLETED;
449 trq->status.endpoint = xfer->bEndpointAddress;
450 trq->status.data = xfer->data_buffer;
451 trq->status.data_len = xfer->actual_num_bytes;
454 if (trq->callback !=
nullptr) {
455 trq->callback(trq->status);
460 trq->client->release_trq(trq);
476 if (mask == ALL_REQUESTS_IN_USE) {
477 ESP_LOGE(TAG,
"All %zu transfer slots in use", MAX_REQUESTS);
486 if (this->
trq_in_use_.compare_exchange_weak(mask, desired, std::memory_order::acquire)) {
487 auto i = __builtin_ctz(lsb);
507 ESP_LOGE(TAG,
"Device close failed: %s", esp_err_to_name(err));
520 const transfer_cb_t &callback,
const std::vector<uint8_t> &data) {
524 auto length = data.size();
525 if (
length > trq->transfer->data_buffer_size - SETUP_PACKET_SIZE) {
526 ESP_LOGE(TAG,
"Control transfer data size too large: %u > %u",
length,
527 trq->transfer->data_buffer_size - SETUP_PACKET_SIZE);
532 control_packet.put_uint8(
type);
533 control_packet.put_uint8(request);
534 control_packet.put_uint16(value);
535 control_packet.put_uint16(index);
536 control_packet.put_uint16(
length);
537 memcpy(trq->transfer->data_buffer, control_packet.get_data().data(), SETUP_PACKET_SIZE);
539 memcpy(trq->transfer->data_buffer + SETUP_PACKET_SIZE, data.data(),
length);
541 trq->callback = callback;
542 trq->transfer->bEndpointAddress =
type & USB_DIR_MASK;
543 trq->transfer->num_bytes =
static_cast<int>(
length + SETUP_PACKET_SIZE);
544 trq->transfer->callback =
reinterpret_cast<usb_transfer_cb_t
>(control_callback);
545 auto err = usb_host_transfer_submit_control(this->
handle_, trq->transfer);
547 ESP_LOGE(TAG,
"Failed to submit control transfer, err=%s", esp_err_to_name(err));
555static void transfer_callback(usb_transfer_t *xfer) {
558 trq->status.success = xfer->status == USB_TRANSFER_STATUS_COMPLETED;
559 trq->status.endpoint = xfer->bEndpointAddress;
560 trq->status.data = xfer->data_buffer;
561 trq->status.data_len = xfer->actual_num_bytes;
565 if (trq->callback !=
nullptr) {
566 trq->callback(trq->status);
573 trq->client->release_trq(trq);
589 if (trq ==
nullptr) {
590 ESP_LOGE(TAG,
"Too many requests queued");
593 if (
length > trq->transfer->data_buffer_size) {
594 ESP_LOGE(TAG,
"transfer_in: data length %u exceeds buffer size %u",
length, trq->transfer->data_buffer_size);
598 trq->callback = callback;
599 trq->transfer->callback = transfer_callback;
600 trq->transfer->bEndpointAddress = ep_address | USB_DIR_IN;
601 trq->transfer->num_bytes =
length;
602 auto err = usb_host_transfer_submit(trq->transfer);
604 ESP_LOGE(TAG,
"Failed to submit transfer, address=%x, length=%d, err=%x", ep_address,
length, err);
630 if (trq ==
nullptr) {
631 ESP_LOGE(TAG,
"Too many requests queued");
634 if (
length > trq->transfer->data_buffer_size) {
635 ESP_LOGE(TAG,
"transfer_out: data length %u exceeds buffer size %u",
length, trq->transfer->data_buffer_size);
639 trq->callback = callback;
640 trq->transfer->callback = transfer_callback;
641 trq->transfer->bEndpointAddress = ep_address | USB_DIR_OUT;
642 trq->transfer->num_bytes =
length;
643 memcpy(trq->transfer->data_buffer, data,
length);
644 auto err = usb_host_transfer_submit(trq->transfer);
646 ESP_LOGE(TAG,
"Failed to submit transfer, address=%x, length=%d, err=%x", ep_address,
length, err);
658 char buf[DESC_STRING_BUF_SIZE];
663 ESP_LOGCONFIG(TAG,
" Product %s", filter_string(this->
product_filter_, buf));
678 if (index >= MAX_REQUESTS) {
679 ESP_LOGE(TAG,
"Invalid TransferRequest pointer");
686 this->
trq_in_use_.fetch_and(mask, std::memory_order_release);
void wake_loop_threadsafe()
Wake the main event loop from another thread or callback.
void mark_failed()
Mark this component as failed.
void disable_loop()
Disable this component's loop.
A class modelled on the Java ByteBuffer class.
TransferRequest * get_trq_()
LazyCallbackManager< void(bool)> connection_callback_
virtual bool reports_connection_itself() const
Whether the subclass reports the device as connected itself, once its own setup of the device has fin...
usb_host_client_handle_t handle_
void on_opened(uint8_t addr)
TransferRequest requests_[MAX_REQUESTS]
void handle_open_state_()
bool transfer_out(uint8_t ep_address, const transfer_cb_t &callback, const uint8_t *data, uint16_t length)
Performs an output transfer operation.
void dump_config() override
void release_trq(TransferRequest *trq)
const char16_t * product_filter_
void usb_task_loop_() const
static void usb_task_fn(void *arg)
virtual void on_connected()
virtual void disconnect()
EventPool< UsbEvent, USB_EVENT_QUEUE_SIZE - 1 > event_pool
bool is_connected() const
Whether a device has been opened and its setup by the subclass has finished.
TaskHandle_t usb_task_handle_
const char16_t * manufacturer_filter_
bool process_usb_events_()
bool get_device_info(UsbDeviceInfo &info) const
Copy the connected device's identity out of the cached USB descriptors.
virtual void on_disconnected()
bool control_transfer(uint8_t type, uint8_t request, uint16_t value, uint16_t index, const transfer_cb_t &callback, const std::vector< uint8_t > &data={})
bool transfer_in(uint8_t ep_address, const transfer_cb_t &callback, uint16_t length)
Performs a transfer input operation.
bool connection_reported_
void on_removed(usb_device_handle_t handle)
usb_device_handle_t device_handle_
std::atomic< trq_bitmask_t > trq_in_use_
void report_connected_()
Report the claimed device to the connection callbacks.
LockFreeQueue< UsbEvent, USB_EVENT_QUEUE_SIZE > event_queue
std::conditional<(MAX_REQUESTS<=16), uint16_t, uint32_t >::type trq_bitmask_t
bool copy_descriptor_string(const usb_str_desc_t *desc, std::span< char, DESC_STRING_BUF_SIZE > buffer)
Copy a USB string descriptor into a NUL-terminated buffer.
std::function< void(const TransferStatus &)> transfer_cb_t
Application App
Global storage of Application pointer - only one Application can exist.
usb_transfer_t * transfer
Identity of a connected USB device, copied out of the descriptors the USB host stack caches for the l...
char serial_number[DESC_STRING_BUF_SIZE]
char product[DESC_STRING_BUF_SIZE]
char manufacturer[DESC_STRING_BUF_SIZE]
union esphome::usb_host::UsbEvent::@181 data
struct esphome::usb_host::UsbEvent::@181::@182 device_new
usb_device_handle_t handle
struct esphome::usb_host::UsbEvent::@181::@183 device_gone
spi_device_handle_t handle