ESPHome 2025.6.0
Loading...
Searching...
No Matches
usb_host_client.cpp
Go to the documentation of this file.
1// Should not be needed, but it's required to pass CI clang-tidy checks
2#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
3#include "usb_host.h"
4#include "esphome/core/log.h"
5#include "esphome/core/hal.h"
7
8#include <cinttypes>
9#include <cstring>
10namespace esphome {
11namespace usb_host {
12
13#pragma GCC diagnostic ignored "-Wparentheses"
14
15using namespace bytebuffer;
16
17#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
18static void print_ep_desc(const usb_ep_desc_t *ep_desc) {
19 const char *ep_type_str;
20 int type = ep_desc->bmAttributes & USB_BM_ATTRIBUTES_XFERTYPE_MASK;
21
22 switch (type) {
23 case USB_BM_ATTRIBUTES_XFER_CONTROL:
24 ep_type_str = "CTRL";
25 break;
26 case USB_BM_ATTRIBUTES_XFER_ISOC:
27 ep_type_str = "ISOC";
28 break;
29 case USB_BM_ATTRIBUTES_XFER_BULK:
30 ep_type_str = "BULK";
31 break;
32 case USB_BM_ATTRIBUTES_XFER_INT:
33 ep_type_str = "INT";
34 break;
35 default:
36 ep_type_str = NULL;
37 break;
38 }
39
40 ESP_LOGV(TAG, "\t\t*** Endpoint descriptor ***");
41 ESP_LOGV(TAG, "\t\tbLength %d", ep_desc->bLength);
42 ESP_LOGV(TAG, "\t\tbDescriptorType %d", ep_desc->bDescriptorType);
43 ESP_LOGV(TAG, "\t\tbEndpointAddress 0x%x\tEP %d %s", ep_desc->bEndpointAddress, USB_EP_DESC_GET_EP_NUM(ep_desc),
44 USB_EP_DESC_GET_EP_DIR(ep_desc) ? "IN" : "OUT");
45 ESP_LOGV(TAG, "\t\tbmAttributes 0x%x\t%s", ep_desc->bmAttributes, ep_type_str);
46 ESP_LOGV(TAG, "\t\twMaxPacketSize %d", ep_desc->wMaxPacketSize);
47 ESP_LOGV(TAG, "\t\tbInterval %d", ep_desc->bInterval);
48}
49
50static void usbh_print_intf_desc(const usb_intf_desc_t *intf_desc) {
51 ESP_LOGV(TAG, "\t*** Interface descriptor ***");
52 ESP_LOGV(TAG, "\tbLength %d", intf_desc->bLength);
53 ESP_LOGV(TAG, "\tbDescriptorType %d", intf_desc->bDescriptorType);
54 ESP_LOGV(TAG, "\tbInterfaceNumber %d", intf_desc->bInterfaceNumber);
55 ESP_LOGV(TAG, "\tbAlternateSetting %d", intf_desc->bAlternateSetting);
56 ESP_LOGV(TAG, "\tbNumEndpoints %d", intf_desc->bNumEndpoints);
57 ESP_LOGV(TAG, "\tbInterfaceClass 0x%x", intf_desc->bInterfaceProtocol);
58 ESP_LOGV(TAG, "\tiInterface %d", intf_desc->iInterface);
59}
60
61static void usbh_print_cfg_desc(const usb_config_desc_t *cfg_desc) {
62 ESP_LOGV(TAG, "*** Configuration descriptor ***");
63 ESP_LOGV(TAG, "bLength %d", cfg_desc->bLength);
64 ESP_LOGV(TAG, "bDescriptorType %d", cfg_desc->bDescriptorType);
65 ESP_LOGV(TAG, "wTotalLength %d", cfg_desc->wTotalLength);
66 ESP_LOGV(TAG, "bNumInterfaces %d", cfg_desc->bNumInterfaces);
67 ESP_LOGV(TAG, "bConfigurationValue %d", cfg_desc->bConfigurationValue);
68 ESP_LOGV(TAG, "iConfiguration %d", cfg_desc->iConfiguration);
69 ESP_LOGV(TAG, "bmAttributes 0x%x", cfg_desc->bmAttributes);
70 ESP_LOGV(TAG, "bMaxPower %dmA", cfg_desc->bMaxPower * 2);
71}
72
73void usb_client_print_device_descriptor(const usb_device_desc_t *devc_desc) {
74 if (devc_desc == NULL) {
75 return;
76 }
77
78 ESP_LOGV(TAG, "*** Device descriptor ***");
79 ESP_LOGV(TAG, "bLength %d", devc_desc->bLength);
80 ESP_LOGV(TAG, "bDescriptorType %d", devc_desc->bDescriptorType);
81 ESP_LOGV(TAG, "bcdUSB %d.%d0", ((devc_desc->bcdUSB >> 8) & 0xF), ((devc_desc->bcdUSB >> 4) & 0xF));
82 ESP_LOGV(TAG, "bDeviceClass 0x%x", devc_desc->bDeviceClass);
83 ESP_LOGV(TAG, "bDeviceSubClass 0x%x", devc_desc->bDeviceSubClass);
84 ESP_LOGV(TAG, "bDeviceProtocol 0x%x", devc_desc->bDeviceProtocol);
85 ESP_LOGV(TAG, "bMaxPacketSize0 %d", devc_desc->bMaxPacketSize0);
86 ESP_LOGV(TAG, "idVendor 0x%x", devc_desc->idVendor);
87 ESP_LOGV(TAG, "idProduct 0x%x", devc_desc->idProduct);
88 ESP_LOGV(TAG, "bcdDevice %d.%d0", ((devc_desc->bcdDevice >> 8) & 0xF), ((devc_desc->bcdDevice >> 4) & 0xF));
89 ESP_LOGV(TAG, "iManufacturer %d", devc_desc->iManufacturer);
90 ESP_LOGV(TAG, "iProduct %d", devc_desc->iProduct);
91 ESP_LOGV(TAG, "iSerialNumber %d", devc_desc->iSerialNumber);
92 ESP_LOGV(TAG, "bNumConfigurations %d", devc_desc->bNumConfigurations);
93}
94
95void usb_client_print_config_descriptor(const usb_config_desc_t *cfg_desc,
96 print_class_descriptor_cb class_specific_cb) {
97 if (cfg_desc == nullptr) {
98 return;
99 }
100
101 int offset = 0;
102 uint16_t w_total_length = cfg_desc->wTotalLength;
103 const usb_standard_desc_t *next_desc = (const usb_standard_desc_t *) cfg_desc;
104
105 do {
106 switch (next_desc->bDescriptorType) {
107 case USB_W_VALUE_DT_CONFIG:
108 usbh_print_cfg_desc((const usb_config_desc_t *) next_desc);
109 break;
110 case USB_W_VALUE_DT_INTERFACE:
111 usbh_print_intf_desc((const usb_intf_desc_t *) next_desc);
112 break;
113 case USB_W_VALUE_DT_ENDPOINT:
114 print_ep_desc((const usb_ep_desc_t *) next_desc);
115 break;
116 default:
117 if (class_specific_cb) {
118 class_specific_cb(next_desc);
119 }
120 break;
121 }
122
123 next_desc = usb_parse_next_descriptor(next_desc, w_total_length, &offset);
124
125 } while (next_desc != NULL);
126}
127#endif
128static std::string get_descriptor_string(const usb_str_desc_t *desc) {
129 char buffer[256];
130 if (desc == nullptr)
131 return "(unknown)";
132 char *p = buffer;
133 for (size_t i = 0; i != desc->bLength / 2; i++) {
134 auto c = desc->wData[i];
135 if (c < 0x100)
136 *p++ = static_cast<char>(c);
137 }
138 *p = '\0';
139 return {buffer};
140}
141
142static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *ptr) {
143 auto *client = static_cast<USBClient *>(ptr);
144 switch (event_msg->event) {
145 case USB_HOST_CLIENT_EVENT_NEW_DEV: {
146 auto addr = event_msg->new_dev.address;
147 ESP_LOGD(TAG, "New device %d", event_msg->new_dev.address);
148 client->on_opened(addr);
149 break;
150 }
151 case USB_HOST_CLIENT_EVENT_DEV_GONE: {
152 client->on_removed(event_msg->dev_gone.dev_hdl);
153 ESP_LOGD(TAG, "Device gone %d", event_msg->new_dev.address);
154 break;
155 }
156 default:
157 ESP_LOGD(TAG, "Unknown event %d", event_msg->event);
158 break;
159 }
160}
162 usb_host_client_config_t config{.is_synchronous = false,
163 .max_num_event_msg = 5,
164 .async = {.client_event_callback = client_event_cb, .callback_arg = this}};
165 auto err = usb_host_client_register(&config, &this->handle_);
166 if (err != ESP_OK) {
167 ESP_LOGE(TAG, "client register failed: %s", esp_err_to_name(err));
168 this->status_set_error("Client register failed");
169 this->mark_failed();
170 return;
171 }
172 for (auto trq : this->trq_pool_) {
173 usb_host_transfer_alloc(64, 0, &trq->transfer);
174 trq->client = this;
175 }
176 ESP_LOGCONFIG(TAG, "client setup complete");
177}
178
180 switch (this->state_) {
181 case USB_CLIENT_OPEN: {
182 int err;
183 ESP_LOGD(TAG, "Open device %d", this->device_addr_);
184 err = usb_host_device_open(this->handle_, this->device_addr_, &this->device_handle_);
185 if (err != ESP_OK) {
186 ESP_LOGW(TAG, "Device open failed: %s", esp_err_to_name(err));
187 this->state_ = USB_CLIENT_INIT;
188 break;
189 }
190 ESP_LOGD(TAG, "Get descriptor device %d", this->device_addr_);
191 const usb_device_desc_t *desc;
192 err = usb_host_get_device_descriptor(this->device_handle_, &desc);
193 if (err != ESP_OK) {
194 ESP_LOGW(TAG, "Device get_desc failed: %s", esp_err_to_name(err));
195 this->disconnect();
196 } else {
197 ESP_LOGD(TAG, "Device descriptor: vid %X pid %X", desc->idVendor, desc->idProduct);
198 if (desc->idVendor == this->vid_ && desc->idProduct == this->pid_ || this->vid_ == 0 && this->pid_ == 0) {
199 usb_device_info_t dev_info;
200 if ((err = usb_host_device_info(this->device_handle_, &dev_info)) != ESP_OK) {
201 ESP_LOGW(TAG, "Device info failed: %s", esp_err_to_name(err));
202 this->disconnect();
203 break;
204 }
206 ESP_LOGD(TAG, "Device connected: Manuf: %s; Prod: %s; Serial: %s",
207 get_descriptor_string(dev_info.str_desc_manufacturer).c_str(),
208 get_descriptor_string(dev_info.str_desc_product).c_str(),
209 get_descriptor_string(dev_info.str_desc_serial_num).c_str());
210
211#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
212 const usb_device_desc_t *device_desc;
213 err = usb_host_get_device_descriptor(this->device_handle_, &device_desc);
214 if (err == ESP_OK)
216 const usb_config_desc_t *config_desc;
217 err = usb_host_get_active_config_descriptor(this->device_handle_, &config_desc);
218 if (err == ESP_OK)
219 usb_client_print_config_descriptor(config_desc, nullptr);
220#endif
221 this->on_connected();
222 } else {
223 ESP_LOGD(TAG, "Not our device, closing");
224 this->disconnect();
225 }
226 }
227 break;
228 }
229
230 default:
231 usb_host_client_handle_events(this->handle_, 0);
232 break;
233 }
234}
235
236void USBClient::on_opened(uint8_t addr) {
237 if (this->state_ == USB_CLIENT_INIT) {
238 this->device_addr_ = addr;
239 this->state_ = USB_CLIENT_OPEN;
240 }
241}
242void USBClient::on_removed(usb_device_handle_t handle) {
243 if (this->device_handle_ == handle) {
244 this->disconnect();
245 }
246}
247
248static void control_callback(const usb_transfer_t *xfer) {
249 auto *trq = static_cast<TransferRequest *>(xfer->context);
250 trq->status.error_code = xfer->status;
251 trq->status.success = xfer->status == USB_TRANSFER_STATUS_COMPLETED;
252 trq->status.endpoint = xfer->bEndpointAddress;
253 trq->status.data = xfer->data_buffer;
254 trq->status.data_len = xfer->actual_num_bytes;
255 if (trq->callback != nullptr)
256 trq->callback(trq->status);
257 trq->client->release_trq(trq);
258}
259
261 if (this->trq_pool_.empty()) {
262 ESP_LOGE(TAG, "Too many requests queued");
263 return nullptr;
264 }
265 auto *trq = this->trq_pool_.front();
266 this->trq_pool_.pop_front();
267 trq->client = this;
268 trq->transfer->context = trq;
269 trq->transfer->device_handle = this->device_handle_;
270 return trq;
271}
273 this->on_disconnected();
274 auto err = usb_host_device_close(this->handle_, this->device_handle_);
275 if (err != ESP_OK) {
276 ESP_LOGE(TAG, "Device close failed: %s", esp_err_to_name(err));
277 }
278 this->state_ = USB_CLIENT_INIT;
279 this->device_handle_ = nullptr;
280 this->device_addr_ = -1;
281}
282
283bool USBClient::control_transfer(uint8_t type, uint8_t request, uint16_t value, uint16_t index,
284 const transfer_cb_t &callback, const std::vector<uint8_t> &data) {
285 auto *trq = this->get_trq_();
286 if (trq == nullptr)
287 return false;
288 auto length = data.size();
289 if (length > sizeof(trq->transfer->data_buffer_size) - SETUP_PACKET_SIZE) {
290 ESP_LOGE(TAG, "Control transfer data size too large: %u > %u", length,
291 sizeof(trq->transfer->data_buffer_size) - sizeof(usb_setup_packet_t));
292 this->release_trq(trq);
293 return false;
294 }
295 auto control_packet = ByteBuffer(SETUP_PACKET_SIZE, LITTLE);
296 control_packet.put_uint8(type);
297 control_packet.put_uint8(request);
298 control_packet.put_uint16(value);
299 control_packet.put_uint16(index);
300 control_packet.put_uint16(length);
301 memcpy(trq->transfer->data_buffer, control_packet.get_data().data(), SETUP_PACKET_SIZE);
302 if (length != 0 && !(type & USB_DIR_IN)) {
303 memcpy(trq->transfer->data_buffer + SETUP_PACKET_SIZE, data.data(), length);
304 }
305 trq->callback = callback;
306 trq->transfer->bEndpointAddress = type & USB_DIR_MASK;
307 trq->transfer->num_bytes = static_cast<int>(length + SETUP_PACKET_SIZE);
308 trq->transfer->callback = reinterpret_cast<usb_transfer_cb_t>(control_callback);
309 auto err = usb_host_transfer_submit_control(this->handle_, trq->transfer);
310 if (err != ESP_OK) {
311 ESP_LOGE(TAG, "Failed to submit control transfer, err=%s", esp_err_to_name(err));
312 this->release_trq(trq);
313 return false;
314 }
315 return true;
316}
317
318static void transfer_callback(usb_transfer_t *xfer) {
319 auto *trq = static_cast<TransferRequest *>(xfer->context);
320 trq->status.error_code = xfer->status;
321 trq->status.success = xfer->status == USB_TRANSFER_STATUS_COMPLETED;
322 trq->status.endpoint = xfer->bEndpointAddress;
323 trq->status.data = xfer->data_buffer;
324 trq->status.data_len = xfer->actual_num_bytes;
325 if (trq->callback != nullptr)
326 trq->callback(trq->status);
327 trq->client->release_trq(trq);
328}
338void USBClient::transfer_in(uint8_t ep_address, const transfer_cb_t &callback, uint16_t length) {
339 auto trq = this->get_trq_();
340 if (trq == nullptr) {
341 ESP_LOGE(TAG, "Too many requests queued");
342 return;
343 }
344 trq->callback = callback;
345 trq->transfer->callback = transfer_callback;
346 trq->transfer->bEndpointAddress = ep_address | USB_DIR_IN;
347 trq->transfer->num_bytes = length;
348 auto err = usb_host_transfer_submit(trq->transfer);
349 if (err != ESP_OK) {
350 ESP_LOGE(TAG, "Failed to submit transfer, address=%x, length=%d, err=%x", ep_address, length, err);
351 this->release_trq(trq);
352 this->disconnect();
353 }
354}
355
366void USBClient::transfer_out(uint8_t ep_address, const transfer_cb_t &callback, const uint8_t *data, uint16_t length) {
367 auto trq = this->get_trq_();
368 if (trq == nullptr) {
369 ESP_LOGE(TAG, "Too many requests queued");
370 return;
371 }
372 trq->callback = callback;
373 trq->transfer->callback = transfer_callback;
374 trq->transfer->bEndpointAddress = ep_address | USB_DIR_OUT;
375 trq->transfer->num_bytes = length;
376 memcpy(trq->transfer->data_buffer, data, length);
377 auto err = usb_host_transfer_submit(trq->transfer);
378 if (err != ESP_OK) {
379 ESP_LOGE(TAG, "Failed to submit transfer, address=%x, length=%d, err=%x", ep_address, length, err);
380 this->release_trq(trq);
381 }
382}
384 ESP_LOGCONFIG(TAG,
385 "USBClient\n"
386 " Vendor id %04X\n"
387 " Product id %04X",
388 this->vid_, this->pid_);
389}
390void USBClient::release_trq(TransferRequest *trq) { this->trq_pool_.push_back(trq); }
391
392} // namespace usb_host
393} // namespace esphome
394#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
virtual void mark_failed()
Mark this component as failed.
void status_set_error(const char *message="unspecified")
A class modelled on the Java ByteBuffer class.
Definition bytebuffer.h:38
usb_host_client_handle_t handle_
Definition usb_host.h:94
void transfer_in(uint8_t ep_address, const transfer_cb_t &callback, uint16_t length)
Performs a transfer input operation.
void transfer_out(uint8_t ep_address, const transfer_cb_t &callback, const uint8_t *data, uint16_t length)
Performs an output transfer operation.
std::list< TransferRequest * > trq_pool_
Definition usb_host.h:100
void release_trq(TransferRequest *trq)
virtual void on_connected()
Definition usb_host.h:91
virtual void on_disconnected()
Definition usb_host.h:92
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={})
void on_removed(usb_device_handle_t handle)
usb_device_handle_t device_handle_
Definition usb_host.h:95
uint8_t type
void usb_client_print_device_descriptor(const usb_device_desc_t *devc_desc)
void usb_client_print_config_descriptor(const usb_config_desc_t *cfg_desc, print_class_descriptor_cb class_specific_cb)
std::function< void(const TransferStatus &)> transfer_cb_t
Definition usb_host.h:40
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
uint16_t length
Definition tt21100.cpp:0