add common cdch_process_set_config() to safely complete set_config() when it failed.

driver_process_set_config() also pass drv index with user_data
This commit is contained in:
hathach
2025-06-17 22:16:13 +07:00
parent 4a44dd5c47
commit d4abf43f22
2 changed files with 453 additions and 447 deletions

View File

@@ -31,8 +31,7 @@ static size_t get_console_inputs(uint8_t* buf, size_t bufsize) {
size_t count = 0;
while (count < bufsize) {
int ch = board_getchar();
if (ch <= 0) break;
if (ch <= 0) { break; }
buf[count] = (uint8_t) ch;
count++;
}
@@ -69,10 +68,12 @@ void tuh_cdc_rx_cb(uint8_t idx) {
uint32_t const bufsize = sizeof(buf) - 1;
// forward cdc interfaces -> console
uint32_t count = tuh_cdc_read(idx, buf, bufsize);
const uint32_t count = tuh_cdc_read(idx, buf, bufsize);
if (count) {
buf[count] = 0;
printf("%s", (char*) buf);
fflush(stdout);
}
}
// Invoked when a device with CDC interface is mounted

View File

@@ -52,17 +52,6 @@
serial_drivers[p_cdc->serial_drid].name, ##__VA_ARGS__)
#define TU_LOG_P_CDC_BOOL(TXT,VAL) TU_LOG_P_CDC(TXT " " #VAL " = %d", VAL)
// assert and set config complete
#define TU_ASSERT_COMPLETE_DEFINE(_cond, _itf_offset) \
do { \
if (!(_cond)) { _MESS_FAILED(); TU_BREAKPOINT(); set_config_complete(idx, _itf_offset, false); } \
} while(0)
#define TU_ASSERT_COMPLETE_1ARGS(_cond) TU_ASSERT_COMPLETE_DEFINE(_cond, 0)
#define TU_ASSERT_COMPLETE_2ARGS(_cond, _itf_offset) TU_ASSERT_COMPLETE_DEFINE(_cond, _itf_offset)
#define TU_ASSERT_COMPLETE(...) _GET_3RD_ARG(__VA_ARGS__, TU_ASSERT_COMPLETE_2ARGS, TU_ASSERT_COMPLETE_1ARGS, _dummy)(__VA_ARGS__)
// handle line control defines
#if defined(CFG_TUH_CDC_LINE_CONTROL_ON_ENUM) && \
(defined(CFG_TUH_CDC_DTR_CONTROL_ON_ENUM) || defined(CFG_TUH_CDC_RTS_CONTROL_ON_ENUM))
@@ -142,9 +131,14 @@ CFG_TUH_MEM_SECTION static cdch_epbuf_t cdch_epbuf[CFG_TUH_CDC];
// Serial Driver
//--------------------------------------------------------------------+
// for process_set_config user_data: idx (byte1) and state (byte0)
#define PROCESS_CONFIG_STATE(_idx, _state) tu_u16(_idx, _state)
static void cdch_process_set_config(tuh_xfer_t *xfer);
//------------- ACM prototypes -------------//
static bool acm_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
static void acm_process_config(tuh_xfer_t * xfer);
static bool acm_process_set_config(tuh_xfer_t * xfer);
static bool acm_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool acm_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
@@ -159,7 +153,7 @@ static uint16_t const ftdi_vid_pid_list[][2] = {CFG_TUH_CDC_FTDI_VID_PID_LIST};
#endif
static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t * itf_desc, uint16_t max_len);
static void ftdi_process_config(tuh_xfer_t * xfer);
static bool ftdi_proccess_set_config(tuh_xfer_t * xfer);
static bool ftdi_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool ftdi_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
@@ -172,7 +166,7 @@ static bool ftdi_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete
static uint16_t const cp210x_vid_pid_list[][2] = {CFG_TUH_CDC_CP210X_VID_PID_LIST};
static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
static void cp210x_process_config(tuh_xfer_t * xfer);
static bool cp210x_process_set_config(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_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
@@ -185,7 +179,7 @@ static bool cp210x_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t comple
static uint16_t const ch34x_vid_pid_list[][2] = {CFG_TUH_CDC_CH34X_VID_PID_LIST};
static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
static void ch34x_process_config(tuh_xfer_t * xfer);
static bool ch34x_process_set_config(tuh_xfer_t *xfer);
static bool ch34x_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool ch34x_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
@@ -201,7 +195,7 @@ static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {PL2303_TYPE
CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN
static bool pl2303_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
static void pl2303_process_config(tuh_xfer_t * xfer);
static bool pl2303_process_set_config(tuh_xfer_t *xfer);
static bool pl2303_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
static bool pl2303_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
@@ -236,7 +230,7 @@ typedef struct {
uint16_t const (*vid_pid_list)[2];
uint16_t const vid_pid_count;
bool (*const open)(uint8_t daddr, const tusb_desc_interface_t * itf_desc, uint16_t max_len);
void (*const process_set_config)(tuh_xfer_t * xfer);
bool (*const process_set_config)(tuh_xfer_t * xfer);
bool (*const set_control_line_state)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
bool (*const set_baudrate)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
bool (*const set_data_format)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
@@ -252,7 +246,7 @@ static const cdch_serial_driver_t serial_drivers[] = {
.vid_pid_list = NULL,
.vid_pid_count = 0,
.open = acm_open,
.process_set_config = acm_process_config,
.process_set_config = acm_process_set_config,
.set_control_line_state = acm_set_control_line_state,
.set_baudrate = acm_set_baudrate,
.set_data_format = acm_set_data_format,
@@ -267,7 +261,7 @@ static const cdch_serial_driver_t serial_drivers[] = {
.vid_pid_list = ftdi_vid_pid_list,
.vid_pid_count = TU_ARRAY_SIZE(ftdi_vid_pid_list),
.open = ftdi_open,
.process_set_config = ftdi_process_config,
.process_set_config = ftdi_proccess_set_config,
.set_control_line_state = ftdi_set_modem_ctrl,
.set_baudrate = ftdi_set_baudrate,
.set_data_format = ftdi_set_data_format,
@@ -283,7 +277,7 @@ static const cdch_serial_driver_t serial_drivers[] = {
.vid_pid_list = cp210x_vid_pid_list,
.vid_pid_count = TU_ARRAY_SIZE(cp210x_vid_pid_list),
.open = cp210x_open,
.process_set_config = cp210x_process_config,
.process_set_config = cp210x_process_set_config,
.set_control_line_state = cp210x_set_modem_ctrl,
.set_baudrate = cp210x_set_baudrate,
.set_data_format = cp210x_set_data_format,
@@ -299,7 +293,7 @@ static const cdch_serial_driver_t serial_drivers[] = {
.vid_pid_list = ch34x_vid_pid_list,
.vid_pid_count = TU_ARRAY_SIZE(ch34x_vid_pid_list),
.open = ch34x_open,
.process_set_config = ch34x_process_config,
.process_set_config = ch34x_process_set_config,
.set_control_line_state = ch34x_set_modem_ctrl,
.set_baudrate = ch34x_set_baudrate,
.set_data_format = ch34x_set_data_format,
@@ -315,7 +309,7 @@ static const cdch_serial_driver_t serial_drivers[] = {
.vid_pid_list = pl2303_vid_pid_list,
.vid_pid_count = TU_ARRAY_SIZE(pl2303_vid_pid_list),
.open = pl2303_open,
.process_set_config = pl2303_process_config,
.process_set_config = pl2303_process_set_config,
.set_control_line_state = pl2303_set_modem_ctrl,
.set_baudrate = pl2303_set_baudrate,
.set_data_format = pl2303_set_data_format,
@@ -378,9 +372,8 @@ static bool open_ep_stream_pair(cdch_interface_t * p_cdc , tusb_desc_endpoint_t
uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num) {
for (uint8_t i = 0; i < CFG_TUH_CDC; i++) {
const cdch_interface_t * p_cdc = &cdch_data[i];
if (p_cdc->daddr == daddr && p_cdc->bInterfaceNumber == itf_num) return i;
if (p_cdc->daddr == daddr && p_cdc->bInterfaceNumber == itf_num) { return i; }
}
return TUSB_INDEX_INVALID_8;
}
@@ -786,19 +779,24 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t
}
} else if (ep_addr == p_cdc->stream.rx.ep_addr) {
#if CFG_TUH_CDC_FTDI
if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI && xferred_bytes > 2) {
if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI) {
// FTDI reserve 2 bytes for status
// uint8_t status[2] = {p_cdc->stream.rx.ep_buf[0], p_cdc->stream.rx.ep_buf[1]};
if (xferred_bytes > 2) {
tu_edpt_stream_read_xfer_complete_with_buf(&p_cdc->stream.rx, p_cdc->stream.rx.ep_buf + 2, xferred_bytes - 2);
if (tuh_cdc_rx_cb) {
tuh_cdc_rx_cb(idx); // invoke receive callback
}
}
} else
#endif
{
tu_edpt_stream_read_xfer_complete(&p_cdc->stream.rx, xferred_bytes);
}
// invoke receive callback
if (tuh_cdc_rx_cb) {
tuh_cdc_rx_cb(idx);
tuh_cdc_rx_cb(idx); // invoke receive callback
}
}
// prepare for next transfer if needed
@@ -894,6 +892,17 @@ static void set_config_complete(uint8_t idx, uint8_t itf_offset, bool success) {
usbh_driver_set_config_complete(p_cdc->daddr, p_cdc->bInterfaceNumber + itf_offset);
}
static void cdch_process_set_config(tuh_xfer_t *xfer) {
const uint8_t idx = tu_u32_byte1(xfer->user_data);
cdch_interface_t *p_cdc = get_itf(idx);
TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT,);
if (!serial_drivers[p_cdc->serial_drid].process_set_config(xfer)) {
const uint8_t itf_offset = (p_cdc->serial_drid == SERIAL_DRIVER_ACM) ? 1 : 0;
set_config_complete(idx, itf_offset, false);
}
}
bool cdch_set_config(uint8_t daddr, uint8_t itf_num) {
tusb_control_request_t request;
request.wIndex = tu_htole16((uint16_t) itf_num);
@@ -907,9 +916,8 @@ bool cdch_set_config(uint8_t daddr, uint8_t itf_num) {
xfer.daddr = daddr;
xfer.result = XFER_RESULT_SUCCESS;
xfer.setup = &request;
xfer.user_data = 0; // initial state 0
serial_drivers[p_cdc->serial_drid].process_set_config(&xfer);
xfer.user_data = PROCESS_CONFIG_STATE(idx, 0); // initial state 0
cdch_process_set_config(&xfer);
return true;
}
@@ -1036,7 +1044,7 @@ static bool acm_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb
enum {
CONFIG_ACM_SET_CONTROL_LINE_STATE = 0,
CONFIG_ACM_SET_LINE_CODING,
CONFIG_ACM_COMPLETE,
CONFIG_ACM_COMPLETE
};
static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
@@ -1084,19 +1092,19 @@ static bool acm_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint
return true;
}
static void acm_process_config(tuh_xfer_t * xfer) {
uintptr_t const state = xfer->user_data;
static bool acm_process_set_config(tuh_xfer_t *xfer) {
const uint8_t state = (uint8_t) 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);
cdch_interface_t *p_cdc = get_itf(idx);
TU_ASSERT_COMPLETE(p_cdc && xfer->result == XFER_RESULT_SUCCESS, 1);
TU_ASSERT(p_cdc && xfer->result == XFER_RESULT_SUCCESS);
switch (state) {
case CONFIG_ACM_SET_CONTROL_LINE_STATE:
#ifdef LINE_CONTROL_ON_ENUM
if (p_cdc->acm_capability.support_line_request) {
p_cdc->requested_line_state.all = LINE_CONTROL_ON_ENUM;
TU_ASSERT_COMPLETE(acm_set_control_line_state(p_cdc, acm_process_config, CONFIG_ACM_SET_LINE_CODING), 1);
TU_ASSERT(acm_set_control_line_state(p_cdc, cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_ACM_SET_LINE_CODING)));
break;
}
#endif
@@ -1106,7 +1114,7 @@ static void acm_process_config(tuh_xfer_t * xfer) {
#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
if (p_cdc->acm_capability.support_line_request) {
p_cdc->requested_line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM;
TU_ASSERT_COMPLETE(acm_set_line_coding(p_cdc, acm_process_config, CONFIG_ACM_COMPLETE), 1);
TU_ASSERT(acm_set_line_coding(p_cdc, cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_ACM_COMPLETE)));
break;
}
#endif
@@ -1118,9 +1126,10 @@ static void acm_process_config(tuh_xfer_t * xfer) {
break;
default:
set_config_complete(idx, 1, false);
break;
return false; // invalid state
}
return true;
}
//--------------------------------------------------------------------+
@@ -1187,8 +1196,7 @@ static bool ftdi_change_speed(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_c
static bool ftdi_set_data_request(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
TU_VERIFY(p_cdc->requested_line_coding.data_bits >= 7 && p_cdc->requested_line_coding.data_bits <= 8, 0);
uint16_t value = (uint16_t) (
(p_cdc->requested_line_coding.data_bits & 0xfUL) | // data bit quantity is stored in bits 0-3
uint16_t value = (uint16_t) ((p_cdc->requested_line_coding.data_bits & 0xfUL) | // data bit quantity is stored in bits 0-3
(p_cdc->requested_line_coding.parity & 0x7UL) << 8 | // parity is stored in bits 8-10, same coding
(p_cdc->requested_line_coding.stop_bits & 0x3UL) << 11); // stop bits quantity is stored in bits 11-12, same coding
// not each FTDI supports 1.5 stop bits
@@ -1317,22 +1325,20 @@ static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t * itf_desc, uin
return open_ep_stream_pair(p_cdc, desc_ep);
}
static void ftdi_process_config(tuh_xfer_t * xfer) {
uintptr_t const state = xfer->user_data;
static bool ftdi_proccess_set_config(tuh_xfer_t *xfer) {
const uint8_t state = (uint8_t) xfer->user_data;
uint8_t const idx = ftdi_get_idx(xfer);
cdch_interface_t *p_cdc = get_itf(idx);
TU_ASSERT_COMPLETE(p_cdc && xfer->result == XFER_RESULT_SUCCESS);
uint8_t const itf_num = p_cdc->bInterfaceNumber;
TU_ASSERT(p_cdc && xfer->result == XFER_RESULT_SUCCESS);
switch (state) {
// from here sequence overtaken from Linux Kernel function ftdi_port_probe()
case CONFIG_FTDI_GET_DESC:
// get device descriptor
p_cdc->user_control_cb = ftdi_process_config; // set once for whole process config
if (itf_num == 0) { // only necessary for 1st interface. other interface overtake type from interface 0
TU_ASSERT_COMPLETE(tuh_descriptor_get_device(xfer->daddr, &desc_dev, sizeof(tusb_desc_device_t),
ftdi_process_config, CONFIG_FTDI_DETERMINE_TYPE));
TU_ASSERT(tuh_descriptor_get_device(xfer->daddr, &desc_dev, sizeof(tusb_desc_device_t),
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_FTDI_DETERMINE_TYPE)));
break;
}
TU_ATTR_FALLTHROUGH;
@@ -1340,23 +1346,21 @@ static void ftdi_process_config(tuh_xfer_t * xfer) {
case CONFIG_FTDI_DETERMINE_TYPE:
// determine type
if (itf_num == 0) {
TU_ASSERT_COMPLETE(ftdi_determine_type(p_cdc));
TU_ASSERT(ftdi_determine_type(p_cdc));
} else {
// other interfaces have same type as interface 0
uint8_t const idx_itf0 = tuh_cdc_itf_get_index(xfer->daddr, 0);
cdch_interface_t const *p_cdc_itf0 = get_itf(idx_itf0);
TU_ASSERT_COMPLETE(p_cdc_itf0);
if (p_cdc_itf0) {
TU_ASSERT(p_cdc_itf0);
p_cdc->ftdi.chip_type = p_cdc_itf0->ftdi.chip_type;
}
}
TU_ATTR_FALLTHROUGH;
case CONFIG_FTDI_WRITE_LATENCY:
#ifdef CFG_TUH_CDC_FTDI_LATENCY
int8_t result = ftdi_write_latency_timer(p_cdc, CFG_TUH_CDC_FTDI_LATENCY, ftdi_process_config,
CONFIG_FTDI_SIO_RESET);
TU_ASSERT_COMPLETE(result != FTDI_FAIL);
TU_ASSERT(result != FTDI_FAIL);
if (result == FTDI_REQUESTED) {
break;
}// else FTDI_NOT_POSSIBLE => continue directly with next state
@@ -1365,14 +1369,16 @@ static void ftdi_process_config(tuh_xfer_t * xfer) {
// from here sequence overtaken from Linux Kernel function ftdi_open()
case CONFIG_FTDI_SIO_RESET:
TU_ASSERT_COMPLETE(ftdi_sio_reset(p_cdc, ftdi_process_config, CONFIG_FTDI_SET_DATA));
p_cdc->user_control_cb = cdch_process_set_config;
TU_ASSERT(ftdi_sio_reset(p_cdc, cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_FTDI_SET_DATA)));
break;
// from here sequence overtaken from Linux Kernel function ftdi_set_termios()
case CONFIG_FTDI_SET_DATA:
#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
p_cdc->requested_line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM;
TU_ASSERT_COMPLETE(ftdi_set_data_request(p_cdc, ftdi_internal_control_complete, CONFIG_FTDI_SET_BAUDRATE));
p_cdc->user_control_cb = cdch_process_set_config;
TU_ASSERT(ftdi_set_data_request(p_cdc, ftdi_internal_control_complete, PROCESS_CONFIG_STATE(idx, CONFIG_FTDI_SET_BAUDRATE)));
break;
#else
TU_ATTR_FALLTHROUGH;
@@ -1380,7 +1386,8 @@ static void ftdi_process_config(tuh_xfer_t * xfer) {
case CONFIG_FTDI_SET_BAUDRATE:
#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
TU_ASSERT_COMPLETE(ftdi_change_speed(p_cdc, ftdi_internal_control_complete, CONFIG_FTDI_FLOW_CONTROL));
p_cdc->user_control_cb = cdch_process_set_config;
TU_ASSERT(ftdi_change_speed(p_cdc, ftdi_internal_control_complete, PROCESS_CONFIG_STATE(idx, CONFIG_FTDI_FLOW_CONTROL)));
break;
#else
TU_ATTR_FALLTHROUGH;
@@ -1388,15 +1395,15 @@ static void ftdi_process_config(tuh_xfer_t * xfer) {
case CONFIG_FTDI_FLOW_CONTROL:
// disable flow control
TU_ASSERT_COMPLETE(ftdi_set_request(p_cdc, FTDI_SIO_SET_FLOW_CTRL_REQUEST, FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
FTDI_SIO_DISABLE_FLOW_CTRL, p_cdc->ftdi.channel,
ftdi_process_config, CONFIG_FTDI_MODEM_CTRL));
TU_ASSERT(ftdi_set_request(p_cdc, FTDI_SIO_SET_FLOW_CTRL_REQUEST, FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, FTDI_SIO_DISABLE_FLOW_CTRL,
p_cdc->ftdi.channel, cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_FTDI_MODEM_CTRL)));
break;
case CONFIG_FTDI_MODEM_CTRL:
#ifdef LINE_CONTROL_ON_ENUM
p_cdc->requested_line_state.all = LINE_CONTROL_ON_ENUM;
TU_ASSERT_COMPLETE(ftdi_update_mctrl(p_cdc, ftdi_internal_control_complete, CONFIG_FTDI_COMPLETE));
p_cdc->user_control_cb = cdch_process_set_config;
TU_ASSERT(ftdi_update_mctrl(p_cdc, ftdi_internal_control_complete, PROCESS_CONFIG_STATE(idx, CONFIG_FTDI_COMPLETE)));
break;
#else
TU_ATTR_FALLTHROUGH;
@@ -1407,15 +1414,15 @@ static void ftdi_process_config(tuh_xfer_t * xfer) {
break;
default:
TU_ASSERT_COMPLETE(false);
break;
return false;
}
return true;
}
//------------- Helper -------------//
static bool ftdi_determine_type(cdch_interface_t * p_cdc)
{
static bool ftdi_determine_type(cdch_interface_t *p_cdc) {
uint16_t const version = desc_dev->bcdDevice;
uint8_t const itf_num = p_cdc->bInterfaceNumber;
@@ -1534,20 +1541,19 @@ static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base) {
divisor = divisor3 >> 3;
divisor |= (uint32_t) divfrac[divisor3 & 0x7] << 14;
/* Deal with special cases for highest baud rates. */
if (divisor == 1) /* 1.0 */
if (divisor == 1) /* 1.0 */ {
divisor = 0;
else if (divisor == 0x4001) /* 1.5 */
} else if (divisor == 0x4001) /* 1.5 */ {
divisor = 1;
}
return divisor;
}
static inline uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud)
{
static inline uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud) {
return ftdi_232bm_baud_base_to_divisor(baud, 48000000);
}
static uint32_t ftdi_2232h_baud_base_to_divisor(uint32_t baud, uint32_t base)
{
static uint32_t ftdi_2232h_baud_base_to_divisor(uint32_t baud, uint32_t base) {
static const unsigned char divfrac[8] = {0, 3, 2, 4, 1, 5, 6, 7};
uint32_t divisor;
uint32_t divisor3;
@@ -1558,10 +1564,11 @@ static uint32_t ftdi_2232h_baud_base_to_divisor(uint32_t baud, uint32_t base)
divisor = divisor3 >> 3;
divisor |= (uint32_t) divfrac[divisor3 & 0x7] << 14;
/* Deal with special cases for highest baud rates. */
if (divisor == 1) /* 1.0 */
if (divisor == 1) /* 1.0 */ {
divisor = 0;
else if (divisor == 0x4001) /* 1.5 */
} else if (divisor == 0x4001) /* 1.5 */ {
divisor = 1;
}
/*
* Set this bit to turn off a divide by 2.5 on baud rate generator
* This enables baud rates up to 12Mbaud but cannot reach below 1200
@@ -1571,13 +1578,11 @@ static uint32_t ftdi_2232h_baud_base_to_divisor(uint32_t baud, uint32_t base)
return divisor;
}
static inline uint32_t ftdi_2232h_baud_to_divisor(uint32_t baud)
{
static inline uint32_t ftdi_2232h_baud_to_divisor(uint32_t baud) {
return ftdi_2232h_baud_base_to_divisor(baud, (uint32_t) 120000000);
}
static inline uint32_t ftdi_get_divisor(cdch_interface_t * p_cdc)
{
static inline uint32_t ftdi_get_divisor(cdch_interface_t *p_cdc) {
uint32_t baud = p_cdc->requested_line_coding.bit_rate;
uint32_t div_value = 0;
TU_VERIFY(baud);
@@ -1717,8 +1722,7 @@ static bool cp210x_set_baudrate_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t
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 <= 8, 0);
uint16_t lcr = (uint16_t) (
(p_cdc->requested_line_coding.data_bits & 0xfUL) << 8 | // data bit quantity is stored in bits 8-11
uint16_t lcr = (uint16_t) ((p_cdc->requested_line_coding.data_bits & 0xfUL) << 8 | // data bit quantity is stored in bits 8-11
(p_cdc->requested_line_coding.parity & 0xfUL) << 4 | // parity is stored in bits 4-7, same coding
(p_cdc->requested_line_coding.stop_bits & 0xfUL)); // parity is stored in bits 0-3, same coding
@@ -1835,25 +1839,25 @@ static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, u
return open_ep_stream_pair(p_cdc, desc_ep);
}
static void cp210x_process_config(tuh_xfer_t * xfer) {
uintptr_t const state = xfer->user_data;
static bool cp210x_process_set_config(tuh_xfer_t *xfer) {
const uint8_t state = (uint8_t) 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);
cdch_interface_t *p_cdc = get_itf(idx);
TU_ASSERT_COMPLETE(p_cdc && xfer->result == XFER_RESULT_SUCCESS);
TU_ASSERT(p_cdc && xfer->result == XFER_RESULT_SUCCESS);
switch (state) {
case CONFIG_CP210X_IFC_ENABLE:
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));
TU_ASSERT(cp210x_ifc_enable(p_cdc, CP210X_UART_ENABLE, cdch_process_set_config,
PROCESS_CONFIG_STATE(idx, CONFIG_CP210X_SET_BAUDRATE_REQUEST)));
break;
case CONFIG_CP210X_SET_BAUDRATE_REQUEST:
#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
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));
p_cdc->user_control_cb = cdch_process_set_config;
TU_ASSERT(cp210x_set_baudrate_request(p_cdc, cp210x_internal_control_complete,
PROCESS_CONFIG_STATE(idx, CONFIG_CP210X_SET_LINE_CTL)));
break;
#else
TU_ATTR_FALLTHROUGH;
@@ -1861,8 +1865,9 @@ static void cp210x_process_config(tuh_xfer_t * xfer) {
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));
p_cdc->user_control_cb = cdch_process_set_config;
TU_ASSERT(cp210x_set_line_ctl(p_cdc, cp210x_internal_control_complete,
PROCESS_CONFIG_STATE(idx, CONFIG_CP210X_SET_DTR_RTS)));
break;
#else
TU_ATTR_FALLTHROUGH;
@@ -1871,8 +1876,9 @@ static void cp210x_process_config(tuh_xfer_t * xfer) {
case CONFIG_CP210X_SET_DTR_RTS:
#ifdef LINE_CONTROL_ON_ENUM
p_cdc->requested_line_state.all = LINE_CONTROL_ON_ENUM;
TU_ASSERT_COMPLETE(cp210x_set_mhs(p_cdc, cp210x_internal_control_complete,
CONFIG_CP210X_COMPLETE));
p_cdc->user_control_cb = cdch_process_set_config;
TU_ASSERT(cp210x_set_mhs(p_cdc, cp210x_internal_control_complete,
PROCESS_CONFIG_STATE(idx, CONFIG_CP210X_COMPLETE)));
break;
#else
TU_ATTR_FALLTHROUGH;
@@ -1883,9 +1889,10 @@ static void cp210x_process_config(tuh_xfer_t * xfer) {
break;
default:
TU_ASSERT_COMPLETE(false);
break;
return false;
}
return true;
}
#endif
@@ -1963,21 +1970,18 @@ static inline bool ch34x_write_reg(cdch_interface_t * p_cdc, uint16_t reg, uint1
static bool ch34x_write_reg_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
uint8_t const lcr = ch34x_get_lcr(p_cdc);
TU_VERIFY(lcr);
return ch34x_write_reg(p_cdc, CH32X_REG16_LCR2_LCR, lcr, complete_cb, user_data);
}
static bool ch34x_write_reg_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
uint16_t const div_ps = ch34x_get_divisor_prescaler(p_cdc);
TU_VERIFY(div_ps);
return ch34x_write_reg(p_cdc, CH34X_REG16_DIVISOR_PRESCALER, div_ps, complete_cb, user_data);
}
static bool ch34x_modem_ctrl_request(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
uint8_t control = ~((p_cdc->requested_line_state.rts ? CH34X_BIT_RTS : 0) |// CH34x signals are inverted
(p_cdc->requested_line_state.dtr ? CH34X_BIT_DTR : 0));
return ch34x_control_out(p_cdc, CH34X_REQ_MODEM_CTRL, control, 0, complete_cb, user_data);
}
@@ -2103,20 +2107,19 @@ static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, ui
return true;
}
static void ch34x_process_config(tuh_xfer_t* xfer) {
uintptr_t const state = xfer->user_data;
// CH34x has only interface 0, because wIndex is used as payload and not for bInterfaceNumber
uint8_t const itf_num = 0;
static bool ch34x_process_set_config(tuh_xfer_t *xfer) {
const uint8_t state = (uint8_t) xfer->user_data;
uint8_t const itf_num = 0; // CH34x has only interface 0, since wIndex is used as payload
uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num);
cdch_interface_t *p_cdc = get_itf(idx);
TU_ASSERT_COMPLETE(p_cdc && xfer->result == XFER_RESULT_SUCCESS);
uint8_t buffer[2];// TODO remove
TU_ASSERT(p_cdc && xfer->result == XFER_RESULT_SUCCESS);
switch (state) {
case CONFIG_CH34X_READ_VERSION:
p_cdc->user_control_cb = ch34x_process_config; // set once for whole process config
TU_ASSERT_COMPLETE(ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, buffer, 2,
ch34x_process_config, CONFIG_CH34X_SERIAL_INIT));
TU_ASSERT(ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, buffer, 2,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_CH34X_SERIAL_INIT)));
break;
case CONFIG_CH34X_SERIAL_INIT: {
@@ -2125,36 +2128,36 @@ static void ch34x_process_config(tuh_xfer_t* xfer) {
TU_LOG_P_CDC("Chip Version = 0x%02x", version);
// only versions >= 0x30 are tested, below 0x30 seems having other programming
// see drivers from WCH vendor, Linux kernel and FreeBSD
TU_ASSERT_COMPLETE(version >= 0x30);
if (version >= 0x30) {
// init CH34x with line coding
p_cdc->requested_line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X;
uint16_t const div_ps = ch34x_get_divisor_prescaler(p_cdc);
TU_ASSERT_COMPLETE(div_ps);
uint8_t const lcr = ch34x_get_lcr(p_cdc);
TU_ASSERT_COMPLETE(lcr);
TU_ASSERT_COMPLETE(ch34x_control_out(p_cdc, CH34X_REQ_SERIAL_INIT, tu_u16(lcr, 0x9c), div_ps,
ch34x_process_config, CONFIG_CH34X_SPECIAL_REG_WRITE));
TU_ASSERT(div_ps != 0 && lcr != 0);
TU_ASSERT(ch34x_control_out(p_cdc, CH34X_REQ_SERIAL_INIT, tu_u16(lcr, 0x9c), div_ps,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_CH34X_SPECIAL_REG_WRITE)));
}
break;
}
case CONFIG_CH34X_SPECIAL_REG_WRITE:
// overtake line coding and do special reg write, purpose unknown, overtaken from WCH driver
p_cdc->line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X;
TU_ASSERT_COMPLETE(ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x0F, CH341_REG_0x2C), 0x0007,
ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL));
TU_ASSERT(ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x0F, CH341_REG_0x2C), 0x0007,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_CH34X_FLOW_CONTROL)));
break;
case CONFIG_CH34X_FLOW_CONTROL:
// no hardware flow control
TU_ASSERT_COMPLETE(ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x27, CH341_REG_0x27), 0x0000,
ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL));
TU_ASSERT(ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x27, CH341_REG_0x27), 0x0000,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_CH34X_MODEM_CONTROL)));
break;
case CONFIG_CH34X_MODEM_CONTROL:
#ifdef LINE_CONTROL_ON_ENUM
p_cdc->requested_line_state.all = LINE_CONTROL_ON_ENUM;
TU_ASSERT_COMPLETE(ch34x_modem_ctrl_request(p_cdc, ch34x_internal_control_complete,
CONFIG_CH34X_COMPLETE));
p_cdc->user_control_cb = cdch_process_set_config;
TU_ASSERT(ch34x_modem_ctrl_request(p_cdc, ch34x_internal_control_complete, PROCESS_CONFIG_STATE(idx, CONFIG_CH34X_COMPLETE)));
break;
#else
TU_ATTR_FALLTHROUGH;
@@ -2165,9 +2168,10 @@ static void ch34x_process_config(tuh_xfer_t* xfer) {
break;
default:
TU_ASSERT_COMPLETE(false);
break;
}
return true;
}
//------------- Helper -------------//
@@ -2276,7 +2280,6 @@ static int8_t pl2303_detect_type(cdch_interface_t * p_cdc, uint8_t step,
static bool pl2303_encode_baud_rate(cdch_interface_t *p_cdc, uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE]);
//------------- Control Request -------------//
static bool pl2303_set_request(cdch_interface_t *p_cdc, uint8_t request, uint8_t requesttype,
uint16_t value, uint16_t index, uint8_t *buffer, uint16_t length,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
@@ -2311,33 +2314,27 @@ static bool pl2303_set_request(cdch_interface_t * p_cdc, uint8_t request, uint8_
}
static bool pl2303_vendor_read(cdch_interface_t *p_cdc, uint16_t value, uint8_t *buf,
tuh_xfer_cb_t complete_cb, uintptr_t user_data)
{
uint8_t request = p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN] ?
PL2303_VENDOR_READ_NREQUEST : PL2303_VENDOR_READ_REQUEST;
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
uint8_t request = p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN] ? PL2303_VENDOR_READ_NREQUEST : PL2303_VENDOR_READ_REQUEST;
return pl2303_set_request(p_cdc, request, PL2303_VENDOR_READ_REQUEST_TYPE, value, 0, buf, 1, complete_cb, user_data);
}
static bool pl2303_vendor_write(cdch_interface_t *p_cdc, uint16_t value, uint16_t index,
tuh_xfer_cb_t complete_cb, uintptr_t user_data)
{
uint8_t request = p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN] ?
PL2303_VENDOR_WRITE_NREQUEST : PL2303_VENDOR_WRITE_REQUEST;
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
uint8_t request = p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN] ? PL2303_VENDOR_WRITE_NREQUEST : PL2303_VENDOR_WRITE_REQUEST;
return pl2303_set_request(p_cdc, request, PL2303_VENDOR_WRITE_REQUEST_TYPE, value, index, NULL, 0, complete_cb, user_data);
}
static inline bool pl2303_supports_hx_status(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
{
static inline bool pl2303_supports_hx_status(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
uint8_t buf = 0;
return pl2303_set_request(p_cdc, PL2303_VENDOR_READ_REQUEST, PL2303_VENDOR_READ_REQUEST_TYPE, PL2303_READ_TYPE_HX_STATUS, 0,
&buf, 1, complete_cb, user_data);
}
static inline bool pl2303_set_control_lines(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
{
static inline bool pl2303_set_control_lines(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
// PL2303 has the same bit coding
return pl2303_set_request(p_cdc, PL2303_SET_CONTROL_REQUEST, PL2303_SET_CONTROL_REQUEST_TYPE,
p_cdc->requested_line_state.all, 0, NULL, 0, complete_cb, user_data);
@@ -2390,8 +2387,7 @@ static bool pl2303_set_line_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t comp
// return pl2303_set_request(p_cdc, PL2303_BREAK_REQUEST, PL2303_BREAK_REQUEST_TYPE, state, 0, NULL, 0);
//}
static inline int pl2303_clear_halt(cdch_interface_t * p_cdc, uint8_t endp, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
{
static inline int pl2303_clear_halt(cdch_interface_t *p_cdc, uint8_t endp, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
/* we don't care if it wasn't halted first. in fact some devices
* (like some ibmcam model 1 units) seem to expect hosts to make
* this request for iso endpoints, which can't halt!
@@ -2514,31 +2510,30 @@ static bool pl2303_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, u
return true;
}
static void pl2303_process_config(tuh_xfer_t * xfer) {
uintptr_t const state = xfer->user_data;
static bool pl2303_process_set_config(tuh_xfer_t *xfer) {
const uint8_t state = (uint8_t) xfer->user_data;
// PL2303 has only interface 0, because wIndex is used 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);
// state CONFIG_PL2303_READ1 may have no success due to expected stall by pl2303_supports_hx_status()
TU_ASSERT_COMPLETE(p_cdc && (xfer->result == XFER_RESULT_SUCCESS || xfer->user_data == CONFIG_PL2303_READ1));
uint8_t buf = 0;
int8_t type;
TU_ASSERT(p_cdc && (xfer->result == XFER_RESULT_SUCCESS || xfer->user_data == CONFIG_PL2303_READ1));
switch (state) {
// from here sequence overtaken from Linux Kernel function pl2303_startup()
case CONFIG_PL2303_GET_DESC:
p_cdc->user_control_cb = pl2303_process_config; // set once for whole process config
p_cdc->user_control_cb = cdch_process_set_config;// set once for whole process config
// get device descriptor
TU_ASSERT_COMPLETE(tuh_descriptor_get_device(xfer->daddr, &desc_dev, sizeof(tusb_desc_device_t),
pl2303_process_config, CONFIG_PL2303_DETECT_TYPE));
TU_ASSERT(tuh_descriptor_get_device(xfer->daddr, &desc_dev, sizeof(tusb_desc_device_t),
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_DETECT_TYPE)));
break;
case CONFIG_PL2303_DETECT_TYPE:
// get type and quirks (step 1)
type = pl2303_detect_type (p_cdc, 1, pl2303_process_config, CONFIG_PL2303_READ1); // step 1
TU_ASSERT_COMPLETE(type!=PL2303_DETECT_TYPE_FAILED);
type = pl2303_detect_type(p_cdc, 1, cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_READ1));
TU_ASSERT(type != PL2303_DETECT_TYPE_FAILED);
if (type == PL2303_SUPPORTS_HX_STATUS_TRIGGERED) {
break;
}// else: no transfer triggered and continue with CONFIG_PL2303_READ1
@@ -2549,7 +2544,7 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
p_cdc->pl2303.supports_hx_status = (// will not be true, if coming directly from previous case
xfer->user_data == CONFIG_PL2303_READ1 && xfer->result == XFER_RESULT_SUCCESS);
type = pl2303_detect_type(p_cdc, 2, NULL, 0); // step 2 now with supports_hx_status
TU_ASSERT_COMPLETE(type!=PL2303_DETECT_TYPE_FAILED);
TU_ASSERT(type != PL2303_DETECT_TYPE_FAILED);
p_cdc->pl2303.serial_private.type = &pl2303_type_data[type];
p_cdc->pl2303.serial_private.quirks |= p_cdc->pl2303.serial_private.type->quirks;
#if CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL && 0// can be activated if necessary
@@ -2557,14 +2552,15 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
desc_dev->bDeviceClass, desc_dev->bMaxPacketSize0,
desc_dev->bcdUSB, desc_dev->bcdDevice);
uint16_t vid, pid;
TU_ASSERT_COMPLETE(tuh_vid_pid_get(p_cdc->daddr, &vid, &pid));
TU_ASSERT(tuh_vid_pid_get(p_cdc->daddr, &vid, &pid));
TU_LOG_P_CDC("vid = 0x%04x pid = 0x%04x supports_hx_status = %u type = %s quirks = %u",
vid, pid, p_cdc->pl2303.supports_hx_status,
p_cdc->pl2303.serial_private.type->name, p_cdc->pl2303.serial_private.quirks);
#endif
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) {
TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0x8484, &buf, pl2303_process_config, CONFIG_PL2303_WRITE1));
TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, &buf,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_WRITE1)));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2572,7 +2568,8 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
case CONFIG_PL2303_WRITE1:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) {
TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 0x0404, 0, pl2303_process_config, CONFIG_PL2303_READ2));
TU_ASSERT(pl2303_vendor_write(p_cdc, 0x0404, 0,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_READ2)));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2580,7 +2577,8 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
case CONFIG_PL2303_READ2:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) {
TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0x8484, &buf, pl2303_process_config, CONFIG_PL2303_READ3));
TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, &buf,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_READ3)));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2588,7 +2586,8 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
case CONFIG_PL2303_READ3:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) {
TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0x8383, &buf, pl2303_process_config, CONFIG_PL2303_READ4));
TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8383, &buf,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_READ4)));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2596,7 +2595,8 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
case CONFIG_PL2303_READ4:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) {
TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0x8484, &buf, pl2303_process_config, CONFIG_PL2303_WRITE2));
TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, &buf,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_WRITE2)));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2604,7 +2604,8 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
case CONFIG_PL2303_WRITE2:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) {
TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 0x0404, 1, pl2303_process_config, CONFIG_PL2303_READ5));
TU_ASSERT(pl2303_vendor_write(p_cdc, 0x0404, 1,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_READ5)));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2612,7 +2613,8 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
case CONFIG_PL2303_READ5:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) {
TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0x8484, &buf, pl2303_process_config, CONFIG_PL2303_READ6));
TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, &buf,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_READ6)));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2620,7 +2622,8 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
case CONFIG_PL2303_READ6:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) {
TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0x8383, &buf, pl2303_process_config, CONFIG_PL2303_WRITE3));
TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8383, &buf,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_WRITE3)));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2628,7 +2631,8 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
case CONFIG_PL2303_WRITE3:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) {
TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 0, 1, pl2303_process_config, CONFIG_PL2303_WRITE4));
TU_ASSERT(pl2303_vendor_write(p_cdc, 0, 1,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_WRITE4)));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2636,7 +2640,8 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
case CONFIG_PL2303_WRITE4:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) {
TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 1, 0, pl2303_process_config, CONFIG_PL2303_WRITE5));
TU_ASSERT(pl2303_vendor_write(p_cdc, 1, 0,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_WRITE5)));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2644,11 +2649,9 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
case CONFIG_PL2303_WRITE5:
// purpose unknown, overtaken from Linux Kernel driver
if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) {
if (p_cdc->pl2303.serial_private.quirks & PL2303_QUIRK_LEGACY) {
TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 2, 0x24, pl2303_process_config, CONFIG_PL2303_RESET_ENDP1));
} else {
TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 2, 0x44, pl2303_process_config, CONFIG_PL2303_RESET_ENDP1));
}
uint16_t const windex = (p_cdc->pl2303.serial_private.quirks & PL2303_QUIRK_LEGACY) ? 0x24 : 0x44;
TU_ASSERT(pl2303_vendor_write(p_cdc, 2, windex,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_RESET_ENDP1)));
break;
}// else: continue with next step
TU_ATTR_FALLTHROUGH;
@@ -2657,15 +2660,17 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
case CONFIG_PL2303_RESET_ENDP1:
// step 1
if (p_cdc->pl2303.serial_private.quirks & PL2303_QUIRK_LEGACY) {
TU_ASSERT_COMPLETE(pl2303_clear_halt(p_cdc, PL2303_OUT_EP, pl2303_process_config, CONFIG_PL2303_RESET_ENDP2));
TU_ASSERT(pl2303_clear_halt(p_cdc, PL2303_OUT_EP,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_RESET_ENDP2)));
} else {
/* reset upstream data pipes */
if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) {
TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, PL2303_HXN_RESET_REG, // skip CONFIG_PL2303_RESET_ENDP2, no 2nd step
TU_ASSERT(pl2303_vendor_write(p_cdc, PL2303_HXN_RESET_REG,// skip CONFIG_PL2303_RESET_ENDP2, no 2nd step
PL2303_HXN_RESET_UPSTREAM_PIPE | PL2303_HXN_RESET_DOWNSTREAM_PIPE,
pl2303_process_config, CONFIG_PL2303_LINE_CODING));
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_LINE_CODING)));
} else {
pl2303_vendor_write(p_cdc, 8, 0, pl2303_process_config, CONFIG_PL2303_RESET_ENDP2);
pl2303_vendor_write(p_cdc, 8, 0,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_RESET_ENDP2));
}
}
break;
@@ -2673,13 +2678,15 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
case CONFIG_PL2303_RESET_ENDP2:
// step 2
if (p_cdc->pl2303.serial_private.quirks & PL2303_QUIRK_LEGACY) {
TU_ASSERT_COMPLETE(pl2303_clear_halt(p_cdc, PL2303_IN_EP, pl2303_process_config, CONFIG_PL2303_LINE_CODING));
TU_ASSERT(pl2303_clear_halt(p_cdc, PL2303_IN_EP,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_LINE_CODING)));
} else {
/* reset upstream data pipes */
if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) {
// here nothing to do, only structure of previous step overtaken for better reading and comparison
} else {
TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 9, 0, pl2303_process_config, CONFIG_PL2303_LINE_CODING));
TU_ASSERT(pl2303_vendor_write(p_cdc, 9, 0,
cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_LINE_CODING)));
}
}
break;
@@ -2689,7 +2696,8 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
case CONFIG_PL2303_LINE_CODING:
#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
p_cdc->requested_line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM;
TU_ASSERT_COMPLETE( pl2303_set_line_request(p_cdc, pl2303_internal_control_complete, CONFIG_PL2303_MODEM_CONTROL));
TU_ASSERT(pl2303_set_line_request(p_cdc, pl2303_internal_control_complete,
PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_MODEM_CONTROL)));
break;
#else
TU_ATTR_FALLTHROUGH;
@@ -2698,7 +2706,8 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
case CONFIG_PL2303_MODEM_CONTROL:
#ifdef LINE_CONTROL_ON_ENUM
p_cdc->requested_line_state.all = LINE_CONTROL_ON_ENUM;
TU_ASSERT_COMPLETE(pl2303_set_control_lines(p_cdc, pl2303_internal_control_complete, CONFIG_PL2303_COMPLETE));
TU_ASSERT(pl2303_set_control_lines(p_cdc, pl2303_internal_control_complete,
PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_COMPLETE)));
break;
#else
TU_ATTR_FALLTHROUGH;
@@ -2709,11 +2718,11 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
// // read flow control register for modify & write back in next step
// if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) {
// TU_LOG_P_CDC ( "1\r\n" );
// TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, PL2303_HXN_FLOWCTRL_REG, &buf, pl2303_process_config,
// CONFIG_PL2303_FLOW_CTRL_WRITE));
// TU_ASSERT(pl2303_vendor_read(p_cdc, PL2303_HXN_FLOWCTRL_REG, &buf,
// cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_FLOW_CTRL_WRITE)));
// } else {
// TU_LOG_P_CDC ( "2\r\n" );
// TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0, &buf, pl2303_process_config, CONFIG_PL2303_FLOW_CTRL_WRITE));
// TU_ASSERT(pl2303_vendor_read(p_cdc, 0, &buf, cdch_process_set_config, CONFIG_PL2303_FLOW_CTRL_WRITE));
// }
// break;
//
@@ -2723,11 +2732,12 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
// if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) {
// buf &= (uint8_t) ~PL2303_HXN_FLOWCTRL_MASK;
// buf |= PL2303_HXN_FLOWCTRL_NONE;
// TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, PL2303_HXN_FLOWCTRL_REG, buf, pl2303_process_config,
// CONFIG_PL2303_COMPLETE));
// TU_ASSERT(pl2303_vendor_write(p_cdc, PL2303_HXN_FLOWCTRL_REG, buf,
// cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_COMPLETE)));
// } else {
// buf &= (uint8_t) ~PL2303_FLOWCTRL_MASK;
// TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 0, buf, pl2303_process_config, CONFIG_PL2303_COMPLETE));
// TU_ASSERT(pl2303_vendor_write(p_cdc, 0, buf,
// cdch_process_set_config, PROCESS_CONFIG_STATE(idx, CONFIG_PL2303_COMPLETE)));
// }
// break;
@@ -2736,16 +2746,16 @@ static void pl2303_process_config(tuh_xfer_t * xfer) {
break;
default:
TU_ASSERT_COMPLETE(false);
break;
return false;
}
return true;
}
//------------- Helper -------------//
static int8_t pl2303_detect_type(cdch_interface_t *p_cdc, uint8_t step,
tuh_xfer_cb_t complete_cb, uintptr_t user_data )
{
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
/*
* Legacy PL2303H, variants 0 and 1 (difference unknown).
*/
@@ -2829,8 +2839,7 @@ static int8_t pl2303_detect_type(cdch_interface_t * p_cdc, uint8_t step,
* Returns the nearest supported baud rate that can be set directly without
* using divisors.
*/
static uint32_t pl2303_get_supported_baud_rate(uint32_t baud)
{
static uint32_t pl2303_get_supported_baud_rate(uint32_t baud) {
static const uint32_t baud_sup[] = {
75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600,
14400, 19200, 28800, 38400, 57600, 115200, 230400, 460800,
@@ -2859,8 +2868,7 @@ static uint32_t pl2303_get_supported_baud_rate(uint32_t baud)
* NOTE: If unsupported baud rates are set directly, the PL2303 seems to
* use 9600 baud.
*/
static uint32_t pl2303_encode_baud_rate_direct(uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE], uint32_t baud)
{
static uint32_t pl2303_encode_baud_rate_direct(uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE], uint32_t baud) {
uint32_t baud_le = tu_htole32(baud);
buf[0] = (uint8_t) ( baud_le & 0xff);
buf[1] = (uint8_t) ((baud_le >> 8) & 0xff);
@@ -2870,8 +2878,7 @@ static uint32_t pl2303_encode_baud_rate_direct(uint8_t buf[PL2303_LINE_CODING_BA
return baud;
}
static uint32_t pl2303_encode_baud_rate_divisor(uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE], uint32_t baud)
{
static uint32_t pl2303_encode_baud_rate_divisor(uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE], uint32_t baud) {
uint32_t baseline, mantissa, exponent;
/*
@@ -2908,8 +2915,7 @@ static uint32_t pl2303_encode_baud_rate_divisor(uint8_t buf[PL2303_LINE_CODING_B
return baud;
}
static uint32_t pl2303_encode_baud_rate_divisor_alt(uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE], uint32_t baud)
{
static uint32_t pl2303_encode_baud_rate_divisor_alt(uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE], uint32_t baud) {
uint32_t baseline, mantissa, exponent;
/*
@@ -2947,8 +2953,7 @@ static uint32_t pl2303_encode_baud_rate_divisor_alt(uint8_t buf[PL2303_LINE_CODI
return baud;
}
static bool pl2303_encode_baud_rate(cdch_interface_t * p_cdc, uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE])
{
static bool pl2303_encode_baud_rate(cdch_interface_t *p_cdc, uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE]) {
uint32_t baud = p_cdc->requested_line_coding.bit_rate;
uint32_t baud_sup;