fix CDC host FTDI multiple channel loop

fix typos for ftdi_process_set_config()
add J-Link GDB + RTT logging instructions
This commit is contained in:
hathach
2026-02-24 23:37:07 +07:00
parent 2e5d5665f3
commit da21dab358
2 changed files with 102 additions and 83 deletions

View File

@ -91,6 +91,22 @@ make BOARD=raspberry_pi_pico all
- Make: `make BOARD=raspberry_pi_pico all uf2`
- **List all targets** (CMake/Ninja): `ninja -t targets`
## J-Link GDB Server + RTT Logging
- Build with RTT logging enabled (example):
`cd examples/device/cdc_msc && make BOARD=stm32h743eval LOG=2 LOGGER=rtt all`
- Flash with J-Link:
`cd examples/device/cdc_msc && make BOARD=stm32h743eval LOG=2 LOGGER=rtt flash-jlink`
- Launch GDB server (keep this running in terminal 1):
`JLinkGDBServer -device stm32h743xi -if SWD -speed 4000 -port 2331 -swoport 2332 -telnetport 2333 -RTTTelnetPort 19021 -nogui`
- Read RTT output (terminal 2):
`JLinkRTTClient`
- Capture RTT to file (optional):
`JLinkRTTClient | tee rtt.log`
- For non-interactive capture:
`timeout 20s JLinkRTTClient > rtt.log`
- Use the board-specific `JLINK_DEVICE` from `hw/bsp/*/boards/*/board.mk` if you are not using `stm32h743eval`.
## Unit Testing
- Install Ceedling: `sudo gem install ceedling`

View File

