2#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
19static optional<CdcEps> get_cdc(
const usb_config_desc_t *config_desc, uint8_t intf_idx) {
20 int conf_offset, ep_offset;
21 const usb_ep_desc_t *notify_ep{}, *in_ep{}, *out_ep{};
22 uint8_t interface_number = 0;
25 auto intf_desc = usb_parse_interface_descriptor(config_desc, intf_idx++, 0, &conf_offset);
27 ESP_LOGE(TAG,
"usb_parse_interface_descriptor failed");
30 if (intf_desc->bNumEndpoints == 1) {
31 ep_offset = conf_offset;
32 notify_ep = usb_parse_endpoint_descriptor_by_index(intf_desc, 0, config_desc->wTotalLength, &ep_offset);
34 ESP_LOGE(TAG,
"notify_ep: usb_parse_endpoint_descriptor_by_index failed");
37 if (notify_ep->bmAttributes != USB_BM_ATTRIBUTES_XFER_INT)
39 }
else if (USB_CLASS_CDC_DATA && intf_desc->bNumEndpoints == 2) {
40 interface_number = intf_desc->bInterfaceNumber;
41 ep_offset = conf_offset;
42 out_ep = usb_parse_endpoint_descriptor_by_index(intf_desc, 0, config_desc->wTotalLength, &ep_offset);
44 ESP_LOGE(TAG,
"out_ep: usb_parse_endpoint_descriptor_by_index failed");
47 if (out_ep->bmAttributes != USB_BM_ATTRIBUTES_XFER_BULK)
49 ep_offset = conf_offset;
50 in_ep = usb_parse_endpoint_descriptor_by_index(intf_desc, 1, config_desc->wTotalLength, &ep_offset);
52 ESP_LOGE(TAG,
"in_ep: usb_parse_endpoint_descriptor_by_index failed");
55 if (in_ep->bmAttributes != USB_BM_ATTRIBUTES_XFER_BULK)
58 if (in_ep !=
nullptr && out_ep !=
nullptr && notify_ep !=
nullptr)
61 if (in_ep->bEndpointAddress & usb_host::USB_DIR_IN)
62 return CdcEps{notify_ep, in_ep, out_ep, interface_number};
63 return CdcEps{notify_ep, out_ep, in_ep, interface_number};
67 const usb_config_desc_t *config_desc;
68 const usb_device_desc_t *device_desc;
70 std::vector<CdcEps> cdc_devs{};
73 if (usb_host_get_device_descriptor(dev_hdl, &device_desc) != ESP_OK) {
74 ESP_LOGE(TAG,
"get_device_descriptor failed");
77 if (usb_host_get_active_config_descriptor(dev_hdl, &config_desc) != ESP_OK) {
78 ESP_LOGE(TAG,
"get_active_config_descriptor failed");
81 if (device_desc->bDeviceClass == USB_CLASS_COMM) {
83 if (
auto eps = get_cdc(config_desc, 0)) {
84 ESP_LOGV(TAG,
"Found CDC-ACM device");
85 cdc_devs.push_back(*eps);
89 if (((device_desc->bDeviceClass == USB_CLASS_MISC) && (device_desc->bDeviceSubClass == USB_SUBCLASS_COMMON) &&
90 (device_desc->bDeviceProtocol == USB_DEVICE_PROTOCOL_IAD)) ||
91 ((device_desc->bDeviceClass == USB_CLASS_PER_INTERFACE) && (device_desc->bDeviceSubClass == USB_SUBCLASS_NULL) &&
92 (device_desc->bDeviceProtocol == USB_PROTOCOL_NULL))) {
94 const auto *this_desc =
reinterpret_cast<const usb_standard_desc_t *
>(config_desc);
96 this_desc = usb_parse_next_descriptor_of_type(this_desc, config_desc->wTotalLength,
97 USB_B_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, &desc_offset);
100 const auto *iad_desc =
reinterpret_cast<const usb_iad_desc_t *
>(this_desc);
102 if (iad_desc->bFunctionClass == USB_CLASS_COMM && iad_desc->bFunctionSubClass == USB_CDC_SUBCLASS_ACM) {
103 ESP_LOGV(TAG,
"Found CDC-ACM device in composite device");
104 if (
auto eps = get_cdc(config_desc, iad_desc->bFirstInterface))
105 cdc_devs.push_back(*eps);
117 for (
size_t i = 0; i !=
len; i++) {
130 for (
size_t i = 0; i !=
len; i++) {
138 ESP_LOGV(TAG,
"Channel not initialised - write ignored");
146 ESP_LOGE(TAG,
"Buffer full - failed to write %d bytes",
len);
148 this->
parent_->start_output(
this);
160 ESP_LOGV(TAG,
"Channel not initialised - read ignored");
166 ESP_LOGV(TAG,
"underflow: requested %zu but returned %d, bytes",
len,
available);
170 for (
size_t i = 0; i !=
len; i++) {
173 this->
parent_->start_input(
this);
179 USBClient::dump_config();
183 " Baud Rate: %" PRIu32
" baud\n"
188 " Dummy receiver: %s",
189 channel->index_, channel->baud_rate_, channel->data_bits_, PARITY_NAMES[channel->parity_],
190 STOP_BITS_NAMES[channel->stop_bits_], YESNO(channel->debug_), YESNO(channel->dummy_receiver_));
199 ESP_LOGV(TAG,
"Transfer result: length: %u; status %X",
status.data_len,
status.error_code);
201 ESP_LOGE(TAG,
"Control transfer failed, status=%s", esp_err_to_name(
status.error_code));
204#ifdef USE_UART_DEBUGGER
212 for (
size_t i = 0; i !=
status.data_len; i++) {
221 this->
transfer_in(ep->bEndpointAddress, callback, ep->wMaxPacketSize);
232 ESP_LOGV(TAG,
"Output Transfer result: length: %u; status %X",
status.data_len,
status.error_code);
237 uint8_t data[ep->wMaxPacketSize];
240#ifdef USE_UART_DEBUGGER
245 ESP_LOGV(TAG,
"Output %d bytes started",
len);
252static void fix_mps(
const usb_ep_desc_t *ep) {
254 auto *ep_mutable =
const_cast<usb_ep_desc_t *
>(ep);
255 if (ep->wMaxPacketSize > 64) {
256 ESP_LOGW(TAG,
"Corrected MPS of EP %u from %u to 64", ep->bEndpointAddress, ep->wMaxPacketSize);
257 ep_mutable->wMaxPacketSize = 64;
263 if (cdc_devs.empty()) {
268 ESP_LOGD(TAG,
"Found %zu CDC-ACM devices", cdc_devs.size());
271 if (i == cdc_devs.size()) {
272 ESP_LOGE(TAG,
"No configuration found for channel %d", channel->index_);
276 channel->cdc_dev_ = cdc_devs[i++];
277 fix_mps(channel->cdc_dev_.in_ep);
278 fix_mps(channel->cdc_dev_.out_ep);
279 channel->initialised_ =
true;
280 auto err = usb_host_interface_claim(this->
handle_, this->
device_handle_, channel->cdc_dev_.interface_number, 0);
282 ESP_LOGE(TAG,
"usb_host_interface_claim failed: %s, channel=%d, intf=%d", esp_err_to_name(err), channel->index_,
283 channel->cdc_dev_.interface_number);
294 if (channel->cdc_dev_.in_ep !=
nullptr) {
295 usb_host_endpoint_halt(this->
device_handle_, channel->cdc_dev_.in_ep->bEndpointAddress);
296 usb_host_endpoint_flush(this->
device_handle_, channel->cdc_dev_.in_ep->bEndpointAddress);
298 if (channel->cdc_dev_.out_ep !=
nullptr) {
299 usb_host_endpoint_halt(this->
device_handle_, channel->cdc_dev_.out_ep->bEndpointAddress);
300 usb_host_endpoint_flush(this->
device_handle_, channel->cdc_dev_.out_ep->bEndpointAddress);
302 if (channel->cdc_dev_.notify_ep !=
nullptr) {
303 usb_host_endpoint_halt(this->
device_handle_, channel->cdc_dev_.notify_ep->bEndpointAddress);
304 usb_host_endpoint_flush(this->
device_handle_, channel->cdc_dev_.notify_ep->bEndpointAddress);
307 channel->initialised_ =
false;
308 channel->input_started_ =
false;
309 channel->output_started_ =
false;
310 channel->input_buffer_.clear();
311 channel->output_buffer_.clear();
313 USBClient::on_disconnected();
318 if (!channel->initialised_)
320 channel->input_started_ =
false;
321 channel->output_started_ =
false;
void status_set_warning(const char *message="unspecified")
void status_set_error(const char *message="unspecified")
void defer(const std::string &name, std::function< void()> &&f)
Defer a callback to the next loop() call.
USBUartComponent * parent_
static void log_hex(UARTDirection direction, std::vector< uint8_t > bytes, uint8_t separator)
Log the bytes as hex values, separated by the provided separator character.
usb_host_client_handle_t handle_
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.
virtual void disconnect()
usb_device_handle_t device_handle_
size_t get_free_space() const
size_t get_available() const
bool peek_byte(uint8_t *data) override
void write_array(const uint8_t *data, size_t len) override
RingBuffer output_buffer_
bool read_array(uint8_t *data, size_t len) override
std::vector< USBUartChannel * > channels_
void start_output(USBUartChannel *channel)
void dump_config() override
void start_input(USBUartChannel *channel)
void on_disconnected() override
void on_connected() override
virtual void enable_channels()
virtual std::vector< CdcEps > parse_descriptors_(usb_device_handle_t dev_hdl)
Providing packet encoding functions for exchanging data with a remote host.
const nullopt_t nullopt((nullopt_t::init()))
const usb_ep_desc_t * out_ep
const usb_ep_desc_t * in_ep