1#if defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32P4)
3#include "usb/usb_host.h"
11using namespace bytebuffer;
26static int ftdi_to_clkbits_am(
int baudrate,
uint32_t *encoded_divisor) {
27 static const char FRAC_CODE[8] = {0, 3, 2, 4, 1, 5, 6, 7};
28 static const char AM_ADJUST_UP[8] = {0, 0, 0, 1, 0, 3, 2, 1};
29 static const char AM_ADJUST_DN[8] = {0, 0, 0, 1, 0, 1, 2, 3};
30 int divisor, best_divisor, best_baud, best_baud_diff;
32 divisor = 24000000 / baudrate;
34 divisor -= AM_ADJUST_DN[divisor & 7];
39 for (i = 0; i < 2; i++) {
40 int try_divisor = divisor + i;
44 if (try_divisor <= 8) {
46 }
else if (divisor < 16) {
49 try_divisor += AM_ADJUST_UP[try_divisor & 7];
50 if (try_divisor > 0x1FFF8) {
52 try_divisor = 0x1FFF8;
55 baud_estimate = (24000000 + (try_divisor / 2)) / try_divisor;
56 if (baud_estimate < baudrate) {
57 baud_diff = baudrate - baud_estimate;
59 baud_diff = baud_estimate - baudrate;
61 if (i == 0 || baud_diff < best_baud_diff) {
62 best_divisor = try_divisor;
63 best_baud = baud_estimate;
64 best_baud_diff = baud_diff;
70 *encoded_divisor = (best_divisor >> 3) | (FRAC_CODE[best_divisor & 7] << 14);
71 if (*encoded_divisor == 1) {
73 }
else if (*encoded_divisor == 0x4001) {
79static int ftdi_to_clkbits(
int baudrate,
unsigned int clk,
int clk_div,
uint32_t *encoded_divisor) {
80 static const char FRAC_CODE[8] = {0, 3, 2, 4, 1, 5, 6, 7};
82 int divisor, best_divisor;
83 if (baudrate >= clk / clk_div) {
85 best_baud = clk / clk_div;
86 }
else if (baudrate >= clk / (clk_div + clk_div / 2)) {
88 best_baud = clk / (clk_div + clk_div / 2);
89 }
else if (baudrate >= clk / (2 * clk_div)) {
91 best_baud = clk / (2 * clk_div);
93 divisor = clk * 16 / clk_div / baudrate;
95 best_divisor = divisor / 2 + 1;
97 best_divisor = divisor / 2;
99 if (best_divisor > 0x20000)
100 best_divisor = 0x1ffff;
101 best_baud = clk * 16 / clk_div / best_divisor;
103 best_baud = best_baud / 2 + 1;
105 best_baud = best_baud / 2;
107 *encoded_divisor = (best_divisor >> 3) | (FRAC_CODE[best_divisor & 0x7] << 14);
112static int ftdi_convert_baudrate(
int baudrate, uint8_t chip_type, uint8_t channel_index, uint16_t *value,
121 static constexpr uint32_t H_CLK = 120000000;
122 static constexpr uint32_t C_CLK = 48000000;
124 if (baudrate * 10 > H_CLK / 0x3fff) {
125 best_baud = ftdi_to_clkbits(baudrate, H_CLK, 10, &encoded_divisor);
126 encoded_divisor |= 0x20000;
128 best_baud = ftdi_to_clkbits(baudrate, C_CLK, 16, &encoded_divisor);
131 best_baud = ftdi_to_clkbits(baudrate, C_CLK, 16, &encoded_divisor);
133 best_baud = ftdi_to_clkbits_am(baudrate, &encoded_divisor);
136 *value = (uint16_t) (encoded_divisor & 0xFFFF);
138 *index = (uint16_t) (encoded_divisor >> 8);
140 *index |= (channel_index + 1);
142 *index = (uint16_t) (encoded_divisor >> 16);
148static optional<CdcEps> get_uart(
const usb_config_desc_t *config_desc, uint8_t intf_idx) {
149 int conf_offset, ep_offset;
152 const auto *intf_desc = usb_parse_interface_descriptor(config_desc, intf_idx, 0, &conf_offset);
154 ESP_LOGD(TAG,
"usb_parse_interface_descriptor failed for intf_idx=%d (end of interfaces)", intf_idx);
158 "intf_desc [idx=%d]: bInterfaceClass=%02X, bInterfaceSubClass=%02X, bInterfaceProtocol=%02X, "
159 "bNumEndpoints=%d, bInterfaceNumber=%d",
160 intf_idx, intf_desc->bInterfaceClass, intf_desc->bInterfaceSubClass, intf_desc->bInterfaceProtocol,
161 intf_desc->bNumEndpoints, intf_desc->bInterfaceNumber);
163 std::vector<const usb_ep_desc_t *> endpoints;
164 for (uint8_t i = 0; i != intf_desc->bNumEndpoints; i++) {
165 ep_offset = conf_offset;
166 const auto *ep = usb_parse_endpoint_descriptor_by_index(intf_desc, i, config_desc->wTotalLength, &ep_offset);
168 ESP_LOGE(TAG,
"Ran out of endpoints at %d before finding all %d endpoints", i, intf_desc->bNumEndpoints);
171 ESP_LOGD(TAG,
"ep: bEndpointAddress=%02X, bmAttributes=%02X", ep->bEndpointAddress, ep->bmAttributes);
173 if (ep->bmAttributes != 0x2) {
174 ESP_LOGD(TAG,
"Skipping non-bulk endpoint: %02X", ep->bEndpointAddress);
177 endpoints.push_back(ep);
180 const usb_ep_desc_t *ep1 =
nullptr;
181 const usb_ep_desc_t *ep2 =
nullptr;
182 for (
const auto *ep : endpoints) {
183 if (ep1 ==
nullptr) {
185 }
else if (ep2 ==
nullptr) {
191 if (ep1 ==
nullptr || ep2 ==
nullptr) {
192 ESP_LOGD(TAG,
"Interface %d has %zu endpoints (need 2 bulk endpoints)", intf_idx, endpoints.size());
196 ESP_LOGD(TAG,
"Interface %d: ep1=0x%02X, ep2=0x%02X", intf_idx, ep1->bEndpointAddress, ep2->bEndpointAddress);
198 if (ep1->bEndpointAddress & usb_host::USB_DIR_IN) {
201 ESP_LOGD(TAG,
"ep1 is IN (RX): ep1=0x%02X (in_ep), ep2=0x%02X (out_ep)", ep1->bEndpointAddress,
202 ep2->bEndpointAddress);
206 ESP_LOGD(TAG,
"ep1 is OUT (TX): ep1=0x%02X (out_ep), ep2=0x%02X (in_ep)", ep1->bEndpointAddress,
207 ep2->bEndpointAddress);
210 eps.bulk_interface_number = intf_desc->bInterfaceNumber;
215 const usb_config_desc_t *config_desc;
216 const usb_device_desc_t *device_desc;
217 std::vector<CdcEps> cdc_devs{};
218 std::string type_string;
220 if (usb_host_get_device_descriptor(dev_hdl, &device_desc) != ESP_OK) {
221 ESP_LOGE(TAG,
"get_device_descriptor failed");
224 if (usb_host_get_active_config_descriptor(dev_hdl, &config_desc) != ESP_OK) {
225 ESP_LOGE(TAG,
"get_active_config_descriptor failed");
228 if (device_desc->bcdDevice == 0x400 || (device_desc->bcdDevice == 0x200 && device_desc->iSerialNumber == 0)) {
230 type_string =
"BM type chip";
231 }
else if (device_desc->bcdDevice == 0x200) {
233 type_string =
"AM type chip";
234 }
else if (device_desc->bcdDevice == 0x500) {
236 type_string =
"2232C chip";
237 }
else if (device_desc->bcdDevice == 0x600) {
239 type_string =
"type R chip";
240 }
else if (device_desc->bcdDevice == 0x700) {
242 type_string =
"2232H chip";
243 }
else if (device_desc->bcdDevice == 0x800) {
245 type_string =
"4232H chip";
246 }
else if (device_desc->bcdDevice == 0x900) {
248 type_string =
"232H type chip";
249 }
else if (device_desc->bcdDevice == 0x1000) {
251 type_string =
"230x chip";
254 ESP_LOGD(TAG,
"Found FTDI %s based device", type_string.c_str());
255 for (
size_t intf_idx = 0; intf_idx < this->
channels_.size(); intf_idx++) {
256 if (
auto eps = get_uart(config_desc,
static_cast<uint8_t
>(intf_idx))) {
257 cdc_devs.push_back(*eps);
258 ESP_LOGD(TAG,
"Found CDC interface at USB interface index %zu", intf_idx);
267 ESP_LOGE(TAG,
"Reset failed, status=%s", esp_err_to_name(
status.error_code));
270 ESP_LOGD(TAG,
"Reset successful, setting baudrate...");
274 bool ok = this->
control_transfer(USB_VENDOR_DEV | usb_host::USB_DIR_OUT, 0x00, 0x00,
277 ESP_LOGE(TAG,
"Reset control_transfer submit failed");
287 ESP_LOGE(TAG,
"Set baudrate failed, status=%s", esp_err_to_name(
status.error_code));
290 ESP_LOGD(TAG,
"Baudrate %d set, setting line properties...", channel->
baud_rate_);
297 uint16_t value, ftdi_index;
298 ftdi_convert_baudrate(baudrate, this->
chip_type_, channel->
index_, &value, &ftdi_index);
299 ESP_LOGD(TAG,
"Baudrate: %d, value=0x%04X, ftdi_index=0x%04X", baudrate, value, ftdi_index);
301 bool ok = this->
control_transfer(USB_VENDOR_DEV | usb_host::USB_DIR_OUT, 0x03, value, usb_index, callback);
303 ESP_LOGE(TAG,
"Set baudrate control_transfer submit failed");
313 ESP_LOGE(TAG,
"Set line properties failed, status=%s", esp_err_to_name(
status.error_code));
317 ESP_LOGD(TAG,
"Line properties set, setting modem control...");
325 value |= (0x00 << 8);
328 value |= (0x01 << 8);
331 value |= (0x02 << 8);
334 value |= (0x03 << 8);
337 value |= (0x04 << 8);
343 value |= (0x00 << 11);
346 value |= (0x01 << 11);
349 value |= (0x02 << 11);
353 value |= (0x00 << 14);
355 bool ok = this->
control_transfer(USB_VENDOR_DEV | usb_host::USB_DIR_OUT, 0x04, value,
358 ESP_LOGE(TAG,
"Set line properties control_transfer submit failed");
368 ESP_LOGE(TAG,
"Set modem control failed, status=%s", esp_err_to_name(
status.error_code));
372 ESP_LOGD(TAG,
"Modem control set for channel %d, starting input...", channel->
index_);
375 uint8_t next_index = channel->
index_ + 1;
376 if (next_index < this->
channels_.size()) {
378 ESP_LOGD(TAG,
"Configuring next channel %d", next_channel->
index_);
379 this->
reset_(next_channel);
382 ESP_LOGI(TAG,
"All channels configured");
386 bool ok = this->
control_transfer(USB_VENDOR_DEV | usb_host::USB_DIR_OUT, 0x01, 0x0000,
389 ESP_LOGE(TAG,
"Set modem control control_transfer submit failed");
404 ESP_LOGE(TAG,
"RX Transfer failed, status=%s", esp_err_to_name(
status.error_code));
409 size_t uart_data_len = (
status.data_len > 2) ? (
status.data_len - 2) : 0;
411 if (uart_data_len > 0) {
412 ESP_LOGV(TAG,
"RX callback: Received %zu bytes, channel=%d", uart_data_len, channel->
index_);
418#ifdef USE_UART_DEBUGGER
423 std::vector<uint8_t>(
status.data + 2,
status.data + 2 + uart_data_len),
',',
429 ESP_LOGVV(TAG,
"RX: Status packet, modem=0x%02X line=0x%02X, ch=%d",
status.data[0],
status.data[1],
441 this->
transfer_in(ep->bEndpointAddress, callback, ep->wMaxPacketSize);
445 if (!this->
channels_.empty() && this->channels_[0]->initialised_.load()) {
450 if (!channel->initialised_.load())
452 channel->input_started_.store(
false);
453 channel->output_started_.store(
false);
static void log_hex(UARTDirection direction, std::vector< uint8_t > bytes, uint8_t separator, StringRef prefix=StringRef())
Log the bytes as hex values, separated by the provided separator character.
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={})
bool transfer_in(uint8_t ep_address, const transfer_cb_t &callback, uint16_t length)
Performs a transfer input operation.
size_t get_free_space() const
std::atomic< bool > input_started_
std::atomic< bool > initialised_
UARTParityOptions parity_
std::vector< USBUartChannel * > channels_
void start_input(USBUartChannel *channel)
void enable_channels() override
int set_baudrate_(USBUartChannel *channel, uint32_t baudrate=0)
int reset_(USBUartChannel *channel)
std::vector< CdcEps > parse_descriptors(usb_device_handle_t dev_hdl) override
int set_dtr_rts_(USBUartChannel *channel)
int set_line_properties_(USBUartChannel *channel)
std::function< void(const TransferStatus &)> transfer_cb_t
@ UART_CONFIG_STOP_BITS_1_5
@ UART_CONFIG_STOP_BITS_1
@ UART_CONFIG_STOP_BITS_2
@ UART_CONFIG_PARITY_SPACE
@ UART_CONFIG_PARITY_MARK
@ UART_CONFIG_PARITY_NONE
@ UART_CONFIG_PARITY_EVEN
uint8_t bulk_interface_number
const usb_ep_desc_t * in_ep