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