@ -127,7 +127,7 @@ static bool acm_set_control_line_state(cdch_interface_t *p_cdc, tuh_xfer_cb_
static uint16_t const ftdi_vid_pid_list[][2] = {CFG_TUH_CDC_FTDI_VID_PID_LIST};
static uint16_t ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len);
static bool ftdi_proccess_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static bool ftdi_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer);
static void ftdi_internal_control_complete(cdch_interface_t *p_cdc, 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);
@ -216,82 +216,84 @@ typedef struct {
#define DRIVER_NAME_DECLARE(_str)
#endif
// clang-format off
// Note driver list must be in the same order as SERIAL_DRIVER enum
static const cdch_serial_driver_t serial_drivers[] = {
{
.vid_pid_list = NULL,
.vid_pid_count = 0,
.open = acm_open,
.process_set_config = acm_process_set_config,
.request_complete = acm_internal_control_complete,
.set_control_line_state = acm_set_control_line_state,
.set_baudrate = acm_set_line_coding,
.set_data_format = acm_set_line_coding,
.set_line_coding = acm_set_line_coding,
DRIVER_NAME_DECLARE("ACM")
.vid_pid_list = NULL,
.vid_pid_count = 0,
.open = acm_open,
.process_set_config = acm_process_set_config,
.request_complete = acm_internal_control_complete,
.set_control_line_state = acm_set_control_line_state,
.set_baudrate = acm_set_line_coding,
.set_data_format = acm_set_line_coding,
.set_line_coding = acm_set_line_coding,
DRIVER_NAME_DECLARE("ACM")
},
#if CFG_TUH_CDC_FTDI
{
.vid_pid_list = ftdi_vid_pid_list,
.vid_pid_count = TU_ARRAY_SIZE(ftdi_vid_pid_list),
.open = ftdi_open,
.process_set_config = ftdi_proccess_set_config,
.request_complete = ftdi_internal_control_complete,
.set_control_line_state = ftdi_set_modem_ctrl,
.set_baudrate = ftdi_set_baudrate,
.set_data_format = ftdi_set_data_format,
.set_line_coding = NULL, // 2 stage set line coding
DRIVER_NAME_DECLARE("FTDI")
.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_set_config,
.request_complete = ftdi_internal_control_complete,
.set_control_line_state = ftdi_set_modem_ctrl,
.set_baudrate = ftdi_set_baudrate,
.set_data_format = ftdi_set_data_format,
.set_line_coding = NULL, // 2 stage set line coding
DRIVER_NAME_DECLARE("FTDI")
},
#endif
#if CFG_TUH_CDC_CP210X
{
.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_set_config,
.request_complete = cp210x_internal_control_complete,
.set_control_line_state = cp210x_set_modem_ctrl,
.set_baudrate = cp210x_set_baudrate,
.set_data_format = cp210x_set_data_format,
.set_line_coding = NULL, // 2 stage set line coding
DRIVER_NAME_DECLARE("CP210x")
.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_set_config,
.request_complete = cp210x_internal_control_complete,
.set_control_line_state = cp210x_set_modem_ctrl,
.set_baudrate = cp210x_set_baudrate,
.set_data_format = cp210x_set_data_format,
.set_line_coding = NULL, // 2 stage set line coding
DRIVER_NAME_DECLARE("CP210x")
},
#endif
#if CFG_TUH_CDC_CH34X
{
.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_set_config,
.request_complete = ch34x_internal_control_complete,
.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_set_config,
.request_complete = ch34x_internal_control_complete,
.set_control_line_state = ch34x_set_modem_ctrl,
.set_baudrate = ch34x_set_baudrate,
.set_data_format = ch34x_set_data_format,
.set_line_coding = NULL, // 2 stage set line coding
DRIVER_NAME_DECLARE("CH34x")
.set_control_line_state = ch34x_set_modem_ctrl,
.set_baudrate = ch34x_set_baudrate,
.set_data_format = ch34x_set_data_format,
.set_line_coding = NULL, // 2 stage set line coding
DRIVER_NAME_DECLARE("CH34x")
},
#endif
#if CFG_TUH_CDC_PL2303
{
.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_set_config,
.request_complete = pl2303_internal_control_complete,
.set_control_line_state = pl2303_set_modem_ctrl,
.set_baudrate = pl2303_set_line_coding,
.set_data_format = pl2303_set_line_coding,
.set_line_coding = pl2303_set_line_coding,
DRIVER_NAME_DECLARE("PL2303")
.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_set_config,
.request_complete = pl2303_internal_control_complete,
.set_control_line_state = pl2303_set_modem_ctrl,
.set_baudrate = pl2303_set_line_coding,
.set_data_format = pl2303_set_line_coding,
.set_line_coding = pl2303_set_line_coding,
DRIVER_NAME_DECLARE("PL2303")
}
#endif
};
// clang-format on
TU_VERIFY_STATIC(TU_ARRAY_SIZE(serial_drivers) == SERIAL_DRIVER_COUNT, "Serial driver count mismatch");
@ -761,7 +763,8 @@ uint16_t cdch_open(uint8_t rhport, uint8_t daddr, const tusb_desc_interface_t *i
for (size_t i = 0; i < driver->vid_pid_count; i++) {
if (driver->vid_pid_list[i][0] == vid && driver->vid_pid_list[i][1] == pid) {
const uint16_t drv_len = driver->open(daddr, itf_desc, max_len);
TU_LOG_DRV("[:%u:%u] CDCh %s open %s\r\n", daddr, itf_desc->bInterfaceNumber, driver->name, drv_len > 0 ? "OK" : "FAILED");
TU_LOG_DRV("[:%u:%u] CDCh %s open %s\r\n", daddr, itf_desc->bInterfaceNumber, driver->name,
drv_len > 0 ? "OK" : "FAILED");
return drv_len;
}
}
@ -773,35 +776,16 @@ uint16_t cdch_open(uint8_t rhport, uint8_t daddr, const tusb_desc_interface_t *i
return 0;
}
bool cdch_set_config(uint8_t daddr, uint8_t itf_num) {
tusb_control_request_t request;
request.wIndex = tu_htole16((uint16_t) itf_num);
uint8_t const idx = tuh_cdc_itf_get_index(daddr, itf_num);
cdch_interface_t *p_cdc = get_itf(idx);
TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
TU_LOG_CDC(p_cdc, "set config");
// fake transfer to kick-off process_set_config()
tuh_xfer_t xfer;
xfer.daddr = daddr;
xfer.result = XFER_RESULT_SUCCESS;
xfer.setup = &request;
xfer.user_data = 0; // initial state 0
cdch_process_set_config(&xfer);
return true;
}
static void set_config_complete(cdch_interface_t *p_cdc, bool success) {
if (success) {
const uint8_t idx = get_idx_by_ptr(p_cdc);
p_cdc->mounted = true;
p_cdc->mounted = true;
tuh_cdc_mount_cb(idx);
// Prepare for incoming data
tu_edpt_stream_read_xfer(&p_cdc->stream.rx);
} else {
// clear the interface entry
p_cdc->daddr = 0;
p_cdc->daddr = 0;
p_cdc->bInterfaceNumber = 0;
}
@ -810,6 +794,33 @@ static void set_config_complete(cdch_interface_t *p_cdc, bool success) {
usbh_driver_set_config_complete(p_cdc->daddr, p_cdc->bInterfaceNumber + itf_offset);
}
bool cdch_set_config(uint8_t daddr, uint8_t itf_num) {
const uint8_t idx = tuh_cdc_itf_get_index(daddr, itf_num);
cdch_interface_t *p_cdc = get_itf(idx);
TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT);
TU_LOG_CDC(p_cdc, "set config");
// fake transfer to kick-off process_set_config()
tusb_control_request_t request;
request.wIndex = tu_htole16((uint16_t)itf_num);
tuh_xfer_t xfer;
xfer.daddr = daddr;
xfer.ep_addr = 0;
xfer.result = XFER_RESULT_SUCCESS;
xfer.setup = &request;
xfer.complete_cb = NULL;
xfer.buffer = NULL;
xfer.user_data = 0; // initial state 0
const cdch_serial_driver_t *driver = &serial_drivers[p_cdc->serial_drid];
if (!driver->process_set_config(p_cdc, &xfer)) {
set_config_complete(p_cdc, false);
}
return true;
}
static void cdch_process_set_config(tuh_xfer_t *xfer) {
cdch_interface_t *p_cdc = get_itf_by_xfer(xfer);
TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT,);
@ -1215,22 +1226,14 @@ static uint16_t ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc,
return drv_len;
}
static bool ftdi_proccess_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
static bool ftdi_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) {
TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS);
const uintptr_t state = xfer->user_data;
switch (state) {
// from here sequence overtaken from Linux Kernel function ftdi_port_probe()
case CONFIG_FTDI_DETERMINE_TYPE:
// determine type
if (p_cdc->bInterfaceNumber == 0) {
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(p_cdc_itf0);
p_cdc->ftdi.chip_type = p_cdc_itf0->ftdi.chip_type;
}
TU_ASSERT(ftdi_determine_type(p_cdc));
TU_ATTR_FALLTHROUGH;
case CONFIG_FTDI_WRITE_LATENCY: