[usb_uart] Add claim_comm_interface option (#18969)

This commit is contained in:
Keith Burzinski
2026-09-15 11:47:06 -05:00
committed by GitHub
parent 457bb3ecc9
commit f19403192d
4 changed files with 66 additions and 13 deletions
+48 -5
View File
@@ -44,6 +44,7 @@ UART_STOP_BITS_OPTIONS = {
}
DEFAULT_BAUD_RATE = 9600
CONF_CLAIM_COMM_INTERFACE = "claim_comm_interface"
class Type:
@@ -56,6 +57,7 @@ class Type:
max_channels: int = 1,
baud_rate_required: bool = True,
max_baud: int = 1_000_000,
has_comm_interface: bool = False,
) -> None:
self.name = name
cls = cls or name
@@ -65,6 +67,9 @@ class Type:
self._max_channels = max_channels
self.baud_rate_required = baud_rate_required
self.max_baud = max_baud
# True for types that claim the CDC comm (interrupt) interface; only these
# accept the claim_comm_interface option.
self.has_comm_interface = has_comm_interface
@property
def max_channels(self) -> int:
@@ -80,11 +85,21 @@ class Type:
uart_types = (
Type("CDC_ACM", 0, 0, "CdcAcm", 1, baud_rate_required=False),
Type(
"CDC_ACM", 0, 0, "CdcAcm", 1, baud_rate_required=False, has_comm_interface=True
),
Type("CH34X", 0x1A86, 0x55D5, "CH34X", 4, max_baud=2_000_000),
Type("CH340", 0x1A86, 0x7523, "CH34X", 1, max_baud=2_000_000),
Type("CP210X", 0x10C4, 0xEA60, "CP210X", 3, max_baud=2_000_000),
Type("ESP_JTAG", 0x303A, 0x1001, "CdcAcm", 1, baud_rate_required=False),
Type(
"ESP_JTAG",
0x303A,
0x1001,
"CdcAcm",
1,
baud_rate_required=False,
has_comm_interface=True,
),
Type("FT232", 0x0403, 0x6001, "FT23XX", 1, max_baud=3_000_000),
Type("FT2232", 0x0403, 0x6010, "FT23XX", 2, max_baud=12_000_000),
Type("FT4232", 0x0403, 0x6011, "FT23XX", 4, max_baud=12_000_000),
@@ -95,12 +110,20 @@ uart_types = (
Type("PL2303GL", 0x067B, 0x23D3, "PL2303", 1, max_baud=6_000_000),
Type("PL2303GS", 0x067B, 0x23F3, "PL2303", 1, max_baud=6_000_000),
Type("PL2303GT", 0x067B, 0x23C3, "PL2303", 1, max_baud=6_000_000),
Type("STM32_VCP", 0x0483, 0x5740, "CdcAcm", 1, baud_rate_required=False),
Type(
"STM32_VCP",
0x0483,
0x5740,
"CdcAcm",
1,
baud_rate_required=False,
has_comm_interface=True,
),
)
def channel_schema(type_: "Type") -> cv.Schema:
return cv.Schema(
schema = cv.Schema(
{
cv.Required(CONF_CHANNELS): cv.All(
cv.ensure_list(
@@ -139,9 +162,26 @@ def channel_schema(type_: "Type") -> cv.Schema:
max=type_.max_channels,
msg=f"Device type {type_.name} supports a maximum of {type_.max_channels} channels",
),
)
),
}
)
if type_.has_comm_interface:
# The comm (interrupt) interface pins a host hardware channel per device;
# disable to save one on channel-poor hosts (some devices may need it
# claimed before enabling data flow).
schema = schema.extend(
{cv.Optional(CONF_CLAIM_COMM_INTERFACE, default=True): cv.boolean}
)
else:
schema = schema.extend(
{
cv.Optional(CONF_CLAIM_COMM_INTERFACE): cv.invalid(
f"'{CONF_CLAIM_COMM_INTERFACE}' is only supported on device types "
f"that claim the CDC comm interface; {type_.name} never claims it"
)
}
)
return schema
CONFIG_SCHEMA = cv.ensure_list(
@@ -172,6 +212,9 @@ async def to_code(config: list[ConfigType]) -> None:
for device in config:
var = await register_usb_client(device)
# The C++ default is true; only emit the override
if not device.get(CONF_CLAIM_COMM_INTERFACE, True):
cg.add(var.set_claim_comm_interface(False))
for index, channel in enumerate(device[CONF_CHANNELS]):
chvar = cg.new_Pvariable(channel[CONF_ID], index, channel[CONF_BUFFER_SIZE])
await cg.register_parented(chvar, var)
+13 -8
View File
@@ -431,15 +431,20 @@ void USBUartTypeCdcAcm::on_connected() {
// they enable data flow on the bulk endpoints.
if (channel->cdc_dev_.interrupt_interface_number != 0xFF &&
channel->cdc_dev_.interrupt_interface_number != channel->cdc_dev_.bulk_interface_number) {
auto err_comm = usb_host_interface_claim(this->handle_, this->device_handle_,
channel->cdc_dev_.interrupt_interface_number, 0);
if (err_comm != ESP_OK) {
// Continue anyway: the interface number stays valid for CDC request addressing
ESP_LOGW(TAG, "Could not claim comm interface %d: %s", channel->cdc_dev_.interrupt_interface_number,
esp_err_to_name(err_comm));
if (!this->claim_comm_interface_) {
ESP_LOGD(TAG, "Skipping comm interface %d (claim_comm_interface: false)",
channel->cdc_dev_.interrupt_interface_number);
} else {
ESP_LOGD(TAG, "Claimed comm interface %d", channel->cdc_dev_.interrupt_interface_number);
channel->cdc_dev_.interrupt_interface_claimed = true;
auto err_comm = usb_host_interface_claim(this->handle_, this->device_handle_,
channel->cdc_dev_.interrupt_interface_number, 0);
if (err_comm != ESP_OK) {
// Continue anyway: the interface number stays valid for CDC request addressing
ESP_LOGW(TAG, "Could not claim comm interface %d: %s", channel->cdc_dev_.interrupt_interface_number,
esp_err_to_name(err_comm));
} else {
ESP_LOGD(TAG, "Claimed comm interface %d", channel->cdc_dev_.interrupt_interface_number);
channel->cdc_dev_.interrupt_interface_claimed = true;
}
}
}
auto err =
+4
View File
@@ -271,12 +271,16 @@ class USBUartComponent : public usb_host::USBClient {
class USBUartTypeCdcAcm : public USBUartComponent {
public:
USBUartTypeCdcAcm(uint16_t vid, uint16_t pid) : USBUartComponent(vid, pid) {}
void set_claim_comm_interface(bool claim) { this->claim_comm_interface_ = claim; }
protected:
virtual std::vector<CdcEps> parse_descriptors(usb_device_handle_t dev_hdl);
void on_connected() override;
void on_disconnected() override;
bool config_step(USBUartChannelBase *channel, uint8_t step, bool reload, bool ok, const uint8_t *response) override;
// Each claimed interface pins one host hardware channel per endpoint; skipping
// the comm (interrupt) interface frees one on channel-poor hosts (ESP32-S3: 8).
bool claim_comm_interface_{true};
};
class USBUartTypeCP210X : public USBUartTypeCdcAcm {
+1
View File
@@ -6,6 +6,7 @@ usb_uart:
type: cdc_acm
vid: 0x1234
pid: 0x5678
claim_comm_interface: false
channels:
- id: channel_0_1
- id: uart_1