2#if defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
15#pragma GCC diagnostic ignored "-Wparentheses"
17using namespace bytebuffer;
19#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
20static void print_ep_desc(
const usb_ep_desc_t *ep_desc) {
21 const char *ep_type_str;
22 int type = ep_desc->bmAttributes & USB_BM_ATTRIBUTES_XFERTYPE_MASK;
25 case USB_BM_ATTRIBUTES_XFER_CONTROL:
28 case USB_BM_ATTRIBUTES_XFER_ISOC:
31 case USB_BM_ATTRIBUTES_XFER_BULK:
34 case USB_BM_ATTRIBUTES_XFER_INT:
43 "\t\t*** Endpoint descriptor ***\n"
45 "\t\tbDescriptorType %d\n"
46 "\t\tbEndpointAddress 0x%x\tEP %d %s\n"
47 "\t\tbmAttributes 0x%x\t%s\n"
48 "\t\twMaxPacketSize %d\n"
50 ep_desc->bLength, ep_desc->bDescriptorType, ep_desc->bEndpointAddress, USB_EP_DESC_GET_EP_NUM(ep_desc),
51 USB_EP_DESC_GET_EP_DIR(ep_desc) ?
"IN" :
"OUT", ep_desc->bmAttributes, ep_type_str, ep_desc->wMaxPacketSize,
55static void usbh_print_intf_desc(
const usb_intf_desc_t *intf_desc) {
57 "\t*** Interface descriptor ***\n"
59 "\tbDescriptorType %d\n"
60 "\tbInterfaceNumber %d\n"
61 "\tbAlternateSetting %d\n"
62 "\tbNumEndpoints %d\n"
63 "\tbInterfaceClass 0x%x\n"
65 intf_desc->bLength, intf_desc->bDescriptorType, intf_desc->bInterfaceNumber, intf_desc->bAlternateSetting,
66 intf_desc->bNumEndpoints, intf_desc->bInterfaceProtocol, intf_desc->iInterface);
69static void usbh_print_cfg_desc(
const usb_config_desc_t *cfg_desc) {
71 "*** Configuration descriptor ***\n"
73 "bDescriptorType %d\n"
76 "bConfigurationValue %d\n"
80 cfg_desc->bLength, cfg_desc->bDescriptorType, cfg_desc->wTotalLength, cfg_desc->bNumInterfaces,
81 cfg_desc->bConfigurationValue, cfg_desc->iConfiguration, cfg_desc->bmAttributes, cfg_desc->bMaxPower * 2);
84static void usb_client_print_device_descriptor(
const usb_device_desc_t *devc_desc) {
85 if (devc_desc == NULL) {
90 "*** Device descriptor ***\n"
92 "bDescriptorType %d\n"
95 "bDeviceSubClass 0x%x\n"
96 "bDeviceProtocol 0x%x\n"
97 "bMaxPacketSize0 %d\n"
104 "bNumConfigurations %d",
105 devc_desc->bLength, devc_desc->bDescriptorType, ((devc_desc->bcdUSB >> 8) & 0xF),
106 ((devc_desc->bcdUSB >> 4) & 0xF), devc_desc->bDeviceClass, devc_desc->bDeviceSubClass,
107 devc_desc->bDeviceProtocol, devc_desc->bMaxPacketSize0, devc_desc->idVendor, devc_desc->idProduct,
108 ((devc_desc->bcdDevice >> 8) & 0xF), ((devc_desc->bcdDevice >> 4) & 0xF), devc_desc->iManufacturer,
109 devc_desc->iProduct, devc_desc->iSerialNumber, devc_desc->bNumConfigurations);
112static void usb_client_print_config_descriptor(
const usb_config_desc_t *cfg_desc,
113 print_class_descriptor_cb class_specific_cb) {
114 if (cfg_desc ==
nullptr) {
119 uint16_t w_total_length = cfg_desc->wTotalLength;
120 const usb_standard_desc_t *next_desc = (
const usb_standard_desc_t *) cfg_desc;
123 switch (next_desc->bDescriptorType) {
124 case USB_W_VALUE_DT_CONFIG:
125 usbh_print_cfg_desc((
const usb_config_desc_t *) next_desc);
127 case USB_W_VALUE_DT_INTERFACE:
128 usbh_print_intf_desc((
const usb_intf_desc_t *) next_desc);
130 case USB_W_VALUE_DT_ENDPOINT:
131 print_ep_desc((
const usb_ep_desc_t *) next_desc);
134 if (class_specific_cb) {
135 class_specific_cb(next_desc);
140 next_desc = usb_parse_next_descriptor(next_desc, w_total_length, &offset);
142 }
while (next_desc != NULL);
145static std::string get_descriptor_string(
const usb_str_desc_t *desc) {
148 return "(unspecified)";
150 for (
int i = 0; i != desc->bLength / 2; i++) {
151 auto c = desc->wData[i];
153 *p++ =
static_cast<char>(c);
160static void client_event_cb(
const usb_host_client_event_msg_t *event_msg,
void *ptr) {
161 auto *client =
static_cast<USBClient *
>(ptr);
164 UsbEvent *
event = client->event_pool.allocate();
165 if (event ==
nullptr) {
167 client->event_queue.increment_dropped_count();
172 switch (event_msg->event) {
173 case USB_HOST_CLIENT_EVENT_NEW_DEV: {
174 ESP_LOGD(TAG,
"New device %d", event_msg->new_dev.address);
176 event->data.device_new.address = event_msg->new_dev.address;
179 case USB_HOST_CLIENT_EVENT_DEV_GONE: {
180 ESP_LOGD(TAG,
"Device gone");
182 event->data.device_gone.handle = event_msg->dev_gone.dev_hdl;
186 ESP_LOGD(TAG,
"Unknown event %d", event_msg->event);
187 client->event_pool.release(event);
192 client->event_queue.push(event);
195#if defined(USE_SOCKET_SELECT_SUPPORT) && defined(USE_WAKE_LOOP_THREADSAFE)
200 usb_host_client_config_t config{.is_synchronous =
false,
201 .max_num_event_msg = 5,
202 .async = {.client_event_callback = client_event_cb, .callback_arg =
this}};
203 auto err = usb_host_client_register(&config, &this->
handle_);
205 ESP_LOGE(TAG,
"client register failed: %s", esp_err_to_name(err));
213 usb_host_transfer_alloc(64, 0, &request.transfer);
214 request.client =
this;
225 ESP_LOGE(TAG,
"Failed to create USB task");
231 auto *client =
static_cast<USBClient *
>(arg);
236 usb_host_client_handle_events(this->
handle_, portMAX_DELAY);
243 while ((event = this->
event_queue.pop()) !=
nullptr) {
244 switch (event->
type) {
257 uint16_t dropped = this->
event_queue.get_and_reset_dropped_count();
259 ESP_LOGW(TAG,
"Dropped %u USB events due to queue overflow", dropped);
268 ESP_LOGW(TAG,
"Device open failed: %s", esp_err_to_name(err));
272 ESP_LOGD(TAG,
"Get descriptor device %d", this->
device_addr_);
273 const usb_device_desc_t *desc;
274 err = usb_host_get_device_descriptor(this->
device_handle_, &desc);
276 ESP_LOGW(TAG,
"Device get_desc failed: %s", esp_err_to_name(err));
279 ESP_LOGD(TAG,
"Device descriptor: vid %X pid %X", desc->idVendor, desc->idProduct);
280 if (desc->idVendor == this->vid_ && desc->idProduct == this->pid_ || this->vid_ == 0 && this->pid_ == 0) {
281 usb_device_info_t dev_info;
284 ESP_LOGW(TAG,
"Device info failed: %s", esp_err_to_name(err));
289 ESP_LOGD(TAG,
"Device connected: Manuf: %s; Prod: %s; Serial: %s",
290 get_descriptor_string(dev_info.str_desc_manufacturer).c_str(),
291 get_descriptor_string(dev_info.str_desc_product).c_str(),
292 get_descriptor_string(dev_info.str_desc_serial_num).c_str());
294#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
295 const usb_device_desc_t *device_desc;
296 err = usb_host_get_device_descriptor(this->
device_handle_, &device_desc);
298 usb_client_print_device_descriptor(device_desc);
299 const usb_config_desc_t *config_desc;
300 err = usb_host_get_active_config_descriptor(this->
device_handle_, &config_desc);
302 usb_client_print_config_descriptor(config_desc,
nullptr);
306 ESP_LOGD(TAG,
"Not our device, closing");
331static void control_callback(
const usb_transfer_t *xfer) {
334 trq->status.success = xfer->status == USB_TRANSFER_STATUS_COMPLETED;
335 trq->status.endpoint = xfer->bEndpointAddress;
336 trq->status.data = xfer->data_buffer;
337 trq->status.data_len = xfer->actual_num_bytes;
340 if (trq->callback !=
nullptr) {
341 trq->callback(trq->status);
346 trq->client->release_trq(trq);
362 if (mask == ALL_REQUESTS_IN_USE) {
363 ESP_LOGE(TAG,
"All %zu transfer slots in use", MAX_REQUESTS);
372 if (this->
trq_in_use_.compare_exchange_weak(mask, desired, std::memory_order::acquire)) {
373 auto i = __builtin_ctz(lsb);
389 ESP_LOGE(TAG,
"Device close failed: %s", esp_err_to_name(err));
399 const transfer_cb_t &callback,
const std::vector<uint8_t> &data) {
403 auto length = data.size();
404 if (
length >
sizeof(trq->transfer->data_buffer_size) - SETUP_PACKET_SIZE) {
405 ESP_LOGE(TAG,
"Control transfer data size too large: %u > %u",
length,
406 sizeof(trq->transfer->data_buffer_size) -
sizeof(usb_setup_packet_t));
411 control_packet.put_uint8(
type);
412 control_packet.put_uint8(request);
413 control_packet.put_uint16(value);
414 control_packet.put_uint16(index);
415 control_packet.put_uint16(
length);
416 memcpy(trq->transfer->data_buffer, control_packet.get_data().data(), SETUP_PACKET_SIZE);
418 memcpy(trq->transfer->data_buffer + SETUP_PACKET_SIZE, data.data(),
length);
420 trq->callback = callback;
421 trq->transfer->bEndpointAddress =
type & USB_DIR_MASK;
422 trq->transfer->num_bytes =
static_cast<int>(
length + SETUP_PACKET_SIZE);
423 trq->transfer->callback =
reinterpret_cast<usb_transfer_cb_t
>(control_callback);
424 auto err = usb_host_transfer_submit_control(this->
handle_, trq->transfer);
426 ESP_LOGE(TAG,
"Failed to submit control transfer, err=%s", esp_err_to_name(err));
434static void transfer_callback(usb_transfer_t *xfer) {
437 trq->status.success = xfer->status == USB_TRANSFER_STATUS_COMPLETED;
438 trq->status.endpoint = xfer->bEndpointAddress;
439 trq->status.data = xfer->data_buffer;
440 trq->status.data_len = xfer->actual_num_bytes;
444 if (trq->callback !=
nullptr) {
445 trq->callback(trq->status);
452 trq->client->release_trq(trq);
468 if (trq ==
nullptr) {
469 ESP_LOGE(TAG,
"Too many requests queued");
472 trq->callback = callback;
473 trq->transfer->callback = transfer_callback;
474 trq->transfer->bEndpointAddress = ep_address | USB_DIR_IN;
475 trq->transfer->num_bytes =
length;
476 auto err = usb_host_transfer_submit(trq->transfer);
478 ESP_LOGE(TAG,
"Failed to submit transfer, address=%x, length=%d, err=%x", ep_address,
length, err);
500 if (trq ==
nullptr) {
501 ESP_LOGE(TAG,
"Too many requests queued");
504 trq->callback = callback;
505 trq->transfer->callback = transfer_callback;
506 trq->transfer->bEndpointAddress = ep_address | USB_DIR_OUT;
507 trq->transfer->num_bytes =
length;
508 memcpy(trq->transfer->data_buffer, data,
length);
509 auto err = usb_host_transfer_submit(trq->transfer);
511 ESP_LOGE(TAG,
"Failed to submit transfer, address=%x, length=%d, err=%x", ep_address,
length, err);
536 if (index >= MAX_REQUESTS) {
537 ESP_LOGE(TAG,
"Invalid TransferRequest pointer");
544 this->
trq_in_use_.fetch_and(mask, std::memory_order_release);
void wake_loop_threadsafe()
Wake the main event loop from a FreeRTOS task Thread-safe, can be called from task context to immedia...
virtual void mark_failed()
Mark this component as failed.
A class modelled on the Java ByteBuffer class.
TransferRequest * get_trq_()
usb_host_client_handle_t handle_
void on_opened(uint8_t addr)
TransferRequest requests_[MAX_REQUESTS]
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)
static void usb_task_fn(void *arg)
virtual void on_connected()
virtual void disconnect()
TaskHandle_t usb_task_handle_
EventPool< UsbEvent, USB_EVENT_QUEUE_SIZE > event_pool
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.
void on_removed(usb_device_handle_t handle)
usb_device_handle_t device_handle_
void usb_task_loop() const
std::atomic< trq_bitmask_t > trq_in_use_
LockFreeQueue< UsbEvent, USB_EVENT_QUEUE_SIZE > event_queue
std::conditional<(MAX_REQUESTS<=16), uint16_t, uint32_t >::type trq_bitmask_t
std::function< void(const TransferStatus &)> transfer_cb_t
Providing packet encoding functions for exchanging data with a remote host.
Application App
Global storage of Application pointer - only one Application can exist.
usb_transfer_t * transfer
struct esphome::usb_host::UsbEvent::@162::@163 device_new
usb_device_handle_t handle
struct esphome::usb_host::UsbEvent::@162::@164 device_gone
union esphome::usb_host::UsbEvent::@162 data