[usb_uart] Be flexible about descriptor layout for CDC-ACM devices (#9425)

This commit is contained in:
Clyde Stubbs 2025-07-13 08:59:49 +10:00 committed by Jesse Hills
parent bc7cfeb9cd
commit 01b4e214b9
No known key found for this signature in database
GPG Key ID: BEAAE804EFD8E83A
4 changed files with 56 additions and 58 deletions

View File

@ -70,7 +70,7 @@ static void usbh_print_cfg_desc(const usb_config_desc_t *cfg_desc) {
ESP_LOGV(TAG, "bMaxPower %dmA", cfg_desc->bMaxPower * 2);
}
void usb_client_print_device_descriptor(const usb_device_desc_t *devc_desc) {
static void usb_client_print_device_descriptor(const usb_device_desc_t *devc_desc) {
if (devc_desc == NULL) {
return;
}
@ -92,8 +92,8 @@ void usb_client_print_device_descriptor(const usb_device_desc_t *devc_desc) {
ESP_LOGV(TAG, "bNumConfigurations %d", devc_desc->bNumConfigurations);
}
void usb_client_print_config_descriptor(const usb_config_desc_t *cfg_desc,
print_class_descriptor_cb class_specific_cb) {
static void usb_client_print_config_descriptor(const usb_config_desc_t *cfg_desc,
print_class_descriptor_cb class_specific_cb) {
if (cfg_desc == nullptr) {
return;
}
@ -128,9 +128,9 @@ void usb_client_print_config_descriptor(const usb_config_desc_t *cfg_desc,
static std::string get_descriptor_string(const usb_str_desc_t *desc) {
char buffer[256];
if (desc == nullptr)
return "(unknown)";
return "(unspecified)";
char *p = buffer;
for (size_t i = 0; i != desc->bLength / 2; i++) {
for (int i = 0; i != desc->bLength / 2; i++) {
auto c = desc->wData[i];
if (c < 0x100)
*p++ = static_cast<char>(c);
@ -169,7 +169,7 @@ void USBClient::setup() {
this->mark_failed();
return;
}
for (auto trq : this->trq_pool_) {
for (auto *trq : this->trq_pool_) {
usb_host_transfer_alloc(64, 0, &trq->transfer);
trq->client = this;
}
@ -197,7 +197,8 @@ void USBClient::loop() {
ESP_LOGD(TAG, "Device descriptor: vid %X pid %X", desc->idVendor, desc->idProduct);
if (desc->idVendor == this->vid_ && desc->idProduct == this->pid_ || this->vid_ == 0 && this->pid_ == 0) {
usb_device_info_t dev_info;
if ((err = usb_host_device_info(this->device_handle_, &dev_info)) != ESP_OK) {
err = usb_host_device_info(this->device_handle_, &dev_info);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Device info failed: %s", esp_err_to_name(err));
this->disconnect();
break;
@ -336,7 +337,7 @@ static void transfer_callback(usb_transfer_t *xfer) {
* @throws None.
*/
void USBClient::transfer_in(uint8_t ep_address, const transfer_cb_t &callback, uint16_t length) {
auto trq = this->get_trq_();
auto *trq = this->get_trq_();
if (trq == nullptr) {
ESP_LOGE(TAG, "Too many requests queued");
return;
@ -349,7 +350,6 @@ void USBClient::transfer_in(uint8_t ep_address, const transfer_cb_t &callback, u
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to submit transfer, address=%x, length=%d, err=%x", ep_address, length, err);
this->release_trq(trq);
this->disconnect();
}
}
@ -364,7 +364,7 @@ void USBClient::transfer_in(uint8_t ep_address, const transfer_cb_t &callback, u
* @throws None.
*/
void USBClient::transfer_out(uint8_t ep_address, const transfer_cb_t &callback, const uint8_t *data, uint16_t length) {
auto trq = this->get_trq_();
auto *trq = this->get_trq_();
if (trq == nullptr) {
ESP_LOGE(TAG, "Too many requests queued");
return;

View File

@ -43,7 +43,7 @@ static constexpr uint8_t SET_BAUDRATE = 0x1E; // Set the baud rate.
static constexpr uint8_t SET_CHARS = 0x19; // Set special characters.
static constexpr uint8_t VENDOR_SPECIFIC = 0xFF; // Vendor specific command.
std::vector<CdcEps> USBUartTypeCP210X::parse_descriptors_(usb_device_handle_t dev_hdl) {
std::vector<CdcEps> USBUartTypeCP210X::parse_descriptors(usb_device_handle_t dev_hdl) {
const usb_config_desc_t *config_desc;
const usb_device_desc_t *device_desc;
int conf_offset = 0, ep_offset;

View File

@ -18,52 +18,48 @@ namespace usb_uart {
*/
static optional<CdcEps> get_cdc(const usb_config_desc_t *config_desc, uint8_t intf_idx) {
int conf_offset, ep_offset;
const usb_ep_desc_t *notify_ep{}, *in_ep{}, *out_ep{};
uint8_t interface_number = 0;
// look for an interface with one interrupt endpoint (notify), and an interface with two bulk endpoints (data in/out)
// look for an interface with an interrupt endpoint (notify), and one with two bulk endpoints (data in/out)
CdcEps eps{};
eps.bulk_interface_number = 0xFF;
for (;;) {
auto intf_desc = usb_parse_interface_descriptor(config_desc, intf_idx++, 0, &conf_offset);
const auto *intf_desc = usb_parse_interface_descriptor(config_desc, intf_idx++, 0, &conf_offset);
if (!intf_desc) {
ESP_LOGE(TAG, "usb_parse_interface_descriptor failed");
return nullopt;
}
if (intf_desc->bNumEndpoints == 1) {
ESP_LOGD(TAG, "intf_desc: bInterfaceClass=%02X, bInterfaceSubClass=%02X, bInterfaceProtocol=%02X, bNumEndpoints=%d",
intf_desc->bInterfaceClass, intf_desc->bInterfaceSubClass, intf_desc->bInterfaceProtocol,
intf_desc->bNumEndpoints);
for (uint8_t i = 0; i != intf_desc->bNumEndpoints; i++) {
ep_offset = conf_offset;
notify_ep = usb_parse_endpoint_descriptor_by_index(intf_desc, 0, config_desc->wTotalLength, &ep_offset);
if (!notify_ep) {
ESP_LOGE(TAG, "notify_ep: usb_parse_endpoint_descriptor_by_index failed");
const auto *ep = usb_parse_endpoint_descriptor_by_index(intf_desc, i, config_desc->wTotalLength, &ep_offset);
if (!ep) {
ESP_LOGE(TAG, "Ran out of interfaces at %d before finding all endpoints", i);
return nullopt;
}
if (notify_ep->bmAttributes != USB_BM_ATTRIBUTES_XFER_INT)
notify_ep = nullptr;
} else if (USB_CLASS_CDC_DATA && intf_desc->bNumEndpoints == 2) {
interface_number = intf_desc->bInterfaceNumber;
ep_offset = conf_offset;
out_ep = usb_parse_endpoint_descriptor_by_index(intf_desc, 0, config_desc->wTotalLength, &ep_offset);
if (!out_ep) {
ESP_LOGE(TAG, "out_ep: usb_parse_endpoint_descriptor_by_index failed");
return nullopt;
ESP_LOGD(TAG, "ep: bEndpointAddress=%02X, bmAttributes=%02X", ep->bEndpointAddress, ep->bmAttributes);
if (ep->bmAttributes == USB_BM_ATTRIBUTES_XFER_INT) {
eps.notify_ep = ep;
eps.interrupt_interface_number = intf_desc->bInterfaceNumber;
} else if (ep->bmAttributes == USB_BM_ATTRIBUTES_XFER_BULK && ep->bEndpointAddress & usb_host::USB_DIR_IN &&
(eps.bulk_interface_number == 0xFF || eps.bulk_interface_number == intf_desc->bInterfaceNumber)) {
eps.in_ep = ep;
eps.bulk_interface_number = intf_desc->bInterfaceNumber;
} else if (ep->bmAttributes == USB_BM_ATTRIBUTES_XFER_BULK && !(ep->bEndpointAddress & usb_host::USB_DIR_IN) &&
(eps.bulk_interface_number == 0xFF || eps.bulk_interface_number == intf_desc->bInterfaceNumber)) {
eps.out_ep = ep;
eps.bulk_interface_number = intf_desc->bInterfaceNumber;
} else {
ESP_LOGE(TAG, "Unexpected endpoint attributes: %02X", ep->bmAttributes);
continue;
}
if (out_ep->bmAttributes != USB_BM_ATTRIBUTES_XFER_BULK)
out_ep = nullptr;
ep_offset = conf_offset;
in_ep = usb_parse_endpoint_descriptor_by_index(intf_desc, 1, config_desc->wTotalLength, &ep_offset);
if (!in_ep) {
ESP_LOGE(TAG, "in_ep: usb_parse_endpoint_descriptor_by_index failed");
return nullopt;
}
if (in_ep->bmAttributes != USB_BM_ATTRIBUTES_XFER_BULK)
in_ep = nullptr;
}
if (in_ep != nullptr && out_ep != nullptr && notify_ep != nullptr)
break;
if (eps.in_ep != nullptr && eps.out_ep != nullptr && eps.notify_ep != nullptr)
return eps;
}
if (in_ep->bEndpointAddress & usb_host::USB_DIR_IN)
return CdcEps{notify_ep, in_ep, out_ep, interface_number};
return CdcEps{notify_ep, out_ep, in_ep, interface_number};
}
std::vector<CdcEps> USBUartTypeCdcAcm::parse_descriptors_(usb_device_handle_t dev_hdl) {
std::vector<CdcEps> USBUartTypeCdcAcm::parse_descriptors(usb_device_handle_t dev_hdl) {
const usb_config_desc_t *config_desc;
const usb_device_desc_t *device_desc;
int desc_offset = 0;
@ -78,7 +74,7 @@ std::vector<CdcEps> USBUartTypeCdcAcm::parse_descriptors_(usb_device_handle_t de
ESP_LOGE(TAG, "get_active_config_descriptor failed");
return {};
}
if (device_desc->bDeviceClass == USB_CLASS_COMM) {
if (device_desc->bDeviceClass == USB_CLASS_COMM || device_desc->bDeviceClass == USB_CLASS_VENDOR_SPEC) {
// single CDC-ACM device
if (auto eps = get_cdc(config_desc, 0)) {
ESP_LOGV(TAG, "Found CDC-ACM device");
@ -194,7 +190,7 @@ void USBUartComponent::start_input(USBUartChannel *channel) {
if (!channel->initialised_ || channel->input_started_ ||
channel->input_buffer_.get_free_space() < channel->cdc_dev_.in_ep->wMaxPacketSize)
return;
auto ep = channel->cdc_dev_.in_ep;
const auto *ep = channel->cdc_dev_.in_ep;
auto callback = [this, channel](const usb_host::TransferStatus &status) {
ESP_LOGV(TAG, "Transfer result: length: %u; status %X", status.data_len, status.error_code);
if (!status.success) {
@ -227,7 +223,7 @@ void USBUartComponent::start_output(USBUartChannel *channel) {
if (channel->output_buffer_.is_empty()) {
return;
}
auto ep = channel->cdc_dev_.out_ep;
const auto *ep = channel->cdc_dev_.out_ep;
auto callback = [this, channel](const usb_host::TransferStatus &status) {
ESP_LOGV(TAG, "Output Transfer result: length: %u; status %X", status.data_len, status.error_code);
channel->output_started_ = false;
@ -259,15 +255,15 @@ static void fix_mps(const usb_ep_desc_t *ep) {
}
}
void USBUartTypeCdcAcm::on_connected() {
auto cdc_devs = this->parse_descriptors_(this->device_handle_);
auto cdc_devs = this->parse_descriptors(this->device_handle_);
if (cdc_devs.empty()) {
this->status_set_error("No CDC-ACM device found");
this->disconnect();
return;
}
ESP_LOGD(TAG, "Found %zu CDC-ACM devices", cdc_devs.size());
auto i = 0;
for (auto channel : this->channels_) {
size_t i = 0;
for (auto *channel : this->channels_) {
if (i == cdc_devs.size()) {
ESP_LOGE(TAG, "No configuration found for channel %d", channel->index_);
this->status_set_warning("No configuration found for channel");
@ -277,10 +273,11 @@ void USBUartTypeCdcAcm::on_connected() {
fix_mps(channel->cdc_dev_.in_ep);
fix_mps(channel->cdc_dev_.out_ep);
channel->initialised_ = true;
auto err = usb_host_interface_claim(this->handle_, this->device_handle_, channel->cdc_dev_.interface_number, 0);
auto err =
usb_host_interface_claim(this->handle_, this->device_handle_, channel->cdc_dev_.bulk_interface_number, 0);
if (err != ESP_OK) {
ESP_LOGE(TAG, "usb_host_interface_claim failed: %s, channel=%d, intf=%d", esp_err_to_name(err), channel->index_,
channel->cdc_dev_.interface_number);
channel->cdc_dev_.bulk_interface_number);
this->status_set_error("usb_host_interface_claim failed");
this->disconnect();
return;
@ -290,7 +287,7 @@ void USBUartTypeCdcAcm::on_connected() {
}
void USBUartTypeCdcAcm::on_disconnected() {
for (auto channel : this->channels_) {
for (auto *channel : this->channels_) {
if (channel->cdc_dev_.in_ep != nullptr) {
usb_host_endpoint_halt(this->device_handle_, channel->cdc_dev_.in_ep->bEndpointAddress);
usb_host_endpoint_flush(this->device_handle_, channel->cdc_dev_.in_ep->bEndpointAddress);
@ -303,7 +300,7 @@ void USBUartTypeCdcAcm::on_disconnected() {
usb_host_endpoint_halt(this->device_handle_, channel->cdc_dev_.notify_ep->bEndpointAddress);
usb_host_endpoint_flush(this->device_handle_, channel->cdc_dev_.notify_ep->bEndpointAddress);
}
usb_host_interface_release(this->handle_, this->device_handle_, channel->cdc_dev_.interface_number);
usb_host_interface_release(this->handle_, this->device_handle_, channel->cdc_dev_.bulk_interface_number);
channel->initialised_ = false;
channel->input_started_ = false;
channel->output_started_ = false;
@ -314,7 +311,7 @@ void USBUartTypeCdcAcm::on_disconnected() {
}
void USBUartTypeCdcAcm::enable_channels() {
for (auto channel : this->channels_) {
for (auto *channel : this->channels_) {
if (!channel->initialised_)
continue;
channel->input_started_ = false;

View File

@ -25,7 +25,8 @@ struct CdcEps {
const usb_ep_desc_t *notify_ep;
const usb_ep_desc_t *in_ep;
const usb_ep_desc_t *out_ep;
uint8_t interface_number;
uint8_t bulk_interface_number;
uint8_t interrupt_interface_number;
};
enum UARTParityOptions {
@ -123,7 +124,7 @@ class USBUartTypeCdcAcm : public USBUartComponent {
USBUartTypeCdcAcm(uint16_t vid, uint16_t pid) : USBUartComponent(vid, pid) {}
protected:
virtual std::vector<CdcEps> parse_descriptors_(usb_device_handle_t dev_hdl);
virtual std::vector<CdcEps> parse_descriptors(usb_device_handle_t dev_hdl);
void on_connected() override;
virtual void enable_channels();
void on_disconnected() override;
@ -134,7 +135,7 @@ class USBUartTypeCP210X : public USBUartTypeCdcAcm {
USBUartTypeCP210X(uint16_t vid, uint16_t pid) : USBUartTypeCdcAcm(vid, pid) {}
protected:
std::vector<CdcEps> parse_descriptors_(usb_device_handle_t dev_hdl) override;
std::vector<CdcEps> parse_descriptors(usb_device_handle_t dev_hdl) override;
void enable_channels() override;
};
class USBUartTypeCH34X : public USBUartTypeCdcAcm {