summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhathach <[email protected]>2024-01-19 12:14:45 +0700
committerhathach <[email protected]>2024-01-19 12:14:45 +0700
commitc568a6793e99dde311802d4b4cb0633d2d6f5830 (patch)
tree3cddeeab1a0f036247d3028bc3a9be9373f5fe38 /src
parent30eb35f17f1baa4698da0cd45dbc3dff8906d7c0 (diff)
- add requested line coding to make update data format easier
- change ch34x_xfer_get_itf_num() to simply 0
Diffstat (limited to 'src')
-rw-r--r--src/class/cdc/cdc_host.c295
1 files changed, 136 insertions, 159 deletions
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index 150ac21f2..3843a8edc 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -60,6 +60,11 @@ typedef struct {
uint8_t line_state; // DTR (bit0), RTS (bit1)
TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding; // Baudrate, stop bits, parity, data width
+ #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X
+ cdc_line_coding_t requested_line_coding;
+ // 1 byte padding
+ #endif
+
tuh_xfer_cb_t user_control_cb;
struct {
@@ -72,10 +77,6 @@ typedef struct {
uint8_t rx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE];
CFG_TUH_MEM_ALIGN uint8_t rx_ep_buf[CFG_TUH_CDC_TX_EPSIZE];
} stream;
-
-#if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CH34X
- uint32_t baudrate_requested;
-#endif
} cdch_interface_t;
CFG_TUH_MEM_SECTION
@@ -429,7 +430,7 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer) {
case FTDI_SIO_SET_BAUD_RATE:
// convert from divisor to baudrate is not supported
- p_cdc->line_coding.bit_rate = p_cdc->baudrate_requested;
+ p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
break;
default: break;
@@ -968,7 +969,7 @@ static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tu
TU_LOG_DRV("CDC FTDI Set BaudRate = %lu, divisor = 0x%04x\r\n", baudrate, divisor);
p_cdc->user_control_cb = complete_cb;
- p_cdc->baudrate_requested = baudrate;
+ p_cdc->requested_line_coding.bit_rate = baudrate;
TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor,
complete_cb ? cdch_internal_control_complete : NULL, user_data));
@@ -1186,6 +1187,9 @@ enum {
CONFIG_CH34X_COMPLETE
};
+static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits);
+static uint16_t ch34x_get_divisor_prescaler(uint32_t baval);
+
static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) {
// CH34x Interface includes 1 vendor interface + 2 bulk + 1 interrupt endpoints
TU_VERIFY (itf_desc->bNumEndpoints == 3);
@@ -1269,20 +1273,10 @@ static bool ch34x_write_reg(cdch_interface_t* p_cdc, uint16_t reg, uint16_t reg_
// return ch34x_control_in ( p_cdc, CH34X_REQ_READ_REG, reg, 0, buffer, buffersize, complete_cb, user_data );
//}
-uint8_t ch34x_xfer_get_itf_num(tuh_xfer_t* xfer) {
-// CH34x needs a special handling to get bInterfaceNumber, because wIndex is used for other purposes and not for bInterfaceNumber
-// CH340 and CH341 derivates have always only one interface, so it's OK to check only daddr
- for (uint8_t i = 0; i < CFG_TUH_CDC; i++) {
- const cdch_interface_t* p_cdc = &cdch_data[i];
- if (p_cdc->daddr == xfer->daddr) return p_cdc->bInterfaceNumber;
- }
-
- return INTERFACE_INVALID_NUMBER;
-}
-
// internal control complete to update state such as line state, encoding
static void ch34x_control_complete(tuh_xfer_t* xfer) {
- uint8_t const itf_num = ch34x_xfer_get_itf_num(xfer);
+ // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber
+ uint8_t const itf_num = 0;
uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
cdch_interface_t* p_cdc = get_itf(idx);
uint16_t value = tu_le16toh (xfer->setup->wValue);
@@ -1296,7 +1290,14 @@ static void ch34x_control_complete(tuh_xfer_t* xfer) {
switch (value) {
case CH34X_REG16_DIVISOR_PRESCALER:
// baudrate write
- p_cdc->line_coding.bit_rate = p_cdc->baudrate_requested;
+ p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
+ break;
+
+ case CH32X_REG16_LCR2_LCR:
+ // data format write
+ p_cdc->line_coding.stop_bits = p_cdc->requested_line_coding.stop_bits;
+ p_cdc->line_coding.parity = p_cdc->requested_line_coding.parity;
+ p_cdc->line_coding.data_bits = p_cdc->requested_line_coding.data_bits;
break;
default:
@@ -1305,68 +1306,26 @@ static void ch34x_control_complete(tuh_xfer_t* xfer) {
}
break;
- case CH34X_REQ_MODEM_CTRL:
- // set modem controls RTS/DTR request
- if (~value & CH34X_BIT_RTS) {
+ case CH34X_REQ_MODEM_CTRL: {
+ // set modem controls RTS/DTR request. Note: signals are inverted
+ uint16_t const modem_signal = ~value;
+ if (modem_signal & CH34X_BIT_RTS) {
p_cdc->line_state |= CDC_CONTROL_LINE_STATE_RTS;
} else {
- p_cdc->line_state &= (uint8_t ) ~CDC_CONTROL_LINE_STATE_RTS;
+ p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_RTS;
}
- if (~value & CH34X_BIT_DTR) {
+ if (modem_signal & CH34X_BIT_DTR) {
p_cdc->line_state |= CDC_CONTROL_LINE_STATE_DTR;
} else {
- p_cdc->line_state &= (uint8_t ) ~CDC_CONTROL_LINE_STATE_DTR;
+ p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_DTR;
}
break;
+ }
case CH34X_REQ_SERIAL_INIT:
// serial init request (set line coding incl. baudrate)
- p_cdc->line_coding.bit_rate = p_cdc->baudrate_requested;
- uint8_t lcr = (uint8_t) (value >> 8);
- TU_ASSERT (lcr & CH34X_LCR_ENABLE_RX && lcr & CH34X_LCR_ENABLE_TX,); // both have to be enabled
- switch (lcr & CH34X_LCR_CS_MASK) {
- case CH34X_LCR_CS5:
- p_cdc->line_coding.data_bits = 5;
- break;
- case CH34X_LCR_CS6:
- p_cdc->line_coding.data_bits = 6;
- break;
- case CH34X_LCR_CS7:
- p_cdc->line_coding.data_bits = 7;
- break;
- case CH34X_LCR_CS8:
- p_cdc->line_coding.data_bits = 8;
- break;
- default:
- TU_ASSERT (false,); // unexpected data_bits lcr
- break;
- }
- if (lcr & CH34X_LCR_STOP_BITS_2) {
- p_cdc->line_coding.stop_bits = CDC_LINE_CODING_STOP_BITS_2;
- } else {
- p_cdc->line_coding.stop_bits = CDC_LINE_CODING_STOP_BITS_1;
- }
- switch (lcr & CH34X_LCR_PAR_MASK) {
- case 0:
- p_cdc->line_coding.parity = CDC_LINE_CODING_PARITY_NONE;
- break;
- case CH34X_LCR_ENABLE_PAR:
- p_cdc->line_coding.parity = CDC_LINE_CODING_PARITY_ODD;
- break;
- case CH34X_LCR_ENABLE_PAR | CH34X_LCR_PAR_EVEN:
- p_cdc->line_coding.parity = CDC_LINE_CODING_PARITY_EVEN;
- break;
- case CH34X_LCR_ENABLE_PAR | CH34X_LCR_MARK_SPACE:
- p_cdc->line_coding.parity = CDC_LINE_CODING_PARITY_MARK;
- break;
- case CH34X_LCR_ENABLE_PAR | CH34X_LCR_MARK_SPACE | CH34X_LCR_PAR_EVEN:
- p_cdc->line_coding.parity = CDC_LINE_CODING_PARITY_SPACE;
- break;
- default:
- TU_ASSERT (false,); // unexpected parity lcr
- break;
- }
+ p_cdc->line_coding = p_cdc->requested_line_coding;
break;
default:
@@ -1381,93 +1340,17 @@ static void ch34x_control_complete(tuh_xfer_t* xfer) {
}
}
-// 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;
-
- switch (baval) {
- case 921600:
- a = 0xf3;
- b = 7;
- break;
-
- case 307200:
- a = 0xd9;
- b = 7;
- break;
-
- default:
- if (baval > 6000000 / 255) {
- b = 3;
- c = 6000000;
- } else if (baval > 750000 / 255) {
- b = 2;
- c = 750000;
- } else if (baval > 93750 / 255) {
- b = 1;
- c = 93750;
- } else {
- b = 0;
- c = 11719;
- }
- a = (unsigned char) (c / baval);
- if (a == 0 || a == 0xFF) {
- return 0;
- }
- if ((c / a - baval) > (baval - c / (a + 1))) {
- a++;
- }
- a = (uint8_t) (256 - a);
- break;
- }
-
- // 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);
-}
-
-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 CDC_LINE_CODING_PARITY_MARK:
- lcr |= CH34X_LCR_MARK_SPACE;
- break;
-
- case CDC_LINE_CODING_PARITY_SPACE:
- lcr |= CH34X_LCR_MARK_SPACE | CH34X_LCR_PAR_EVEN;
- break;
-
- default: break;
- }
-
- // 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 lcr;
-}
-
-//static bool ch34x_set_line_data(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits,
+//static bool ch34x_set_data_format(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) {
+// uint8_t const lcr = ch34x_get_lcr(stop_bits, parity, data_bits);
+// TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, CH32X_REG16_LCR2_LCR, lcr,
+// complete_cb ? ch34x_control_complete : NULL, user_data));
+// return false;
+//}
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->requested_line_coding.bit_rate = baudrate;
p_cdc->user_control_cb = complete_cb;
uint16_t const div_ps = ch34x_get_divisor_prescaler(baudrate);
TU_VERIFY(div_ps != 0);
@@ -1496,24 +1379,29 @@ static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t con
static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- p_cdc->user_control_cb = complete_cb;
- uint16_t control = 0;
+ uint8_t control = 0;
if (line_state & CDC_CONTROL_LINE_STATE_RTS) {
control |= CH34X_BIT_RTS;
}
if (line_state & CDC_CONTROL_LINE_STATE_DTR) {
control |= CH34X_BIT_DTR;
}
- TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_MODEM_CTRL, (uint8_t) ~control, 0,
+
+ // CH34x signals are inverted
+ control = ~control;
+
+ p_cdc->user_control_cb = complete_cb;
+ TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_MODEM_CTRL, control, 0,
complete_cb ? ch34x_control_complete : NULL, user_data));
return true;
}
static void ch34x_process_config(tuh_xfer_t* xfer) {
- uint8_t const itf_num = ch34x_xfer_get_itf_num(xfer);
- uintptr_t const state = xfer->user_data;
+ // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber
+ uint8_t const itf_num = 0;
uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
cdch_interface_t* p_cdc = get_itf(idx);
+ uintptr_t const state = xfer->user_data;
cdc_line_coding_t const line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X;
uint8_t buffer[2]; // TODO remove
TU_ASSERT (p_cdc,);
@@ -1526,14 +1414,17 @@ static void ch34x_process_config(tuh_xfer_t* xfer) {
case CONFIG_CH34X_SERIAL_INIT: {
// handle version read data, set CH34x line coding (incl. baudrate)
- uint8_t version = xfer->buffer[0];
+ uint8_t const 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,);
+
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);
+
+ // Init CH34x with line coding
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;
@@ -1541,6 +1432,7 @@ static void ch34x_process_config(tuh_xfer_t* xfer) {
case CONFIG_CH34X_SPECIAL_REG_WRITE:
// do special reg write, purpose unknown, overtaken from WCH driver
+ p_cdc->line_coding = line_coding;
TU_ASSERT (ch34x_write_reg(p_cdc, 0x0f2c, 0x0007, ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL),);
break;
@@ -1564,6 +1456,91 @@ static void ch34x_process_config(tuh_xfer_t* xfer) {
}
}
+//------------- CH34x helper -------------//
+
+// 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;
+
+ switch (baval) {
+ case 921600:
+ a = 0xf3;
+ b = 7;
+ break;
+
+ case 307200:
+ a = 0xd9;
+ b = 7;
+ break;
+
+ default:
+ if (baval > 6000000 / 255) {
+ b = 3;
+ c = 6000000;
+ } else if (baval > 750000 / 255) {
+ b = 2;
+ c = 750000;
+ } else if (baval > 93750 / 255) {
+ b = 1;
+ c = 93750;
+ } else {
+ b = 0;
+ c = 11719;
+ }
+ a = (unsigned char) (c / baval);
+ if (a == 0 || a == 0xFF) {
+ return 0;
+ }
+ if ((c / a - baval) > (baval - c / (a + 1))) {
+ a++;
+ }
+ a = (uint8_t) (256 - a);
+ break;
+ }
+
+ // 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);
+}
+
+// calculate lcr value from data coding
+static 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 CDC_LINE_CODING_PARITY_MARK:
+ lcr |= CH34X_LCR_MARK_SPACE;
+ break;
+
+ case CDC_LINE_CODING_PARITY_SPACE:
+ lcr |= CH34X_LCR_MARK_SPACE | CH34X_LCR_PAR_EVEN;
+ break;
+
+ default: break;
+ }
+
+ // 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 lcr;
+}
+
+
#endif // CFG_TUH_CDC_CH34X
#endif