ESPHome 2026.3.0
Loading...
Searching...
No Matches
ch34x.cpp
Go to the documentation of this file.
1#if defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
2#include "usb_uart.h"
3#include "usb/usb_host.h"
4#include "esphome/core/log.h"
5
7
9
10using namespace bytebuffer;
11
12struct CH34xEntry {
13 uint16_t pid;
14 uint8_t byte_idx; // which status.data[] byte to inspect
15 uint8_t mask; // bitmask applied before comparison
16 uint8_t match; // 0xFF = wildcard (default/fallthrough for this PID)
17 CH34xChipType chiptype;
18 const char *name;
19 uint8_t num_ports;
20};
21
22static const CH34xEntry CH34X_TABLE[] = {
23 {0x55D2, 1, 0xFF, 0x41, CHIP_CH342K, "CH342K", 2},
24 {0x55D2, 1, 0xFF, 0xFF, CHIP_CH342F, "CH342F", 2},
25 {0x55D3, 1, 0xFF, 0x02, CHIP_CH343J, "CH343J", 1},
26 {0x55D3, 1, 0xFF, 0x01, CHIP_CH343K, "CH343K", 1},
27 {0x55D3, 1, 0xFF, 0x18, CHIP_CH343G_AUTOBAUD, "CH343G_AUTOBAUD", 1},
28 {0x55D3, 1, 0xFF, 0xFF, CHIP_CH343GP, "CH343GP", 1},
29 {0x55D4, 1, 0xFF, 0x09, CHIP_CH9102X, "CH9102X", 1},
30 {0x55D4, 1, 0xFF, 0xFF, CHIP_CH9102F, "CH9102F", 1},
31 {0x55D5, 1, 0xFF, 0xC0, CHIP_CH344L, "CH344L", 4}, // CH344L vs CH344L_V2 resolved below
32 {0x55D5, 1, 0xFF, 0xFF, CHIP_CH344Q, "CH344Q", 4},
33 {0x55D7, 1, 0xFF, 0xFF, CHIP_CH9103M, "CH9103M", 2},
34 {0x55D8, 1, 0xFF, 0x0A, CHIP_CH9101RY, "CH9101RY", 1},
35 {0x55D8, 1, 0xFF, 0xFF, CHIP_CH9101UH, "CH9101UH", 1},
36 {0x55DB, 1, 0xFF, 0xFF, CHIP_CH347TF, "CH347TF", 1},
37 {0x55DD, 1, 0xFF, 0xFF, CHIP_CH347TF, "CH347TF", 1},
38 {0x55DA, 1, 0xFF, 0xFF, CHIP_CH347TF, "CH347TF", 2},
39 {0x55DE, 1, 0xFF, 0xFF, CHIP_CH347TF, "CH347TF", 2},
40 {0x55E7, 1, 0xFF, 0xFF, CHIP_CH339W, "CH339W", 1},
41 {0x55DF, 1, 0xFF, 0xFF, CHIP_CH9104L, "CH9104L", 4},
42 {0x55E9, 1, 0xFF, 0xFF, CHIP_CH9111L_M0, "CH9111L_M0", 1},
43 {0x55EA, 1, 0xFF, 0xFF, CHIP_CH9111L_M1, "CH9111L_M1", 1},
44 {0x55E8, 2, 0xFF, 0x48, CHIP_CH9114L, "CH9114L", 4},
45 {0x55E8, 2, 0xFF, 0x49, CHIP_CH9114W, "CH9114W", 4},
46 {0x55E8, 2, 0xFF, 0x4A, CHIP_CH9114F, "CH9114F", 4},
47 {0x55EB, 4, 0x01, 0x01, CHIP_CH346C_M1, "CH346C_M1", 1},
48 {0x55EB, 4, 0x01, 0xFF, CHIP_CH346C_M0, "CH346C_M0", 1},
49 {0x55EC, 1, 0xFF, 0xFF, CHIP_CH346C_M2, "CH346C_M2", 2},
50};
51
54 if (!status.success) {
55 this->defer([this, error_code = status.error_code]() {
56 ESP_LOGE(TAG, "CH34x chip detection failed: %s", esp_err_to_name(error_code));
57 this->apply_line_settings_();
58 });
59 return;
60 }
61 CH34xChipType chiptype = CHIP_UNKNOWN;
62 uint8_t num_ports = 1;
63 for (const auto &e : CH34X_TABLE) {
64 if (e.pid != this->pid_)
65 continue;
66 if (e.match != 0xFF && (status.data[e.byte_idx] & e.mask) != e.match)
67 continue;
68 chiptype = e.chiptype;
69 num_ports = e.num_ports;
70 break;
71 }
72 // CH344L vs CH344L_V2 requires chipver (data[0]) in addition to chiptype (data[1])
73 if (chiptype == CHIP_CH344L && (status.data[0] & 0xF0) != 0x40)
74 chiptype = CHIP_CH344L_V2;
75 const char *name = "unknown";
76 for (const auto &e : CH34X_TABLE) {
77 if (e.chiptype == chiptype) {
78 name = e.name;
79 break;
80 }
81 }
82 this->defer([this, chiptype, num_ports, name]() {
83 this->chiptype_ = chiptype;
84 this->chip_name_ = name;
85 this->num_ports_ = num_ports;
86 ESP_LOGD(TAG, "CH34x chip: %s, ports: %u", name, this->num_ports_);
87 this->apply_line_settings_();
88 });
89 };
90 // Vendor-specific GET_CHIP_VERSION request (bRequest=0x5F): returns chip ID bytes
91 // used to distinguish CH34x variants sharing the same PID.
92 this->control_transfer(USB_VENDOR_DEV | usb_host::USB_DIR_IN, 0x5F, 0, 0, cb, {0, 0, 0, 0, 0, 0, 0, 0});
93}
94
97 ESP_LOGCONFIG(TAG, " CH34x chip: %s", this->chip_name_);
98}
99
100void USBUartTypeCH34X::apply_line_settings_() {
101 for (auto *channel : this->channels_) {
102 if (!channel->initialised_.load())
103 continue;
105 if (!status.success) {
106 ESP_LOGE(TAG, "Control transfer failed, status=%s", esp_err_to_name(status.error_code));
107 channel->initialised_.store(false);
108 }
109 };
110
111 uint8_t divisor = 7;
112 uint32_t clk = 12000000;
113
114 auto baud_rate = channel->baud_rate_;
115 if (baud_rate < 256000) {
116 if (baud_rate > 6000000 / 255) {
117 divisor = 3;
118 clk = 6000000;
119 } else if (baud_rate > 750000 / 255) {
120 divisor = 2;
121 clk = 750000;
122 } else if (baud_rate > 93750 / 255) {
123 divisor = 1;
124 clk = 93750;
125 } else {
126 divisor = 0;
127 clk = 11719;
128 }
129 }
130 ESP_LOGV(TAG, "baud_rate: %" PRIu32 ", divisor: %d, clk: %" PRIu32, baud_rate, divisor, clk);
131 auto factor = static_cast<uint8_t>(clk / baud_rate);
132 if (factor == 0 || factor == 0xFF) {
133 ESP_LOGE(TAG, "Invalid baud rate %" PRIu32, baud_rate);
134 channel->initialised_.store(false);
135 continue;
136 }
137 if ((clk / factor - baud_rate) > (baud_rate - clk / (factor + 1)))
138 factor++;
139 factor = 256 - factor;
140
141 uint16_t value = 0xC0;
142 if (channel->stop_bits_ == UART_CONFIG_STOP_BITS_2)
143 value |= 4;
144 switch (channel->parity_) {
146 break;
147 default:
148 value |= 8 | ((channel->parity_ - 1) << 4);
149 break;
150 }
151 value |= channel->data_bits_ - 5;
152 value <<= 8;
153 value |= 0x8C;
154 uint8_t cmd = 0xA1 + channel->index_;
155 if (channel->index_ >= 2)
156 cmd += 0xE;
157 this->control_transfer(USB_VENDOR_DEV | usb_host::USB_DIR_OUT, cmd, value, (factor << 8) | divisor, callback);
158 this->control_transfer(USB_VENDOR_DEV | usb_host::USB_DIR_OUT, cmd + 3, 0x80, 0, callback);
159 }
160 this->start_channels();
161}
162
163std::vector<CdcEps> USBUartTypeCH34X::parse_descriptors(usb_device_handle_t dev_hdl) {
164 auto result = USBUartTypeCdcAcm::parse_descriptors(dev_hdl);
165 // ch34x doesn't use the interrupt endpoint, and we don't have endpoints to spare
166 for (auto &cdc_dev : result) {
167 cdc_dev.interrupt_interface_number = 0xFF;
168 }
169 return result;
170}
171} // namespace esphome::usb_uart
172
173#endif // USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
uint8_t status
Definition bl0942.h:8
ESPDEPRECATED("Use const char* overload instead. Removed in 2026.7.0", "2026.1.0") void defer(const std voi defer)(const char *name, std::function< void()> &&f)
Defer a callback to the next loop() call.
Definition component.h:501
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={})
std::vector< USBUartChannel * > channels_
Definition usb_uart.h:200
std::vector< CdcEps > parse_descriptors(usb_device_handle_t dev_hdl) override
Definition ch34x.cpp:163
void start_channels()
Resets per-channel transfer flags and posts the first bulk IN transfer.
Definition usb_uart.cpp:536
virtual std::vector< CdcEps > parse_descriptors(usb_device_handle_t dev_hdl)
Definition usb_uart.cpp:62
std::function< void(const TransferStatus &)> transfer_cb_t
Definition usb_host.h:83
static void uint32_t