improved CP210x support
This commit is contained in:
@@ -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_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) {
|
||||
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;
|
||||
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_line_ctl(p_cdc, 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 -------------//
|
||||
|
||||
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;
|
||||
|
@@ -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
|
||||
|
@@ -482,7 +482,9 @@
|
||||
#ifndef CFG_TUH_CDC_CP210X_VID_PID_LIST
|
||||
// List of product IDs that can use the CP210X CDC driver. 0x10C4 is Silicon Labs' VID
|
||||
#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
|
||||
|
||||
#ifndef CFG_TUH_CDC_CH34X
|
||||
|
Reference in New Issue
Block a user