[usb_uart] Fix invalid stop bits and parity enum mappings (#20408)

This commit is contained in:
puddly
2026-10-12 11:51:44 +13:00
committed by Jesse Hills
parent fb1ef29d54
commit 2727d35655
11 changed files with 91 additions and 107 deletions
+2 -2
View File
@@ -10,8 +10,8 @@ namespace esphome::uart {
static const char *const TAG = "uart";
// UART parity strings indexed by UARTParityOptions enum (0-2): NONE, EVEN, ODD
PROGMEM_STRING_TABLE(UARTParityStrings, "NONE", "EVEN", "ODD", "UNKNOWN");
// UART parity strings indexed by UARTParityOptions enum (0-4): NONE, EVEN, ODD, MARK, SPACE
PROGMEM_STRING_TABLE(UARTParityStrings, "NONE", "EVEN", "ODD", "MARK", "SPACE", "UNKNOWN");
void UARTDevice::check_uart_settings(uint32_t baud_rate, uint8_t stop_bits, UARTParityOptions parity,
uint8_t data_bits) {
+3
View File
@@ -18,6 +18,9 @@ enum UARTParityOptions {
UART_CONFIG_PARITY_NONE,
UART_CONFIG_PARITY_EVEN,
UART_CONFIG_PARITY_ODD,
// Only USB serial adapters (usb_uart) support these
UART_CONFIG_PARITY_MARK,
UART_CONFIG_PARITY_SPACE,
};
#ifdef USE_UART_DEBUGGER
@@ -158,6 +158,8 @@ void HostUartComponent::setup() {
options.c_cflag |= PARENB;
options.c_cflag |= PARODD;
break;
default:
break;
};
// Set stop bits
if (this->stop_bits_ == 2) {
@@ -38,6 +38,8 @@ uint16_t LibreTinyUARTComponent::get_config() {
case UART_CONFIG_PARITY_ODD:
config |= SERIAL_PARITY_ODD;
break;
default:
break;
}
config |= (this->data_bits_ - 4) << 8;
+6 -2
View File
@@ -1,7 +1,12 @@
import esphome.codegen as cg
from esphome.components.const import CONF_DATA_BITS, CONF_PARITY, CONF_STOP_BITS
from esphome.components.esp32 import VARIANT_ESP32P4, get_esp32_variant
from esphome.components.uart import CONF_DEBUG_PREFIX, CONF_FLUSH_TIMEOUT, UARTComponent
from esphome.components.uart import (
CONF_DEBUG_PREFIX,
CONF_FLUSH_TIMEOUT,
UARTComponent,
UARTParityOptions,
)
from esphome.components.usb_host import (
get_max_packet_size,
register_usb_client,
@@ -38,7 +43,6 @@ def is_usb_uart_channel(uart_id: ID, full_config: ConfigType) -> bool:
)
UARTParityOptions = usb_uart_ns.enum("UARTParityOptions")
UART_PARITY_OPTIONS = {
"NONE": UARTParityOptions.UART_CONFIG_PARITY_NONE,
"EVEN": UARTParityOptions.UART_CONFIG_PARITY_EVEN,
+5 -8
View File
@@ -132,15 +132,12 @@ bool USBUartTypeCH34X::config_step(USBUartChannelBase *channel, uint8_t step, bo
factor = 256 - factor;
uint16_t value = 0xC0;
if (channel->stop_bits_ == UART_CONFIG_STOP_BITS_2)
if (channel->line_stop_bits_() == LineStopBits::LINE_STOP_BITS_2)
value |= 4;
switch (channel->parity_) {
case UART_CONFIG_PARITY_NONE:
break;
default:
value |= 8 | ((channel->parity_ - 1) << 4);
break;
}
// Parity enable, then 0 = odd, 1 = even, 2 = mark, 3 = space
const LineParity parity = channel->line_parity_();
if (parity != LineParity::LINE_PARITY_NONE)
value |= 8 | ((static_cast<uint8_t>(parity) - 1) << 4);
value |= channel->data_bits_ - 5;
value <<= 8;
value |= 0x8C;
+2 -2
View File
@@ -107,8 +107,8 @@ bool USBUartTypeCP210X::config_step(USBUartChannelBase *channel, uint8_t step, b
this->config_transfer_(USB_VENDOR_IFC | usb_host::USB_DIR_OUT, IFC_ENABLE, 1, channel->index_);
return true;
case 1: {
uint16_t line_control = channel->stop_bits_;
line_control |= static_cast<uint8_t>(channel->parity_) << 4;
uint16_t line_control = static_cast<uint8_t>(channel->line_stop_bits_());
line_control |= static_cast<uint8_t>(channel->line_parity_()) << 4;
line_control |= channel->data_bits_ << 8;
ESP_LOGD(TAG, "Line control value 0x%X", line_control);
this->config_transfer_(USB_VENDOR_IFC | usb_host::USB_DIR_OUT, SET_LINE_CTL, line_control, channel->index_);
+2 -28
View File
@@ -362,34 +362,8 @@ bool USBUartTypeFT23XX::config_step(USBUartChannelBase *channel, uint8_t step, b
}
case 2: { // set line properties (data bits / parity / stop bits)
uint16_t value = channel->data_bits_;
switch (channel->parity_) {
case UART_CONFIG_PARITY_NONE:
value |= (0x00 << 8);
break;
case UART_CONFIG_PARITY_ODD:
value |= (0x01 << 8);
break;
case UART_CONFIG_PARITY_EVEN:
value |= (0x02 << 8);
break;
case UART_CONFIG_PARITY_MARK:
value |= (0x03 << 8);
break;
case UART_CONFIG_PARITY_SPACE:
value |= (0x04 << 8);
break;
}
switch (channel->stop_bits_) {
default: // 1 bit
value |= (0x00 << 11);
break;
case UART_CONFIG_STOP_BITS_1_5:
value |= (0x01 << 11);
break;
case UART_CONFIG_STOP_BITS_2:
value |= (0x02 << 11);
break;
}
value |= static_cast<uint8_t>(channel->line_parity_()) << 8;
value |= static_cast<uint8_t>(channel->line_stop_bits_()) << 11;
value |= (0x00 << 14);
this->config_transfer_(USB_VENDOR_DEV | usb_host::USB_DIR_OUT, 0x04, value,
channel->cdc_dev_.bulk_interface_number + 1);
+2 -27
View File
@@ -261,34 +261,9 @@ bool USBUartTypePL2303::config_step(USBUartChannelBase *channel, uint8_t step, b
encode_baud_divisor(line_coding, baud);
}
// Stop bits: 0=1, 1=1.5, 2=2
switch (channel->get_stop_bits()) {
case 2:
line_coding[4] = 2;
break;
default:
line_coding[4] = 0;
break;
}
line_coding[4] = static_cast<uint8_t>(channel->line_stop_bits_());
// Parity: 0=none, 1=odd, 2=even, 3=mark, 4=space
switch (channel->parity_) {
case UART_CONFIG_PARITY_ODD:
line_coding[5] = 1;
break;
case UART_CONFIG_PARITY_EVEN:
line_coding[5] = 2;
break;
case UART_CONFIG_PARITY_MARK:
line_coding[5] = 3;
break;
case UART_CONFIG_PARITY_SPACE:
line_coding[5] = 4;
break;
default:
line_coding[5] = 0;
break;
}
line_coding[5] = static_cast<uint8_t>(channel->line_parity_());
// Data bits
line_coding[6] = channel->get_data_bits();
+40 -10
View File
@@ -270,9 +270,10 @@ void USBUartComponent::dump_config() {
" Flush Timeout: %" PRIu32 " ms\n"
" Debug: %s\n"
" Dummy receiver: %s",
channel->index_, channel->baud_rate_, channel->data_bits_, PARITY_NAMES[channel->parity_],
STOP_BITS_NAMES[channel->stop_bits_], channel->flush_timeout_ms_, YESNO(channel->debug_),
YESNO(channel->dummy_receiver_));
channel->index_, channel->baud_rate_, channel->data_bits_,
PARITY_NAMES[static_cast<uint8_t>(channel->line_parity_())],
STOP_BITS_NAMES[static_cast<uint8_t>(channel->line_stop_bits_())], channel->flush_timeout_ms_,
YESNO(channel->debug_), YESNO(channel->dummy_receiver_));
}
}
void USBUartComponent::start_input(USBUartChannelBase *channel) {
@@ -515,14 +516,17 @@ bool USBUartTypeCdcAcm::config_step(USBUartChannelBase *channel, uint8_t step, b
// sends RSTACK.
uint32_t baud = channel->baud_rate_;
std::vector<uint8_t> line_coding = {
static_cast<uint8_t>(baud & 0xFF), static_cast<uint8_t>((baud >> 8) & 0xFF),
static_cast<uint8_t>((baud >> 16) & 0xFF), static_cast<uint8_t>((baud >> 24) & 0xFF),
static_cast<uint8_t>(channel->stop_bits_), // bCharFormat: 0=1stop, 1=1.5stop, 2=2stop
static_cast<uint8_t>(channel->parity_), // bParityType: 0=None, 1=Odd, 2=Even, 3=Mark, 4=Space
static_cast<uint8_t>(channel->data_bits_), // bDataBits
static_cast<uint8_t>(baud & 0xFF),
static_cast<uint8_t>((baud >> 8) & 0xFF),
static_cast<uint8_t>((baud >> 16) & 0xFF),
static_cast<uint8_t>((baud >> 24) & 0xFF),
static_cast<uint8_t>(channel->line_stop_bits_()), // bCharFormat
static_cast<uint8_t>(channel->line_parity_()), // bParityType
static_cast<uint8_t>(channel->data_bits_), // bDataBits
};
ESP_LOGD(TAG, "SET_LINE_CODING: baud=%u stop=%u parity=%u data=%u", (unsigned) baud, channel->stop_bits_,
(unsigned) channel->parity_, channel->data_bits_);
ESP_LOGD(TAG, "SET_LINE_CODING: baud=%u stop=%s parity=%s data=%u", (unsigned) baud,
STOP_BITS_NAMES[static_cast<uint8_t>(channel->line_stop_bits_())],
PARITY_NAMES[static_cast<uint8_t>(channel->line_parity_())], channel->data_bits_);
this->config_transfer_(CDC_REQUEST_TYPE, CDC_SET_LINE_CODING, 0, channel->cdc_dev_.interrupt_interface_number,
line_coding);
return true;
@@ -675,6 +679,32 @@ bool USBUartComponent::run_config_machine_() {
return true;
}
LineStopBits USBUartChannelBase::line_stop_bits_() const {
switch (this->stop_bits_) {
case UART_CONFIG_STOP_BITS_1_5:
return LineStopBits::LINE_STOP_BITS_1_5;
case UART_CONFIG_STOP_BITS_2:
return LineStopBits::LINE_STOP_BITS_2;
default:
return LineStopBits::LINE_STOP_BITS_1;
}
}
LineParity USBUartChannelBase::line_parity_() const {
switch (this->parity_) {
case uart::UART_CONFIG_PARITY_ODD:
return LineParity::LINE_PARITY_ODD;
case uart::UART_CONFIG_PARITY_EVEN:
return LineParity::LINE_PARITY_EVEN;
case uart::UART_CONFIG_PARITY_MARK:
return LineParity::LINE_PARITY_MARK;
case uart::UART_CONFIG_PARITY_SPACE:
return LineParity::LINE_PARITY_SPACE;
default:
return LineParity::LINE_PARITY_NONE;
}
}
void USBUartChannelBase::load_settings(bool /*dump_config*/) {
// The per-channel control transfers already log their values at debug level.
this->parent_->apply_channel_settings(this);
+25 -28
View File
@@ -71,21 +71,33 @@ enum CH34xChipType : uint8_t {
CHIP_UNKNOWN = 0xFF,
};
enum UARTParityOptions {
UART_CONFIG_PARITY_NONE = 0,
UART_CONFIG_PARITY_ODD,
UART_CONFIG_PARITY_EVEN,
UART_CONFIG_PARITY_MARK,
UART_CONFIG_PARITY_SPACE,
};
// 1 and 2 are the stop bit count the uart component uses, so a count set through the
// UARTComponent API (as serial_proxy does) means the same here. 1.5 has no count.
enum UARTStopBitsOptions {
UART_CONFIG_STOP_BITS_1 = 0,
UART_CONFIG_STOP_BITS_1_5,
UART_CONFIG_STOP_BITS_2,
UART_CONFIG_STOP_BITS_1 = 1,
UART_CONFIG_STOP_BITS_2 = 2,
UART_CONFIG_STOP_BITS_1_5 = 3,
};
// Stop bits and parity as adapters take them on the wire: CDC SET_LINE_CODING, and the
// CP210x, FTDI and PL2303 requests, all use these values
enum class LineStopBits : uint8_t {
LINE_STOP_BITS_1 = 0,
LINE_STOP_BITS_1_5 = 1,
LINE_STOP_BITS_2 = 2,
};
enum class LineParity : uint8_t {
LINE_PARITY_NONE = 0,
LINE_PARITY_ODD = 1,
LINE_PARITY_EVEN = 2,
LINE_PARITY_MARK = 3,
LINE_PARITY_SPACE = 4,
};
// Indexed by LineParity
static const char *const PARITY_NAMES[] = {"NONE", "ODD", "EVEN", "MARK", "SPACE"};
// Indexed by LineStopBits
static const char *const STOP_BITS_NAMES[] = {"1", "1.5", "2"};
class RingBuffer {
@@ -156,22 +168,6 @@ class USBUartChannelBase : public uart::UARTComponent, public Parented<USBUartCo
// Re-apply the current line settings (baud, parity, etc) to this already-open channel.
void load_settings(bool dump_config) override;
using UARTComponent::load_settings; // also bring in the no-arg overload for convenience
void set_parity(UARTParityOptions parity) {
this->parity_ = parity;
// Keep the base-class parity in sync so uart::UARTComponent::get_parity() reports the configured value.
// MARK/SPACE have no uart:: equivalent and report as NONE.
switch (parity) {
case UART_CONFIG_PARITY_EVEN:
uart::UARTComponent::set_parity(uart::UART_CONFIG_PARITY_EVEN);
break;
case UART_CONFIG_PARITY_ODD:
uart::UARTComponent::set_parity(uart::UART_CONFIG_PARITY_ODD);
break;
default:
uart::UARTComponent::set_parity(uart::UART_CONFIG_PARITY_NONE);
break;
}
}
void set_debug(bool debug) { this->debug_ = debug; }
void set_dummy_receiver(bool dummy_receiver) { this->dummy_receiver_ = dummy_receiver; }
void set_debug_prefix(const char *prefix) { this->debug_prefix_ = StringRef(prefix); }
@@ -194,6 +190,8 @@ class USBUartChannelBase : public uart::UARTComponent, public Parented<USBUartCo
// Not directly instantiable; construct a concrete channel type instead.
USBUartChannelBase(uint8_t index, uint16_t buffer_size) : input_buffer_(RingBuffer(buffer_size)), index_(index) {}
void check_logger_conflict() override {}
LineStopBits line_stop_bits_() const;
LineParity line_parity_() const;
// Larger structures first (8+ bytes)
RingBuffer input_buffer_;
LockFreeQueue<UsbOutputChunk, USB_OUTPUT_CHUNK_COUNT> output_queue_;
@@ -205,7 +203,6 @@ class USBUartChannelBase : public uart::UARTComponent, public Parented<USBUartCo
CdcEps cdc_dev_{};
StringRef debug_prefix_{};
// 4-byte fields
UARTParityOptions parity_{UART_CONFIG_PARITY_NONE};
uint32_t flush_timeout_ms_{100};
// 1-byte fields (no padding between groups)
std::atomic<bool> input_started_{true};