improved CP210x support

This commit is contained in:
IngHK
2024-02-22 15:42:33 +01:00
parent 7fef5943ef
commit 4547737833
3 changed files with 163 additions and 47 deletions

View File

@@ -83,7 +83,7 @@ typedef struct {
uint8_t requested_line_state; uint8_t requested_line_state;
tuh_xfer_cb_t user_control_cb; 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; tuh_xfer_cb_t requested_complete_cb;
#endif #endif
@@ -1533,7 +1533,8 @@ static uint8_t ftdi_get_idx(tuh_xfer_t * xfer) {
//------------- Control Request -------------// //------------- 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 = { tusb_control_request_t const request = {
.bmRequestType_bit = { .bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_INTERFACE, .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, .bRequest = command,
.wValue = tu_htole16(value), .wValue = tu_htole16(value),
.wIndex = p_cdc->bInterfaceNumber, .wIndex = tu_htole16(p_cdc->bInterfaceNumber),
.wLength = tu_htole16(length) .wLength = tu_htole16(length)
}; };
// use usbh enum buf since application variable does not live long enough // 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) { if (buffer && length > 0) {
enum_buf = usbh_get_enum_buf(); 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); 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); 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 -------------// //------------- Driver API -------------//
// internal control complete to update state such as line state, encoding // internal control complete to update state such as line state, encoding
static void cp210x_internal_control_complete(tuh_xfer_t * xfer) { static void cp210x_internal_control_complete(tuh_xfer_t * xfer) {
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
cdch_interface_t* p_cdc = get_itf(idx); cdch_interface_t * p_cdc = get_itf(idx);
TU_ASSERT(p_cdc, ); TU_ASSERT(p_cdc,);
bool const success = (xfer->result == XFER_RESULT_SUCCESS); bool const success = (xfer->result == XFER_RESULT_SUCCESS);
TU_LOG_P_CDC("control complete success = %u", 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; p_cdc->line_state = p_cdc->requested_line_state;
break; 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: case CP210X_SET_BAUDRATE:
p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate;
break; 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) { 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);
p_cdc->user_control_cb = complete_cb; p_cdc->user_control_cb = complete_cb;
return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4, TU_ASSERT(cp210x_set_baudrate_request(p_cdc, complete_cb ? cp210x_internal_control_complete : NULL, user_data));
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) { 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_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 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; p_cdc->user_control_cb = complete_cb;
return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | p_cdc->requested_line_state, NULL, 0, TU_ASSERT(cp210x_set_line_ctl(p_cdc, complete_cb ? cp210x_internal_control_complete : NULL, user_data));
complete_cb ? cp210x_internal_control_complete : NULL, user_data);
return true;
}
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) {
p_cdc->user_control_cb = complete_cb;
TU_ASSERT(cp210x_set_mhs(p_cdc, complete_cb ? cp210x_internal_control_complete : NULL, user_data));
return true;
} }
//------------- Enumeration -------------// //------------- Enumeration -------------//
enum { enum {
CONFIG_CP210X_IFC_ENABLE = 0, CONFIG_CP210X_IFC_ENABLE = 0,
CONFIG_CP210X_SET_BAUDRATE, CONFIG_CP210X_SET_BAUDRATE_REQUEST,
CONFIG_CP210X_SET_LINE_CTL, CONFIG_CP210X_SET_LINE_CTL,
CONFIG_CP210X_SET_DTR_RTS, CONFIG_CP210X_SET_DTR_RTS,
CONFIG_CP210X_COMPLETE 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 // 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(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); 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); 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; uintptr_t const state = xfer->user_data;
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); 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); 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) { switch (state) {
case CONFIG_CP210X_IFC_ENABLE: 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; break;
case CONFIG_CP210X_SET_BAUDRATE: { case CONFIG_CP210X_SET_BAUDRATE_REQUEST:
#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM #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; p_cdc->requested_line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM;
TU_ASSERT_COMPLETE(cp210x_set_baudrate(p_cdc, cp210x_process_config, CONFIG_CP210X_SET_LINE_CTL)); TU_ASSERT_COMPLETE(cp210x_set_baudrate_request(p_cdc, cp210x_internal_control_complete,
CONFIG_CP210X_SET_LINE_CTL));
break; break;
#else #else
TU_ATTR_FALLTHROUGH; TU_ATTR_FALLTHROUGH;
#endif #endif
}
case CONFIG_CP210X_SET_LINE_CTL: { case CONFIG_CP210X_SET_LINE_CTL:
#if defined(CFG_TUH_CDC_LINE_CODING_ON_ENUM) && 0 // skip for now #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; TU_ASSERT_COMPLETE(cp210x_set_line_ctl(p_cdc, cp210x_internal_control_complete,
break; CONFIG_CP210X_SET_DTR_RTS));
break;
#else #else
TU_ATTR_FALLTHROUGH; TU_ATTR_FALLTHROUGH;
#endif #endif
}
case CONFIG_CP210X_SET_DTR_RTS: case CONFIG_CP210X_SET_DTR_RTS:
#ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM
p_cdc->requested_line_state = 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; break;
#else #else
TU_ATTR_FALLTHROUGH; TU_ATTR_FALLTHROUGH;

View File

@@ -28,7 +28,8 @@
// Protocol details can be found at AN571: CP210x Virtual COM Port Interface // Protocol details can be found at AN571: CP210x Virtual COM Port Interface
// https://www.silabs.com/documents/public/application-notes/AN571.pdf // 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 */ /* Config request codes */
#define CP210X_IFC_ENABLE 0x00 #define CP210X_IFC_ENABLE 0x00
@@ -59,4 +60,64 @@
#define CP210X_SET_BAUDRATE 0x1E #define CP210X_SET_BAUDRATE 0x1E
#define CP210X_VENDOR_SPECIFIC 0xFF // GPIO, Recipient must be Device #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 #endif //TUSB_CP210X_H

View File

@@ -482,7 +482,9 @@
#ifndef CFG_TUH_CDC_CP210X_VID_PID_LIST #ifndef CFG_TUH_CDC_CP210X_VID_PID_LIST
// List of product IDs that can use the CP210X CDC driver. 0x10C4 is Silicon Labs' VID // List of product IDs that can use the CP210X CDC driver. 0x10C4 is Silicon Labs' VID
#define CFG_TUH_CDC_CP210X_VID_PID_LIST \ #define CFG_TUH_CDC_CP210X_VID_PID_LIST \
{0x10C4, 0xEA60}, {0x10C4, 0xEA70} { 0x10C4, 0xEA60 }, /* Silicon Labs factory default */ \
{ 0x10C4, 0xEA61 }, /* Silicon Labs factory default */ \
{ 0x10C4, 0xEA70 } /* Silicon Labs Dual Port factory default */
#endif #endif
#ifndef CFG_TUH_CDC_CH34X #ifndef CFG_TUH_CDC_CH34X