From 7fef5943eff0d7f1b7122e878cf2caa78bea06aa Mon Sep 17 00:00:00 2001 From: IngHK Date: Sat, 24 Feb 2024 12:58:45 +0100 Subject: improved FTDI support --- src/class/cdc/serial/ftdi_sio.h | 385 +++++++++++++++++++--------------------- 1 file changed, 185 insertions(+), 200 deletions(-) (limited to 'src/class/cdc/serial') diff --git a/src/class/cdc/serial/ftdi_sio.h b/src/class/cdc/serial/ftdi_sio.h index 0825f0719..42716f73e 100644 --- a/src/class/cdc/serial/ftdi_sio.h +++ b/src/class/cdc/serial/ftdi_sio.h @@ -25,222 +25,207 @@ #ifndef TUSB_FTDI_SIO_H #define TUSB_FTDI_SIO_H -// VID for matching FTDI devices -#define TU_FTDI_VID 0x0403 +#include // Commands -#define FTDI_SIO_RESET 0 /* Reset the port */ -#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ -#define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */ -#define FTDI_SIO_SET_BAUD_RATE 3 /* Set baud rate */ -#define FTDI_SIO_SET_DATA 4 /* Set the data characteristics of the port */ -#define FTDI_SIO_GET_MODEM_STATUS 5 /* Retrieve current value of modem status register */ -#define FTDI_SIO_SET_EVENT_CHAR 6 /* Set the event character */ -#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */ -#define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */ -#define FTDI_SIO_GET_LATENCY_TIMER 0x0a /* Get the latency timer */ -#define FTDI_SIO_SET_BITMODE 0x0b /* Set bitbang mode */ -#define FTDI_SIO_READ_PINS 0x0c /* Read immediate value of pins */ -#define FTDI_SIO_READ_EEPROM 0x90 /* Read EEPROM */ - -/* FTDI_SIO_RESET */ +#define FTDI_SIO_RESET 0 // Reset the port +#define FTDI_SIO_MODEM_CTRL 1 // Set the modem control register +#define FTDI_SIO_SET_FLOW_CTRL 2 // Set flow control register +#define FTDI_SIO_SET_BAUD_RATE 3 // Set baud rate +#define FTDI_SIO_SET_DATA 4 // Set the data characteristics of the port +#define FTDI_SIO_GET_MODEM_STATUS 5 // Retrieve current value of modem status register +#define FTDI_SIO_SET_EVENT_CHAR 6 // Set the event character +#define FTDI_SIO_SET_ERROR_CHAR 7 // Set the error character +#define FTDI_SIO_SET_LATENCY_TIMER 9 // Set the latency timer +#define FTDI_SIO_GET_LATENCY_TIMER 10 // Get the latency timer +#define FTDI_SIO_SET_BITMODE 11 // Set bitbang mode +#define FTDI_SIO_READ_PINS 12 // Read immediate value of pins +#define FTDI_SIO_READ_EEPROM 0x90 // Read EEPROM + +// Channel indices for FT2232, FT2232H and FT4232H devices +#define CHANNEL_A 1 +#define CHANNEL_B 2 +#define CHANNEL_C 3 +#define CHANNEL_D 4 + +// Port Identifier Table +#define PIT_DEFAULT 0 // SIOA +#define PIT_SIOA 1 // SIOA +// The device this driver is tested with one has only one port +#define PIT_SIOB 2 // SIOB +#define PIT_PARALLEL 3 // Parallel + +// FTDI_SIO_RESET +#define FTDI_SIO_RESET_REQUEST FTDI_SIO_RESET +#define FTDI_SIO_RESET_REQUEST_TYPE 0x40 #define FTDI_SIO_RESET_SIO 0 #define FTDI_SIO_RESET_PURGE_RX 1 #define FTDI_SIO_RESET_PURGE_TX 2 -/* - * BmRequestType: 0100 0000B - * bRequest: FTDI_SIO_RESET - * wValue: Control Value - * 0 = Reset SIO - * 1 = Purge RX buffer - * 2 = Purge TX buffer - * wIndex: Port - * wLength: 0 - * Data: None - * - * The Reset SIO command has this effect: - * - * Sets flow control set to 'none' - * Event char = $0D - * Event trigger = disabled - * Purge RX buffer - * Purge TX buffer - * Clear DTR - * Clear RTS - * baud and data format not reset - * - * The Purge RX and TX buffer commands affect nothing except the buffers - * - */ - -/* FTDI_SIO_MODEM_CTRL */ -/* - * BmRequestType: 0100 0000B - * bRequest: FTDI_SIO_MODEM_CTRL - * wValue: ControlValue (see below) - * wIndex: Port - * wLength: 0 - * Data: None - * - * NOTE: If the device is in RTS/CTS flow control, the RTS set by this - * command will be IGNORED without an error being returned - * Also - you can not set DTR and RTS with one control message - */ +// FTDI_SIO_SET_BAUDRATE +#define FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_BAUDRATE_REQUEST 3 + +enum ftdi_sio_baudrate { + ftdi_sio_b300 = 0, + ftdi_sio_b600 = 1, + ftdi_sio_b1200 = 2, + ftdi_sio_b2400 = 3, + ftdi_sio_b4800 = 4, + ftdi_sio_b9600 = 5, + ftdi_sio_b19200 = 6, + ftdi_sio_b38400 = 7, + ftdi_sio_b57600 = 8, + ftdi_sio_b115200 = 9 +}; + +// FTDI_SIO_SET_DATA +#define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA +#define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8) +#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8) +#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8) +#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8) +#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8) +#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11) +#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11) +#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11) +#define FTDI_SIO_SET_BREAK (0x1 << 14) + +// FTDI_SIO_MODEM_CTRL +#define FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_MODEM_CTRL_REQUEST FTDI_SIO_MODEM_CTRL #define FTDI_SIO_SET_DTR_MASK 0x1 -#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1) -#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0) +#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1) +#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0) #define FTDI_SIO_SET_RTS_MASK 0x2 #define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2) #define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0) -/* - * ControlValue - * B0 DTR state - * 0 = reset - * 1 = set - * B1 RTS state - * 0 = reset - * 1 = set - * B2..7 Reserved - * B8 DTR state enable - * 0 = ignore - * 1 = use DTR state - * B9 RTS state enable - * 0 = ignore - * 1 = use RTS state - * B10..15 Reserved - */ - -/* FTDI_SIO_SET_FLOW_CTRL */ +// FTDI_SIO_SET_FLOW_CTRL +#define FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_FLOW_CTRL_REQUEST FTDI_SIO_SET_FLOW_CTRL #define FTDI_SIO_DISABLE_FLOW_CTRL 0x0 #define FTDI_SIO_RTS_CTS_HS (0x1 << 8) #define FTDI_SIO_DTR_DSR_HS (0x2 << 8) #define FTDI_SIO_XON_XOFF_HS (0x4 << 8) -/* - * BmRequestType: 0100 0000b - * bRequest: FTDI_SIO_SET_FLOW_CTRL - * wValue: Xoff/Xon - * wIndex: Protocol/Port - hIndex is protocol / lIndex is port - * wLength: 0 - * Data: None - * - * hIndex protocol is: - * B0 Output handshaking using RTS/CTS - * 0 = disabled - * 1 = enabled - * B1 Output handshaking using DTR/DSR - * 0 = disabled - * 1 = enabled - * B2 Xon/Xoff handshaking - * 0 = disabled - * 1 = enabled - * - * A value of zero in the hIndex field disables handshaking - * - * If Xon/Xoff handshaking is specified, the hValue field should contain the - * XOFF character and the lValue field contains the XON character. - */ - -/* FTDI_SIO_SET_BAUD_RATE */ -/* - * BmRequestType: 0100 0000B - * bRequest: FTDI_SIO_SET_BAUDRATE - * wValue: BaudDivisor value - see below - * wIndex: Port - * wLength: 0 - * Data: None - * The BaudDivisor values are calculated as follows (too complicated): - */ - -/* FTDI_SIO_SET_DATA */ -#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8) -#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8) -#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8) -#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8) -#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8) -#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11) -#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11) -#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11) -#define FTDI_SIO_SET_BREAK (0x1 << 14) - -/* - * BmRequestType: 0100 0000B - * bRequest: FTDI_SIO_SET_DATA - * wValue: Data characteristics (see below) - * wIndex: Port - * wLength: 0 - * Data: No - * - * Data characteristics - * - * B0..7 Number of data bits - * B8..10 Parity - * 0 = None - * 1 = Odd - * 2 = Even - * 3 = Mark - * 4 = Space - * B11..13 Stop Bits - * 0 = 1 - * 1 = 1.5 - * 2 = 2 - * B14 - * 1 = TX ON (break) - * 0 = TX OFF (normal state) - * B15 Reserved - * - */ - -/* -* DATA FORMAT -* -* IN Endpoint -* -* The device reserves the first two bytes of data on this endpoint to contain -* the current values of the modem and line status registers. In the absence of -* data, the device generates a message consisting of these two status bytes - * every 40 ms - * - * Byte 0: Modem Status -* -* Offset Description -* B0 Reserved - must be 1 -* B1 Reserved - must be 0 -* B2 Reserved - must be 0 -* B3 Reserved - must be 0 -* B4 Clear to Send (CTS) -* B5 Data Set Ready (DSR) -* B6 Ring Indicator (RI) -* B7 Receive Line Signal Detect (RLSD) -* -* Byte 1: Line Status -* -* Offset Description -* B0 Data Ready (DR) -* B1 Overrun Error (OE) -* B2 Parity Error (PE) -* B3 Framing Error (FE) -* B4 Break Interrupt (BI) -* B5 Transmitter Holding Register (THRE) -* B6 Transmitter Empty (TEMT) -* B7 Error in RCVR FIFO -* -*/ -#define FTDI_RS0_CTS (1 << 4) -#define FTDI_RS0_DSR (1 << 5) -#define FTDI_RS0_RI (1 << 6) -#define FTDI_RS0_RLSD (1 << 7) - -#define FTDI_RS_DR 1 -#define FTDI_RS_OE (1<<1) -#define FTDI_RS_PE (1<<2) -#define FTDI_RS_FE (1<<3) -#define FTDI_RS_BI (1<<4) -#define FTDI_RS_THRE (1<<5) -#define FTDI_RS_TEMT (1<<6) -#define FTDI_RS_FIFO (1<<7) +// FTDI_SIO_GET_LATENCY_TIMER +#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST FTDI_SIO_GET_LATENCY_TIMER +#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE 0xC0 + +// FTDI_SIO_SET_LATENCY_TIMER +#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER +#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40 + +// FTDI_SIO_SET_EVENT_CHAR +#define FTDI_SIO_SET_EVENT_CHAR_REQUEST FTDI_SIO_SET_EVENT_CHAR +#define FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE 0x40 + +// FTDI_SIO_GET_MODEM_STATUS +#define FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE 0xc0 +#define FTDI_SIO_GET_MODEM_STATUS_REQUEST FTDI_SIO_GET_MODEM_STATUS +#define FTDI_SIO_CTS_MASK 0x10 +#define FTDI_SIO_DSR_MASK 0x20 +#define FTDI_SIO_RI_MASK 0x40 +#define FTDI_SIO_RLSD_MASK 0x80 + +// FTDI_SIO_SET_BITMODE +#define FTDI_SIO_SET_BITMODE_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_BITMODE_REQUEST FTDI_SIO_SET_BITMODE + +// Possible bitmodes for FTDI_SIO_SET_BITMODE_REQUEST +#define FTDI_SIO_BITMODE_RESET 0x00 +#define FTDI_SIO_BITMODE_CBUS 0x20 + +// FTDI_SIO_READ_PINS +#define FTDI_SIO_READ_PINS_REQUEST_TYPE 0xc0 +#define FTDI_SIO_READ_PINS_REQUEST FTDI_SIO_READ_PINS + +// FTDI_SIO_READ_EEPROM +#define FTDI_SIO_READ_EEPROM_REQUEST_TYPE 0xc0 +#define FTDI_SIO_READ_EEPROM_REQUEST FTDI_SIO_READ_EEPROM + +#define FTDI_FTX_CBUS_MUX_GPIO 0x8 +#define FTDI_FT232R_CBUS_MUX_GPIO 0xa + +#define FTDI_RS0_CTS (1 << 4) +#define FTDI_RS0_DSR (1 << 5) +#define FTDI_RS0_RI (1 << 6) +#define FTDI_RS0_RLSD (1 << 7) + +#define FTDI_RS_DR 1 +#define FTDI_RS_OE (1<<1) +#define FTDI_RS_PE (1<<2) +#define FTDI_RS_FE (1<<3) +#define FTDI_RS_BI (1<<4) +#define FTDI_RS_THRE (1<<5) +#define FTDI_RS_TEMT (1<<6) +#define FTDI_RS_FIFO (1<<7) + +// chip types and names +enum ftdi_chip_type { + SIO = 0, +// FT232A, + FT232B, + FT2232C, + FT232R, + FT232H, + FT2232H, + FT4232H, + FT4232HA, + FT232HP, + FT233HP, + FT2232HP, + FT2233HP, + FT4232HP, + FT4233HP, + FTX, + UNKNOWN +}; + +#define FTDI_CHIP_NAMES \ + [SIO] = (uint8_t const*) "SIO", /* the serial part of FT8U100AX */ \ +/* [FT232A] = (uint8_t const*) "FT232A", */ \ + [FT232B] = (uint8_t const*) "FT232B", \ + [FT2232C] = (uint8_t const*) "FT2232C/D", \ + [FT232R] = (uint8_t const*) "FT232R", \ + [FT232H] = (uint8_t const*) "FT232H", \ + [FT2232H] = (uint8_t const*) "FT2232H", \ + [FT4232H] = (uint8_t const*) "FT4232H", \ + [FT4232HA] = (uint8_t const*) "FT4232HA", \ + [FT232HP] = (uint8_t const*) "FT232HP", \ + [FT233HP] = (uint8_t const*) "FT233HP", \ + [FT2232HP] = (uint8_t const*) "FT2232HP", \ + [FT2233HP] = (uint8_t const*) "FT2233HP", \ + [FT4232HP] = (uint8_t const*) "FT4232HP", \ + [FT4233HP] = (uint8_t const*) "FT4233HP", \ + [FTX] = (uint8_t const*) "FT-X", \ + [UNKNOWN] = (uint8_t const*) "UNKNOWN" + +// private interface data +typedef struct ftdi_private { + enum ftdi_chip_type chip_type; + uint8_t channel; // channel index, or 0 for legacy types +} ftdi_private_t; + +#define FTDI_OK true +#define FTDI_FAIL false +#define FTDI_NOT_POSSIBLE -1 +#define FTDI_REQUESTED -2 + +// division and round function overtaken from math.h +#define DIV_ROUND_CLOSEST(x, divisor)( \ +{ \ + typeof(x) __x = x; \ + typeof(divisor) __d = divisor; \ + (((typeof(x))-1) > 0 || \ + ((typeof(divisor))-1) > 0 || \ + (((__x) > 0) == ((__d) > 0))) ? \ + (((__x) + ((__d) / 2)) / (__d)) : \ + (((__x) - ((__d) / 2)) / (__d)); \ +} \ +) #endif //TUSB_FTDI_SIO_H -- cgit v1.3.1 From 4547737833de0368354cb4823d908c6b250f92df Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 22 Feb 2024 15:42:33 +0100 Subject: improved CP210x support --- src/class/cdc/cdc_host.c | 135 +++++++++++++++++++++++++++++------------- src/class/cdc/serial/cp210x.h | 63 +++++++++++++++++++- src/tusb_option.h | 4 +- 3 files changed, 159 insertions(+), 43 deletions(-) (limited to 'src/class/cdc/serial') 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 diff --git a/src/tusb_option.h b/src/tusb_option.h index ebf9a4d4d..281341685 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.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 -- cgit v1.3.1 From aabee25e189d2a05cb52e5c54329ab1ec875f084 Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 22 Feb 2024 11:45:38 +0100 Subject: added PL2303 support --- examples/host/cdc_msc_hid/src/tusb_config.h | 1 + .../host/cdc_msc_hid_freertos/src/tusb_config.h | 1 + src/class/cdc/cdc_host.c | 754 ++++++++++++++++++++- src/class/cdc/serial/pl2303.h | 172 +++++ src/tusb_option.h | 18 + 5 files changed, 942 insertions(+), 4 deletions(-) create mode 100644 src/class/cdc/serial/pl2303.h (limited to 'src/class/cdc/serial') diff --git a/examples/host/cdc_msc_hid/src/tusb_config.h b/examples/host/cdc_msc_hid/src/tusb_config.h index e4d74077f..fc956c6d3 100644 --- a/examples/host/cdc_msc_hid/src/tusb_config.h +++ b/examples/host/cdc_msc_hid/src/tusb_config.h @@ -106,6 +106,7 @@ #define CFG_TUH_CDC_FTDI 1 // FTDI Serial. FTDI is not part of CDC class, only to re-use CDC driver API #define CFG_TUH_CDC_CP210X 1 // CP210x Serial. CP210X is not part of CDC class, only to re-use CDC driver API #define CFG_TUH_CDC_CH34X 1 // CH340 or CH341 Serial. CH34X is not part of CDC class, only to re-use CDC driver API +#define CFG_TUH_CDC_PL2303 1 // PL2303 Serial. PL2303 is not part of CDC class, only to re-use CDC driver API #define CFG_TUH_HID (3*CFG_TUH_DEVICE_MAX) // typical keyboard + mouse device can have 3-4 HID interfaces #define CFG_TUH_MSC 1 #define CFG_TUH_VENDOR 0 diff --git a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h index 9dc89dc55..dd732c700 100644 --- a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h +++ b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h @@ -111,6 +111,7 @@ #define CFG_TUH_CDC_FTDI 1 // FTDI Serial. FTDI is not part of CDC class, only to re-use CDC driver API #define CFG_TUH_CDC_CP210X 1 // CP210x Serial. CP210X is not part of CDC class, only to re-use CDC driver API #define CFG_TUH_CDC_CH34X 1 // CH340 or CH341 Serial. CH34X is not part of CDC class, only to re-use CDC driver API +#define CFG_TUH_CDC_PL2303 1 // PL2303 Serial. PL2303 is not part of CDC class, only to re-use CDC driver API #define CFG_TUH_HID (3*CFG_TUH_DEVICE_MAX) // typical keyboard + mouse device can have 3-4 HID interfaces #define CFG_TUH_MSC 1 #define CFG_TUH_VENDOR 0 diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 86c010fa7..429d0a113 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -38,6 +38,7 @@ #include "serial/ftdi_sio.h" #include "serial/cp210x.h" #include "serial/ch34x.h" +#include "serial/pl2303.h" // Level where CFG_TUSB_DEBUG must be at least for this driver is logged #ifndef CFG_TUH_CDC_LOG_LEVEL @@ -91,6 +92,10 @@ typedef struct { ftdi_private_t ftdi; #endif + #if CFG_TUH_CDC_PL2303 + pl2303_private_t pl2303; + #endif + struct { tu_edpt_stream_t tx; tu_edpt_stream_t rx; @@ -105,7 +110,7 @@ typedef struct { CFG_TUH_MEM_SECTION static cdch_interface_t cdch_data[CFG_TUH_CDC]; -#if CFG_TUH_CDC_FTDI +#if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_PL2303 static tusb_desc_device_t desc_dev[CFG_TUH_CDC][CFG_TUH_ENUMERATION_BUFSIZE]; #endif @@ -164,6 +169,22 @@ static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complet static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #endif +//------------- PL2303 prototypes -------------// +#if CFG_TUH_CDC_PL2303 +static uint16_t const pl2303_vid_pid_list[][2] = {CFG_TUH_CDC_PL2303_VID_PID_LIST}; +static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {PL2303_TYPE_DATA}; + +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_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); +static bool pl2303_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool pl2303_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +#endif + //------------- Common -------------// enum { SERIAL_DRIVER_ACM = 0, @@ -180,6 +201,10 @@ enum { SERIAL_DRIVER_CH34X, #endif +#if CFG_TUH_CDC_PL2303 + SERIAL_DRIVER_PL2303, +#endif + SERIAL_DRIVER_COUNT }; @@ -260,6 +285,22 @@ static const cdch_serial_driver_t serial_drivers[] = { #endif }, #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_config, + .set_control_line_state = pl2303_set_modem_ctrl, + .set_baudrate = pl2303_set_baudrate, + .set_data_format = pl2303_set_data_format, + .set_line_coding = pl2303_set_line_coding, + #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL + .name = (uint8_t const *) "PL2303" + #endif + } + #endif }; TU_VERIFY_STATIC(TU_ARRAY_SIZE(serial_drivers) == SERIAL_DRIVER_COUNT, "Serial driver count mismatch"); @@ -1863,7 +1904,7 @@ static bool ch34x_modem_ctrl_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t com // internal control complete to update state such as line state, encoding static void ch34x_internal_control_complete(tuh_xfer_t * xfer) { - // CH34x only has 1 interface and wIndex used as payload and not for bInterfaceNumber + // CH34x has only interface 0, because wIndex is used as payload and not for bInterfaceNumber uint8_t const itf_num = 0; uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); cdch_interface_t* p_cdc = get_itf(idx); @@ -1921,7 +1962,8 @@ static bool ch34x_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_ } static void ch34x_set_line_coding_stage1_complete(tuh_xfer_t * xfer) { - uint8_t const itf_num = 0; // CH34x has only interface 0, because wIndex is used as payload and not for bInterfaceNumber + // CH34x has only interface 0, because wIndex is used as payload and not for bInterfaceNumber + uint8_t const itf_num = 0; set_line_coding_stage1_complete(xfer, itf_num, ch34x_write_reg_data_format, // control request function to set data format ch34x_internal_control_complete); // control complete function to be called after request @@ -1981,7 +2023,7 @@ static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uin } static void ch34x_process_config(tuh_xfer_t* xfer) { - // CH34x only has 1 interface and wIndex used as payload and not for bInterfaceNumber + // CH34x has only interface 0, because wIndex is used as payload and not for bInterfaceNumber uintptr_t const state = xfer->user_data; uint8_t const itf_num = 0; uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); @@ -2142,4 +2184,708 @@ static uint8_t ch34x_get_lcr(cdch_interface_t * p_cdc) { #endif // CFG_TUH_CDC_CH34X +//--------------------------------------------------------------------+ +// PL2303 +//--------------------------------------------------------------------+ +#if CFG_TUH_CDC_PL2303 + +static int8_t pl2303_detect_type(cdch_interface_t * p_cdc, uint8_t const idx, uint8_t step, + tuh_xfer_cb_t complete_cb, uintptr_t user_data); +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) { + tusb_control_request_t const request_setup = { + .bmRequestType = requesttype, + .bRequest = request, + .wValue = tu_htole16 (value), + .wIndex = tu_htole16 (index), + .wLength = tu_htole16 (length) + }; + + // use usbh enum buf since application variable does not live long enough + uint8_t * enum_buf = NULL; + + if (buffer && length > 0) { + enum_buf = usbh_get_enum_buf(); + if (request_setup.bmRequestType_bit.direction == TUSB_DIR_OUT) { + tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length); + } + } + + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request_setup, + .buffer = enum_buf, + .complete_cb = complete_cb, + .user_data = user_data + }; + + return tuh_control_xfer(&xfer); +} + +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; + + 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; + + 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) +{ + uint8_t buf; + + 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) +{ + return pl2303_set_request(p_cdc, PL2303_SET_CONTROL_REQUEST, PL2303_SET_CONTROL_REQUEST_TYPE, + p_cdc->requested_line_state, 0, NULL, 0, complete_cb, user_data); +} + +//static bool pl2303_get_line_request(cdch_interface_t * p_cdc, uint8_t buf[PL2303_LINE_CODING_BUFSIZE]) +//{ +// return pl2303_set_request(p_cdc, PL2303_GET_LINE_REQUEST, PL2303_GET_LINE_REQUEST_TYPE, 0, 0, buf, PL2303_LINE_CODING_BUFSIZE); +//} + +static bool pl2303_set_line_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + // the caller has to precheck, that the new line coding different than the current, else false returned + uint8_t buf[PL2303_LINE_CODING_BUFSIZE]; + /* + * Some PL2303 are known to lose bytes if you change serial settings + * even to the same values as before. Thus we actually need to filter + * in this specific case. + */ + // TODO really necessary to check? what to do in this case when no transfer will happen? + // callback is not called... + TU_VERIFY(memcmp(&p_cdc->requested_line_coding, &p_cdc->line_coding, sizeof(cdc_line_coding_t) ) != 0); + + /* For reference buf[6] data bits value */ + TU_VERIFY(p_cdc->requested_line_coding.data_bits >= 5 && p_cdc->requested_line_coding.data_bits <= 8, 0); + buf[6] = p_cdc->requested_line_coding.data_bits; + + /* For reference buf[0]:buf[3] baud rate value */ + TU_VERIFY(pl2303_encode_baud_rate(p_cdc, &buf[0])); + + /* For reference buf[4]=0 is 1 stop bits */ + /* For reference buf[4]=1 is 1.5 stop bits */ + /* For reference buf[4]=2 is 2 stop bits */ + buf[4] = p_cdc->requested_line_coding.stop_bits; // PL2303 has the same coding + + /* For reference buf[5]=0 is none parity */ + /* For reference buf[5]=1 is odd parity */ + /* For reference buf[5]=2 is even parity */ + /* For reference buf[5]=3 is mark parity */ + /* For reference buf[5]=4 is space parity */ + buf[5] = p_cdc->requested_line_coding.parity; // PL2303 has the same coding + + return pl2303_set_request(p_cdc, PL2303_SET_LINE_REQUEST, PL2303_SET_LINE_REQUEST_TYPE, 0, 0, + buf, PL2303_LINE_CODING_BUFSIZE, complete_cb, user_data); +} + +//static bool pl2303_set_break(cdch_interface_t * p_cdc, bool enable) +//{ +// uint16_t state = enable ? PL2303_BREAK_ON : PL2303_BREAK_OFF; +// 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) +{ + /* 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! + */ + return pl2303_set_request(p_cdc, TUSB_REQ_CLEAR_FEATURE, PL2303_CLEAR_HALT_REQUEST_TYPE, TUSB_REQ_FEATURE_EDPT_HALT, endp, + NULL, 0, complete_cb, user_data); +} + +//------------- Driver API -------------// + +// internal control complete to update state such as line state, encoding +static void pl2303_internal_control_complete(tuh_xfer_t * xfer) { + // PL2303 has only interface 0, because wIndex is used as payload and not for bInterfaceNumber + uint8_t const itf_num = 0; + uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + 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); + + if (success) { + if (xfer->setup->bRequest == PL2303_SET_LINE_REQUEST && + xfer->setup->bmRequestType == PL2303_SET_LINE_REQUEST_TYPE) { + p_cdc->line_coding = p_cdc->requested_line_coding; + } + if (xfer->setup->bRequest == PL2303_SET_CONTROL_REQUEST && + xfer->setup->bmRequestType == PL2303_SET_CONTROL_REQUEST_TYPE) { + p_cdc->line_state = p_cdc->requested_line_state; + } + } + + xfer->complete_cb = p_cdc->user_control_cb; + if (xfer->complete_cb) { + xfer->complete_cb(xfer); + } +} + +static bool pl2303_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + p_cdc->user_control_cb = complete_cb; + TU_ASSERT(pl2303_set_line_request(p_cdc, complete_cb ? pl2303_internal_control_complete : NULL, user_data)); + + return true; +} + +static bool pl2303_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + p_cdc->requested_line_coding.bit_rate = p_cdc->line_coding.bit_rate; + p_cdc->user_control_cb = complete_cb; + TU_ASSERT(pl2303_set_line_request(p_cdc, complete_cb ? pl2303_internal_control_complete : NULL, user_data)); + + return true; +} + +static bool pl2303_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + p_cdc->requested_line_coding.stop_bits = p_cdc->line_coding.stop_bits; + p_cdc->requested_line_coding.parity = p_cdc->line_coding.parity; + p_cdc->requested_line_coding.data_bits = p_cdc->line_coding.data_bits; + p_cdc->user_control_cb = complete_cb; + TU_ASSERT(pl2303_set_line_request(p_cdc, complete_cb ? pl2303_internal_control_complete : NULL, user_data)); + + return true; +} + +static bool pl2303_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + // PL2303 has the same bit coding + p_cdc->user_control_cb = complete_cb; + TU_ASSERT(pl2303_set_control_lines(p_cdc, complete_cb ? pl2303_internal_control_complete : NULL, user_data)); + + return true; +} + +//------------- Enumeration -------------// + +enum { + CONFIG_PL2303_GET_DESC = 0, + CONFIG_PL2303_DETECT_TYPE, + CONFIG_PL2303_READ1, + CONFIG_PL2303_WRITE1, + CONFIG_PL2303_READ2, + CONFIG_PL2303_READ3, + CONFIG_PL2303_READ4, + CONFIG_PL2303_WRITE2, + CONFIG_PL2303_READ5, + CONFIG_PL2303_READ6, + CONFIG_PL2303_WRITE3, + CONFIG_PL2303_WRITE4, + CONFIG_PL2303_WRITE5, + CONFIG_PL2303_RESET_ENDP1, + CONFIG_PL2303_RESET_ENDP2, + CONFIG_PL2303_LINE_CODING, + CONFIG_PL2303_MODEM_CONTROL, + CONFIG_PL2303_FLOW_CTRL_READ, + CONFIG_PL2303_FLOW_CTRL_WRITE, + CONFIG_PL2303_COMPLETE +}; + +static bool pl2303_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len) { + // PL2303 Interface includes 1 vendor interface + 1 interrupt endpoints + 2 bulk + TU_VERIFY(itf_desc->bNumEndpoints == 3); + TU_VERIFY(sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t) <= max_len); + + cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); + TU_VERIFY(p_cdc); + + p_cdc->serial_drid = SERIAL_DRIVER_PL2303; + p_cdc->pl2303.serial_private.quirks = 0; + p_cdc->pl2303.supports_hx_status = false; + + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const * ) tu_desc_next(itf_desc); + + // Interrupt endpoint: not used for now + TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(desc_ep) && + TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer); + TU_ASSERT(tuh_edpt_open(daddr, desc_ep)); + p_cdc->ep_notif = desc_ep->bEndpointAddress; + desc_ep += 1; + + // data endpoints expected to be in pairs + TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep)); + + return true; +} + +static void pl2303_process_config(tuh_xfer_t * xfer) { + // 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; + int8_t type; + + switch (xfer->user_data) { + + // 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 + // get device descriptor + TU_ASSERT_COMPLETE(tuh_descriptor_get_device(xfer->daddr, desc_dev[idx], sizeof(tusb_desc_device_t), + pl2303_process_config, CONFIG_PL2303_DETECT_TYPE)); + break; + + case CONFIG_PL2303_DETECT_TYPE: + // get type and quirks (step 1) + type = pl2303_detect_type (p_cdc, idx, 1, pl2303_process_config, CONFIG_PL2303_READ1); // step 1 + TU_ASSERT_COMPLETE(type!=PL2303_DETECT_TYPE_FAILED); + if (type == PL2303_SUPPORTS_HX_STATUS_TRIGGERED) { + break; + } // else: no transfer triggered and continue with CONFIG_PL2303_READ1 + TU_ATTR_FALLTHROUGH; + + case CONFIG_PL2303_READ1: + // get supports_hx_status, type and quirks (step 2), do special read + 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, idx, 2, NULL, 0); // step 2 now with supports_hx_status + TU_ASSERT_COMPLETE(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 + TU_LOG(CFG_TUH_CDC_LOG_LEVEL, "PL2303 bDeviceClass = 0x%02x bMaxPacketSize0 = %u bcdUSB = 0x%04x bcdDevice = 0x%04x", + desc_dev[idx]->bDeviceClass, desc_dev[idx]->bMaxPacketSize0, desc_dev[idx]->bcdUSB, desc_dev[idx]->bcdDevice ); + uint16_t vid, pid; + TU_ASSERT_COMPLETE(tuh_vid_pid_get(p_cdc->daddr, &vid, &pid)); + TU_LOG(CFG_TUH_CDC_LOG_LEVEL, " 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)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + 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)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + 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)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + 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)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + 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)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + 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)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + 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)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + 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)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + 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)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + 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)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + 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)); + } + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + // from here sequence overtaken from Linux Kernel function pl2303_open() + 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)); + } 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 + PL2303_HXN_RESET_UPSTREAM_PIPE | PL2303_HXN_RESET_DOWNSTREAM_PIPE, + pl2303_process_config, CONFIG_PL2303_LINE_CODING)); + } else { + pl2303_vendor_write(p_cdc, 8, 0, pl2303_process_config, CONFIG_PL2303_RESET_ENDP2); + } + } + break; + + 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)); + } 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)); + } + } + break; + + // from here sequence overtaken from Linux Kernel function pl2303_set_termios() + // unnecessary pl2303_get_line_request() is skipped due to a stall + 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)); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + + case CONFIG_PL2303_MODEM_CONTROL: + #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + TU_ASSERT_COMPLETE(pl2303_set_control_lines(p_cdc, pl2303_internal_control_complete, CONFIG_PL2303_FLOW_CTRL_READ)); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + + case CONFIG_PL2303_FLOW_CTRL_READ: + // read flow control register for modify & write back in next step + if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) { + TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, PL2303_HXN_FLOWCTRL_REG, &buf, pl2303_process_config, + CONFIG_PL2303_FLOW_CTRL_WRITE)); + } else { + TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0, &buf, pl2303_process_config, CONFIG_PL2303_FLOW_CTRL_WRITE)); + } + break; + + case CONFIG_PL2303_FLOW_CTRL_WRITE: + // no flow control + buf = xfer->buffer[0]; + 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)); + } else { + buf &= (uint8_t) ~PL2303_FLOWCTRL_MASK; + TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 0, buf, pl2303_process_config, CONFIG_PL2303_COMPLETE)); + } + break; + + case CONFIG_PL2303_COMPLETE: + set_config_complete(idx, 0, true); + break; + + default: + set_config_complete(idx, 0, false); + break; + } +} + +//------------- Helper -------------// + +static int8_t pl2303_detect_type(cdch_interface_t * p_cdc, uint8_t const idx, uint8_t step, + tuh_xfer_cb_t complete_cb, uintptr_t user_data ) +{ + /* + * Legacy PL2303H, variants 0 and 1 (difference unknown). + */ + if (desc_dev[idx]->bDeviceClass == 0x02) { + return TYPE_H; /* variant 0 */ + } + + if (desc_dev[idx]->bMaxPacketSize0 != 0x40) { + if (desc_dev[idx]->bDeviceClass == 0x00 || desc_dev[idx]->bDeviceClass == 0xff) { + return TYPE_H; /* variant 1 */ + } + return TYPE_H; /* variant 0 */ + } + + switch (desc_dev[idx]->bcdUSB) { + case 0x101: + /* USB 1.0.1? Let's assume they meant 1.1... */ + TU_ATTR_FALLTHROUGH; + case 0x110: + switch (desc_dev[idx]->bcdDevice) { + case 0x300: + return TYPE_HX; + case 0x400: + return TYPE_HXD; + default: + return TYPE_HX; + } + break; + case 0x200: + switch (desc_dev[idx]->bcdDevice) { + case 0x100: /* GC */ + case 0x105: + return TYPE_HXN; + case 0x300: /* GT / TA */ + if (step == 1) { + // step 1 trigger pl2303_supports_hx_status() request + TU_ASSERT(pl2303_supports_hx_status (p_cdc, complete_cb, user_data), PL2303_DETECT_TYPE_FAILED); + return PL2303_SUPPORTS_HX_STATUS_TRIGGERED; + } else { + // step 2 use supports_hx_status + if (p_cdc->pl2303.supports_hx_status) { + return TYPE_TA; + } + } + TU_ATTR_FALLTHROUGH; + case 0x305: + case 0x400: /* GL */ + case 0x405: + return TYPE_HXN; + case 0x500: /* GE / TB */ + if (step == 1) { + // step 1 trigger pl2303_supports_hx_status() request + TU_ASSERT(pl2303_supports_hx_status (p_cdc, complete_cb, user_data), PL2303_DETECT_TYPE_FAILED); + return PL2303_SUPPORTS_HX_STATUS_TRIGGERED; + } else { + // step 2 use supports_hx_status + if (p_cdc->pl2303.supports_hx_status) { + return TYPE_TB; + } + } + TU_ATTR_FALLTHROUGH; + case 0x505: + case 0x600: /* GS */ + case 0x605: + case 0x700: /* GR */ + case 0x705: + return TYPE_HXN; + default: + break; + } + break; + default: break; + } + + TU_LOG_P_CDC("unknown device type bcdUSB = 0x%04x", desc_dev[idx]->bcdUSB); + + return PL2303_DETECT_TYPE_FAILED; +} + +/* + * 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 const uint32_t baud_sup[] = { + 75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, + 14400, 19200, 28800, 38400, 57600, 115200, 230400, 460800, + 614400, 921600, 1228800, 2457600, 3000000, 6000000 + }; + + uint8_t i; + for (i = 0; i < TU_ARRAY_SIZE(baud_sup); ++i) { + if (baud_sup[i] > baud) { + break; + } + } + + if (i == TU_ARRAY_SIZE(baud_sup)) { + baud = baud_sup[i - 1]; + } else if (i > 0 && (baud_sup[i] - baud) > (baud - baud_sup[i - 1])) { + baud = baud_sup[i - 1]; + } else { + baud = baud_sup[i]; + } + + return 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) +{ + uint32_t baud_le = tu_htole32(baud); + buf[0] = (uint8_t) ( baud_le & 0xff); + buf[1] = (uint8_t) ((baud_le >> 8) & 0xff); + buf[2] = (uint8_t) ((baud_le >> 16) & 0xff); + buf[3] = (uint8_t) ((baud_le >> 24) & 0xff); + + return 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; + + /* + * Apparently the formula is: + * baudrate = 12M * 32 / (mantissa * 4^exponent) + * where + * mantissa = buf[8:0] + * exponent = buf[11:9] + */ + baseline = 12000000 * 32; + mantissa = baseline / baud; + if (mantissa == 0) + mantissa = 1; /* Avoid dividing by zero if baud > 32 * 12M. */ + exponent = 0; + while (mantissa >= 512) { + if (exponent < 7) { + mantissa >>= 2; /* divide by 4 */ + exponent++; + } else { + /* Exponent is maxed. Trim mantissa and leave. */ + mantissa = 511; + break; + } + } + + buf[3] = 0x80; + buf[2] = 0; + buf[1] = (uint8_t) ((exponent << 1 | mantissa >> 8) & 0xff); + buf[0] = (uint8_t) (mantissa & 0xff); + + /* Calculate and return the exact baud rate. */ + baud = (baseline / mantissa) >> (exponent << 1); + + return 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; + + /* + * Apparently, for the TA version the formula is: + * baudrate = 12M * 32 / (mantissa * 2^exponent) + * where + * mantissa = buf[10:0] + * exponent = buf[15:13 16] + */ + baseline = 12000000 * 32; + mantissa = baseline / baud; + if (mantissa == 0) { + mantissa = 1; /* Avoid dividing by zero if baud > 32 * 12M. */ + } + exponent = 0; + while (mantissa >= 2048) { + if (exponent < 15) { + mantissa >>= 1; /* divide by 2 */ + exponent++; + } else { + /* Exponent is maxed. Trim mantissa and leave. */ + mantissa = 2047; + break; + } + } + + buf[3] = 0x80; + buf[2] = (uint8_t) (exponent & 0x01); + buf[1] = (uint8_t) (((exponent & (uint32_t) ~0x01) << 4 | mantissa >> 8 ) & 0xff); + buf[0] = (uint8_t) (mantissa & 0xff); + + /* Calculate and return the exact baud rate. */ + baud = (baseline / mantissa) >> exponent; + + return baud; +} + +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; + + TU_VERIFY(baud && baud <= p_cdc->pl2303.serial_private.type->max_baud_rate); + /* + * Use direct method for supported baud rates, otherwise use divisors. + * Newer chip types do not support divisor encoding. + */ + if (p_cdc->pl2303.serial_private.type->no_divisors) { + baud_sup = baud; + } else { + baud_sup = pl2303_get_supported_baud_rate(baud); + } + + if (baud == baud_sup) { + baud = pl2303_encode_baud_rate_direct(buf, baud); + } else if (p_cdc->pl2303.serial_private.type->alt_divisors) { + baud = pl2303_encode_baud_rate_divisor_alt(buf, baud); + } else { + baud = pl2303_encode_baud_rate_divisor(buf, baud); + } + TU_LOG_P_CDC("real baudrate = %lu", baud); + + return true; +} + +#endif // CFG_TUH_CDC_PL2303 + #endif diff --git a/src/class/cdc/serial/pl2303.h b/src/class/cdc/serial/pl2303.h new file mode 100644 index 000000000..bf264191b --- /dev/null +++ b/src/class/cdc/serial/pl2303.h @@ -0,0 +1,172 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 Heiko Kuester + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _PL2303_H_ +#define _PL2303_H_ + +#include +#include + +// There is no official documentation for the PL2303 chips. +// Reference can be found +// - https://github.com/torvalds/linux/blob/master/drivers/usb/serial/pl2303.h and +// https://github.com/torvalds/linux/blob/master/drivers/usb/serial/pl2303.c +// - https://github.com/freebsd/freebsd-src/blob/main/sys/dev/usb/serial/uplcom.c + +/* quirks */ +#define PL2303_QUIRK_UART_STATE_IDX0 1 +#define PL2303_QUIRK_LEGACY 2 +#define PL2303_QUIRK_ENDPOINT_HACK 4 + +/* requests and bits */ +#define PL2303_SET_LINE_REQUEST_TYPE 0x21 // class request host to device interface +#define PL2303_SET_LINE_REQUEST 0x20 // dec 32 + +#define PL2303_SET_CONTROL_REQUEST_TYPE 0x21 // class request host to device interface +#define PL2303_SET_CONTROL_REQUEST 0x22 // dec 34 +#define PL2303_CONTROL_DTR 0x01 // dec 1 +#define PL2303_CONTROL_RTS 0x02 // dec 2 + +#define PL2303_BREAK_REQUEST_TYPE 0x21 // class request host to device interface +#define PL2303_BREAK_REQUEST 0x23 // dec 35 +#define PL2303_BREAK_ON 0xffff +#define PL2303_BREAK_OFF 0x0000 + +#define PL2303_GET_LINE_REQUEST_TYPE 0xa1 // class request device to host interface +#define PL2303_GET_LINE_REQUEST 0x21 // dec 33 + +#define PL2303_VENDOR_WRITE_REQUEST_TYPE 0x40 // vendor request host to device interface +#define PL2303_VENDOR_WRITE_REQUEST 0x01 // dec 1 +#define PL2303_VENDOR_WRITE_NREQUEST 0x80 // dec 128 + +#define PL2303_VENDOR_READ_REQUEST_TYPE 0xc0 // vendor request device to host interface +#define PL2303_VENDOR_READ_REQUEST 0x01 // dec 1 +#define PL2303_VENDOR_READ_NREQUEST 0x81 // dec 129 + +#define PL2303_UART_STATE_INDEX 8 +#define PL2303_UART_STATE_MSR_MASK 0x8b +#define PL2303_UART_STATE_TRANSIENT_MASK 0x74 +#define PL2303_UART_DCD 0x01 +#define PL2303_UART_DSR 0x02 +#define PL2303_UART_BREAK_ERROR 0x04 +#define PL2303_UART_RING 0x08 +#define PL2303_UART_FRAME_ERROR 0x10 +#define PL2303_UART_PARITY_ERROR 0x20 +#define PL2303_UART_OVERRUN_ERROR 0x40 +#define PL2303_UART_CTS 0x80 + +#define PL2303_FLOWCTRL_MASK 0xf0 + +#define PL2303_CLEAR_HALT_REQUEST_TYPE 0x02 // standard request host to device endpoint + +/* registers via vendor read/write requests */ +#define PL2303_READ_TYPE_HX_STATUS 0x8080 + +#define PL2303_HXN_RESET_REG 0x07 +#define PL2303_HXN_RESET_UPSTREAM_PIPE 0x02 +#define PL2303_HXN_RESET_DOWNSTREAM_PIPE 0x01 + +#define PL2303_HXN_FLOWCTRL_REG 0x0a +#define PL2303_HXN_FLOWCTRL_MASK 0x1c +#define PL2303_HXN_FLOWCTRL_NONE 0x1c +#define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18 +#define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c + +/* type data */ +enum pl2303_type { + TYPE_H, + TYPE_HX, + TYPE_TA, + TYPE_TB, + TYPE_HXD, + TYPE_HXN, + TYPE_COUNT +}; + +struct pl2303_type_data { + uint8_t const *name; + uint32_t const max_baud_rate; + uint8_t const quirks; + uint16_t const no_autoxonxoff:1; + uint16_t const no_divisors:1; + uint16_t const alt_divisors:1; +}; + +#define PL2303_TYPE_DATA \ + [TYPE_H] = { \ + .name = (uint8_t const*)"H", \ + .max_baud_rate = 1228800, \ + .quirks = PL2303_QUIRK_LEGACY, \ + .no_autoxonxoff = true, \ + }, \ + [TYPE_HX] = { \ + .name = (uint8_t const*)"HX", \ + .max_baud_rate = 6000000, \ + }, \ + [TYPE_TA] = { \ + .name = (uint8_t const*)"TA", \ + .max_baud_rate = 6000000, \ + .alt_divisors = true, \ + }, \ + [TYPE_TB] = { \ + .name = (uint8_t const*)"TB", \ + .max_baud_rate = 12000000, \ + .alt_divisors = true, \ + }, \ + [TYPE_HXD] = { \ + .name = (uint8_t const*)"HXD", \ + .max_baud_rate = 12000000, \ + }, \ + [TYPE_HXN] = { \ + .name = (uint8_t const*)"G (HXN)", \ + .max_baud_rate = 12000000, \ + .no_divisors = true, \ + } + +/* private data types */ +struct pl2303_serial_private { + const struct pl2303_type_data* type; + uint8_t quirks; +}; + +typedef struct TU_ATTR_PACKED { + struct pl2303_serial_private serial_private; + bool supports_hx_status; +} pl2303_private_t; + +/* buffer sizes for line coding data */ +#define PL2303_LINE_CODING_BUFSIZE 7 +#define PL2303_LINE_CODING_BAUDRATE_BUFSIZE 4 + +/* bulk endpoints */ +#define PL2303_OUT_EP 0x02 +#define PL2303_IN_EP 0x83 + +/* return values of pl2303_detect_type() */ +#define PL2303_SUPPORTS_HX_STATUS_TRIGGERED -1 +#define PL2303_DETECT_TYPE_FAILED -2 + +#endif /* _PL2303_H_ */ diff --git a/src/tusb_option.h b/src/tusb_option.h index 281341685..f023b490a 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -504,6 +504,24 @@ { 0x9986, 0x7523 } /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */ #endif +#ifndef CFG_TUH_CDC_PL2303 + // PL2303 is not part of CDC class, only to re-use CDC driver API + #define CFG_TUH_CDC_PL2303 0 +#endif + +#ifndef CFG_TUH_CDC_PL2303_VID_PID_QUIRKS_LIST + // List of product IDs that can use the PL2303 CDC driver + #define CFG_TUH_CDC_PL2303_VID_PID_LIST \ + { 0x067b, 0x2303 }, /* initial 2303 */ \ + { 0x067b, 0x2304 }, /* TB */ \ + { 0x067b, 0x23a3 }, /* GC */ \ + { 0x067b, 0x23b3 }, /* GB */ \ + { 0x067b, 0x23c3 }, /* GT */ \ + { 0x067b, 0x23d3 }, /* GL */ \ + { 0x067b, 0x23e3 }, /* GE */ \ + { 0x067b, 0x23f3 } /* GS */ +#endif + #ifndef CFG_TUH_HID #define CFG_TUH_HID 0 #endif -- cgit v1.3.1 From cb69ed0d0423b8e09ae77307fcc01165ae4c9386 Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 21 Mar 2024 08:29:28 +0100 Subject: code style and clean up CDC serial header files --- src/class/cdc/serial/ch34x.h | 70 ++++++++-------- src/class/cdc/serial/cp210x.h | 86 ++++++++++---------- src/class/cdc/serial/ftdi_sio.h | 172 ++++++++++++++++++++-------------------- src/class/cdc/serial/pl2303.h | 146 +++++++++++++++++----------------- 4 files changed, 237 insertions(+), 237 deletions(-) (limited to 'src/class/cdc/serial') diff --git a/src/class/cdc/serial/ch34x.h b/src/class/cdc/serial/ch34x.h index c18066f57..7d91f01fe 100644 --- a/src/class/cdc/serial/ch34x.h +++ b/src/class/cdc/serial/ch34x.h @@ -24,8 +24,8 @@ * This file is part of the TinyUSB stack. */ -#ifndef _CH34X_H_ -#define _CH34X_H_ +#ifndef TUSB_CH34X_H +#define TUSB_CH34X_H // There is no official documentation for the CH34x (CH340, CH341) chips. Reference can be found // - https://github.com/WCHSoftGroup/ch341ser_linux @@ -40,45 +40,45 @@ #endif // USB requests -#define CH34X_REQ_READ_VERSION 0x5F // dec 95 -#define CH34X_REQ_WRITE_REG 0x9A // dec 154 -#define CH34X_REQ_READ_REG 0x95 // dec 149 -#define CH34X_REQ_SERIAL_INIT 0xA1 // dec 161 -#define CH34X_REQ_MODEM_CTRL 0xA4 // dev 164 +#define CH34X_REQ_READ_VERSION 0x5F // dec 95 +#define CH34X_REQ_WRITE_REG 0x9A // dec 154 +#define CH34X_REQ_READ_REG 0x95 // dec 149 +#define CH34X_REQ_SERIAL_INIT 0xA1 // dec 161 +#define CH34X_REQ_MODEM_CTRL 0xA4 // dev 164 // registers -#define CH34X_REG_BREAK 0x05 -#define CH34X_REG_PRESCALER 0x12 -#define CH34X_REG_DIVISOR 0x13 -#define CH34X_REG_LCR 0x18 -#define CH34X_REG_LCR2 0x25 -#define CH34X_REG_MCR_MSR 0x06 -#define CH34X_REG_MCR_MSR2 0x07 -#define CH34X_NBREAK_BITS 0x01 +#define CH34X_REG_BREAK 0x05 +#define CH34X_REG_PRESCALER 0x12 +#define CH34X_REG_DIVISOR 0x13 +#define CH34X_REG_LCR 0x18 +#define CH34X_REG_LCR2 0x25 +#define CH34X_REG_MCR_MSR 0x06 +#define CH34X_REG_MCR_MSR2 0x07 +#define CH34X_NBREAK_BITS 0x01 -#define CH341_REG_0x0F 0x0F // undocumented register -#define CH341_REG_0x2C 0x2C // undocumented register -#define CH341_REG_0x27 0x27 // hardware flow control (cts/rts) +#define CH341_REG_0x0F 0x0F // undocumented register +#define CH341_REG_0x2C 0x2C // undocumented register +#define CH341_REG_0x27 0x27 // hardware flow control (cts/rts) -#define CH34X_REG16_DIVISOR_PRESCALER TU_U16(CH34X_REG_DIVISOR, CH34X_REG_PRESCALER) -#define CH32X_REG16_LCR2_LCR TU_U16(CH34X_REG_LCR2, CH34X_REG_LCR) +#define CH34X_REG16_DIVISOR_PRESCALER TU_U16(CH34X_REG_DIVISOR, CH34X_REG_PRESCALER) +#define CH32X_REG16_LCR2_LCR TU_U16(CH34X_REG_LCR2, CH34X_REG_LCR) // modem control bits -#define CH34X_BIT_RTS ( 1 << 6 ) -#define CH34X_BIT_DTR ( 1 << 5 ) +#define CH34X_BIT_RTS (1 << 6) +#define CH34X_BIT_DTR (1 << 5) // line control bits -#define CH34X_LCR_ENABLE_RX 0x80 -#define CH34X_LCR_ENABLE_TX 0x40 -#define CH34X_LCR_MARK_SPACE 0x20 -#define CH34X_LCR_PAR_EVEN 0x10 -#define CH34X_LCR_ENABLE_PAR 0x08 -#define CH34X_LCR_PAR_MASK 0x38 // all parity bits -#define CH34X_LCR_STOP_BITS_2 0x04 -#define CH34X_LCR_CS8 0x03 -#define CH34X_LCR_CS7 0x02 -#define CH34X_LCR_CS6 0x01 -#define CH34X_LCR_CS5 0x00 -#define CH34X_LCR_CS_MASK 0x03 // all CSx bits +#define CH34X_LCR_ENABLE_RX 0x80 +#define CH34X_LCR_ENABLE_TX 0x40 +#define CH34X_LCR_MARK_SPACE 0x20 +#define CH34X_LCR_PAR_EVEN 0x10 +#define CH34X_LCR_ENABLE_PAR 0x08 +#define CH34X_LCR_PAR_MASK 0x38 // all parity bits +#define CH34X_LCR_STOP_BITS_2 0x04 +#define CH34X_LCR_CS8 0x03 +#define CH34X_LCR_CS7 0x02 +#define CH34X_LCR_CS6 0x01 +#define CH34X_LCR_CS5 0x00 +#define CH34X_LCR_CS_MASK 0x03 // all CSx bits -#endif /* _CH34X_H_ */ +#endif // TUSB_CH34X_H diff --git a/src/class/cdc/serial/cp210x.h b/src/class/cdc/serial/cp210x.h index e18da7d51..a553a54da 100644 --- a/src/class/cdc/serial/cp210x.h +++ b/src/class/cdc/serial/cp210x.h @@ -31,7 +31,7 @@ // 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_SET_BAUDDIV 0x01 #define CP210X_GET_BAUDDIV 0x02 @@ -60,56 +60,56 @@ #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_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_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 +// 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_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 +#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_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 +// 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_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 +#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" diff --git a/src/class/cdc/serial/ftdi_sio.h b/src/class/cdc/serial/ftdi_sio.h index 42716f73e..4afedec9b 100644 --- a/src/class/cdc/serial/ftdi_sio.h +++ b/src/class/cdc/serial/ftdi_sio.h @@ -28,43 +28,43 @@ #include // Commands -#define FTDI_SIO_RESET 0 // Reset the port -#define FTDI_SIO_MODEM_CTRL 1 // Set the modem control register -#define FTDI_SIO_SET_FLOW_CTRL 2 // Set flow control register -#define FTDI_SIO_SET_BAUD_RATE 3 // Set baud rate -#define FTDI_SIO_SET_DATA 4 // Set the data characteristics of the port -#define FTDI_SIO_GET_MODEM_STATUS 5 // Retrieve current value of modem status register -#define FTDI_SIO_SET_EVENT_CHAR 6 // Set the event character -#define FTDI_SIO_SET_ERROR_CHAR 7 // Set the error character -#define FTDI_SIO_SET_LATENCY_TIMER 9 // Set the latency timer -#define FTDI_SIO_GET_LATENCY_TIMER 10 // Get the latency timer -#define FTDI_SIO_SET_BITMODE 11 // Set bitbang mode -#define FTDI_SIO_READ_PINS 12 // Read immediate value of pins -#define FTDI_SIO_READ_EEPROM 0x90 // Read EEPROM +#define FTDI_SIO_RESET 0 // Reset the port +#define FTDI_SIO_MODEM_CTRL 1 // Set the modem control register +#define FTDI_SIO_SET_FLOW_CTRL 2 // Set flow control register +#define FTDI_SIO_SET_BAUD_RATE 3 // Set baud rate +#define FTDI_SIO_SET_DATA 4 // Set the data characteristics of the port +#define FTDI_SIO_GET_MODEM_STATUS 5 // Retrieve current value of modem status register +#define FTDI_SIO_SET_EVENT_CHAR 6 // Set the event character +#define FTDI_SIO_SET_ERROR_CHAR 7 // Set the error character +#define FTDI_SIO_SET_LATENCY_TIMER 9 // Set the latency timer +#define FTDI_SIO_GET_LATENCY_TIMER 10 // Get the latency timer +#define FTDI_SIO_SET_BITMODE 11 // Set bitbang mode +#define FTDI_SIO_READ_PINS 12 // Read immediate value of pins +#define FTDI_SIO_READ_EEPROM 0x90 // Read EEPROM // Channel indices for FT2232, FT2232H and FT4232H devices -#define CHANNEL_A 1 -#define CHANNEL_B 2 -#define CHANNEL_C 3 -#define CHANNEL_D 4 +#define CHANNEL_A 1 +#define CHANNEL_B 2 +#define CHANNEL_C 3 +#define CHANNEL_D 4 // Port Identifier Table -#define PIT_DEFAULT 0 // SIOA -#define PIT_SIOA 1 // SIOA +#define PIT_DEFAULT 0 // SIOA +#define PIT_SIOA 1 // SIOA // The device this driver is tested with one has only one port -#define PIT_SIOB 2 // SIOB -#define PIT_PARALLEL 3 // Parallel +#define PIT_SIOB 2 // SIOB +#define PIT_PARALLEL 3 // Parallel // FTDI_SIO_RESET -#define FTDI_SIO_RESET_REQUEST FTDI_SIO_RESET -#define FTDI_SIO_RESET_REQUEST_TYPE 0x40 -#define FTDI_SIO_RESET_SIO 0 -#define FTDI_SIO_RESET_PURGE_RX 1 -#define FTDI_SIO_RESET_PURGE_TX 2 +#define FTDI_SIO_RESET_REQUEST FTDI_SIO_RESET +#define FTDI_SIO_RESET_REQUEST_TYPE 0x40 +#define FTDI_SIO_RESET_SIO 0 +#define FTDI_SIO_RESET_PURGE_RX 1 +#define FTDI_SIO_RESET_PURGE_TX 2 // FTDI_SIO_SET_BAUDRATE -#define FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE 0x40 -#define FTDI_SIO_SET_BAUDRATE_REQUEST 3 +#define FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_BAUDRATE_REQUEST 3 enum ftdi_sio_baudrate { ftdi_sio_b300 = 0, @@ -80,89 +80,89 @@ enum ftdi_sio_baudrate { }; // FTDI_SIO_SET_DATA -#define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA -#define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40 -#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8) -#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8) -#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8) -#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8) -#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8) -#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11) -#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11) -#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11) -#define FTDI_SIO_SET_BREAK (0x1 << 14) +#define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA +#define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8) +#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8) +#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8) +#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8) +#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8) +#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11) // same coding as ACM +#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11) // 1.5 not supported, for future use? +#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11) +#define FTDI_SIO_SET_BREAK (0x1 << 14) // FTDI_SIO_MODEM_CTRL -#define FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE 0x40 -#define FTDI_SIO_SET_MODEM_CTRL_REQUEST FTDI_SIO_MODEM_CTRL +#define FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_MODEM_CTRL_REQUEST FTDI_SIO_MODEM_CTRL -#define FTDI_SIO_SET_DTR_MASK 0x1 -#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1) -#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0) -#define FTDI_SIO_SET_RTS_MASK 0x2 -#define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2) -#define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0) +#define FTDI_SIO_SET_DTR_MASK 0x1 +#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1) +#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0) +#define FTDI_SIO_SET_RTS_MASK 0x2 +#define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2) +#define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0) // FTDI_SIO_SET_FLOW_CTRL -#define FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE 0x40 -#define FTDI_SIO_SET_FLOW_CTRL_REQUEST FTDI_SIO_SET_FLOW_CTRL +#define FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_FLOW_CTRL_REQUEST FTDI_SIO_SET_FLOW_CTRL #define FTDI_SIO_DISABLE_FLOW_CTRL 0x0 -#define FTDI_SIO_RTS_CTS_HS (0x1 << 8) -#define FTDI_SIO_DTR_DSR_HS (0x2 << 8) -#define FTDI_SIO_XON_XOFF_HS (0x4 << 8) +#define FTDI_SIO_RTS_CTS_HS (0x1 << 8) +#define FTDI_SIO_DTR_DSR_HS (0x2 << 8) +#define FTDI_SIO_XON_XOFF_HS (0x4 << 8) // FTDI_SIO_GET_LATENCY_TIMER -#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST FTDI_SIO_GET_LATENCY_TIMER -#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE 0xC0 +#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST FTDI_SIO_GET_LATENCY_TIMER +#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE 0xC0 // FTDI_SIO_SET_LATENCY_TIMER -#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER -#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER +#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40 // FTDI_SIO_SET_EVENT_CHAR -#define FTDI_SIO_SET_EVENT_CHAR_REQUEST FTDI_SIO_SET_EVENT_CHAR -#define FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_EVENT_CHAR_REQUEST FTDI_SIO_SET_EVENT_CHAR +#define FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE 0x40 // FTDI_SIO_GET_MODEM_STATUS -#define FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE 0xc0 -#define FTDI_SIO_GET_MODEM_STATUS_REQUEST FTDI_SIO_GET_MODEM_STATUS -#define FTDI_SIO_CTS_MASK 0x10 -#define FTDI_SIO_DSR_MASK 0x20 -#define FTDI_SIO_RI_MASK 0x40 -#define FTDI_SIO_RLSD_MASK 0x80 +#define FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE 0xc0 +#define FTDI_SIO_GET_MODEM_STATUS_REQUEST FTDI_SIO_GET_MODEM_STATUS +#define FTDI_SIO_CTS_MASK 0x10 +#define FTDI_SIO_DSR_MASK 0x20 +#define FTDI_SIO_RI_MASK 0x40 +#define FTDI_SIO_RLSD_MASK 0x80 // FTDI_SIO_SET_BITMODE -#define FTDI_SIO_SET_BITMODE_REQUEST_TYPE 0x40 -#define FTDI_SIO_SET_BITMODE_REQUEST FTDI_SIO_SET_BITMODE +#define FTDI_SIO_SET_BITMODE_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_BITMODE_REQUEST FTDI_SIO_SET_BITMODE // Possible bitmodes for FTDI_SIO_SET_BITMODE_REQUEST -#define FTDI_SIO_BITMODE_RESET 0x00 -#define FTDI_SIO_BITMODE_CBUS 0x20 +#define FTDI_SIO_BITMODE_RESET 0x00 +#define FTDI_SIO_BITMODE_CBUS 0x20 // FTDI_SIO_READ_PINS -#define FTDI_SIO_READ_PINS_REQUEST_TYPE 0xc0 -#define FTDI_SIO_READ_PINS_REQUEST FTDI_SIO_READ_PINS +#define FTDI_SIO_READ_PINS_REQUEST_TYPE 0xc0 +#define FTDI_SIO_READ_PINS_REQUEST FTDI_SIO_READ_PINS // FTDI_SIO_READ_EEPROM -#define FTDI_SIO_READ_EEPROM_REQUEST_TYPE 0xc0 -#define FTDI_SIO_READ_EEPROM_REQUEST FTDI_SIO_READ_EEPROM +#define FTDI_SIO_READ_EEPROM_REQUEST_TYPE 0xc0 +#define FTDI_SIO_READ_EEPROM_REQUEST FTDI_SIO_READ_EEPROM #define FTDI_FTX_CBUS_MUX_GPIO 0x8 #define FTDI_FT232R_CBUS_MUX_GPIO 0xa #define FTDI_RS0_CTS (1 << 4) #define FTDI_RS0_DSR (1 << 5) -#define FTDI_RS0_RI (1 << 6) +#define FTDI_RS0_RI (1 << 6) #define FTDI_RS0_RLSD (1 << 7) -#define FTDI_RS_DR 1 -#define FTDI_RS_OE (1<<1) -#define FTDI_RS_PE (1<<2) -#define FTDI_RS_FE (1<<3) -#define FTDI_RS_BI (1<<4) -#define FTDI_RS_THRE (1<<5) -#define FTDI_RS_TEMT (1<<6) -#define FTDI_RS_FIFO (1<<7) +#define FTDI_RS_DR 1 +#define FTDI_RS_OE (1 << 1) +#define FTDI_RS_PE (1 << 2) +#define FTDI_RS_FE (1 << 3) +#define FTDI_RS_BI (1 << 4) +#define FTDI_RS_THRE (1 << 5) +#define FTDI_RS_TEMT (1 << 6) +#define FTDI_RS_FIFO (1 << 7) // chip types and names enum ftdi_chip_type { @@ -206,14 +206,14 @@ enum ftdi_chip_type { // private interface data typedef struct ftdi_private { - enum ftdi_chip_type chip_type; - uint8_t channel; // channel index, or 0 for legacy types + enum ftdi_chip_type chip_type; + uint8_t channel; // channel index, or 0 for legacy types } ftdi_private_t; -#define FTDI_OK true -#define FTDI_FAIL false +#define FTDI_OK true +#define FTDI_FAIL false #define FTDI_NOT_POSSIBLE -1 -#define FTDI_REQUESTED -2 +#define FTDI_REQUESTED -2 // division and round function overtaken from math.h #define DIV_ROUND_CLOSEST(x, divisor)( \ diff --git a/src/class/cdc/serial/pl2303.h b/src/class/cdc/serial/pl2303.h index bf264191b..d69bdbfae 100644 --- a/src/class/cdc/serial/pl2303.h +++ b/src/class/cdc/serial/pl2303.h @@ -24,8 +24,8 @@ * This file is part of the TinyUSB stack. */ -#ifndef _PL2303_H_ -#define _PL2303_H_ +#ifndef TUSB_PL2303_H +#define TUSB_PL2303_H #include #include @@ -36,66 +36,66 @@ // https://github.com/torvalds/linux/blob/master/drivers/usb/serial/pl2303.c // - https://github.com/freebsd/freebsd-src/blob/main/sys/dev/usb/serial/uplcom.c -/* quirks */ -#define PL2303_QUIRK_UART_STATE_IDX0 1 -#define PL2303_QUIRK_LEGACY 2 -#define PL2303_QUIRK_ENDPOINT_HACK 4 - -/* requests and bits */ -#define PL2303_SET_LINE_REQUEST_TYPE 0x21 // class request host to device interface -#define PL2303_SET_LINE_REQUEST 0x20 // dec 32 - -#define PL2303_SET_CONTROL_REQUEST_TYPE 0x21 // class request host to device interface -#define PL2303_SET_CONTROL_REQUEST 0x22 // dec 34 -#define PL2303_CONTROL_DTR 0x01 // dec 1 -#define PL2303_CONTROL_RTS 0x02 // dec 2 - -#define PL2303_BREAK_REQUEST_TYPE 0x21 // class request host to device interface -#define PL2303_BREAK_REQUEST 0x23 // dec 35 -#define PL2303_BREAK_ON 0xffff -#define PL2303_BREAK_OFF 0x0000 - -#define PL2303_GET_LINE_REQUEST_TYPE 0xa1 // class request device to host interface -#define PL2303_GET_LINE_REQUEST 0x21 // dec 33 - -#define PL2303_VENDOR_WRITE_REQUEST_TYPE 0x40 // vendor request host to device interface -#define PL2303_VENDOR_WRITE_REQUEST 0x01 // dec 1 -#define PL2303_VENDOR_WRITE_NREQUEST 0x80 // dec 128 - -#define PL2303_VENDOR_READ_REQUEST_TYPE 0xc0 // vendor request device to host interface -#define PL2303_VENDOR_READ_REQUEST 0x01 // dec 1 -#define PL2303_VENDOR_READ_NREQUEST 0x81 // dec 129 - -#define PL2303_UART_STATE_INDEX 8 -#define PL2303_UART_STATE_MSR_MASK 0x8b -#define PL2303_UART_STATE_TRANSIENT_MASK 0x74 -#define PL2303_UART_DCD 0x01 -#define PL2303_UART_DSR 0x02 -#define PL2303_UART_BREAK_ERROR 0x04 -#define PL2303_UART_RING 0x08 -#define PL2303_UART_FRAME_ERROR 0x10 -#define PL2303_UART_PARITY_ERROR 0x20 -#define PL2303_UART_OVERRUN_ERROR 0x40 -#define PL2303_UART_CTS 0x80 - -#define PL2303_FLOWCTRL_MASK 0xf0 - -#define PL2303_CLEAR_HALT_REQUEST_TYPE 0x02 // standard request host to device endpoint - -/* registers via vendor read/write requests */ -#define PL2303_READ_TYPE_HX_STATUS 0x8080 - -#define PL2303_HXN_RESET_REG 0x07 -#define PL2303_HXN_RESET_UPSTREAM_PIPE 0x02 -#define PL2303_HXN_RESET_DOWNSTREAM_PIPE 0x01 - -#define PL2303_HXN_FLOWCTRL_REG 0x0a -#define PL2303_HXN_FLOWCTRL_MASK 0x1c -#define PL2303_HXN_FLOWCTRL_NONE 0x1c -#define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18 -#define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c - -/* type data */ +// quirks +#define PL2303_QUIRK_UART_STATE_IDX0 1 +#define PL2303_QUIRK_LEGACY 2 +#define PL2303_QUIRK_ENDPOINT_HACK 4 + +// requests and bits +#define PL2303_SET_LINE_REQUEST_TYPE 0x21 // class request host to device interface +#define PL2303_SET_LINE_REQUEST 0x20 // dec 32 + +#define PL2303_SET_CONTROL_REQUEST_TYPE 0x21 // class request host to device interface +#define PL2303_SET_CONTROL_REQUEST 0x22 // dec 34 +#define PL2303_CONTROL_DTR 0x01 // dec 1 +#define PL2303_CONTROL_RTS 0x02 // dec 2 + +#define PL2303_BREAK_REQUEST_TYPE 0x21 // class request host to device interface +#define PL2303_BREAK_REQUEST 0x23 // dec 35 +#define PL2303_BREAK_ON 0xffff +#define PL2303_BREAK_OFF 0x0000 + +#define PL2303_GET_LINE_REQUEST_TYPE 0xa1 // class request device to host interface +#define PL2303_GET_LINE_REQUEST 0x21 // dec 33 + +#define PL2303_VENDOR_WRITE_REQUEST_TYPE 0x40 // vendor request host to device interface +#define PL2303_VENDOR_WRITE_REQUEST 0x01 // dec 1 +#define PL2303_VENDOR_WRITE_NREQUEST 0x80 // dec 128 + +#define PL2303_VENDOR_READ_REQUEST_TYPE 0xc0 // vendor request device to host interface +#define PL2303_VENDOR_READ_REQUEST 0x01 // dec 1 +#define PL2303_VENDOR_READ_NREQUEST 0x81 // dec 129 + +#define PL2303_UART_STATE_INDEX 8 +#define PL2303_UART_STATE_MSR_MASK 0x8b +#define PL2303_UART_STATE_TRANSIENT_MASK 0x74 +#define PL2303_UART_DCD 0x01 +#define PL2303_UART_DSR 0x02 +#define PL2303_UART_BREAK_ERROR 0x04 +#define PL2303_UART_RING 0x08 +#define PL2303_UART_FRAME_ERROR 0x10 +#define PL2303_UART_PARITY_ERROR 0x20 +#define PL2303_UART_OVERRUN_ERROR 0x40 +#define PL2303_UART_CTS 0x80 + +#define PL2303_FLOWCTRL_MASK 0xf0 + +#define PL2303_CLEAR_HALT_REQUEST_TYPE 0x02 // standard request host to device endpoint + +// registers via vendor read/write requests +#define PL2303_READ_TYPE_HX_STATUS 0x8080 + +#define PL2303_HXN_RESET_REG 0x07 +#define PL2303_HXN_RESET_UPSTREAM_PIPE 0x02 +#define PL2303_HXN_RESET_DOWNSTREAM_PIPE 0x01 + +#define PL2303_HXN_FLOWCTRL_REG 0x0a +#define PL2303_HXN_FLOWCTRL_MASK 0x1c +#define PL2303_HXN_FLOWCTRL_NONE 0x1c +#define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18 +#define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c + +// type data enum pl2303_type { TYPE_H, TYPE_HX, @@ -107,9 +107,9 @@ enum pl2303_type { }; struct pl2303_type_data { - uint8_t const *name; + uint8_t const *name; uint32_t const max_baud_rate; - uint8_t const quirks; + uint8_t const quirks; uint16_t const no_autoxonxoff:1; uint16_t const no_divisors:1; uint16_t const alt_divisors:1; @@ -146,7 +146,7 @@ struct pl2303_type_data { .no_divisors = true, \ } -/* private data types */ +// private data types struct pl2303_serial_private { const struct pl2303_type_data* type; uint8_t quirks; @@ -157,16 +157,16 @@ typedef struct TU_ATTR_PACKED { bool supports_hx_status; } pl2303_private_t; -/* buffer sizes for line coding data */ -#define PL2303_LINE_CODING_BUFSIZE 7 +// buffer sizes for line coding data +#define PL2303_LINE_CODING_BUFSIZE 7 #define PL2303_LINE_CODING_BAUDRATE_BUFSIZE 4 -/* bulk endpoints */ -#define PL2303_OUT_EP 0x02 -#define PL2303_IN_EP 0x83 +// bulk endpoints +#define PL2303_OUT_EP 0x02 +#define PL2303_IN_EP 0x83 -/* return values of pl2303_detect_type() */ +// return values of pl2303_detect_type() #define PL2303_SUPPORTS_HX_STATUS_TRIGGERED -1 -#define PL2303_DETECT_TYPE_FAILED -2 +#define PL2303_DETECT_TYPE_FAILED -2 -#endif /* _PL2303_H_ */ +#endif // TUSB_PL2303_H -- cgit v1.3.1 From 5e67b92b8c398d6fee92010ecd63cf50b51f8f9a Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 4 Apr 2024 14:10:31 +0200 Subject: fixed compile warnings --- src/class/cdc/cdc_host.c | 29 ++++++++++++++++------------- src/class/cdc/serial/cp210x.h | 4 ++-- src/class/cdc/serial/ftdi_sio.h | 14 +++++++------- 3 files changed, 25 insertions(+), 22 deletions(-) (limited to 'src/class/cdc/serial') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index e6579ad82..2d900a753 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -1181,9 +1181,9 @@ 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) ( - ((uint32_t) p_cdc->requested_line_coding.data_bits & 0xfu) | // data bit quantity is stored in bits 0-3 - ((uint32_t) p_cdc->requested_line_coding.parity & 0x7u) << 8 | // parity is stored in bits 8-10, same coding - ((uint32_t) p_cdc->requested_line_coding.stop_bits & 0x3u) << 11 ); // stop bits quantity is stored in bits 11-12, same coding + (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 return ftdi_set_request(p_cdc, FTDI_SIO_SET_DATA_REQUEST, FTDI_SIO_SET_DATA_REQUEST_TYPE, @@ -1191,8 +1191,8 @@ static bool ftdi_set_data_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t comple } static inline bool ftdi_update_mctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - uint16_t value = (uint16_t) ((p_cdc->requested_line_state.dtr ? (uint32_t) FTDI_SIO_SET_DTR_HIGH : (uint32_t) FTDI_SIO_SET_DTR_LOW) | - (p_cdc->requested_line_state.rts ? (uint32_t) FTDI_SIO_SET_RTS_HIGH : (uint32_t) FTDI_SIO_SET_RTS_LOW)); + uint16_t value = (uint16_t) ((p_cdc->requested_line_state.dtr ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW) | + (p_cdc->requested_line_state.rts ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW)); return ftdi_set_request(p_cdc, FTDI_SIO_SET_MODEM_CTRL_REQUEST, FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, value, p_cdc->ftdi.channel, complete_cb, user_data); @@ -1338,7 +1338,10 @@ static void ftdi_process_config(tuh_xfer_t * xfer) { // 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); - p_cdc->ftdi.chip_type = p_cdc_itf0->ftdi.chip_type; + TU_ASSERT_COMPLETE(p_cdc_itf0); + if (p_cdc_itf0) { + p_cdc->ftdi.chip_type = p_cdc_itf0->ftdi.chip_type; + } } TU_ATTR_FALLTHROUGH; @@ -1715,9 +1718,9 @@ 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 <= 9, 0); uint16_t lcr = (uint16_t) ( - ((uint32_t) p_cdc->requested_line_coding.data_bits & 0xfu) << 8 | // data bit quantity is stored in bits 8-11 - ((uint32_t) p_cdc->requested_line_coding.parity & 0xfu) << 4 | // parity is stored in bits 4-7, same coding - ((uint32_t) p_cdc->requested_line_coding.stop_bits & 0xfu)); // parity is stored in bits 0-3, same coding + (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 return cp210x_set_request(p_cdc, CP210X_SET_LINE_CTL, lcr, NULL, 0, complete_cb, user_data); } @@ -1725,8 +1728,8 @@ static bool cp210x_set_line_ctl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete 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.all), + (uint16_t) (CP210X_CONTROL_WRITE_DTR | CP210X_CONTROL_WRITE_RTS | + p_cdc->requested_line_state.all), NULL, 0, complete_cb, user_data); } @@ -2327,7 +2330,7 @@ static bool pl2303_vendor_write(cdch_interface_t * p_cdc, uint16_t value, uint16 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; + 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); @@ -2519,7 +2522,7 @@ static void pl2303_process_config(tuh_xfer_t * xfer) { 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; + uint8_t buf = 0; int8_t type; switch (state) { diff --git a/src/class/cdc/serial/cp210x.h b/src/class/cdc/serial/cp210x.h index a553a54da..ac9c27330 100644 --- a/src/class/cdc/serial/cp210x.h +++ b/src/class/cdc/serial/cp210x.h @@ -101,8 +101,8 @@ #define CP210X_MSR_DCD 0x0080 #define CP210X_MSR_ALL 0x00F0 -#define CP210X_CONTROL_WRITE_DTR 0x0100 -#define CP210X_CONTROL_WRITE_RTS 0x0200 +#define CP210X_CONTROL_WRITE_DTR 0x0100UL +#define CP210X_CONTROL_WRITE_RTS 0x0200UL #define CP210X_LSR_BREAK 0x0001 #define CP210X_LSR_FRAMING_ERROR 0x0002 diff --git a/src/class/cdc/serial/ftdi_sio.h b/src/class/cdc/serial/ftdi_sio.h index 4afedec9b..f621b3912 100644 --- a/src/class/cdc/serial/ftdi_sio.h +++ b/src/class/cdc/serial/ftdi_sio.h @@ -96,17 +96,17 @@ enum ftdi_sio_baudrate { #define FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE 0x40 #define FTDI_SIO_SET_MODEM_CTRL_REQUEST FTDI_SIO_MODEM_CTRL -#define FTDI_SIO_SET_DTR_MASK 0x1 -#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1) -#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0) -#define FTDI_SIO_SET_RTS_MASK 0x2 -#define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2) -#define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0) +#define FTDI_SIO_SET_DTR_MASK 0x1UL +#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1UL) +#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0UL) +#define FTDI_SIO_SET_RTS_MASK 0x2UL +#define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2UL) +#define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0UL) // FTDI_SIO_SET_FLOW_CTRL #define FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE 0x40 #define FTDI_SIO_SET_FLOW_CTRL_REQUEST FTDI_SIO_SET_FLOW_CTRL -#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0 +#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0 #define FTDI_SIO_RTS_CTS_HS (0x1 << 8) #define FTDI_SIO_DTR_DSR_HS (0x2 << 8) #define FTDI_SIO_XON_XOFF_HS (0x4 << 8) -- cgit v1.3.1 From e07ee4a7b1f7c7d18f385d605dd41e08962ddba4 Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 4 Apr 2024 14:12:14 +0200 Subject: CP210x removed baudrate check, fixed data bits check --- src/class/cdc/cdc_host.c | 12 ++---------- src/class/cdc/serial/cp210x.h | 9 --------- 2 files changed, 2 insertions(+), 19 deletions(-) (limited to 'src/class/cdc/serial') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 2d900a753..73d800469 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -1701,22 +1701,14 @@ static inline bool cp210x_ifc_enable(cdch_interface_t * p_cdc, uint16_t enabled, } 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]); + // Not every baud rate is supported. See datasheets and AN205 "CP210x Baud Rate Support" 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); + 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 (p_cdc->requested_line_coding.parity & 0xfUL) << 4 | // parity is stored in bits 4-7, same coding diff --git a/src/class/cdc/serial/cp210x.h b/src/class/cdc/serial/cp210x.h index ac9c27330..a0eff9e40 100644 --- a/src/class/cdc/serial/cp210x.h +++ b/src/class/cdc/serial/cp210x.h @@ -111,13 +111,4 @@ #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 -- cgit v1.3.1 From 2adb305ea71f69b38c6c01cc054d8e323dd25e0e Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 19 Jun 2025 15:30:02 +0700 Subject: house keeping --- src/class/cdc/cdc_host.c | 183 ++++++++++++++++++++-------------------- src/class/cdc/serial/ftdi_sio.h | 74 ++++++++-------- src/class/cdc/serial/pl2303.h | 42 ++++----- 3 files changed, 151 insertions(+), 148 deletions(-) (limited to 'src/class/cdc/serial') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index d704d82d7..ce98e3b3d 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -111,13 +111,13 @@ typedef struct { tu_edpt_stream_t rx; uint8_t tx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE]; - uint8_t rx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE]; + uint8_t rx_ff_buf[CFG_TUH_CDC_RX_BUFSIZE]; } stream; } cdch_interface_t; typedef struct { TUH_EPBUF_DEF(tx, CFG_TUH_CDC_TX_EPSIZE); - TUH_EPBUF_DEF(rx, CFG_TUH_CDC_TX_EPSIZE); + TUH_EPBUF_DEF(rx, CFG_TUH_CDC_RX_EPSIZE); } cdch_epbuf_t; static cdch_interface_t cdch_data[CFG_TUH_CDC]; @@ -141,10 +141,6 @@ static bool acm_set_control_line_state(cdch_interface_t * p_cdc, tuh_xfer_cb_t c //------------- FTDI prototypes -------------// #if CFG_TUH_CDC_FTDI static uint16_t const ftdi_vid_pid_list[][2] = {CFG_TUH_CDC_FTDI_VID_PID_LIST}; -#if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL - static uint8_t const * ftdi_chip_name[] = { FTDI_CHIP_NAMES }; -#endif - static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t * itf_desc, uint16_t max_len); static bool ftdi_proccess_set_config(tuh_xfer_t * xfer); @@ -183,7 +179,7 @@ static bool ch34x_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complet //------------- PL2303 prototypes -------------// #if CFG_TUH_CDC_PL2303 static uint16_t const pl2303_vid_pid_list[][2] = {CFG_TUH_CDC_PL2303_VID_PID_LIST}; -static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {PL2303_TYPE_DATA}; +static const pl2303_type_data_t pl2303_type_data[PL2303_TYPE_COUNT] = {PL2303_TYPE_DATA}; static bool pl2303_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len); static bool pl2303_process_set_config(tuh_xfer_t *xfer); @@ -1209,7 +1205,7 @@ static bool ftdi_set_request(cdch_interface_t *p_cdc, uint8_t request, uint8_t r #ifdef CFG_TUH_CDC_FTDI_LATENCY static int8_t ftdi_write_latency_timer(cdch_interface_t * p_cdc, uint16_t latency, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - if (p_cdc->ftdi.chip_type == SIO /* || p_cdc->ftdi.chip_type == FT232A */ ) + if (p_cdc->ftdi.chip_type == FTDI_SIO /* || p_cdc->ftdi.chip_type == FT232A */ ) return FTDI_NOT_POSSIBLE; return ftdi_set_request(p_cdc, FTDI_SIO_SET_LATENCY_TIMER_REQUEST, FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE, latency, p_cdc->ftdi.channel, complete_cb, user_data) ? FTDI_REQUESTED : FTDI_FAIL; @@ -1458,7 +1454,7 @@ 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; - p_cdc->ftdi.chip_type = UNKNOWN; + p_cdc->ftdi.chip_type = FTDI_UNKNOWN; /* Assume Hi-Speed type */ p_cdc->ftdi.channel = CHANNEL_A + itf_num; @@ -1476,35 +1472,40 @@ static bool ftdi_determine_type(cdch_interface_t *p_cdc) { // */ // if (desc->iSerialNumber == 0 && // _read_latency_timer(port) >= 0) { - // p_cdc->ftdi.chip_type = FT232B; + // p_cdc->ftdi.chip_type = FTDI_FT232B; // } break; - case 0x400: p_cdc->ftdi.chip_type = FT232B; p_cdc->ftdi.channel = 0; break; - case 0x500: p_cdc->ftdi.chip_type = FT2232C; break; - case 0x600: p_cdc->ftdi.chip_type = FT232R; p_cdc->ftdi.channel = 0; break; - case 0x700: p_cdc->ftdi.chip_type = FT2232H; break; - case 0x800: p_cdc->ftdi.chip_type = FT4232H; break; - case 0x900: p_cdc->ftdi.chip_type = FT232H; break; - case 0x1000: p_cdc->ftdi.chip_type = FTX; break; - case 0x2800: p_cdc->ftdi.chip_type = FT2233HP; break; - case 0x2900: p_cdc->ftdi.chip_type = FT4233HP; break; - case 0x3000: p_cdc->ftdi.chip_type = FT2232HP; break; - case 0x3100: p_cdc->ftdi.chip_type = FT4232HP; break; - case 0x3200: p_cdc->ftdi.chip_type = FT233HP; break; - case 0x3300: p_cdc->ftdi.chip_type = FT232HP; break; - case 0x3600: p_cdc->ftdi.chip_type = FT4232HA; break; + + case 0x400 : p_cdc->ftdi.chip_type = FTDI_FT232B; p_cdc->ftdi.channel = 0; break; + case 0x500 : p_cdc->ftdi.chip_type = FTDI_FT2232C; break; + case 0x600 : p_cdc->ftdi.chip_type = FTDI_FT232R; p_cdc->ftdi.channel = 0; break; + case 0x700 : p_cdc->ftdi.chip_type = FTDI_FT2232H; break; + case 0x800 : p_cdc->ftdi.chip_type = FTDI_FT4232H; break; + case 0x900 : p_cdc->ftdi.chip_type = FTDI_FT232H; break; + case 0x1000: p_cdc->ftdi.chip_type = FTDI_FTX; break; + case 0x2800: p_cdc->ftdi.chip_type = FTDI_FT2233HP; break; + case 0x2900: p_cdc->ftdi.chip_type = FTDI_FT4233HP; break; + case 0x3000: p_cdc->ftdi.chip_type = FTDI_FT2232HP; break; + case 0x3100: p_cdc->ftdi.chip_type = FTDI_FT4232HP; break; + case 0x3200: p_cdc->ftdi.chip_type = FTDI_FT233HP; break; + case 0x3300: p_cdc->ftdi.chip_type = FTDI_FT232HP; break; + case 0x3600: p_cdc->ftdi.chip_type = FTDI_FT4232HA; break; + default: if (version < 0x200) { - p_cdc->ftdi.chip_type = SIO; + p_cdc->ftdi.chip_type = FTDI_SIO; p_cdc->ftdi.channel = 0; } break; } + #if CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL + const char * ftdi_chip_name[] = { FTDI_CHIP_NAMES }; TU_LOG_P_CDC("%s detected (bcdDevice = 0x%04x)", ftdi_chip_name[p_cdc->ftdi.chip_type], version); + #endif - return (p_cdc->ftdi.chip_type != UNKNOWN); + return (p_cdc->ftdi.chip_type != FTDI_UNKNOWN); } // FT232A not supported @@ -1589,9 +1590,9 @@ static inline uint32_t ftdi_get_divisor(cdch_interface_t *p_cdc) { TU_VERIFY(baud); switch (p_cdc->ftdi.chip_type) { - case UNKNOWN: + case FTDI_UNKNOWN: return 0; - case SIO: + case FTDI_SIO: switch (baud) { case 300: div_value = ftdi_sio_b300; break; case 600: div_value = ftdi_sio_b600; break; @@ -1620,23 +1621,23 @@ static inline uint32_t ftdi_get_divisor(cdch_interface_t *p_cdc) { // div_okay = false; // } // break; - case FT232B: - case FT2232C: - case FT232R: - case FTX: + case FTDI_FT232B: + case FTDI_FT2232C: + case FTDI_FT232R: + case FTDI_FTX: TU_VERIFY(baud <= 3000000); // else Baud rate too high! div_value = ftdi_232bm_baud_to_divisor(baud); break; - case FT232H: - case FT2232H: - case FT4232H: - case FT4232HA: - case FT232HP: - case FT233HP: - case FT2232HP: - case FT2233HP: - case FT4232HP: - case FT4233HP: + case FTDI_FT232H: + case FTDI_FT2232H: + case FTDI_FT4232H: + case FTDI_FT4232HA: + case FTDI_FT232HP: + case FTDI_FT233HP: + case FTDI_FT2232HP: + case FTDI_FT2233HP: + case FTDI_FT4232HP: + case FTDI_FT4233HP: default: TU_VERIFY(baud <= 12000000); // else Baud rate too high! if (baud >= 1200) { @@ -2273,8 +2274,7 @@ static uint8_t ch34x_get_lcr(cdch_interface_t *p_cdc) { //--------------------------------------------------------------------+ #if CFG_TUH_CDC_PL2303 -static int8_t pl2303_detect_type(cdch_interface_t *p_cdc, uint8_t step, - tuh_xfer_cb_t complete_cb, uintptr_t user_data); +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 -------------// @@ -2313,14 +2313,13 @@ static bool pl2303_set_request(cdch_interface_t *p_cdc, uint8_t request, uint8_t 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; - + uint8_t request = p_cdc->pl2303.serial_private.type == &pl2303_type_data[PL2303_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; + uint8_t request = p_cdc->pl2303.serial_private.type == &pl2303_type_data[PL2303_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); } @@ -2517,25 +2516,30 @@ static bool pl2303_process_set_config(tuh_xfer_t *xfer) { uint8_t buf = 0; int8_t type; - TU_ASSERT(p_cdc && (xfer->result == XFER_RESULT_SUCCESS || xfer->user_data == CONFIG_PL2303_READ1)); + TU_ASSERT(p_cdc && (xfer->result == XFER_RESULT_SUCCESS || state == CONFIG_PL2303_READ1)); switch (state) { // from here sequence overtaken from Linux Kernel function pl2303_startup() case CONFIG_PL2303_DETECT_TYPE: p_cdc->user_control_cb = cdch_process_set_config;// set once for whole process config // get type and quirks (step 1) - type = pl2303_detect_type(p_cdc, 1, cdch_process_set_config, CONFIG_PL2303_READ1); - TU_ASSERT(type != PL2303_DETECT_TYPE_FAILED); - if (type == PL2303_SUPPORTS_HX_STATUS_TRIGGERED) { + type = pl2303_detect_type(p_cdc, 1); + TU_ASSERT(type != PL2303_TYPE_UNKNOWN); + if (type == PL2303_TYPE_NEED_SUPPORTS_HX_STATUS) { + TU_ASSERT(pl2303_supports_hx_status(p_cdc, cdch_process_set_config, CONFIG_PL2303_READ1)); break; - }// else: no transfer triggered and continue with CONFIG_PL2303_READ1 - TU_ATTR_FALLTHROUGH; + } else { + // no transfer triggered and continue with CONFIG_PL2303_READ1 + TU_ATTR_FALLTHROUGH; + } case CONFIG_PL2303_READ1: // get supports_hx_status, type and quirks (step 2), do special read - 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(type != PL2303_DETECT_TYPE_FAILED); + // will not be true, if coming directly from previous case + if (xfer->user_data == CONFIG_PL2303_READ1 && xfer->result == XFER_RESULT_SUCCESS) { + p_cdc->pl2303.supports_hx_status = true; + } + type = pl2303_detect_type(p_cdc, 2); // step 2 now with supports_hx_status + TU_ASSERT(type != PL2303_TYPE_UNKNOWN); 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 @@ -2549,7 +2553,7 @@ static bool pl2303_process_set_config(tuh_xfer_t *xfer) { 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]) { + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[PL2303_TYPE_HXN]) { TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, &buf, cdch_process_set_config, CONFIG_PL2303_WRITE1)); break; }// else: continue with next step @@ -2557,7 +2561,7 @@ static bool pl2303_process_set_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]) { + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[PL2303_TYPE_HXN]) { TU_ASSERT(pl2303_vendor_write(p_cdc, 0x0404, 0, cdch_process_set_config, CONFIG_PL2303_READ2)); break; }// else: continue with next step @@ -2565,7 +2569,7 @@ static bool pl2303_process_set_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]) { + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[PL2303_TYPE_HXN]) { TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, &buf, cdch_process_set_config, CONFIG_PL2303_READ3)); break; }// else: continue with next step @@ -2573,7 +2577,7 @@ static bool pl2303_process_set_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]) { + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[PL2303_TYPE_HXN]) { TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8383, &buf, cdch_process_set_config, CONFIG_PL2303_READ4)); break; }// else: continue with next step @@ -2581,7 +2585,7 @@ static bool pl2303_process_set_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]) { + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[PL2303_TYPE_HXN]) { TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, &buf, cdch_process_set_config, CONFIG_PL2303_WRITE2)); break; }// else: continue with next step @@ -2589,7 +2593,7 @@ static bool pl2303_process_set_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]) { + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[PL2303_TYPE_HXN]) { TU_ASSERT(pl2303_vendor_write(p_cdc, 0x0404, 1, cdch_process_set_config, CONFIG_PL2303_READ5)); break; }// else: continue with next step @@ -2597,7 +2601,7 @@ static bool pl2303_process_set_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]) { + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[PL2303_TYPE_HXN]) { TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, &buf, cdch_process_set_config, CONFIG_PL2303_READ6)); break; }// else: continue with next step @@ -2605,7 +2609,7 @@ static bool pl2303_process_set_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]) { + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[PL2303_TYPE_HXN]) { TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8383, &buf, cdch_process_set_config, CONFIG_PL2303_WRITE3)); break; }// else: continue with next step @@ -2613,7 +2617,7 @@ static bool pl2303_process_set_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]) { + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[PL2303_TYPE_HXN]) { TU_ASSERT(pl2303_vendor_write(p_cdc, 0, 1, cdch_process_set_config, CONFIG_PL2303_WRITE4)); break; }// else: continue with next step @@ -2621,7 +2625,7 @@ static bool pl2303_process_set_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]) { + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[PL2303_TYPE_HXN]) { TU_ASSERT(pl2303_vendor_write(p_cdc, 1, 0, cdch_process_set_config, CONFIG_PL2303_WRITE5)); break; }// else: continue with next step @@ -2629,7 +2633,7 @@ static bool pl2303_process_set_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.type != &pl2303_type_data[PL2303_TYPE_HXN]) { 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, CONFIG_PL2303_RESET_ENDP1)); break; @@ -2643,7 +2647,7 @@ static bool pl2303_process_set_config(tuh_xfer_t *xfer) { TU_ASSERT(pl2303_clear_halt(p_cdc, PL2303_OUT_EP, cdch_process_set_config, CONFIG_PL2303_RESET_ENDP2)); } else { /* reset upstream data pipes */ - if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) { + if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[PL2303_TYPE_HXN]) { 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, cdch_process_set_config, CONFIG_PL2303_LINE_CODING)); @@ -2659,7 +2663,7 @@ static bool pl2303_process_set_config(tuh_xfer_t *xfer) { TU_ASSERT(pl2303_clear_halt(p_cdc, PL2303_IN_EP, cdch_process_set_config, CONFIG_PL2303_LINE_CODING)); } else { /* reset upstream data pipes */ - if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) { + if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[PL2303_TYPE_HXN]) { // here nothing to do, only structure of previous step overtaken for better reading and comparison } else { TU_ASSERT(pl2303_vendor_write(p_cdc, 9, 0, cdch_process_set_config, CONFIG_PL2303_LINE_CODING)); @@ -2690,7 +2694,7 @@ static bool pl2303_process_set_config(tuh_xfer_t *xfer) { // skipped, because it's not working with each PL230x. flow control can be also set by PL2303 EEPROM Writer Program // case CONFIG_PL2303_FLOW_CTRL_READ: // // read flow control register for modify & write back in next step - // if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) { + // if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[PL2303_TYPE_HXN]) { // TU_LOG_P_CDC ( "1\r\n" ); // TU_ASSERT(pl2303_vendor_read(p_cdc, PL2303_HXN_FLOWCTRL_REG, &buf, // cdch_process_set_config, CONFIG_PL2303_FLOW_CTRL_WRITE)); @@ -2703,7 +2707,7 @@ static bool pl2303_process_set_config(tuh_xfer_t *xfer) { // case CONFIG_PL2303_FLOW_CTRL_WRITE: // // no flow control // buf = xfer->buffer[0]; - // if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) { + // if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[PL2303_TYPE_HXN]) { // buf &= (uint8_t) ~PL2303_HXN_FLOWCTRL_MASK; // buf |= PL2303_HXN_FLOWCTRL_NONE; // TU_ASSERT(pl2303_vendor_write(p_cdc, PL2303_HXN_FLOWCTRL_REG, buf, @@ -2728,21 +2732,20 @@ static bool pl2303_process_set_config(tuh_xfer_t *xfer) { //------------- 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) { +static int8_t pl2303_detect_type(cdch_interface_t *p_cdc, uint8_t step) { tusb_desc_device_t desc_dev; - TU_VERIFY(tuh_descriptor_get_device_local(p_cdc->daddr, &desc_dev), PL2303_DETECT_TYPE_FAILED); + TU_VERIFY(tuh_descriptor_get_device_local(p_cdc->daddr, &desc_dev), PL2303_TYPE_UNKNOWN); // Legacy PL2303H, variants 0 and 1 (difference unknown). if (desc_dev.bDeviceClass == 0x02) { - return TYPE_H; /* variant 0 */ + return PL2303_TYPE_H; /* variant 0 */ } if (desc_dev.bMaxPacketSize0 != 0x40) { if (desc_dev.bDeviceClass == 0x00 || desc_dev.bDeviceClass == 0xff) { - return TYPE_H; /* variant 1 */ + return PL2303_TYPE_H; /* variant 1 */ } - return TYPE_H; /* variant 0 */ + return PL2303_TYPE_H; /* variant 0 */ } switch (desc_dev.bcdUSB) { @@ -2751,9 +2754,9 @@ static int8_t pl2303_detect_type(cdch_interface_t *p_cdc, uint8_t step, TU_ATTR_FALLTHROUGH; case 0x110: switch (desc_dev.bcdDevice) { - case 0x300: return TYPE_HX; - case 0x400: return TYPE_HXD; - default: return TYPE_HX; + case 0x300: return PL2303_TYPE_HX; + case 0x400: return PL2303_TYPE_HXD; + default: return PL2303_TYPE_HX; } break; @@ -2761,34 +2764,32 @@ static int8_t pl2303_detect_type(cdch_interface_t *p_cdc, uint8_t step, switch (desc_dev.bcdDevice) { case 0x100: /* GC */ case 0x105: - return TYPE_HXN; + return PL2303_TYPE_HXN; case 0x300: /* GT / TA */ if (step == 1) { // step 1 trigger pl2303_supports_hx_status() request - TU_ASSERT(pl2303_supports_hx_status(p_cdc, complete_cb, user_data), PL2303_DETECT_TYPE_FAILED); - return PL2303_SUPPORTS_HX_STATUS_TRIGGERED; + return PL2303_TYPE_NEED_SUPPORTS_HX_STATUS; } else { // step 2 use supports_hx_status if (p_cdc->pl2303.supports_hx_status) { - return TYPE_TA; + return PL2303_TYPE_TA; } } TU_ATTR_FALLTHROUGH; case 0x305: case 0x400: /* GL */ case 0x405: - return TYPE_HXN; + return PL2303_TYPE_HXN; case 0x500: /* GE / TB */ if (step == 1) { // step 1 trigger pl2303_supports_hx_status() request - TU_ASSERT(pl2303_supports_hx_status(p_cdc, complete_cb, user_data), PL2303_DETECT_TYPE_FAILED); - return PL2303_SUPPORTS_HX_STATUS_TRIGGERED; + return PL2303_TYPE_NEED_SUPPORTS_HX_STATUS; } else { // step 2 use supports_hx_status if (p_cdc->pl2303.supports_hx_status) { - return TYPE_TB; + return PL2303_TYPE_TB; } } TU_ATTR_FALLTHROUGH; @@ -2797,7 +2798,7 @@ static int8_t pl2303_detect_type(cdch_interface_t *p_cdc, uint8_t step, case 0x605: case 0x700: /* GR */ case 0x705: - return TYPE_HXN; + return PL2303_TYPE_HXN; default: break; @@ -2807,7 +2808,7 @@ static int8_t pl2303_detect_type(cdch_interface_t *p_cdc, uint8_t step, } TU_LOG_P_CDC("unknown device type bcdUSB = 0x%04x", desc_dev.bcdUSB); - return PL2303_DETECT_TYPE_FAILED; + return PL2303_TYPE_UNKNOWN; } /* diff --git a/src/class/cdc/serial/ftdi_sio.h b/src/class/cdc/serial/ftdi_sio.h index f621b3912..8abf74f11 100644 --- a/src/class/cdc/serial/ftdi_sio.h +++ b/src/class/cdc/serial/ftdi_sio.h @@ -165,48 +165,48 @@ enum ftdi_sio_baudrate { #define FTDI_RS_FIFO (1 << 7) // chip types and names -enum ftdi_chip_type { - SIO = 0, -// FT232A, - FT232B, - FT2232C, - FT232R, - FT232H, - FT2232H, - FT4232H, - FT4232HA, - FT232HP, - FT233HP, - FT2232HP, - FT2233HP, - FT4232HP, - FT4233HP, - FTX, - UNKNOWN -}; +typedef enum ftdi_chip_type { + FTDI_SIO = 0, +// FTDI_FT232A, + FTDI_FT232B, + FTDI_FT2232C, + FTDI_FT232R, + FTDI_FT232H, + FTDI_FT2232H, + FTDI_FT4232H, + FTDI_FT4232HA, + FTDI_FT232HP, + FTDI_FT233HP, + FTDI_FT2232HP, + FTDI_FT2233HP, + FTDI_FT4232HP, + FTDI_FT4233HP, + FTDI_FTX, + FTDI_UNKNOWN +} ftdi_chip_type_t; #define FTDI_CHIP_NAMES \ - [SIO] = (uint8_t const*) "SIO", /* the serial part of FT8U100AX */ \ -/* [FT232A] = (uint8_t const*) "FT232A", */ \ - [FT232B] = (uint8_t const*) "FT232B", \ - [FT2232C] = (uint8_t const*) "FT2232C/D", \ - [FT232R] = (uint8_t const*) "FT232R", \ - [FT232H] = (uint8_t const*) "FT232H", \ - [FT2232H] = (uint8_t const*) "FT2232H", \ - [FT4232H] = (uint8_t const*) "FT4232H", \ - [FT4232HA] = (uint8_t const*) "FT4232HA", \ - [FT232HP] = (uint8_t const*) "FT232HP", \ - [FT233HP] = (uint8_t const*) "FT233HP", \ - [FT2232HP] = (uint8_t const*) "FT2232HP", \ - [FT2233HP] = (uint8_t const*) "FT2233HP", \ - [FT4232HP] = (uint8_t const*) "FT4232HP", \ - [FT4233HP] = (uint8_t const*) "FT4233HP", \ - [FTX] = (uint8_t const*) "FT-X", \ - [UNKNOWN] = (uint8_t const*) "UNKNOWN" + [FTDI_SIO] = "SIO", /* the serial part of FT8U100AX */ \ +/* [FTDI_FT232A] = "FT232A", */ \ + [FTDI_FT232B] = "FT232B", \ + [FTDI_FT2232C] = "FT2232C/D", \ + [FTDI_FT232R] = "FT232R", \ + [FTDI_FT232H] = "FT232H", \ + [FTDI_FT2232H] = "FTDI_FT2232H", \ + [FTDI_FT4232H] = "FT4232H", \ + [FTDI_FT4232HA] = "FT4232HA", \ + [FTDI_FT232HP] = "FT232HP", \ + [FTDI_FT233HP] = "FT233HP", \ + [FTDI_FT2232HP] = "FT2232HP", \ + [FTDI_FT2233HP] = "FT2233HP", \ + [FTDI_FT4232HP] = "FT4232HP", \ + [FTDI_FT4233HP] = "FT4233HP", \ + [FTDI_FTX] = "FT-X", \ + [FTDI_UNKNOWN] = "UNKNOWN" // private interface data typedef struct ftdi_private { - enum ftdi_chip_type chip_type; + ftdi_chip_type_t chip_type; uint8_t channel; // channel index, or 0 for legacy types } ftdi_private_t; diff --git a/src/class/cdc/serial/pl2303.h b/src/class/cdc/serial/pl2303.h index d69bdbfae..40311260d 100644 --- a/src/class/cdc/serial/pl2303.h +++ b/src/class/cdc/serial/pl2303.h @@ -96,51 +96,53 @@ #define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c // type data -enum pl2303_type { - TYPE_H, - TYPE_HX, - TYPE_TA, - TYPE_TB, - TYPE_HXD, - TYPE_HXN, - TYPE_COUNT -}; - -struct pl2303_type_data { +typedef enum pl2303_type { + PL2303_TYPE_H, + PL2303_TYPE_HX, + PL2303_TYPE_TA, + PL2303_TYPE_TB, + PL2303_TYPE_HXD, + PL2303_TYPE_HXN, + // PL2303_TYPE_NEED_SUPPORTS_HX_STATUS, + // PL2303_TYPE_UNKNOWN, + PL2303_TYPE_COUNT +} pl2303_type_t; + +typedef struct pl2303_type_data { uint8_t const *name; uint32_t const max_baud_rate; uint8_t const quirks; uint16_t const no_autoxonxoff:1; uint16_t const no_divisors:1; uint16_t const alt_divisors:1; -}; +} pl2303_type_data_t; #define PL2303_TYPE_DATA \ - [TYPE_H] = { \ + [PL2303_TYPE_H] = { \ .name = (uint8_t const*)"H", \ .max_baud_rate = 1228800, \ .quirks = PL2303_QUIRK_LEGACY, \ .no_autoxonxoff = true, \ }, \ - [TYPE_HX] = { \ + [PL2303_TYPE_HX] = { \ .name = (uint8_t const*)"HX", \ .max_baud_rate = 6000000, \ }, \ - [TYPE_TA] = { \ + [PL2303_TYPE_TA] = { \ .name = (uint8_t const*)"TA", \ .max_baud_rate = 6000000, \ .alt_divisors = true, \ }, \ - [TYPE_TB] = { \ + [PL2303_TYPE_TB] = { \ .name = (uint8_t const*)"TB", \ .max_baud_rate = 12000000, \ .alt_divisors = true, \ }, \ - [TYPE_HXD] = { \ + [PL2303_TYPE_HXD] = { \ .name = (uint8_t const*)"HXD", \ .max_baud_rate = 12000000, \ }, \ - [TYPE_HXN] = { \ + [PL2303_TYPE_HXN] = { \ .name = (uint8_t const*)"G (HXN)", \ .max_baud_rate = 12000000, \ .no_divisors = true, \ @@ -166,7 +168,7 @@ typedef struct TU_ATTR_PACKED { #define PL2303_IN_EP 0x83 // return values of pl2303_detect_type() -#define PL2303_SUPPORTS_HX_STATUS_TRIGGERED -1 -#define PL2303_DETECT_TYPE_FAILED -2 +#define PL2303_TYPE_NEED_SUPPORTS_HX_STATUS -1 +#define PL2303_TYPE_UNKNOWN -2 #endif // TUSB_PL2303_H -- cgit v1.3.1 From ec1a26251d0161d5e7c11f4ca552af154cf08dbd Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 19 Jun 2025 17:05:21 +0700 Subject: clean up pl2303 type data --- src/class/cdc/cdc_host.c | 130 +++++++++++++++++++----------------------- src/class/cdc/serial/pl2303.h | 46 +++++---------- 2 files changed, 74 insertions(+), 102 deletions(-) (limited to 'src/class/cdc/serial') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index ce98e3b3d..dae38ba42 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -2274,7 +2274,7 @@ static uint8_t ch34x_get_lcr(cdch_interface_t *p_cdc) { //--------------------------------------------------------------------+ #if CFG_TUH_CDC_PL2303 -static int8_t pl2303_detect_type(cdch_interface_t *p_cdc, uint8_t step); +static pl2303_type_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 -------------// @@ -2313,20 +2313,18 @@ static bool pl2303_set_request(cdch_interface_t *p_cdc, uint8_t request, uint8_t 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[PL2303_TYPE_HXN] ? PL2303_VENDOR_READ_NREQUEST : PL2303_VENDOR_READ_REQUEST; + uint8_t request = p_cdc->pl2303.type == PL2303_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[PL2303_TYPE_HXN] ? PL2303_VENDOR_WRITE_NREQUEST : PL2303_VENDOR_WRITE_REQUEST; - + uint8_t request = p_cdc->pl2303.type == PL2303_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) { 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); } @@ -2425,7 +2423,6 @@ static void pl2303_internal_control_complete(tuh_xfer_t *xfer) { static bool pl2303_set_line_coding(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->user_control_cb = complete_cb; TU_ASSERT(pl2303_set_line_request(p_cdc, complete_cb ? pl2303_internal_control_complete : NULL, user_data)); - return true; } @@ -2433,7 +2430,6 @@ static bool pl2303_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t comple p_cdc->requested_line_coding.bit_rate = p_cdc->line_coding.bit_rate; p_cdc->user_control_cb = complete_cb; TU_ASSERT(pl2303_set_line_request(p_cdc, complete_cb ? pl2303_internal_control_complete : NULL, user_data)); - return true; } @@ -2443,7 +2439,6 @@ static bool pl2303_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_ p_cdc->requested_line_coding.data_bits = p_cdc->line_coding.data_bits; p_cdc->user_control_cb = complete_cb; TU_ASSERT(pl2303_set_line_request(p_cdc, complete_cb ? pl2303_internal_control_complete : NULL, user_data)); - return true; } @@ -2451,7 +2446,6 @@ static bool pl2303_set_modem_ctrl(cdch_interface_t *p_cdc, tuh_xfer_cb_t complet // PL2303 has the same bit coding p_cdc->user_control_cb = complete_cb; TU_ASSERT(pl2303_set_control_lines(p_cdc, complete_cb ? pl2303_internal_control_complete : NULL, user_data)); - return true; } @@ -2488,7 +2482,7 @@ static bool pl2303_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, ui TU_VERIFY(p_cdc); p_cdc->serial_drid = SERIAL_DRIVER_PL2303; - p_cdc->pl2303.serial_private.quirks = 0; + p_cdc->pl2303.quirks = 0; p_cdc->pl2303.supports_hx_status = false; tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); @@ -2514,7 +2508,7 @@ static bool pl2303_process_set_config(tuh_xfer_t *xfer) { 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() uint8_t buf = 0; - int8_t type; + pl2303_type_t type; TU_ASSERT(p_cdc && (xfer->result == XFER_RESULT_SUCCESS || state == CONFIG_PL2303_READ1)); switch (state) { @@ -2540,20 +2534,13 @@ static bool pl2303_process_set_config(tuh_xfer_t *xfer) { } type = pl2303_detect_type(p_cdc, 2); // step 2 now with supports_hx_status TU_ASSERT(type != PL2303_TYPE_UNKNOWN); - 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 - TU_LOG_P_CDC("bDeviceClass = 0x%02x bMaxPacketSize0 = %u bcdUSB = 0x%04x bcdDevice = 0x%04x", - desc_dev->bDeviceClass, desc_dev->bMaxPacketSize0, - desc_dev->bcdUSB, desc_dev->bcdDevice); - uint16_t 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 + TU_LOG_DRV(" PL2303 type detected: %u\r\n", type); + + p_cdc->pl2303.type = type; + p_cdc->pl2303.quirks |= pl2303_type_data[type].quirks; + // purpose unknown, overtaken from Linux Kernel driver - if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[PL2303_TYPE_HXN]) { + if (p_cdc->pl2303.type != PL2303_TYPE_HXN) { TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, &buf, cdch_process_set_config, CONFIG_PL2303_WRITE1)); break; }// else: continue with next step @@ -2561,7 +2548,7 @@ static bool pl2303_process_set_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[PL2303_TYPE_HXN]) { + if (p_cdc->pl2303.type != PL2303_TYPE_HXN) { TU_ASSERT(pl2303_vendor_write(p_cdc, 0x0404, 0, cdch_process_set_config, CONFIG_PL2303_READ2)); break; }// else: continue with next step @@ -2569,7 +2556,7 @@ static bool pl2303_process_set_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[PL2303_TYPE_HXN]) { + if (p_cdc->pl2303.type != PL2303_TYPE_HXN) { TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, &buf, cdch_process_set_config, CONFIG_PL2303_READ3)); break; }// else: continue with next step @@ -2577,7 +2564,7 @@ static bool pl2303_process_set_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[PL2303_TYPE_HXN]) { + if (p_cdc->pl2303.type != PL2303_TYPE_HXN) { TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8383, &buf, cdch_process_set_config, CONFIG_PL2303_READ4)); break; }// else: continue with next step @@ -2585,7 +2572,7 @@ static bool pl2303_process_set_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[PL2303_TYPE_HXN]) { + if (p_cdc->pl2303.type != PL2303_TYPE_HXN) { TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, &buf, cdch_process_set_config, CONFIG_PL2303_WRITE2)); break; }// else: continue with next step @@ -2593,7 +2580,7 @@ static bool pl2303_process_set_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[PL2303_TYPE_HXN]) { + if (p_cdc->pl2303.type != PL2303_TYPE_HXN) { TU_ASSERT(pl2303_vendor_write(p_cdc, 0x0404, 1, cdch_process_set_config, CONFIG_PL2303_READ5)); break; }// else: continue with next step @@ -2601,7 +2588,7 @@ static bool pl2303_process_set_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[PL2303_TYPE_HXN]) { + if (p_cdc->pl2303.type != PL2303_TYPE_HXN) { TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8484, &buf, cdch_process_set_config, CONFIG_PL2303_READ6)); break; }// else: continue with next step @@ -2609,7 +2596,7 @@ static bool pl2303_process_set_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[PL2303_TYPE_HXN]) { + if (p_cdc->pl2303.type != PL2303_TYPE_HXN) { TU_ASSERT(pl2303_vendor_read(p_cdc, 0x8383, &buf, cdch_process_set_config, CONFIG_PL2303_WRITE3)); break; }// else: continue with next step @@ -2617,7 +2604,7 @@ static bool pl2303_process_set_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[PL2303_TYPE_HXN]) { + if (p_cdc->pl2303.type != PL2303_TYPE_HXN) { TU_ASSERT(pl2303_vendor_write(p_cdc, 0, 1, cdch_process_set_config, CONFIG_PL2303_WRITE4)); break; }// else: continue with next step @@ -2625,7 +2612,7 @@ static bool pl2303_process_set_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[PL2303_TYPE_HXN]) { + if (p_cdc->pl2303.type != PL2303_TYPE_HXN) { TU_ASSERT(pl2303_vendor_write(p_cdc, 1, 0, cdch_process_set_config, CONFIG_PL2303_WRITE5)); break; }// else: continue with next step @@ -2633,21 +2620,21 @@ static bool pl2303_process_set_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[PL2303_TYPE_HXN]) { - uint16_t const windex = (p_cdc->pl2303.serial_private.quirks & PL2303_QUIRK_LEGACY) ? 0x24 : 0x44; + if (p_cdc->pl2303.type != PL2303_TYPE_HXN) { + uint16_t const windex = (p_cdc->pl2303.quirks & PL2303_QUIRK_LEGACY) ? 0x24 : 0x44; TU_ASSERT(pl2303_vendor_write(p_cdc, 2, windex, cdch_process_set_config, CONFIG_PL2303_RESET_ENDP1)); break; }// else: continue with next step TU_ATTR_FALLTHROUGH; - // from here sequence overtaken from Linux Kernel function pl2303_open() + // from here sequence overtaken from Linux Kernel function pl2303_open() case CONFIG_PL2303_RESET_ENDP1: // step 1 - if (p_cdc->pl2303.serial_private.quirks & PL2303_QUIRK_LEGACY) { + if (p_cdc->pl2303.quirks & PL2303_QUIRK_LEGACY) { TU_ASSERT(pl2303_clear_halt(p_cdc, PL2303_OUT_EP, cdch_process_set_config, CONFIG_PL2303_RESET_ENDP2)); } else { /* reset upstream data pipes */ - if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[PL2303_TYPE_HXN]) { + if (p_cdc->pl2303.type == PL2303_TYPE_HXN) { 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, cdch_process_set_config, CONFIG_PL2303_LINE_CODING)); @@ -2659,11 +2646,11 @@ static bool pl2303_process_set_config(tuh_xfer_t *xfer) { case CONFIG_PL2303_RESET_ENDP2: // step 2 - if (p_cdc->pl2303.serial_private.quirks & PL2303_QUIRK_LEGACY) { + if (p_cdc->pl2303.quirks & PL2303_QUIRK_LEGACY) { TU_ASSERT(pl2303_clear_halt(p_cdc, PL2303_IN_EP, cdch_process_set_config, CONFIG_PL2303_LINE_CODING)); } else { /* reset upstream data pipes */ - if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[PL2303_TYPE_HXN]) { + if (p_cdc->pl2303.type == PL2303_TYPE_HXN) { // here nothing to do, only structure of previous step overtaken for better reading and comparison } else { TU_ASSERT(pl2303_vendor_write(p_cdc, 9, 0, cdch_process_set_config, CONFIG_PL2303_LINE_CODING)); @@ -2691,33 +2678,33 @@ static bool pl2303_process_set_config(tuh_xfer_t *xfer) { TU_ATTR_FALLTHROUGH; #endif - // skipped, because it's not working with each PL230x. flow control can be also set by PL2303 EEPROM Writer Program - // case CONFIG_PL2303_FLOW_CTRL_READ: - // // read flow control register for modify & write back in next step - // if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[PL2303_TYPE_HXN]) { - // TU_LOG_P_CDC ( "1\r\n" ); - // TU_ASSERT(pl2303_vendor_read(p_cdc, PL2303_HXN_FLOWCTRL_REG, &buf, - // cdch_process_set_config, CONFIG_PL2303_FLOW_CTRL_WRITE)); - // } else { - // TU_LOG_P_CDC ( "2\r\n" ); - // TU_ASSERT(pl2303_vendor_read(p_cdc, 0, &buf, cdch_process_set_config, CONFIG_PL2303_FLOW_CTRL_WRITE)); - // } - // break; - // - // case CONFIG_PL2303_FLOW_CTRL_WRITE: - // // no flow control - // buf = xfer->buffer[0]; - // if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[PL2303_TYPE_HXN]) { - // buf &= (uint8_t) ~PL2303_HXN_FLOWCTRL_MASK; - // buf |= PL2303_HXN_FLOWCTRL_NONE; - // TU_ASSERT(pl2303_vendor_write(p_cdc, PL2303_HXN_FLOWCTRL_REG, buf, - // cdch_process_set_config, CONFIG_PL2303_COMPLETE)); - // } else { - // buf &= (uint8_t) ~PL2303_FLOWCTRL_MASK; - // TU_ASSERT(pl2303_vendor_write(p_cdc, 0, buf, - // cdch_process_set_config, CONFIG_PL2303_COMPLETE)); - // } - // break; + // skipped, because it's not working with each PL230x. flow control can be also set by PL2303 EEPROM Writer Program + // case CONFIG_PL2303_FLOW_CTRL_READ: + // // read flow control register for modify & write back in next step + // if (p_cdc->pl2303.type == PL2303_TYPE_HXN) { + // TU_LOG_P_CDC ( "1\r\n" ); + // TU_ASSERT(pl2303_vendor_read(p_cdc, PL2303_HXN_FLOWCTRL_REG, &buf, + // cdch_process_set_config, CONFIG_PL2303_FLOW_CTRL_WRITE)); + // } else { + // TU_LOG_P_CDC ( "2\r\n" ); + // TU_ASSERT(pl2303_vendor_read(p_cdc, 0, &buf, cdch_process_set_config, CONFIG_PL2303_FLOW_CTRL_WRITE)); + // } + // break; + // + // case CONFIG_PL2303_FLOW_CTRL_WRITE: + // // no flow control + // buf = xfer->buffer[0]; + // if (p_cdc->pl2303.type == PL2303_TYPE_HXN) { + // buf &= (uint8_t) ~PL2303_HXN_FLOWCTRL_MASK; + // buf |= PL2303_HXN_FLOWCTRL_NONE; + // TU_ASSERT(pl2303_vendor_write(p_cdc, PL2303_HXN_FLOWCTRL_REG, buf, + // cdch_process_set_config, CONFIG_PL2303_COMPLETE)); + // } else { + // buf &= (uint8_t) ~PL2303_FLOWCTRL_MASK; + // TU_ASSERT(pl2303_vendor_write(p_cdc, 0, buf, + // cdch_process_set_config, CONFIG_PL2303_COMPLETE)); + // } + // break; case CONFIG_PL2303_COMPLETE: set_config_complete(idx, 0, true); @@ -2732,7 +2719,7 @@ static bool pl2303_process_set_config(tuh_xfer_t *xfer) { //------------- Helper -------------// -static int8_t pl2303_detect_type(cdch_interface_t *p_cdc, uint8_t step) { +static pl2303_type_t pl2303_detect_type(cdch_interface_t *p_cdc, uint8_t step) { tusb_desc_device_t desc_dev; TU_VERIFY(tuh_descriptor_get_device_local(p_cdc->daddr, &desc_dev), PL2303_TYPE_UNKNOWN); @@ -2932,13 +2919,14 @@ static uint32_t pl2303_encode_baud_rate_divisor_alt(uint8_t buf[PL2303_LINE_CODI 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; + const pl2303_type_data_t* type_data = &pl2303_type_data[p_cdc->pl2303.type]; - TU_VERIFY(baud && baud <= p_cdc->pl2303.serial_private.type->max_baud_rate); + TU_VERIFY(baud && baud <= type_data->max_baud_rate); /* * Use direct method for supported baud rates, otherwise use divisors. * Newer chip types do not support divisor encoding. */ - if (p_cdc->pl2303.serial_private.type->no_divisors) { + if (type_data->no_divisors) { baud_sup = baud; } else { baud_sup = pl2303_get_supported_baud_rate(baud); @@ -2946,7 +2934,7 @@ static bool pl2303_encode_baud_rate(cdch_interface_t *p_cdc, uint8_t buf[PL2303_ if (baud == baud_sup) { baud = pl2303_encode_baud_rate_direct(buf, baud); - } else if (p_cdc->pl2303.serial_private.type->alt_divisors) { + } else if (type_data->alt_divisors) { baud = pl2303_encode_baud_rate_divisor_alt(buf, baud); } else { baud = pl2303_encode_baud_rate_divisor(buf, baud); diff --git a/src/class/cdc/serial/pl2303.h b/src/class/cdc/serial/pl2303.h index 40311260d..c01dac0e1 100644 --- a/src/class/cdc/serial/pl2303.h +++ b/src/class/cdc/serial/pl2303.h @@ -97,65 +97,53 @@ // type data typedef enum pl2303_type { - PL2303_TYPE_H, - PL2303_TYPE_HX, - PL2303_TYPE_TA, - PL2303_TYPE_TB, - PL2303_TYPE_HXD, - PL2303_TYPE_HXN, - // PL2303_TYPE_NEED_SUPPORTS_HX_STATUS, - // PL2303_TYPE_UNKNOWN, - PL2303_TYPE_COUNT + PL2303_TYPE_H = 0, // 0 + PL2303_TYPE_HX, // 1 + PL2303_TYPE_TA, // 2 + PL2303_TYPE_TB, // 3 + PL2303_TYPE_HXD, // 4 + PL2303_TYPE_HXN, // 5 + PL2303_TYPE_COUNT, + PL2303_TYPE_NEED_SUPPORTS_HX_STATUS, + PL2303_TYPE_UNKNOWN, } pl2303_type_t; typedef struct pl2303_type_data { - uint8_t const *name; uint32_t const max_baud_rate; - uint8_t const quirks; - uint16_t const no_autoxonxoff:1; - uint16_t const no_divisors:1; - uint16_t const alt_divisors:1; + uint8_t const quirks; + uint8_t const no_autoxonxoff : 1; + uint8_t const no_divisors : 1; + uint8_t const alt_divisors : 1; } pl2303_type_data_t; #define PL2303_TYPE_DATA \ [PL2303_TYPE_H] = { \ - .name = (uint8_t const*)"H", \ .max_baud_rate = 1228800, \ .quirks = PL2303_QUIRK_LEGACY, \ .no_autoxonxoff = true, \ }, \ [PL2303_TYPE_HX] = { \ - .name = (uint8_t const*)"HX", \ .max_baud_rate = 6000000, \ }, \ [PL2303_TYPE_TA] = { \ - .name = (uint8_t const*)"TA", \ .max_baud_rate = 6000000, \ .alt_divisors = true, \ }, \ [PL2303_TYPE_TB] = { \ - .name = (uint8_t const*)"TB", \ .max_baud_rate = 12000000, \ .alt_divisors = true, \ }, \ [PL2303_TYPE_HXD] = { \ - .name = (uint8_t const*)"HXD", \ .max_baud_rate = 12000000, \ }, \ [PL2303_TYPE_HXN] = { \ - .name = (uint8_t const*)"G (HXN)", \ .max_baud_rate = 12000000, \ .no_divisors = true, \ } -// private data types -struct pl2303_serial_private { - const struct pl2303_type_data* type; - uint8_t quirks; -}; - typedef struct TU_ATTR_PACKED { - struct pl2303_serial_private serial_private; + pl2303_type_t type; + uint8_t quirks; bool supports_hx_status; } pl2303_private_t; @@ -167,8 +155,4 @@ typedef struct TU_ATTR_PACKED { #define PL2303_OUT_EP 0x02 #define PL2303_IN_EP 0x83 -// return values of pl2303_detect_type() -#define PL2303_TYPE_NEED_SUPPORTS_HX_STATUS -1 -#define PL2303_TYPE_UNKNOWN -2 - #endif // TUSB_PL2303_H -- cgit v1.3.1 From adf6cbfe03523cd8f7e23cb7f2edbb4304a6cd89 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 20 Jun 2025 12:52:34 +0700 Subject: cdch clean up and refactor, add explicit sync() API --- examples/host/cdc_msc_hid/src/cdc_app.c | 2 +- src/class/cdc/cdc_host.c | 172 +++++++++++++------------------- src/class/cdc/cdc_host.h | 48 +++++++-- src/class/cdc/serial/pl2303.h | 33 +++--- 4 files changed, 127 insertions(+), 128 deletions(-) (limited to 'src/class/cdc/serial') diff --git a/examples/host/cdc_msc_hid/src/cdc_app.c b/examples/host/cdc_msc_hid/src/cdc_app.c index 6f4433f22..2fa9a8560 100644 --- a/examples/host/cdc_msc_hid/src/cdc_app.c +++ b/examples/host/cdc_msc_hid/src/cdc_app.c @@ -89,7 +89,7 @@ void tuh_cdc_mount_cb(uint8_t idx) { // If CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined, line coding will be set by tinyusb stack // while eneumerating new cdc device cdc_line_coding_t line_coding = {0}; - if (tuh_cdc_get_local_line_coding(idx, &line_coding)) { + if (tuh_cdc_get_line_coding_local(idx, &line_coding)) { printf(" Baudrate: %" PRIu32 ", Stop Bits : %u\r\n", line_coding.bit_rate, line_coding.stop_bits); printf(" Parity : %u, Data Width: %u\r\n", line_coding.parity, line_coding.data_bits); } diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 2fe2c85bc..50d0a020d 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -42,15 +42,15 @@ // Level where CFG_TUSB_DEBUG must be at least for this driver is logged #ifndef CFG_TUH_CDC_LOG_LEVEL - #define CFG_TUH_CDC_LOG_LEVEL CFG_TUH_LOG_LEVEL + #define CFG_TUH_CDC_LOG_LEVEL 1 #endif -#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_CDC_LOG_LEVEL, __VA_ARGS__) -#define TU_LOG_CDC(TXT,DADDR,ITF_NUM,NAME,...) TU_LOG_DRV("[:%u:%u] CDCh %s " TXT "\r\n", \ - DADDR, ITF_NUM, NAME, ##__VA_ARGS__) -#define TU_LOG_P_CDC(TXT,...) TU_LOG_CDC(TXT, p_cdc->daddr, p_cdc->bInterfaceNumber, \ - 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) +#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_CDC_LOG_LEVEL, __VA_ARGS__) +#define TU_LOG_CDC(_cdc, _format, ...) TU_LOG_DRV("[:%u:%u] CDCh %s " _format "\r\n", _cdc->daddr, _cdc->bInterfaceNumber, \ + serial_drivers[_cdc->serial_drid].name, ##__VA_ARGS__) + +// Driver that need to set line coding in two stages: baudrate then data format. +#define DRIVER_2STAGE_SET_LINE_CODING (CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X) //--------------------------------------------------------------------+ // Host CDC Interface @@ -72,7 +72,8 @@ typedef struct { } line, requested_line; tuh_xfer_cb_t user_complete_cb; // required since we handle request internally first - #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X + + #if DRIVER_2STAGE_SET_LINE_CODING tuh_xfer_cb_t requested_complete_cb; #endif @@ -197,49 +198,57 @@ enum { SERIAL_DRIVER_COUNT }; +typedef bool (*serial_driver_func_t)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + typedef struct { uint16_t const (*vid_pid_list)[2]; uint16_t const vid_pid_count; + bool is_2stage_line_coding; // true if driver requires to set baudrate then data format separately + bool (*const open)(uint8_t daddr, const tusb_desc_interface_t * itf_desc, uint16_t max_len); 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); - bool (*const set_line_coding)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + const serial_driver_func_t set_control_line_state; + const serial_driver_func_t set_baudrate; + const serial_driver_func_t set_data_format; + const serial_driver_func_t set_line_coding; #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL - uint8_t const * name; + const char * name; #endif } cdch_serial_driver_t; +#if CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL + #define DRIVER_NAME_DECLARE(_str) .name = _str +#else + #define DRIVER_NAME_DECLARE(_str) +#endif + // 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, + .is_2stage_line_coding = false, .open = acm_open, .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, .set_line_coding = acm_set_line_coding, - #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL - .name = (uint8_t const *) "ACM" - #endif + 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), + .is_2stage_line_coding = true, .open = ftdi_open, .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, .set_line_coding = ftdi_set_line_coding, - #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL - .name = (uint8_t const *) "FTDI" - #endif + DRIVER_NAME_DECLARE("FTDI") }, #endif @@ -247,15 +256,14 @@ 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), + .is_2stage_line_coding = true, .open = cp210x_open, .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, .set_line_coding = cp210x_set_line_coding, - #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL - .name = (uint8_t const *) "CP210x" - #endif + DRIVER_NAME_DECLARE("CP210x") }, #endif @@ -263,15 +271,14 @@ 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), + .is_2stage_line_coding = true, .open = ch34x_open, .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, .set_line_coding = ch34x_set_line_coding, - #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL - .name = (uint8_t const *) "CH34x" - #endif + DRIVER_NAME_DECLARE("CH34x") }, #endif @@ -279,15 +286,14 @@ 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), + .is_2stage_line_coding = false, .open = pl2303_open, .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, .set_line_coding = pl2303_set_line_coding, - #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL - .name = (uint8_t const *) "PL2303" - #endif + DRIVER_NAME_DECLARE("PL2303") } #endif }; @@ -440,13 +446,7 @@ bool tuh_cdc_get_control_line_state_local(uint8_t idx, uint16_t* line_state) { bool tuh_cdc_get_line_coding_local(uint8_t idx, cdc_line_coding_t * line_coding) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); - *line_coding = p_cdc->line.coding; - TU_LOG_P_CDC("get line coding %lu %u%c%s", - p_cdc->line.coding.bit_rate, p_cdc->line.coding.data_bits, - CDC_LINE_CODING_PARITY_CHAR(p_cdc->line.coding.parity), - CDC_LINE_CODING_STOP_BITS_TEXT(line_coding->stop_bits)); - return true; } @@ -520,29 +520,27 @@ bool tuh_cdc_read_clear (uint8_t idx) { // Control Endpoint API //--------------------------------------------------------------------+ +#if DRIVER_2STAGE_SET_LINE_CODING + // set line coding using sequence with 2 stages: set baudrate (stage1) + set data format (stage2) static bool set_line_coding_sequence( cdch_interface_t * p_cdc, - // control request function to set baudrate - bool (*set_baudrate_request)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data), - // control request function to set data format - bool (*set_data_format_request)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data), - // function to be called after stage 1 completed - void (*set_line_coding_stage1_complete)(tuh_xfer_t * xfer), - // control complete function to be called after request - void (*internal_control_complete)(tuh_xfer_t * xfer), + serial_driver_func_t set_baudrate, + serial_driver_func_t set_data_format, + tuh_xfer_cb_t set_line_coding_stage1_complete, // function to be called after stage 1 completed + tuh_xfer_cb_t internal_control_complete, // control complete function to be called after request tuh_xfer_cb_t complete_cb, uintptr_t user_data) { if (complete_cb) { // non-blocking // stage 1 set baudrate p_cdc->requested_complete_cb = complete_cb; // store complete_cb to be used in set_line_coding_stage1_complete() p_cdc->user_complete_cb = set_line_coding_stage1_complete; - return set_baudrate_request(p_cdc, internal_control_complete, user_data); + return set_baudrate(p_cdc, internal_control_complete, user_data); } else { // blocking sequence // stage 1 set baudrate xfer_result_t result = XFER_RESULT_INVALID; // use local result, because user_data ptr may be NULL - bool ret = set_baudrate_request(p_cdc, NULL, (uintptr_t) &result); + bool ret = set_baudrate(p_cdc, NULL, (uintptr_t) &result); if (user_data) { *((xfer_result_t *) user_data) = result; @@ -556,7 +554,7 @@ static bool set_line_coding_sequence( // stage 2 set data format result = XFER_RESULT_INVALID; - ret = set_data_format_request(p_cdc, NULL, (uintptr_t) &result); + ret = set_data_format(p_cdc, NULL, (uintptr_t) &result); if (user_data) { *((xfer_result_t *) user_data) = result; @@ -590,58 +588,35 @@ static void set_line_coding_stage1_complete( } } } - -// call of (non-)blocking set-functions (to set line state, baudrate, ...) -static bool set_function_call ( - cdch_interface_t * p_cdc, - bool (*set_function)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data), - tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - if (complete_cb) { - // non-blocking with call back - return set_function(p_cdc, complete_cb, user_data); - } else { - // blocking - xfer_result_t result = XFER_RESULT_INVALID; // use local result, because user_data ptr may be NULL - bool ret = set_function(p_cdc, NULL, (uintptr_t) &result); - - if (user_data) { - *((xfer_result_t *) user_data) = result; - } - - return (ret && result == XFER_RESULT_SUCCESS); - } -} +#endif bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + TU_LOG_CDC(p_cdc, "set control line state dtr = %u rts = %u", p_cdc->requested_line.control_state.dtr, p_cdc->requested_line.control_state.rts); cdch_serial_driver_t const * driver = &serial_drivers[p_cdc->serial_drid]; p_cdc->requested_line.control_state.value = (uint8_t) line_state; - TU_LOG_P_CDC("set control line state dtr = %u rts = %u", p_cdc->requested_line.control_state.dtr, p_cdc->requested_line.control_state.rts); - - const bool ret = set_function_call(p_cdc, driver->set_control_line_state, complete_cb, user_data); - + const bool ret = driver->set_control_line_state(p_cdc, complete_cb, user_data); if (ret && !complete_cb) { - p_cdc->line.control_state = p_cdc->requested_line.control_state; + // blocking, update line state if request was successful + p_cdc->line.control_state.value = (uint8_t) line_state; } + return ret; } bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { cdch_interface_t *p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); - TU_LOG_P_CDC("set baudrate %lu", baudrate); + TU_LOG_CDC(p_cdc, "set baudrate %lu", baudrate); cdch_serial_driver_t const *driver = &serial_drivers[p_cdc->serial_drid]; p_cdc->requested_line.coding.bit_rate = baudrate; - - bool ret = set_function_call(p_cdc, driver->set_baudrate, complete_cb, user_data); - + const bool ret = driver->set_baudrate(p_cdc, complete_cb, user_data); if (ret && !complete_cb) { p_cdc->line.coding.bit_rate = baudrate; } -// TU_LOG_P_CDC_BOOL("set baudrate", ret); return ret; } @@ -650,7 +625,7 @@ bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uin tuh_xfer_cb_t complete_cb, uintptr_t user_data) { cdch_interface_t *p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); - TU_LOG_P_CDC("set data format %u%c%s", + TU_LOG_CDC(p_cdc, "set data format %u%c%s", data_bits, CDC_LINE_CODING_PARITY_CHAR(parity), CDC_LINE_CODING_STOP_BITS_TEXT(stop_bits)); cdch_serial_driver_t const *driver = &serial_drivers[p_cdc->serial_drid]; @@ -659,15 +634,13 @@ bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uin p_cdc->requested_line.coding.parity = parity; p_cdc->requested_line.coding.data_bits = data_bits; - bool ret = set_function_call(p_cdc, driver->set_data_format, complete_cb, user_data); + const bool ret = driver->set_data_format(p_cdc, complete_cb, user_data); if (ret && !complete_cb) { p_cdc->line.coding.stop_bits = stop_bits; p_cdc->line.coding.parity = parity; p_cdc->line.coding.data_bits = data_bits; } -// TU_LOG_P_CDC_BOOL("set data format", ret); - return ret; } @@ -675,7 +648,7 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const *line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { cdch_interface_t *p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); - TU_LOG_P_CDC("set line coding %lu %u%c%s", + TU_LOG_CDC(p_cdc, "set line coding %lu %u%c%s", line_coding->bit_rate, line_coding->data_bits, CDC_LINE_CODING_PARITY_CHAR(line_coding->parity), CDC_LINE_CODING_STOP_BITS_TEXT(line_coding->stop_bits)); @@ -683,12 +656,11 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const *line_coding, p_cdc->requested_line.coding = *line_coding; - bool ret = set_function_call(p_cdc, driver->set_line_coding, complete_cb, user_data); + const bool ret = driver->set_line_coding(p_cdc, complete_cb, user_data); if (ret && !complete_cb) { p_cdc->line.coding = *line_coding; } -// TU_LOG_P_CDC_BOOL("set line coding", ret); return ret; } @@ -728,7 +700,7 @@ void cdch_close(uint8_t daddr) { for (uint8_t idx = 0; idx < CFG_TUH_CDC; idx++) { cdch_interface_t *p_cdc = &cdch_data[idx]; if (p_cdc->daddr == daddr) { - TU_LOG_P_CDC("close"); + TU_LOG_CDC(p_cdc, "close"); // Invoke application callback if (tuh_cdc_umount_cb) { @@ -847,9 +819,8 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_d } if (driver_detected) { - TU_LOG_CDC("open", daddr, itf_desc->bInterfaceNumber, driver_detected->name); - bool ret = driver_detected->open(daddr, itf_desc, max_len); - // TU_LOG_CDC("opened ret = %s", daddr, itf_desc->bInterfaceNumber, driver_detected->name, ret ? "true" : "FALSE" ); + const bool ret = driver_detected->open(daddr, itf_desc, max_len); + TU_LOG_DRV("[:%u:%u] CDCh %s open %s\r\n", daddr, itf_desc->bInterfaceNumber, driver_detected->name, ret ? "OK" : "FAILED"); return ret; } @@ -859,7 +830,6 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_d static void set_config_complete(uint8_t idx, uint8_t itf_offset, bool success) { cdch_interface_t *p_cdc = get_itf(idx); TU_ASSERT(p_cdc, ); - TU_LOG_P_CDC_BOOL("set config complete", success); if (success) { p_cdc->mounted = true; @@ -883,6 +853,8 @@ static void cdch_process_set_config(tuh_xfer_t *xfer) { TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT,); const uint8_t idx = get_idx_by_ptr(p_cdc); + TU_LOG_DRV(" state = %u\r\n", xfer->user_data); + 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); @@ -895,7 +867,7 @@ bool cdch_set_config(uint8_t daddr, uint8_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_P_CDC("set config"); + TU_LOG_CDC(p_cdc, "set config"); // fake transfer to kick-off process_set_config() tuh_xfer_t xfer; @@ -919,7 +891,6 @@ static void acm_internal_control_complete(tuh_xfer_t *xfer) { cdch_interface_t *p_cdc = get_itf(idx); TU_ASSERT(p_cdc, ); bool const success = (xfer->result == XFER_RESULT_SUCCESS); - TU_LOG_P_CDC_BOOL("control complete", success); if (success) { switch (xfer->setup->bRequest) { @@ -1204,7 +1175,6 @@ static void ftdi_internal_control_complete(tuh_xfer_t *xfer) { cdch_interface_t *p_cdc = get_itf(idx); TU_ASSERT(p_cdc, ); bool const success = (xfer->result == XFER_RESULT_SUCCESS); - TU_LOG_P_CDC_BOOL("control complete", success); if (success) { if (xfer->setup->bRequest == FTDI_SIO_SET_MODEM_CTRL_REQUEST && @@ -1232,14 +1202,12 @@ static void ftdi_internal_control_complete(tuh_xfer_t *xfer) { static bool ftdi_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->user_complete_cb = complete_cb; TU_ASSERT(ftdi_set_data_request(p_cdc, complete_cb ? ftdi_internal_control_complete : NULL, user_data)); - return true; } static bool ftdi_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->user_complete_cb = complete_cb; TU_ASSERT(ftdi_change_speed(p_cdc, complete_cb ? ftdi_internal_control_complete : NULL, user_data)); - return true; } @@ -1271,7 +1239,6 @@ static bool ftdi_set_modem_ctrl(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_ } //------------- Enumeration -------------// - enum { CONFIG_FTDI_DETERMINE_TYPE = 0, CONFIG_FTDI_WRITE_LATENCY, @@ -1448,7 +1415,7 @@ static bool ftdi_determine_type(cdch_interface_t *p_cdc) { #if CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL const char * ftdi_chip_name[] = { FTDI_CHIP_NAMES }; - TU_LOG_P_CDC("%s detected (bcdDevice = 0x%04x)", + TU_LOG_CDC(p_cdc, "%s detected (bcdDevice = 0x%04x)", ftdi_chip_name[p_cdc->ftdi.chip_type], version); #endif @@ -1595,7 +1562,7 @@ static inline uint32_t ftdi_get_divisor(cdch_interface_t *p_cdc) { break; } - TU_LOG_P_CDC("Baudrate divisor = 0x%lu", div_value); + TU_LOG_CDC(p_cdc, "Baudrate divisor = 0x%lu", div_value); return div_value; } @@ -1695,7 +1662,6 @@ static void cp210x_internal_control_complete(tuh_xfer_t *xfer) { cdch_interface_t *p_cdc = get_itf(idx); TU_ASSERT(p_cdc, ); bool const success = (xfer->result == XFER_RESULT_SUCCESS); - TU_LOG_P_CDC_BOOL("control complete", success); if (success) { switch (xfer->setup->bRequest) { @@ -1795,7 +1761,7 @@ static bool cp210x_process_set_config(tuh_xfer_t *xfer) { cdch_interface_t *p_cdc = get_itf(idx); TU_ASSERT(p_cdc && xfer->result == XFER_RESULT_SUCCESS); - switch (state) { + switch (state) { case CONFIG_CP210X_IFC_ENABLE: TU_ASSERT(cp210x_ifc_enable(p_cdc, CP210X_UART_ENABLE, cdch_process_set_config, CONFIG_CP210X_SET_BAUDRATE_REQUEST)); break; @@ -1941,7 +1907,6 @@ static void ch34x_internal_control_complete(tuh_xfer_t *xfer) { cdch_interface_t *p_cdc = get_itf(idx); TU_ASSERT(p_cdc, ); bool const success = (xfer->result == XFER_RESULT_SUCCESS); - TU_LOG_P_CDC_BOOL("control complete", success); if (success) { switch (xfer->setup->bRequest) { @@ -2059,7 +2024,6 @@ static bool ch34x_process_set_config(tuh_xfer_t *xfer) { uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); cdch_interface_t *p_cdc = get_itf(idx); uint8_t buffer[2];// TODO remove - TU_ASSERT(p_cdc && xfer->result == XFER_RESULT_SUCCESS); switch (state) { @@ -2071,7 +2035,7 @@ static bool ch34x_process_set_config(tuh_xfer_t *xfer) { case CONFIG_CH34X_SERIAL_INIT: { // handle version read data, set CH34x line coding (incl. baudrate) uint8_t const version = xfer->buffer[0]; - TU_LOG_P_CDC("Chip Version = 0x%02x", version); + TU_LOG_CDC(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 if (version >= 0x30) { @@ -2348,7 +2312,6 @@ static void pl2303_internal_control_complete(tuh_xfer_t *xfer) { cdch_interface_t *p_cdc = get_itf(idx); TU_ASSERT(p_cdc, ); bool const success = (xfer->result == XFER_RESULT_SUCCESS); - TU_LOG_P_CDC_BOOL("control complete", success); if (success) { if (xfer->setup->bRequest == PL2303_SET_LINE_REQUEST && @@ -2458,6 +2421,7 @@ static bool pl2303_process_set_config(tuh_xfer_t *xfer) { pl2303_type_t type; TU_ASSERT(p_cdc && (xfer->result == XFER_RESULT_SUCCESS || state == CONFIG_PL2303_READ1)); + switch (state) { // from here sequence overtaken from Linux Kernel function pl2303_startup() case CONFIG_PL2303_DETECT_TYPE: @@ -2741,7 +2705,7 @@ static pl2303_type_t pl2303_detect_type(cdch_interface_t *p_cdc, uint8_t step) { default: break; } - TU_LOG_P_CDC("unknown device type bcdUSB = 0x%04x", desc_dev.bcdUSB); + TU_LOG_CDC(p_cdc, "unknown device type bcdUSB = 0x%04x", desc_dev.bcdUSB); return PL2303_TYPE_UNKNOWN; } @@ -2886,7 +2850,7 @@ static bool pl2303_encode_baud_rate(cdch_interface_t *p_cdc, uint8_t buf[PL2303_ } else { baud = pl2303_encode_baud_rate_divisor(buf, baud); } - TU_LOG_P_CDC("real baudrate %lu", baud); + TU_LOG_CDC(p_cdc, "real baudrate %lu", baud); return true; } diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h index 688164cb2..37bfca270 100644 --- a/src/class/cdc/cdc_host.h +++ b/src/class/cdc/cdc_host.h @@ -137,13 +137,12 @@ bool tuh_cdc_peek(uint8_t idx, uint8_t* ch); bool tuh_cdc_read_clear (uint8_t idx); //--------------------------------------------------------------------+ -// Control Endpoint (Request) API +// Control Request API // Each Function will make a USB control transfer request to/from device // - If complete_cb is provided, the function will return immediately and invoke // the callback when request is complete. // - If complete_cb is NULL, the function will block until request is complete. -// - In this case, user_data should be pointed to xfer_result_t to hold the transfer result. -// - The function will return true if transfer is successful, false otherwise. +// In this case, user_data should be usb_xfer_result_t* to hold the transfer result. //--------------------------------------------------------------------+ // Request to Set Control Line State: DTR (bit 0), RTS (bit 1) @@ -179,17 +178,52 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, // bool tuh_cdc_get_line_coding(uint8_t idx, cdc_line_coding_t* coding); // Connect by set both DTR, RTS -TU_ATTR_ALWAYS_INLINE static inline -bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { return tuh_cdc_set_control_line_state(idx, CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS, complete_cb, user_data); } // Disconnect by clear both DTR, RTS -TU_ATTR_ALWAYS_INLINE static inline -bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { return tuh_cdc_set_control_line_state(idx, 0x00, complete_cb, user_data); } +//--------------------------------------------------------------------+ +// Control Request Sync API +// Each Function will make a USB control transfer request to/from device the function will block until request is +// complete. The function will return the transfer request result +//--------------------------------------------------------------------+ +TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_control_line_state_sync(uint8_t idx, uint16_t line_state) { + TU_API_SYNC(tuh_cdc_set_control_line_state, idx, line_state); +} + +TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_dtr_sync(uint8_t idx, bool dtr_state) { + TU_API_SYNC(tuh_cdc_set_dtr, idx, dtr_state); +} + +TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_rts_sync(uint8_t idx, bool rts_state) { + TU_API_SYNC(tuh_cdc_set_rts, idx, rts_state); +} + +TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_baudrate_sync(uint8_t idx, uint32_t baudrate) { + TU_API_SYNC(tuh_cdc_set_baudrate, idx, baudrate); +} + +TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_data_format_sync(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits) { + TU_API_SYNC(tuh_cdc_set_data_format, idx, stop_bits, parity, data_bits); +} + +TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_set_line_coding_sync(uint8_t idx, cdc_line_coding_t const* line_coding) { + TU_API_SYNC(tuh_cdc_set_line_coding, idx, line_coding); +} + +TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_connect_sync(uint8_t idx) { + TU_API_SYNC(tuh_cdc_connect, idx); +} + +TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_cdc_disconnect_sync(uint8_t idx) { + TU_API_SYNC(tuh_cdc_disconnect, idx); +} + //--------------------------------------------------------------------+ // CDC APPLICATION CALLBACKS //--------------------------------------------------------------------+ diff --git a/src/class/cdc/serial/pl2303.h b/src/class/cdc/serial/pl2303.h index c01dac0e1..63910c7bb 100644 --- a/src/class/cdc/serial/pl2303.h +++ b/src/class/cdc/serial/pl2303.h @@ -109,36 +109,37 @@ typedef enum pl2303_type { } pl2303_type_t; typedef struct pl2303_type_data { - uint32_t const max_baud_rate; - uint8_t const quirks; - uint8_t const no_autoxonxoff : 1; - uint8_t const no_divisors : 1; - uint8_t const alt_divisors : 1; + uint32_t max_baud_rate; + uint8_t quirks; + uint8_t no_autoxonxoff : 1; + uint8_t no_divisors : 1; + uint8_t alt_divisors : 1; } pl2303_type_data_t; #define PL2303_TYPE_DATA \ [PL2303_TYPE_H] = { \ - .max_baud_rate = 1228800, \ - .quirks = PL2303_QUIRK_LEGACY, \ - .no_autoxonxoff = true, \ + .max_baud_rate = 1228800, .quirks = PL2303_QUIRK_LEGACY, \ + .no_autoxonxoff = 1, .no_divisors = 0, .alt_divisors = 0 \ }, \ [PL2303_TYPE_HX] = { \ - .max_baud_rate = 6000000, \ + .max_baud_rate = 6000000, .quirks = 0, \ + .no_autoxonxoff = 0, .no_divisors = 0, .alt_divisors = 0 \ }, \ [PL2303_TYPE_TA] = { \ - .max_baud_rate = 6000000, \ - .alt_divisors = true, \ + .max_baud_rate = 6000000, .quirks = 0, \ + .no_autoxonxoff = 0, .no_divisors = 0, .alt_divisors = 1 \ }, \ [PL2303_TYPE_TB] = { \ - .max_baud_rate = 12000000, \ - .alt_divisors = true, \ + .max_baud_rate = 12000000, .quirks = 0, \ + .no_autoxonxoff = 0, .no_divisors = 0, .alt_divisors = 1 \ }, \ [PL2303_TYPE_HXD] = { \ - .max_baud_rate = 12000000, \ + .max_baud_rate = 12000000, .quirks = 0, \ + .no_autoxonxoff = 0, .no_divisors = 0, .alt_divisors = 0 \ }, \ [PL2303_TYPE_HXN] = { \ - .max_baud_rate = 12000000, \ - .no_divisors = true, \ + .max_baud_rate = 12000000, .quirks = 0, \ + .no_autoxonxoff = 0, .no_divisors = 1, .alt_divisors = 0 \ } typedef struct TU_ATTR_PACKED { -- cgit v1.3.1 From 8b5d703f74153c75dd9e4cfb224dbe74e4e262fa Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 27 Jun 2025 15:57:18 +0700 Subject: major refactor to generalize cdch serial driver - add common 2 stage set line coding for driver without direct set_line_coding support e.g ftdi, cp210x, ch34x - add common cdch_process_line_state_on_enum() to handle cfg line state on enum e.g CFG_TUH_CDC_LINE_CONTROL/CODING_ON_ENUM - refactor cdch_internal_control_complete and user_complete_cb to be managed by tuh_cdc_ API instead of serial driver --- src/class/cdc/cdc_host.c | 661 ++++++++++++++++--------------------------- src/class/cdc/serial/ch34x.h | 4 +- 2 files changed, 245 insertions(+), 420 deletions(-) (limited to 'src/class/cdc/serial') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 1786f7815..7926aa9fc 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -45,12 +45,9 @@ #define CFG_TUH_CDC_LOG_LEVEL 1 #endif -#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_CDC_LOG_LEVEL, __VA_ARGS__) -#define TU_LOG_CDC(_cdc, _format, ...) TU_LOG_DRV("[:%u:%u] CDCh %s " _format "\r\n", _cdc->daddr, _cdc->bInterfaceNumber, \ - serial_drivers[_cdc->serial_drid].name, ##__VA_ARGS__) - -// Driver that need to set line coding in two stages: baudrate then data format. -#define DRIVER_2STAGE_SET_LINE_CODING (CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X) +#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_CDC_LOG_LEVEL, __VA_ARGS__) +#define TU_LOG_CDC(_cdc, _format, ...) TU_LOG_DRV("[:%u:%u] CDCh %s " _format "\r\n", _cdc->daddr, _cdc->bInterfaceNumber, \ + serial_drivers[_cdc->serial_drid].name, ##__VA_ARGS__) //--------------------------------------------------------------------+ // Host CDC Interface @@ -73,10 +70,6 @@ typedef struct { tuh_xfer_cb_t user_complete_cb; // required since we handle request internally first - #if DRIVER_2STAGE_SET_LINE_CODING - tuh_xfer_cb_t requested_complete_cb; - #endif - union { struct { cdc_acm_capability_t capability; @@ -114,7 +107,10 @@ CFG_TUH_MEM_SECTION static cdch_epbuf_t cdch_epbuf[CFG_TUH_CDC]; // General driver static void cdch_process_set_config(tuh_xfer_t *xfer); +static void cdch_process_line_state_on_enum(tuh_xfer_t *xfer); // invoked after set config is processed static void cdch_internal_control_complete(tuh_xfer_t *xfer); +static void cdch_set_line_coding_stage1_baudrate_complete(tuh_xfer_t *xfer); +static void cdch_set_line_coding_stage2_data_format_complete(tuh_xfer_t *xfer); //------------- ACM prototypes -------------// static bool acm_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len); @@ -135,7 +131,6 @@ static void ftdi_internal_control_complete(cdch_interface_t* p_cdc, tuh_xfer_t * 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); -static bool ftdi_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool ftdi_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #endif @@ -149,7 +144,6 @@ static void cp210x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t 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); -static bool cp210x_set_line_coding(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); #endif @@ -163,7 +157,6 @@ static void ch34x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t 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); -static bool ch34x_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool ch34x_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #endif @@ -210,8 +203,6 @@ typedef bool (*serial_driver_func_t)(cdch_interface_t * p_cdc, tuh_xfer_cb_t com typedef struct { uint16_t const (*vid_pid_list)[2]; uint16_t const vid_pid_count; - bool is_2stage_line_coding; // true if driver requires to set baudrate then data format separately - bool (*const open)(uint8_t daddr, const tusb_desc_interface_t * itf_desc, uint16_t max_len); bool (*const process_set_config)(cdch_interface_t * p_cdc, tuh_xfer_t * xfer); void (*const request_complete)(cdch_interface_t * p_cdc, tuh_xfer_t * xfer); // internal request complete handler to update line state @@ -234,7 +225,6 @@ static const cdch_serial_driver_t serial_drivers[] = { { .vid_pid_list = NULL, .vid_pid_count = 0, - .is_2stage_line_coding = false, .open = acm_open, .process_set_config = acm_process_set_config, .request_complete = acm_internal_control_complete, @@ -250,15 +240,13 @@ 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), - .is_2stage_line_coding = true, .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 = ftdi_set_line_coding, + .set_line_coding = NULL, // 2 stage set line coding DRIVER_NAME_DECLARE("FTDI") }, #endif @@ -267,15 +255,13 @@ 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), - .is_2stage_line_coding = true, .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 = cp210x_set_line_coding, + .set_line_coding = NULL, // 2 stage set line coding DRIVER_NAME_DECLARE("CP210x") }, #endif @@ -284,7 +270,6 @@ 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), - .is_2stage_line_coding = true, .open = ch34x_open, .process_set_config = ch34x_process_set_config, .request_complete = ch34x_internal_control_complete, @@ -292,7 +277,7 @@ static const cdch_serial_driver_t serial_drivers[] = { .set_control_line_state = ch34x_set_modem_ctrl, .set_baudrate = ch34x_set_baudrate, .set_data_format = ch34x_set_data_format, - .set_line_coding = ch34x_set_line_coding, + .set_line_coding = NULL, // 2 stage set line coding DRIVER_NAME_DECLARE("CH34x") }, #endif @@ -301,11 +286,9 @@ 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), - .is_2stage_line_coding = false, .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_baudrate, .set_data_format = pl2303_set_data_format, @@ -474,28 +457,24 @@ bool tuh_cdc_get_line_coding_local(uint8_t idx, cdc_line_coding_t * line_coding) uint32_t tuh_cdc_write(uint8_t idx, void const * buffer, uint32_t bufsize) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); - return tu_edpt_stream_write(p_cdc->daddr, &p_cdc->stream.tx, buffer, bufsize); } uint32_t tuh_cdc_write_flush(uint8_t idx) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); - return tu_edpt_stream_write_xfer(p_cdc->daddr, &p_cdc->stream.tx); } bool tuh_cdc_write_clear(uint8_t idx) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); - return tu_edpt_stream_clear(&p_cdc->stream.tx); } uint32_t tuh_cdc_write_available(uint8_t idx) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); - return tu_edpt_stream_write_available(p_cdc->daddr, &p_cdc->stream.tx); } @@ -506,21 +485,18 @@ uint32_t tuh_cdc_write_available(uint8_t idx) { uint32_t tuh_cdc_read (uint8_t idx, void * buffer, uint32_t bufsize) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); - return tu_edpt_stream_read(p_cdc->daddr, &p_cdc->stream.rx, buffer, bufsize); } uint32_t tuh_cdc_read_available(uint8_t idx) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); - return tu_edpt_stream_read_available(&p_cdc->stream.rx); } bool tuh_cdc_peek(uint8_t idx, uint8_t * ch) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); - return tu_edpt_stream_peek(&p_cdc->stream.rx, ch); } @@ -537,105 +513,40 @@ bool tuh_cdc_read_clear (uint8_t idx) { // Control Endpoint API //--------------------------------------------------------------------+ -#if DRIVER_2STAGE_SET_LINE_CODING - -// set line coding using sequence with 2 stages: set baudrate (stage1) + set data format (stage2) -static bool set_line_coding_sequence( - cdch_interface_t * p_cdc, - serial_driver_func_t set_baudrate, - serial_driver_func_t set_data_format, - tuh_xfer_cb_t set_line_coding_stage1_complete, // function to be called after stage 1 completed - tuh_xfer_cb_t internal_control_complete, // control complete function to be called after request - tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - if (complete_cb) { - // non-blocking - // stage 1 set baudrate - p_cdc->requested_complete_cb = complete_cb; // store complete_cb to be used in set_line_coding_stage1_complete() - p_cdc->user_complete_cb = set_line_coding_stage1_complete; - return set_baudrate(p_cdc, internal_control_complete, user_data); - } else { - // blocking sequence - // stage 1 set baudrate - xfer_result_t result = XFER_RESULT_INVALID; // use local result, because user_data ptr may be NULL - bool ret = set_baudrate(p_cdc, NULL, (uintptr_t) &result); - - if (user_data) { - *((xfer_result_t *) user_data) = result; - } - - TU_ASSERT(ret); - TU_VERIFY(result == XFER_RESULT_SUCCESS); - - // overtake baudrate after successful request - p_cdc->line.coding.bit_rate = p_cdc->requested_line.coding.bit_rate; - - // stage 2 set data format - result = XFER_RESULT_INVALID; - ret = set_data_format(p_cdc, NULL, (uintptr_t) &result); - - if (user_data) { - *((xfer_result_t *) user_data) = result; - } - - TU_ASSERT(ret); - return (result == XFER_RESULT_SUCCESS); - // the overtaking of remaining requested_line_coding will be done in tuh_cdc_set_line_coding() - } -} - -static void set_line_coding_stage1_complete( - tuh_xfer_t * xfer, uint8_t const itf_num, - // control request function to set data format - bool (*set_data_format_request)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data), - // control complete function to be called after request - void (*internal_control_complete)(tuh_xfer_t * xfer)) { - uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); - cdch_interface_t * p_cdc = get_itf(idx); - TU_ASSERT(p_cdc,); - - if (xfer->result == XFER_RESULT_SUCCESS) { - // stage 1 success, continue with stage 2 - p_cdc->user_complete_cb = p_cdc->requested_complete_cb; - set_data_format_request(p_cdc, internal_control_complete, xfer->user_data); - } else { - // stage 1 failed, notify user - xfer->complete_cb = p_cdc->requested_complete_cb; - if (xfer->complete_cb) { - xfer->complete_cb(xfer); - } - } -} -#endif - bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); TU_LOG_CDC(p_cdc, "set control line state dtr = %u rts = %u", p_cdc->requested_line.control_state.dtr, p_cdc->requested_line.control_state.rts); - cdch_serial_driver_t const * driver = &serial_drivers[p_cdc->serial_drid]; + const cdch_serial_driver_t * driver = &serial_drivers[p_cdc->serial_drid]; p_cdc->requested_line.control_state.value = (uint8_t) line_state; - const bool ret = driver->set_control_line_state(p_cdc, complete_cb, user_data); - if (ret && !complete_cb) { + p_cdc->user_complete_cb = complete_cb; + TU_VERIFY(driver->set_control_line_state(p_cdc, complete_cb ? cdch_internal_control_complete : NULL, user_data)); + + if (!complete_cb) { // blocking, update line state if request was successful p_cdc->line.control_state.value = (uint8_t) line_state; } - return ret; + return true; } bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { cdch_interface_t *p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); TU_LOG_CDC(p_cdc, "set baudrate %lu", baudrate); - cdch_serial_driver_t const *driver = &serial_drivers[p_cdc->serial_drid]; + const cdch_serial_driver_t *driver = &serial_drivers[p_cdc->serial_drid]; + p_cdc->requested_line = p_cdc->line; // keep current line coding p_cdc->requested_line.coding.bit_rate = baudrate; - const bool ret = driver->set_baudrate(p_cdc, complete_cb, user_data); - if (ret && !complete_cb) { + p_cdc->user_complete_cb = complete_cb; + TU_VERIFY(driver->set_baudrate(p_cdc, complete_cb ? cdch_internal_control_complete : NULL, user_data)); + + if (!complete_cb) { p_cdc->line.coding.bit_rate = baudrate; } - return ret; + return true; } bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, @@ -645,20 +556,24 @@ bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uin TU_LOG_CDC(p_cdc, "set data format %u%c%s", data_bits, CDC_LINE_CODING_PARITY_CHAR(parity), CDC_LINE_CODING_STOP_BITS_TEXT(stop_bits)); - cdch_serial_driver_t const *driver = &serial_drivers[p_cdc->serial_drid]; + const cdch_serial_driver_t *driver = &serial_drivers[p_cdc->serial_drid]; + p_cdc->requested_line = p_cdc->line; // keep current line coding p_cdc->requested_line.coding.stop_bits = stop_bits; p_cdc->requested_line.coding.parity = parity; p_cdc->requested_line.coding.data_bits = data_bits; - const bool ret = driver->set_data_format(p_cdc, complete_cb, user_data); + p_cdc->user_complete_cb = complete_cb; + TU_VERIFY(driver->set_data_format(p_cdc, complete_cb ? cdch_internal_control_complete : NULL, user_data)); - if (ret && !complete_cb) { + if (!complete_cb) { + // blocking p_cdc->line.coding.stop_bits = stop_bits; p_cdc->line.coding.parity = parity; p_cdc->line.coding.data_bits = data_bits; } - return ret; + + return true; } bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const *line_coding, @@ -670,16 +585,43 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const *line_coding, CDC_LINE_CODING_PARITY_CHAR(line_coding->parity), CDC_LINE_CODING_STOP_BITS_TEXT(line_coding->stop_bits)); cdch_serial_driver_t const *driver = &serial_drivers[p_cdc->serial_drid]; - p_cdc->requested_line.coding = *line_coding; - const bool ret = driver->set_line_coding(p_cdc, complete_cb, user_data); + if (driver->set_line_coding) { + // driver support set_line_coding request + TU_VERIFY(driver->set_line_coding(p_cdc, complete_cb, user_data)); + + if (!complete_cb) { + p_cdc->line.coding = *line_coding; + } + } else { + // driver does not support set_line_coding and need 2 stage to set baudrate and data format separately + if (complete_cb) { + // non-blocking + p_cdc->user_complete_cb = complete_cb; + TU_VERIFY(driver->set_baudrate(p_cdc, cdch_set_line_coding_stage1_baudrate_complete, user_data)); + } else { + // blocking + xfer_result_t result = XFER_RESULT_INVALID; + + TU_VERIFY(driver->set_baudrate(p_cdc, NULL, (uintptr_t) &result)); + if (user_data) { + *((xfer_result_t *) user_data) = result; + } + TU_VERIFY(result == XFER_RESULT_SUCCESS); + p_cdc->line.coding.bit_rate = p_cdc->requested_line.coding.bit_rate; // update baudrate - if (ret && !complete_cb) { - p_cdc->line.coding = *line_coding; + result = XFER_RESULT_INVALID; + TU_VERIFY(driver->set_data_format(p_cdc, NULL, (uintptr_t) &result)); + if (user_data) { + *((xfer_result_t *) user_data) = result; + } + TU_VERIFY(result == XFER_RESULT_SUCCESS); + p_cdc->line.coding = p_cdc->requested_line.coding; // update data format + } } - return ret; + return true; } //--------------------------------------------------------------------+ @@ -809,41 +751,50 @@ static bool open_ep_stream_pair(cdch_interface_t *p_cdc, tusb_desc_endpoint_t co bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { (void) rhport; - cdch_serial_driver_t const *driver_detected = NULL; - // For CDC: only support ACM subclass // Note: Protocol 0xFF can be RNDIS device if (TUSB_CLASS_CDC == itf_desc->bInterfaceClass && CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass) { return acm_open(daddr, itf_desc, max_len); - } else if (SERIAL_DRIVER_COUNT > 1 && - TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass) { - uint16_t vid, pid; - TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid)); - - for (size_t dr = 1; dr < SERIAL_DRIVER_COUNT; dr++) { - cdch_serial_driver_t const *driver = &serial_drivers[dr]; - 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) { - driver_detected = driver; - break; + } else if (SERIAL_DRIVER_COUNT > 1 && + TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass) { + uint16_t vid, pid; + TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid)); + + for (size_t dr = 1; dr < SERIAL_DRIVER_COUNT; dr++) { + const cdch_serial_driver_t *driver = &serial_drivers[dr]; + 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 bool ret = driver->open(daddr, itf_desc, max_len); + TU_LOG_DRV("[:%u:%u] CDCh %s open %s\r\n", daddr, itf_desc->bInterfaceNumber, driver->name, ret ? "OK" : "FAILED"); + return ret; + } + } } - } - if (driver_detected) { - break; - } - } - } - - if (driver_detected) { - const bool ret = driver_detected->open(daddr, itf_desc, max_len); - TU_LOG_DRV("[:%u:%u] CDCh %s open %s\r\n", daddr, itf_desc->bInterfaceNumber, driver_detected->name, ret ? "OK" : "FAILED"); - return ret; - } + } return 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); + 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, uint8_t itf_offset, bool success) { if (success) { const uint8_t idx = get_idx_by_ptr(p_cdc); @@ -863,43 +814,81 @@ static void set_config_complete(cdch_interface_t *p_cdc, uint8_t itf_offset, boo usbh_driver_set_config_complete(p_cdc->daddr, p_cdc->bInterfaceNumber + itf_offset); } -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"); +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,); + TU_LOG_DRV(" state = %u\r\n", xfer->user_data); + const cdch_serial_driver_t *driver = &serial_drivers[p_cdc->serial_drid]; - // 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); + if (!driver->process_set_config(p_cdc, xfer)) { + const uint8_t itf_offset = (p_cdc->serial_drid == SERIAL_DRIVER_ACM) ? 1 : 0; + set_config_complete(p_cdc, itf_offset, false); + } +} + +static bool set_line_state_on_enum(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) { + enum { + ENUM_SET_LINE_CODING = 0, + ENUM_SET_LINE_CONTROL, + ENUM_SET_LINE_COMPLETE, + }; + const uint8_t idx = get_idx_by_ptr(p_cdc); + const uintptr_t state = xfer->user_data; + + switch (state) { + case ENUM_SET_LINE_CODING: { + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + // ch34x already set line coding in serial init + if (p_cdc->serial_drid != SERIAL_DRIVER_CH34X) { + const cdc_line_coding_t line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT(tuh_cdc_set_line_coding(idx, &line_coding, + cdch_process_line_state_on_enum, ENUM_SET_LINE_CONTROL)); + break; + } + #endif + TU_ATTR_FALLTHROUGH; + } + + case ENUM_SET_LINE_CONTROL: + #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + TU_ASSERT(tuh_cdc_set_control_line_state(idx, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, + cdch_process_line_state_on_enum, ENUM_SET_LINE_COMPLETE)); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + + case ENUM_SET_LINE_COMPLETE: { + const uint8_t itf_offset = (p_cdc->serial_drid == SERIAL_DRIVER_ACM) ? 1 : 0; + set_config_complete(p_cdc, itf_offset, true); + break; + } + + default: + return false; + } return true; } - -static void cdch_process_set_config(tuh_xfer_t *xfer) { +static void cdch_process_line_state_on_enum(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,); - TU_LOG_DRV(" state = %u\r\n", xfer->user_data); + TU_LOG_DRV(" xfer result = %u\r\n", xfer->result); - if (!serial_drivers[p_cdc->serial_drid].process_set_config(p_cdc, xfer)) { + if (xfer->result != XFER_RESULT_SUCCESS || !set_line_state_on_enum(p_cdc, xfer)) { const uint8_t itf_offset = (p_cdc->serial_drid == SERIAL_DRIVER_ACM) ? 1 : 0; set_config_complete(p_cdc, itf_offset, false); } } + static void cdch_internal_control_complete(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,); - TU_LOG_DRV(" request result = %u\r\n", xfer->result); - serial_drivers[p_cdc->serial_drid].request_complete(p_cdc, xfer); + const cdch_serial_driver_t *driver = &serial_drivers[p_cdc->serial_drid]; + driver->request_complete(p_cdc, xfer); // Invoke application callback xfer->complete_cb = p_cdc->user_complete_cb; @@ -908,6 +897,38 @@ static void cdch_internal_control_complete(tuh_xfer_t *xfer) { } } +static void cdch_set_line_coding_stage1_baudrate_complete(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,); + TU_LOG_DRV(" stage1 set baudrate result = %u\r\n", xfer->result); + const cdch_serial_driver_t *driver = &serial_drivers[p_cdc->serial_drid]; + + if (xfer->result == XFER_RESULT_SUCCESS) { + p_cdc->line.coding.bit_rate = p_cdc->requested_line.coding.bit_rate; // update baudrate + TU_ASSERT(driver->set_data_format(p_cdc, cdch_set_line_coding_stage2_data_format_complete, xfer->user_data),); + } else { + xfer->complete_cb = p_cdc->user_complete_cb; + if (xfer->complete_cb) { + xfer->complete_cb(xfer); + } + } +} + +static void cdch_set_line_coding_stage2_data_format_complete(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,); + TU_LOG_DRV(" stage2 set data format result = %u\r\n", xfer->result); + + if (xfer->result == XFER_RESULT_SUCCESS) { + p_cdc->line.coding = p_cdc->requested_line.coding; // update data format + } + + xfer->complete_cb = p_cdc->user_complete_cb; + if (xfer->complete_cb) { + xfer->complete_cb(xfer); + } +} + //--------------------------------------------------------------------+ // ACM //--------------------------------------------------------------------+ @@ -1000,16 +1021,10 @@ static bool acm_set_line_coding(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_ } static bool acm_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - p_cdc->requested_line.coding.bit_rate = p_cdc->line.coding.bit_rate; - return acm_set_line_coding(p_cdc, complete_cb, user_data); } static bool acm_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - p_cdc->requested_line.coding.stop_bits = p_cdc->line.coding.stop_bits; - p_cdc->requested_line.coding.parity = p_cdc->line.coding.parity; - p_cdc->requested_line.coding.data_bits = p_cdc->line.coding.data_bits; - return acm_set_line_coding(p_cdc, complete_cb, user_data); } @@ -1108,7 +1123,6 @@ static bool acm_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) { static bool ftdi_determine_type(cdch_interface_t *p_cdc); static uint32_t ftdi_get_divisor(cdch_interface_t *p_cdc); -static uint8_t ftdi_get_idx(tuh_xfer_t *xfer); //------------- Control Request -------------// @@ -1150,29 +1164,6 @@ static inline bool ftdi_sio_reset(cdch_interface_t *p_cdc, tuh_xfer_cb_t complet p_cdc->ftdi.channel, complete_cb, user_data); } -static bool ftdi_change_speed(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - uint32_t index_value = ftdi_get_divisor(p_cdc); - TU_VERIFY(index_value); - uint16_t value = (uint16_t) index_value; - uint16_t index = (uint16_t) (index_value >> 16); - if (p_cdc->ftdi.channel) { - index = (uint16_t) ((index << 8) | p_cdc->ftdi.channel); - } - - return ftdi_set_request(p_cdc, FTDI_SIO_SET_BAUDRATE_REQUEST, FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE, - value, index, complete_cb, user_data); -} - -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 - (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 - - return ftdi_set_request(p_cdc, FTDI_SIO_SET_DATA_REQUEST, FTDI_SIO_SET_DATA_REQUEST_TYPE, - value, p_cdc->ftdi.channel, complete_cb, user_data); -} //------------- Driver API -------------// @@ -1199,41 +1190,31 @@ static void ftdi_internal_control_complete(cdch_interface_t* p_cdc, tuh_xfer_t * } static bool ftdi_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - p_cdc->user_complete_cb = complete_cb; - TU_ASSERT(ftdi_set_data_request(p_cdc, complete_cb ? cdch_internal_control_complete : NULL, user_data)); - return true; + 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 + (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 + return ftdi_set_request(p_cdc, FTDI_SIO_SET_DATA_REQUEST, FTDI_SIO_SET_DATA_REQUEST_TYPE, + value, p_cdc->ftdi.channel, complete_cb, user_data); } static bool ftdi_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - p_cdc->user_complete_cb = complete_cb; - TU_ASSERT(ftdi_change_speed(p_cdc, complete_cb ? cdch_internal_control_complete : NULL, user_data)); - return true; -} - -static void ftdi_set_line_coding_stage1_complete(tuh_xfer_t *xfer) { - uint8_t const idx = ftdi_get_idx(xfer); - cdch_interface_t *p_cdc = get_itf(idx); - TU_ASSERT(p_cdc, ); - uint8_t const itf_num = p_cdc->bInterfaceNumber; - set_line_coding_stage1_complete(xfer, itf_num, - ftdi_set_data_request, // control request function to set data format - cdch_internal_control_complete); // control complete function to be called after request -} + uint32_t index_value = ftdi_get_divisor(p_cdc); + TU_VERIFY(index_value); + uint16_t value = (uint16_t) index_value; + uint16_t index = (uint16_t) (index_value >> 16); + if (p_cdc->ftdi.channel) { + index = (uint16_t) ((index << 8) | p_cdc->ftdi.channel); + } -// 2 stages: set baudrate (stage1) + set data format (stage2) -static bool ftdi_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, - ftdi_change_speed, // control request function to set baudrate - ftdi_set_data_request, // control request function to set data format - ftdi_set_line_coding_stage1_complete, // function to be called after stage 1 completed - cdch_internal_control_complete, // control complete function to be called after request - complete_cb, user_data); + return ftdi_set_request(p_cdc, FTDI_SIO_SET_BAUDRATE_REQUEST, FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE, + value, index, complete_cb, user_data); } static bool ftdi_set_modem_ctrl(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { uint16_t line_state = (uint16_t) ((p_cdc->requested_line.control_state.dtr ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW) | (p_cdc->requested_line.control_state.rts ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW)); - p_cdc->user_complete_cb = complete_cb; return ftdi_set_request(p_cdc, FTDI_SIO_SET_MODEM_CTRL_REQUEST, FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, line_state, p_cdc->ftdi.channel, complete_cb ? cdch_internal_control_complete : NULL, user_data); } @@ -1243,10 +1224,7 @@ enum { CONFIG_FTDI_DETERMINE_TYPE = 0, CONFIG_FTDI_WRITE_LATENCY, CONFIG_FTDI_SIO_RESET, - CONFIG_FTDI_SET_DATA, - CONFIG_FTDI_SET_BAUDRATE, CONFIG_FTDI_FLOW_CONTROL, - CONFIG_FTDI_MODEM_CTRL, CONFIG_FTDI_COMPLETE }; @@ -1305,48 +1283,20 @@ static bool ftdi_proccess_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) // from here sequence overtaken from Linux Kernel function ftdi_open() case CONFIG_FTDI_SIO_RESET: - p_cdc->user_complete_cb = cdch_process_set_config; - TU_ASSERT(ftdi_sio_reset(p_cdc, cdch_process_set_config, 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; - p_cdc->user_complete_cb = cdch_process_set_config; - TU_ASSERT(ftdi_set_data_request(p_cdc, cdch_internal_control_complete, CONFIG_FTDI_SET_BAUDRATE)); - break; - #else - TU_ATTR_FALLTHROUGH; - #endif - - case CONFIG_FTDI_SET_BAUDRATE: - #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM - p_cdc->user_complete_cb = cdch_process_set_config; - TU_ASSERT(ftdi_change_speed(p_cdc, cdch_internal_control_complete, CONFIG_FTDI_FLOW_CONTROL)); + TU_ASSERT(ftdi_sio_reset(p_cdc, cdch_process_set_config, CONFIG_FTDI_FLOW_CONTROL)); break; - #else - TU_ATTR_FALLTHROUGH; - #endif case CONFIG_FTDI_FLOW_CONTROL: // disable flow control 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, CONFIG_FTDI_MODEM_CTRL)); + p_cdc->ftdi.channel, cdch_process_set_config, CONFIG_FTDI_COMPLETE)); break; - case CONFIG_FTDI_MODEM_CTRL: - #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - p_cdc->requested_line.control_state.value = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; - TU_ASSERT(ftdi_set_modem_ctrl(p_cdc, cdch_process_set_config, CONFIG_FTDI_COMPLETE)); - break; - #else - TU_ATTR_FALLTHROUGH; - #endif - - case CONFIG_FTDI_COMPLETE: - set_config_complete(p_cdc, 0, true); + case CONFIG_FTDI_COMPLETE: { + xfer->user_data = 0; // kick-off set line state on enum + cdch_process_line_state_on_enum(xfer); break; + } default: return false; @@ -1562,20 +1512,6 @@ static inline uint32_t ftdi_get_divisor(cdch_interface_t *p_cdc) { return div_value; } -static uint8_t ftdi_get_idx(tuh_xfer_t *xfer) { - uint8_t const channel = (uint8_t) tu_le16toh(xfer->setup->wIndex);// channel index, or 0 for legacy types - for (uint8_t i = 0; i < CFG_TUH_CDC; i++) { - const cdch_interface_t *p_cdc = &cdch_data[i]; - if (p_cdc->daddr == xfer->daddr && - (!p_cdc->ftdi.channel || // 0 for legacy types (only interface 0) - channel == p_cdc->ftdi.channel)) {// or multi-channel types (interfaces 0..n) - return i; - } - } - - return TUSB_INDEX_INVALID_8; -} - #endif //--------------------------------------------------------------------+ @@ -1619,32 +1555,14 @@ static bool cp210x_set_request(cdch_interface_t * p_cdc, uint8_t command, uint16 return tuh_control_xfer(&xfer); } -static inline bool cp210x_ifc_enable(cdch_interface_t *p_cdc, uint16_t enabled, - tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +TU_ATTR_ALWAYS_INLINE 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) { - // Not every baud rate is supported. See datasheets and AN205 "CP210x Baud Rate Support" - 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 <= 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 - (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 - - 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) { +TU_ATTR_ALWAYS_INLINE 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) (CP210X_CONTROL_WRITE_DTR | CP210X_CONTROL_WRITE_RTS | - p_cdc->requested_line.control_state.value), + (uint16_t) (CP210X_CONTROL_WRITE_DTR | CP210X_CONTROL_WRITE_RTS | p_cdc->requested_line.control_state.value), NULL, 0, complete_cb, user_data); } @@ -1673,47 +1591,28 @@ static void cp210x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t } static bool cp210x_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - p_cdc->user_complete_cb = complete_cb; - TU_ASSERT(cp210x_set_baudrate_request(p_cdc, complete_cb ? cdch_internal_control_complete : NULL, user_data)); - return true; + // Not every baud rate is supported. See datasheets and AN205 "CP210x Baud Rate Support" + 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_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - p_cdc->user_complete_cb = complete_cb; - TU_ASSERT(cp210x_set_line_ctl(p_cdc, complete_cb ? cdch_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 - cdch_internal_control_complete); // control complete function to be called after request -} + 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 + (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 -// 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 - cdch_internal_control_complete, // control complete function to be called after request - complete_cb, user_data); + return cp210x_set_request(p_cdc, CP210X_SET_LINE_CTL, lcr, NULL, 0, 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_complete_cb = complete_cb; - TU_ASSERT(cp210x_set_mhs(p_cdc, complete_cb ? cdch_internal_control_complete : NULL, user_data)); - return true; + return cp210x_set_mhs(p_cdc, complete_cb, user_data); } //------------- Enumeration -------------// enum { CONFIG_CP210X_IFC_ENABLE = 0, - CONFIG_CP210X_SET_BAUDRATE_REQUEST, - CONFIG_CP210X_SET_LINE_CTL, - CONFIG_CP210X_SET_DTR_RTS, CONFIG_CP210X_COMPLETE }; @@ -1740,40 +1639,12 @@ static bool cp210x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) switch (state) { case CONFIG_CP210X_IFC_ENABLE: - TU_ASSERT(cp210x_ifc_enable(p_cdc, CP210X_UART_ENABLE, cdch_process_set_config, CONFIG_CP210X_SET_BAUDRATE_REQUEST)); + TU_ASSERT(cp210x_ifc_enable(p_cdc, CP210X_UART_ENABLE, cdch_process_set_config, CONFIG_CP210X_COMPLETE)); 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; - p_cdc->user_complete_cb = cdch_process_set_config; - TU_ASSERT(cp210x_set_baudrate_request(p_cdc, cdch_internal_control_complete, CONFIG_CP210X_SET_LINE_CTL)); - break; - #else - TU_ATTR_FALLTHROUGH; - #endif - - case CONFIG_CP210X_SET_LINE_CTL: - #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM - p_cdc->user_complete_cb = cdch_process_set_config; - TU_ASSERT(cp210x_set_line_ctl(p_cdc, cdch_internal_control_complete, CONFIG_CP210X_SET_DTR_RTS)); - break; - #else - TU_ATTR_FALLTHROUGH; - #endif - - case CONFIG_CP210X_SET_DTR_RTS: - #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - p_cdc->requested_line.control_state.value = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; - p_cdc->user_complete_cb = cdch_process_set_config; - TU_ASSERT(cp210x_set_mhs(p_cdc, cdch_internal_control_complete, CONFIG_CP210X_COMPLETE)); - break; - #else - TU_ATTR_FALLTHROUGH; - #endif - case CONFIG_CP210X_COMPLETE: - set_config_complete(p_cdc, 0, true); + xfer->user_data = 0;// kick-off set line state on enum + cdch_process_line_state_on_enum(xfer); break; default: @@ -1833,19 +1704,19 @@ static bool ch34x_set_request(cdch_interface_t *p_cdc, uint8_t direction, uint8_ return tuh_control_xfer(&xfer); } -static inline bool ch34x_control_out(cdch_interface_t *p_cdc, uint8_t request, uint16_t value, uint16_t index, - tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +TU_ATTR_ALWAYS_INLINE static inline bool ch34x_control_out(cdch_interface_t *p_cdc, uint8_t request, uint16_t value, uint16_t index, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { return ch34x_set_request(p_cdc, TUSB_DIR_OUT, request, value, index, NULL, 0, complete_cb, user_data); } -static inline bool ch34x_control_in(cdch_interface_t *p_cdc, uint8_t request, uint16_t value, uint16_t index, - uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +TU_ATTR_ALWAYS_INLINE static inline bool ch34x_control_in(cdch_interface_t *p_cdc, uint8_t request, uint16_t value, uint16_t index, + uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { return ch34x_set_request(p_cdc, TUSB_DIR_IN, request, value, index, buffer, buffersize, complete_cb, user_data); } -static inline bool ch34x_write_reg(cdch_interface_t *p_cdc, uint16_t reg, uint16_t reg_value, - tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +TU_ATTR_ALWAYS_INLINE static inline bool ch34x_write_reg(cdch_interface_t *p_cdc, uint16_t reg, uint16_t reg_value, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { return ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, reg, reg_value, complete_cb, user_data); } @@ -1855,25 +1726,6 @@ static inline bool ch34x_write_reg(cdch_interface_t *p_cdc, uint16_t reg, uint16 // return ch34x_control_in ( p_cdc, CH34X_REQ_READ_REG, reg, 0, buffer, buffersize, complete_cb, user_data ); //} -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) { - // CH34x signals are inverted - uint8_t control = ~((p_cdc->requested_line.control_state.rts ? CH34X_BIT_RTS : 0) | - (p_cdc->requested_line.control_state.dtr ? CH34X_BIT_DTR : 0)); - return ch34x_control_out(p_cdc, CH34X_REQ_MODEM_CTRL, control, 0, complete_cb, user_data); -} - //------------- Driver API -------------// // internal control complete to update state such as line state, encoding @@ -1908,39 +1760,22 @@ static void ch34x_internal_control_complete(cdch_interface_t *p_cdc, tuh_xfer_t } static bool ch34x_set_data_format(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - p_cdc->user_complete_cb = complete_cb; - TU_ASSERT(ch34x_write_reg_data_format(p_cdc, complete_cb ? cdch_internal_control_complete : NULL, user_data)); - return true; + const uint8_t 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_set_baudrate(cdch_interface_t *p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - p_cdc->user_complete_cb = complete_cb; - TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, complete_cb ? cdch_internal_control_complete : NULL, user_data)); - return true; -} - -static void ch34x_set_line_coding_stage1_complete(tuh_xfer_t *xfer) { - // CH34x has only interface 0, because wIndex is used as payload and not for bInterfaceNumber - uint8_t const itf_num = 0; - set_line_coding_stage1_complete(xfer, itf_num, - ch34x_write_reg_data_format, // control request function to set data format - cdch_internal_control_complete); // control complete function to be called after request -} - -// 2 stages: set baudrate (stage1) + set data format (stage2) -static bool ch34x_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, - ch34x_write_reg_baudrate, // control request function to set baudrate - ch34x_write_reg_data_format, // control request function to set data format - ch34x_set_line_coding_stage1_complete, // function to be called after stage 1 completed - cdch_internal_control_complete, // control complete function to be called after request - complete_cb, user_data); + const uint16_t 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_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - p_cdc->user_complete_cb = complete_cb; - TU_ASSERT(ch34x_modem_ctrl_request(p_cdc, complete_cb ? cdch_internal_control_complete : NULL, user_data)); - return true; + // CH34x signals are inverted + uint8_t control = ~((p_cdc->requested_line.control_state.rts ? CH34X_BIT_RTS : 0) | + (p_cdc->requested_line.control_state.dtr ? CH34X_BIT_DTR : 0)); + return ch34x_control_out(p_cdc, CH34X_REQ_MODEM_CTRL, control, 0, complete_cb, user_data); } //------------- Enumeration -------------// @@ -1950,7 +1785,6 @@ enum { CONFIG_CH34X_SERIAL_INIT, CONFIG_CH34X_SPECIAL_REG_WRITE, CONFIG_CH34X_FLOW_CONTROL, - CONFIG_CH34X_MODEM_CONTROL, CONFIG_CH34X_COMPLETE }; @@ -2018,25 +1852,16 @@ static bool ch34x_process_set_config(cdch_interface_t *p_cdc, tuh_xfer_t *xfer) case CONFIG_CH34X_FLOW_CONTROL: // no hardware flow control TU_ASSERT(ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x27, CH341_REG_0x27), 0x0000, - cdch_process_set_config, CONFIG_CH34X_MODEM_CONTROL)); + cdch_process_set_config, CONFIG_CH34X_COMPLETE)); break; - case CONFIG_CH34X_MODEM_CONTROL: - #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - p_cdc->requested_line.control_state.value = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; - p_cdc->user_complete_cb = cdch_process_set_config; - TU_ASSERT(ch34x_modem_ctrl_request(p_cdc, cdch_internal_control_complete, CONFIG_CH34X_COMPLETE)); - break; - #else - TU_ATTR_FALLTHROUGH; - #endif - case CONFIG_CH34X_COMPLETE: - set_config_complete(p_cdc, 0, true); + xfer->user_data = 0; // kick-off set line state on enum + cdch_process_line_state_on_enum(xfer); break; default: - break; + return false; } return true; diff --git a/src/class/cdc/serial/ch34x.h b/src/class/cdc/serial/ch34x.h index 7d91f01fe..0e08b0acd 100644 --- a/src/class/cdc/serial/ch34x.h +++ b/src/class/cdc/serial/ch34x.h @@ -34,9 +34,9 @@ // set line_coding @ enumeration #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM -#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X CFG_TUH_CDC_LINE_CODING_ON_ENUM + #define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X CFG_TUH_CDC_LINE_CODING_ON_ENUM #else // this default is necessary to work properly -#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X { 9600, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 } + #define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X { 9600, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 } #endif // USB requests -- cgit v1.3.1