summaryrefslogtreecommitdiff
path: root/src/class
diff options
context:
space:
mode:
authorIngHK <[email protected]>2024-02-22 15:42:33 +0100
committerIngHK <[email protected]>2024-02-24 12:58:59 +0100
commit4547737833de0368354cb4823d908c6b250f92df (patch)
treee3af98f6f5f0fe82a3f1dbd203d43aa221176762 /src/class
parent7fef5943eff0d7f1b7122e878cf2caa78bea06aa (diff)
improved CP210x support
Diffstat (limited to 'src/class')
-rw-r--r--src/class/cdc/cdc_host.c135
-rw-r--r--src/class/cdc/serial/cp210x.h63
2 files changed, 156 insertions, 42 deletions
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index f9540efbe..86c010fa7 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -83,7 +83,7 @@ typedef struct {
uint8_t requested_line_state;
tuh_xfer_cb_t user_control_cb;
- #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CH34X
+ #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X
tuh_xfer_cb_t requested_complete_cb;
#endif
@@ -1533,7 +1533,8 @@ static uint8_t ftdi_get_idx(tuh_xfer_t * xfer) {
//------------- Control Request -------------//
-static bool cp210x_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+static bool cp210x_set_request(cdch_interface_t * p_cdc, uint8_t command, uint16_t value,
+ uint8_t * buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request = {
.bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_INTERFACE,
@@ -1542,12 +1543,12 @@ static bool cp210x_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_
},
.bRequest = command,
.wValue = tu_htole16(value),
- .wIndex = p_cdc->bInterfaceNumber,
+ .wIndex = tu_htole16(p_cdc->bInterfaceNumber),
.wLength = tu_htole16(length)
};
// use usbh enum buf since application variable does not live long enough
- uint8_t* enum_buf = NULL;
+ uint8_t * enum_buf = NULL;
if (buffer && length > 0) {
enum_buf = usbh_get_enum_buf();
@@ -1566,18 +1567,52 @@ static bool cp210x_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_
return tuh_control_xfer(&xfer);
}
-static bool cp210x_ifc_enable(cdch_interface_t* p_cdc, uint16_t enabled, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+static inline bool cp210x_ifc_enable(cdch_interface_t * p_cdc, uint16_t enabled,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
return cp210x_set_request(p_cdc, CP210X_IFC_ENABLE, enabled, NULL, 0, complete_cb, user_data);
}
+static bool cp210x_set_baudrate_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ // Check baudrate is supported. It's only a specific list. reference: datasheets and AN205 "CP210x Baud Rate Support"
+ uint32_t const supported_baudrates_list[] = CP210X_SUPPORTED_BAUDRATES_LIST;
+ uint8_t i;
+ for ( i=0; supported_baudrates_list[i]; i++ ){
+ if (p_cdc->requested_line_coding.bit_rate == supported_baudrates_list[i]) {
+ break;
+ }
+ }
+ TU_VERIFY(supported_baudrates_list[i]);
+ uint32_t baud_le = tu_htole32(p_cdc->requested_line_coding.bit_rate);
+
+ return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4, complete_cb, user_data);
+}
+
+static bool cp210x_set_line_ctl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ TU_VERIFY(p_cdc->requested_line_coding.data_bits >= 5 && p_cdc->requested_line_coding.data_bits <= 9, 0);
+ uint16_t lcr = (uint16_t) (
+ ((uint32_t) p_cdc->requested_line_coding.data_bits & 0xf) << 8 | // data bit quantity is stored in bits 8-11
+ ((uint32_t) p_cdc->requested_line_coding.parity & 0xf) << 4 | // parity is stored in bits 4-7, same coding
+ ((uint32_t) p_cdc->requested_line_coding.stop_bits & 0xf)); // parity is stored in bits 0-3, same coding
+
+ return cp210x_set_request(p_cdc, CP210X_SET_LINE_CTL, lcr, NULL, 0, complete_cb, user_data);
+}
+
+static inline bool cp210x_set_mhs(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ // CP210x has the same bit coding
+ return cp210x_set_request(p_cdc, CP210X_SET_MHS,
+ (uint16_t) ((uint32_t) CP210X_CONTROL_WRITE_DTR |
+ (uint32_t) CP210X_CONTROL_WRITE_RTS | p_cdc->requested_line_state),
+ NULL, 0, complete_cb, user_data);
+}
+
//------------- Driver API -------------//
// internal control complete to update state such as line state, encoding
static void cp210x_internal_control_complete(tuh_xfer_t * xfer) {
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
- cdch_interface_t* p_cdc = get_itf(idx);
- TU_ASSERT(p_cdc, );
+ cdch_interface_t * p_cdc = get_itf(idx);
+ TU_ASSERT(p_cdc,);
bool const success = (xfer->result == XFER_RESULT_SUCCESS);
TU_LOG_P_CDC("control complete success = %u", success);
@@ -1587,6 +1622,12 @@ static void cp210x_internal_control_complete(tuh_xfer_t * xfer) {
p_cdc->line_state = p_cdc->requested_line_state;
break;
+ case CP210X_SET_LINE_CTL:
+ 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;
+
case CP210X_SET_BAUDRATE:
p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
break;
@@ -1601,46 +1642,55 @@ static void cp210x_internal_control_complete(tuh_xfer_t * xfer) {
}
}
-static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- uint32_t baud_le = tu_htole32(p_cdc->requested_line_coding.bit_rate);
+static bool cp210x_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
p_cdc->user_control_cb = complete_cb;
- return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4,
- complete_cb ? cp210x_internal_control_complete : NULL, user_data);
+ TU_ASSERT(cp210x_set_baudrate_request(p_cdc, complete_cb ? cp210x_internal_control_complete : NULL, user_data));
+
+ return true;
}
-static bool cp210x_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- (void) p_cdc;
- (void) complete_cb;
- (void) user_data;
- // TODO not implemented yet
- return false;
+static bool cp210x_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ p_cdc->user_control_cb = complete_cb;
+ TU_ASSERT(cp210x_set_line_ctl(p_cdc, complete_cb ? cp210x_internal_control_complete : NULL, user_data));
+
+ return true;
}
-static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
- // TODO implement later
- (void) p_cdc;
- (void) complete_cb;
- (void) user_data;
- return false;
+static void cp210x_set_line_coding_stage1_complete(tuh_xfer_t * xfer) {
+ uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+ set_line_coding_stage1_complete(xfer, itf_num,
+ cp210x_set_line_ctl, // control request function to set data format
+ cp210x_internal_control_complete); // control complete function to be called after request
+}
+
+// 2 stages: set baudrate (stage1) + set data format (stage2)
+static bool cp210x_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+ return set_line_coding_sequence(p_cdc,
+ cp210x_set_baudrate_request, // control request function to set baudrate
+ cp210x_set_line_ctl, // control request function to set data format
+ cp210x_set_line_coding_stage1_complete, // function to be called after stage 1 completed
+ cp210x_internal_control_complete, // control complete function to be called after request
+ complete_cb, user_data);
}
-static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
+static bool cp210x_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
p_cdc->user_control_cb = complete_cb;
- return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | p_cdc->requested_line_state, NULL, 0,
- complete_cb ? cp210x_internal_control_complete : NULL, user_data);
+ TU_ASSERT(cp210x_set_mhs(p_cdc, complete_cb ? cp210x_internal_control_complete : NULL, user_data));
+
+ return true;
}
//------------- Enumeration -------------//
enum {
CONFIG_CP210X_IFC_ENABLE = 0,
- CONFIG_CP210X_SET_BAUDRATE,
+ CONFIG_CP210X_SET_BAUDRATE_REQUEST,
CONFIG_CP210X_SET_LINE_CTL,
CONFIG_CP210X_SET_DTR_RTS,
CONFIG_CP210X_COMPLETE
};
-static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
+static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len) {
// CP210x Interface includes 1 vendor interface + 2 bulk endpoints
TU_VERIFY(itf_desc->bInterfaceSubClass == 0 && itf_desc->bInterfaceProtocol == 0 && itf_desc->bNumEndpoints == 2);
TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len);
@@ -1657,7 +1707,7 @@ static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, ui
return open_ep_stream_pair(p_cdc, desc_ep);
}
-static void cp210x_process_config(tuh_xfer_t* xfer) {
+static void cp210x_process_config(tuh_xfer_t * xfer) {
uintptr_t const state = xfer->user_data;
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
@@ -1666,32 +1716,35 @@ static void cp210x_process_config(tuh_xfer_t* xfer) {
switch (state) {
case CONFIG_CP210X_IFC_ENABLE:
- TU_ASSERT_COMPLETE(cp210x_ifc_enable(p_cdc, 1, cp210x_process_config, CONFIG_CP210X_SET_BAUDRATE));
+ p_cdc->user_control_cb = cp210x_process_config; // set once for whole process config
+ TU_ASSERT_COMPLETE(cp210x_ifc_enable(p_cdc, CP210X_UART_ENABLE, cp210x_process_config,
+ CONFIG_CP210X_SET_BAUDRATE_REQUEST));
break;
- case CONFIG_CP210X_SET_BAUDRATE: {
+ case CONFIG_CP210X_SET_BAUDRATE_REQUEST:
#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
- p_cdc->requested_line_coding.bit_rate = ((cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM).bit_rate;
- TU_ASSERT_COMPLETE(cp210x_set_baudrate(p_cdc, cp210x_process_config, CONFIG_CP210X_SET_LINE_CTL));
+ p_cdc->requested_line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM;
+ TU_ASSERT_COMPLETE(cp210x_set_baudrate_request(p_cdc, cp210x_internal_control_complete,
+ CONFIG_CP210X_SET_LINE_CTL));
break;
#else
TU_ATTR_FALLTHROUGH;
#endif
- }
- case CONFIG_CP210X_SET_LINE_CTL: {
- #if defined(CFG_TUH_CDC_LINE_CODING_ON_ENUM) && 0 // skip for now
- cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM;
- break;
+ case CONFIG_CP210X_SET_LINE_CTL:
+ #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
+ TU_ASSERT_COMPLETE(cp210x_set_line_ctl(p_cdc, cp210x_internal_control_complete,
+ CONFIG_CP210X_SET_DTR_RTS));
+ break;
#else
- TU_ATTR_FALLTHROUGH;
+ TU_ATTR_FALLTHROUGH;
#endif
- }
case CONFIG_CP210X_SET_DTR_RTS:
#ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM;
- TU_ASSERT_COMPLETE(cp210x_set_modem_ctrl(p_cdc, cp210x_process_config, CONFIG_CP210X_COMPLETE));
+ TU_ASSERT_COMPLETE(cp210x_set_mhs(p_cdc, cp210x_internal_control_complete,
+ CONFIG_CP210X_COMPLETE));
break;
#else
TU_ATTR_FALLTHROUGH;
diff --git a/src/class/cdc/serial/cp210x.h b/src/class/cdc/serial/cp210x.h
index 2c749f522..e18da7d51 100644
--- a/src/class/cdc/serial/cp210x.h
+++ b/src/class/cdc/serial/cp210x.h
@@ -28,7 +28,8 @@
// Protocol details can be found at AN571: CP210x Virtual COM Port Interface
// https://www.silabs.com/documents/public/application-notes/AN571.pdf
-#define TU_CP210X_VID 0x10C4
+// parts are overtaken from vendors driver
+// https://www.silabs.com/documents/public/software/cp210x-3.1.0.tar.gz
/* Config request codes */
#define CP210X_IFC_ENABLE 0x00
@@ -59,4 +60,64 @@
#define CP210X_SET_BAUDRATE 0x1E
#define CP210X_VENDOR_SPECIFIC 0xFF // GPIO, Recipient must be Device
+/* SILABSER_IFC_ENABLE_REQUEST_CODE */
+#define CP210X_UART_ENABLE 0x0001
+#define CP210X_UART_DISABLE 0x0000
+
+/* SILABSER_SET_BAUDDIV_REQUEST_CODE */
+#define CP210X_BAUD_RATE_GEN_FREQ 0x384000
+
+/*SILABSER_SET_LINE_CTL_REQUEST_CODE */
+#define CP210X_BITS_DATA_MASK 0x0f00
+#define CP210X_BITS_DATA_5 0x0500
+#define CP210X_BITS_DATA_6 0x0600
+#define CP210X_BITS_DATA_7 0x0700
+#define CP210X_BITS_DATA_8 0x0800
+#define CP210X_BITS_DATA_9 0x0900
+
+#define CP210X_BITS_PARITY_MASK 0x00f0
+#define CP210X_BITS_PARITY_NONE 0x0000
+#define CP210X_BITS_PARITY_ODD 0x0010
+#define CP210X_BITS_PARITY_EVEN 0x0020
+#define CP210X_BITS_PARITY_MARK 0x0030
+#define CP210X_BITS_PARITY_SPACE 0x0040
+
+#define CP210X_BITS_STOP_MASK 0x000f
+#define CP210X_BITS_STOP_1 0x0000
+#define CP210X_BITS_STOP_1_5 0x0001
+#define CP210X_BITS_STOP_2 0x0002
+
+/* SILABSER_SET_BREAK_REQUEST_CODE */
+#define CP210X_BREAK_ON 0x0001
+#define CP210X_BREAK_OFF 0x0000
+
+/* SILABSER_SET_MHS_REQUEST_CODE */
+#define CP210X_MCR_DTR 0x0001
+#define CP210X_MCR_RTS 0x0002
+#define CP210X_MCR_ALL 0x0003
+#define CP210X_MSR_CTS 0x0010
+#define CP210X_MSR_DSR 0x0020
+#define CP210X_MSR_RING 0x0040
+#define CP210X_MSR_DCD 0x0080
+#define CP210X_MSR_ALL 0x00F0
+
+#define CP210X_CONTROL_WRITE_DTR 0x0100
+#define CP210X_CONTROL_WRITE_RTS 0x0200
+
+#define CP210X_LSR_BREAK 0x0001
+#define CP210X_LSR_FRAMING_ERROR 0x0002
+#define CP210X_LSR_HW_OVERRUN 0x0004
+#define CP210X_LSR_QUEUE_OVERRUN 0x0008
+#define CP210X_LSR_PARITY_ERROR 0x0010
+#define CP210X_LSR_ALL 0x001F
+
+// supported baudrates
+// reference: datasheets and AN205 "CP210x Baud Rate Support"
+#define CP210X_SUPPORTED_BAUDRATES_LIST { \
+ 300, 600, \
+ 1200, 1800, 2400, 4000, 4800, 7200, 9600, \
+ 14400, 16000, 19200, 28800, 38400, 51200, 56000, 57600, 64000, 76800, \
+ 115200, 128000, 153600, 230400, 250000, 256000, 460800, 500000, 576000, 921600, \
+ 0 }
+
#endif //TUSB_CP210X_H