summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhathach <[email protected]>2024-01-19 01:06:24 +0700
committerhathach <[email protected]>2024-01-19 01:06:24 +0700
commit30eb35f17f1baa4698da0cd45dbc3dff8906d7c0 (patch)
treef6eba82bbee847cd73979565a8800ca953f49079
parent3968a0fff87c794266284af7901e334d122e894c (diff)
- revert the use of serial init for set line coding
- update ch34x_get_lcr and ch34x_get_factor_divisor
-rw-r--r--src/class/cdc/cdc_host.c146
-rw-r--r--src/class/cdc/serial/ch34x.h6
2 files changed, 87 insertions, 65 deletions
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index 45a03a31d..150ac21f2 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -1179,7 +1179,7 @@ static void cp210x_process_config(tuh_xfer_t* xfer) {
enum {
CONFIG_CH34X_READ_VERSION = 0,
- CONFIG_CH34X_SET_LINE_CODING,
+ CONFIG_CH34X_SERIAL_INIT,
CONFIG_CH34X_SPECIAL_REG_WRITE,
CONFIG_CH34X_FLOW_CONTROL,
CONFIG_CH34X_MODEM_CONTROL,
@@ -1231,7 +1231,9 @@ static bool ch34x_set_request(cdch_interface_t* p_cdc, uint8_t direction, uint8_
if (buffer && length > 0) {
enum_buf = usbh_get_enum_buf();
- tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length);
+ if (direction == TUSB_DIR_OUT) {
+ tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length);
+ }
}
tuh_xfer_t xfer = {
@@ -1257,8 +1259,8 @@ static inline bool ch34x_control_in(cdch_interface_t* p_cdc, uint8_t request, ui
complete_cb, user_data);
}
-static bool ch34x_write_reg(cdch_interface_t* p_cdc, uint16_t reg, uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- return ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, reg, value, complete_cb, user_data);
+static bool ch34x_write_reg(cdch_interface_t* p_cdc, uint16_t reg, uint16_t reg_value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, reg, reg_value, complete_cb, user_data);
}
//static bool ch34x_read_reg_request ( cdch_interface_t* p_cdc, uint16_t reg,
@@ -1292,10 +1294,11 @@ static void ch34x_control_complete(tuh_xfer_t* xfer) {
case CH34X_REQ_WRITE_REG:
// register write request
switch (value) {
- case 0x1312:
+ case CH34X_REG16_DIVISOR_PRESCALER:
// baudrate write
p_cdc->line_coding.bit_rate = p_cdc->baudrate_requested;
break;
+
default:
TU_ASSERT(false,); // unexpected register write
break;
@@ -1309,6 +1312,7 @@ static void ch34x_control_complete(tuh_xfer_t* xfer) {
} else {
p_cdc->line_state &= (uint8_t ) ~CDC_CONTROL_LINE_STATE_RTS;
}
+
if (~value & CH34X_BIT_DTR) {
p_cdc->line_state |= CDC_CONTROL_LINE_STATE_DTR;
} else {
@@ -1377,8 +1381,8 @@ static void ch34x_control_complete(tuh_xfer_t* xfer) {
}
}
-// calc baudrate factor and divisor
-static bool ch34x_get_factor_divisor(uint32_t baval, uint8_t* factor, uint8_t* divisor) {
+// calculate divisor and prescaler for baudrate, return it as 16-bit combined value
+static uint16_t ch34x_get_divisor_prescaler(uint32_t baval) {
uint8_t a;
uint8_t b;
uint32_t c;
@@ -1410,7 +1414,7 @@ static bool ch34x_get_factor_divisor(uint32_t baval, uint8_t* factor, uint8_t* d
}
a = (unsigned char) (c / baval);
if (a == 0 || a == 0xFF) {
- return false;
+ return 0;
}
if ((c / a - baval) > (baval - c / (a + 1))) {
a++;
@@ -1418,74 +1422,75 @@ static bool ch34x_get_factor_divisor(uint32_t baval, uint8_t* factor, uint8_t* d
a = (uint8_t) (256 - a);
break;
}
- *factor = a;
- *divisor = b;
- return true;
+ // reg divisor = a, reg prescaler = b
+ // According to linux code we need to set bit 7 of UCHCOM_REG_BPS_PRE,
+ // otherwise the chip will buffer data.
+ return (uint16_t) (a << 8 | 0x80 | b);
}
-// calc lcr register value (data bits, parity, stop bits)
-static bool ch34x_get_lcr(cdc_line_coding_t const* line_coding, uint8_t *lcr) {
- *lcr = CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX;
- switch (line_coding->data_bits) {
- case 5:
- *lcr |= CH34X_LCR_CS5;
- break;
- case 6:
- *lcr |= CH34X_LCR_CS6;
- break;
- case 7:
- *lcr |= CH34X_LCR_CS7;
+static inline uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits) {
+ uint8_t lcr = CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX;
+ TU_VERIFY(data_bits >= 5, 0);
+ lcr |= (uint8_t) (data_bits - 5);
+
+ if (parity) {
+ lcr |= CH34X_LCR_ENABLE_PAR;
+ }
+ switch(parity) {
+ case CDC_LINE_CODING_PARITY_EVEN:
+ lcr |= CH34X_LCR_PAR_EVEN;
break;
- case 8:
- *lcr |= CH34X_LCR_CS8;
+
+ case CDC_LINE_CODING_PARITY_MARK:
+ lcr |= CH34X_LCR_MARK_SPACE;
break;
- default:
- TU_ASSERT (false); // data_bits not supported
+
+ case CDC_LINE_CODING_PARITY_SPACE:
+ lcr |= CH34X_LCR_MARK_SPACE | CH34X_LCR_PAR_EVEN;
break;
+
+ default: break;
}
- TU_ASSERT (line_coding->parity == CDC_LINE_CODING_PARITY_NONE || // supported parities
- line_coding->parity == CDC_LINE_CODING_PARITY_ODD || line_coding->parity == CDC_LINE_CODING_PARITY_EVEN ||
- line_coding->parity == CDC_LINE_CODING_PARITY_MARK || line_coding->parity == CDC_LINE_CODING_PARITY_SPACE);
- if (line_coding->parity != CDC_LINE_CODING_PARITY_NONE) {
- *lcr |= CH34X_LCR_ENABLE_PAR;
- if (line_coding->parity == CDC_LINE_CODING_PARITY_EVEN || line_coding->parity == CDC_LINE_CODING_PARITY_SPACE) {
- *lcr |= CH34X_LCR_PAR_EVEN;
- }
- if (line_coding->parity == CDC_LINE_CODING_PARITY_MARK || line_coding->parity == CDC_LINE_CODING_PARITY_SPACE) {
- *lcr |= CH34X_LCR_MARK_SPACE;
- }
- }
- TU_ASSERT (line_coding->stop_bits == CDC_LINE_CODING_STOP_BITS_1 ||
- line_coding->stop_bits == CDC_LINE_CODING_STOP_BITS_2); // not supported 1.5 stop bits
- if (line_coding->stop_bits == CDC_LINE_CODING_STOP_BITS_2) {
- *lcr |= CH34X_LCR_STOP_BITS_2;
+
+ // 1.5 stop bits not supported
+ TU_VERIFY(stop_bits != CDC_LINE_CODING_STOP_BITS_1_5, 0);
+ if (stop_bits == CDC_LINE_CODING_STOP_BITS_2) {
+ lcr |= CH34X_LCR_STOP_BITS_2;
}
- return true;
+ return lcr;
}
-static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding,
- tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- p_cdc->baudrate_requested = line_coding->bit_rate;
- p_cdc->user_control_cb = complete_cb;
- uint8_t factor, divisor, lcr;
- TU_ASSERT (ch34x_get_factor_divisor(line_coding->bit_rate, &factor, &divisor));
- TU_ASSERT (ch34x_get_lcr(line_coding, &lcr));
- TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_SERIAL_INIT, (uint16_t) (lcr << 8 | 0x9c), (uint16_t) (factor << 8 | 0x80 | divisor),
- complete_cb ? ch34x_control_complete : NULL, user_data));
- return true;
-}
+//static bool ch34x_set_line_data(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+// tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
p_cdc->baudrate_requested = baudrate;
p_cdc->user_control_cb = complete_cb;
- uint8_t factor, divisor;
- TU_ASSERT (ch34x_get_factor_divisor(baudrate, &factor, &divisor));
- uint16_t const value = (uint16_t ) (factor << 8 | 0x80 | divisor);
- TU_ASSERT (ch34x_write_reg(p_cdc, 0x1312, value,
- complete_cb ? ch34x_control_complete : NULL, user_data));
+ uint16_t const div_ps = ch34x_get_divisor_prescaler(baudrate);
+ TU_VERIFY(div_ps != 0);
+ TU_ASSERT(ch34x_write_reg(p_cdc, CH34X_REG16_DIVISOR_PRESCALER, div_ps,
+ complete_cb ? ch34x_control_complete : NULL, user_data));
+
+ return true;
+}
+
+static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+// p_cdc->baudrate_requested = line_coding->bit_rate;
+// p_cdc->user_control_cb = complete_cb;
+// uint8_t factor, divisor, lcr;
+// TU_ASSERT (ch34x_get_factor_divisor(line_coding->bit_rate, &factor, &divisor));
+// TU_ASSERT (ch34x_get_lcr(line_coding, &lcr));
+// TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_SERIAL_INIT, (uint16_t) (lcr << 8 | 0x9c), (uint16_t) (factor << 8 | 0x80 | divisor),
+// complete_cb ? ch34x_control_complete : NULL, user_data));
+ (void) p_cdc;
+ (void) line_coding;
+ (void) complete_cb;
+ (void) user_data;
+
return true;
}
@@ -1515,35 +1520,46 @@ static void ch34x_process_config(tuh_xfer_t* xfer) {
switch (state) {
case CONFIG_CH34X_READ_VERSION:
- // version read request
TU_LOG_DRV("[%u] CDCh CH34x attempt to read Chip Version\r\n", p_cdc->daddr);
- TU_ASSERT (ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, buffer, 2, ch34x_process_config, CONFIG_CH34X_SET_LINE_CODING),);
+ TU_ASSERT (ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, buffer, 2, ch34x_process_config, CONFIG_CH34X_SERIAL_INIT),);
break;
- case CONFIG_CH34X_SET_LINE_CODING:
+
+ case CONFIG_CH34X_SERIAL_INIT: {
// handle version read data, set CH34x line coding (incl. baudrate)
uint8_t version = xfer->buffer[0];
TU_LOG_DRV("[%u] CDCh CH34x Chip Version = %02x\r\n", p_cdc->daddr, version);
// only versions >= 0x30 are tested, below 0x30 seems having other programming, see drivers from WCH vendor, Linux kernel and FreeBSD
TU_ASSERT (version >= 0x30,);
- TU_ASSERT (ch34x_set_line_coding(p_cdc, &line_coding, ch34x_process_config, CONFIG_CH34X_SPECIAL_REG_WRITE),);
+ uint16_t const div_ps = ch34x_get_divisor_prescaler(line_coding.bit_rate);
+ TU_ASSERT(div_ps != 0, );
+
+ uint8_t const lcr = ch34x_get_lcr(line_coding.stop_bits, line_coding.parity, line_coding.data_bits);
+ TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_SERIAL_INIT, tu_u16(lcr, 0x9c), div_ps,
+ ch34x_process_config, CONFIG_CH34X_SPECIAL_REG_WRITE),);
break;
+ }
+
case CONFIG_CH34X_SPECIAL_REG_WRITE:
// do special reg write, purpose unknown, overtaken from WCH driver
TU_ASSERT (ch34x_write_reg(p_cdc, 0x0f2c, 0x0007, ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL),);
break;
+
case CONFIG_CH34X_FLOW_CONTROL:
// no hardware flow control
TU_ASSERT (ch34x_write_reg(p_cdc, 0x2727, 0x0000, ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL),);
break;
+
case CONFIG_CH34X_MODEM_CONTROL:
// !always! set modem controls RTS/DTR (CH34x has no reset state after CH34X_REQ_SERIAL_INIT)
TU_ASSERT (ch34x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ch34x_process_config, CONFIG_CH34X_COMPLETE),);
break;
+
case CONFIG_CH34X_COMPLETE:
set_config_complete(p_cdc, idx, itf_num);
break;
+
default:
- TU_ASSERT (false,); // something gone wrong, should never reached
+ TU_ASSERT (false,);
break;
}
}
diff --git a/src/class/cdc/serial/ch34x.h b/src/class/cdc/serial/ch34x.h
index b4ed7420c..3f78a3007 100644
--- a/src/class/cdc/serial/ch34x.h
+++ b/src/class/cdc/serial/ch34x.h
@@ -59,6 +59,12 @@
#define CH34X_REG_MCR_MSR2 0x07
#define CH34X_NBREAK_BITS 0x01
+#define CH341_REG_0x0F 0x0F // undocumented register
+#define CH341_REG_0x2C 0x2C // undocumented register
+#define CH341_REG_0x27 0x27 // hardware flow control (cts/rts)
+
+#define CH34X_REG16_DIVISOR_PRESCALER TU_U16(CH34X_REG_DIVISOR, CH34X_REG_PRESCALER)
+
// modem control bits
#define CH34X_BIT_RTS ( 1 << 6 )
#define CH34X_BIT_DTR ( 1 << 5 )