From 1a229f3ce2623f8e0cf072e66e3770ee7f95e095 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 24 Apr 2023 21:42:31 +0700 Subject: [PATCH 01/13] fix pio vbus typo --- hw/bsp/rp2040/family.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/bsp/rp2040/family.c b/hw/bsp/rp2040/family.c index 920869585..3aa989c0e 100644 --- a/hw/bsp/rp2040/family.c +++ b/hw/bsp/rp2040/family.c @@ -125,10 +125,10 @@ void board_init(void) // Set the system clock to a multiple of 120mhz for bitbanging USB with pico-usb set_sys_clock_khz(120000, true); -#ifdef PIO_USB_VBUSEN_PIN +#ifdef PICO_DEFAULT_PIO_USB_VBUSEN_PIN gpio_init(PICO_DEFAULT_PIO_USB_VBUSEN_PIN); gpio_set_dir(PICO_DEFAULT_PIO_USB_VBUSEN_PIN, GPIO_OUT); - gpio_put(PICO_DEFAULT_PIO_USB_VBUSEN_PIN, PIO_USB_VBUSEN_STATE); + gpio_put(PICO_DEFAULT_PIO_USB_VBUSEN_PIN, PICO_DEFAULT_PIO_USB_VBUSEN_STATE); #endif // rp2040 use pico-pio-usb for host tuh_configure() can be used to passed pio configuration to the host stack From 45169d833d05ec8b64d0ce11a5ff1efbea0b7917 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 26 Apr 2023 12:43:46 +0700 Subject: [PATCH 02/13] hacky, but ftdi work with hard code baudrate = 9600 --- .codespell/ignore-words.txt | 1 + src/class/cdc/cdc_host.c | 366 ++++++++++++++++++++++++------- src/class/cdc/serial/ftdi_host.h | 258 ++++++++++++++++++++++ src/host/usbh.c | 2 +- src/tusb_option.h | 5 + 5 files changed, 549 insertions(+), 83 deletions(-) create mode 100644 src/class/cdc/serial/ftdi_host.h diff --git a/.codespell/ignore-words.txt b/.codespell/ignore-words.txt index 5c89bae1c..2513691cb 100644 --- a/.codespell/ignore-words.txt +++ b/.codespell/ignore-words.txt @@ -1,6 +1,7 @@ synopsys sie tre +thre hsi fro dout diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index c0cc41adf..7949ff8d3 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -33,6 +33,10 @@ #include "cdc_host.h" +#if CFG_TUH_CDC_FTDI + #include "serial/ftdi_host.h" +#endif + // Debug level, TUSB_CFG_DEBUG must be at least this level for debug message #define CDCH_DEBUG 2 @@ -43,12 +47,18 @@ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ +enum { + SERIAL_PROTOCOL_ACM = 0, + SERIAL_PROTOCOL_FTDI = 1, +}; + typedef struct { uint8_t daddr; uint8_t bInterfaceNumber; uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; + uint8_t serial_protocol; cdc_acm_capability_t acm_capability; uint8_t ep_notif; @@ -111,6 +121,11 @@ static cdch_interface_t* find_new_itf(void) return NULL; } +static inline bool support_line_request(cdch_interface_t const* p_cdc) { + return (p_cdc->serial_protocol == SERIAL_PROTOCOL_ACM && p_cdc->acm_capability.support_line_request) || + (p_cdc->serial_protocol == SERIAL_PROTOCOL_FTDI); +} + //--------------------------------------------------------------------+ // APPLICATION API //--------------------------------------------------------------------+ @@ -270,21 +285,32 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer) if (xfer->result == XFER_RESULT_SUCCESS) { - switch(xfer->setup->bRequest) - { - case CDC_REQUEST_SET_CONTROL_LINE_STATE: - p_cdc->line_state = (uint8_t) tu_le16toh(xfer->setup->wValue); - break; + if (p_cdc->serial_protocol == SERIAL_PROTOCOL_ACM) { + switch (xfer->setup->bRequest) { + case CDC_REQUEST_SET_CONTROL_LINE_STATE: + p_cdc->line_state = (uint8_t) tu_le16toh(xfer->setup->wValue); + break; - case CDC_REQUEST_SET_LINE_CODING: - { - uint16_t const len = tu_min16(sizeof(cdc_line_coding_t), tu_le16toh(xfer->setup->wLength)); - memcpy(&p_cdc->line_coding, xfer->buffer, len); + case CDC_REQUEST_SET_LINE_CODING: { + uint16_t const len = tu_min16(sizeof(cdc_line_coding_t), tu_le16toh(xfer->setup->wLength)); + memcpy(&p_cdc->line_coding, xfer->buffer, len); + } + break; + + default: break; } - break; - - default: break; } + #if CFG_TUH_CDC_FTDI + else if (p_cdc->serial_protocol == SERIAL_PROTOCOL_FTDI) { + switch (xfer->setup->bRequest) { + case FTDI_SIO_MODEM_CTRL: + p_cdc->line_state = (uint8_t) (tu_le16toh(xfer->setup->wValue) & 0x00ff); + break; + + default: break; + } + } + #endif } xfer->complete_cb = p_cdc->user_control_cb; @@ -294,27 +320,51 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer) 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->acm_capability.support_line_request); + TU_VERIFY(p_cdc && support_line_request(p_cdc)); TU_LOG_CDCH("CDC Set Control Line State\r\n"); - tusb_control_request_t const request = - { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_INTERFACE, - .type = TUSB_REQ_TYPE_CLASS, - .direction = TUSB_DIR_OUT - }, - .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE, - .wValue = tu_htole16(line_state), - .wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber), - .wLength = 0 - }; + tusb_control_request_t request; + + if(p_cdc->serial_protocol == SERIAL_PROTOCOL_ACM ) { + tusb_control_request_t const acm_request = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT + }, + .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE, + .wValue = tu_htole16(line_state), + .wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber), + .wLength = 0 + }; + + request = acm_request; + } + #if CFG_TUH_CDC_FTDI + else if (p_cdc->serial_protocol == SERIAL_PROTOCOL_FTDI) { + // FTDI use vendor specific request to set control line state + tusb_control_request_t const ftdi_request = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_VENDOR, + .direction = TUSB_DIR_OUT + }, + .bRequest = FTDI_SIO_MODEM_CTRL, + .wValue = tu_htole16(0x0300 | line_state), // 0x0300 is DTR and RTS enable + .wIndex = 0, // port + .wLength = 0 + }; + + request = ftdi_request; + } + #endif + else { + return false; + } p_cdc->user_control_cb = complete_cb; - tuh_xfer_t xfer = - { + tuh_xfer_t xfer = { .daddr = p_cdc->daddr, .ep_addr = 0, .setup = &request, @@ -324,34 +374,60 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c }; TU_ASSERT(tuh_control_xfer(&xfer)); + return true; } 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->acm_capability.support_line_request); + TU_VERIFY(p_cdc && support_line_request(p_cdc)); TU_LOG_CDCH("CDC Set Line Conding\r\n"); - tusb_control_request_t const request = - { - .bmRequestType_bit = - { - .recipient = TUSB_REQ_RCPT_INTERFACE, - .type = TUSB_REQ_TYPE_CLASS, - .direction = TUSB_DIR_OUT - }, - .bRequest = CDC_REQUEST_SET_LINE_CODING, - .wValue = 0, - .wIndex = tu_htole16(p_cdc->bInterfaceNumber), - .wLength = tu_htole16(sizeof(cdc_line_coding_t)) - }; + tusb_control_request_t request; + uint8_t* enum_buf = NULL; - // use usbh enum buf to hold line coding since user line_coding variable may not live long enough - // for the transfer to complete - uint8_t* enum_buf = usbh_get_enum_buf(); - memcpy(enum_buf, line_coding, sizeof(cdc_line_coding_t)); + if (p_cdc->serial_protocol == SERIAL_PROTOCOL_ACM) { + tusb_control_request_t const acm_request = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT + }, + .bRequest = CDC_REQUEST_SET_LINE_CODING, + .wValue = 0, + .wIndex = tu_htole16(p_cdc->bInterfaceNumber), + .wLength = tu_htole16(sizeof(cdc_line_coding_t)) + }; + + request = acm_request; + + // use usbh enum buf to hold line coding since user line_coding variable does not live long enough + enum_buf = usbh_get_enum_buf(); + memcpy(enum_buf, line_coding, sizeof(cdc_line_coding_t)); + } + #if CFG_TUH_CDC_FTDI + else if (p_cdc->serial_protocol == SERIAL_PROTOCOL_FTDI) { + // FTDI need to set baud rate and data bits, parity, stop bits separately + tusb_control_request_t const ftdi_request = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_VENDOR, + .direction = TUSB_DIR_OUT + }, + .bRequest = FTDI_SIO_SET_BAUD_RATE, + .wValue = 0x4138, // FIXME hardcoded to 9600 baud + .wIndex = 0, // port + .wLength = tu_htole16(sizeof(cdc_line_coding_t)) + }; + + request = ftdi_request; + } + #endif + else { + return false; + } p_cdc->user_control_cb = complete_cb; tuh_xfer_t xfer = @@ -434,8 +510,17 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t { tu_edpt_stream_read_xfer_complete(&p_cdc->stream.rx, xferred_bytes); + #if CFG_TUH_CDC_FTDI + // FTDI reserve 2 bytes for status + if (p_cdc->serial_protocol == SERIAL_PROTOCOL_FTDI) { + uint8_t status[2]; + tu_edpt_stream_read(&p_cdc->stream.rx, status, 2); + (void) status; // TODO handle status + } + #endif + // invoke receive callback - if (tuh_cdc_rx_cb) tuh_cdc_rx_cb(idx); + if (tuh_cdc_rx_cb) tuh_cdc_rx_cb(idx); // prepare for next transfer if needed tu_edpt_stream_read_xfer(&p_cdc->stream.rx); @@ -453,17 +538,99 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t //--------------------------------------------------------------------+ // Enumeration //--------------------------------------------------------------------+ - -bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) +enum { - (void) rhport; + // ACM + CONFIG_SET_CONTROL_LINE_STATE, + CONFIG_SET_LINE_CODING, + CONFIG_COMPLETE, - // Only support ACM subclass - // Protocol 0xFF can be RNDIS device for windows XP - TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass && - CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass && - 0xFF != itf_desc->bInterfaceProtocol); + // FTDI + CONFIG_FTDI_RESET +}; +static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t const *desc_ep) +{ + for(size_t i=0; i<2; i++) + { + TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && + TUSB_XFER_BULK == desc_ep->bmAttributes.xfer); + + TU_ASSERT(tuh_edpt_open(p_cdc->daddr, desc_ep)); + + if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN ) + { + tu_edpt_stream_open(&p_cdc->stream.rx, p_cdc->daddr, desc_ep); + }else + { + tu_edpt_stream_open(&p_cdc->stream.tx, p_cdc->daddr, desc_ep); + } + + desc_ep = (tusb_desc_endpoint_t const*) tu_desc_next(desc_ep); + } + + return true; +} + +#if CFG_TUH_CDC_FTDI +bool ftdih_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { + // FTDI configuration includes 1 vendor interface + 2 bulk endpoints + TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff && itf_desc->bNumEndpoints == 2); + TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len); + + cdch_interface_t * p_cdc = find_new_itf(); + TU_VERIFY(p_cdc); + + p_cdc->daddr = daddr; + p_cdc->bInterfaceNumber = itf_desc->bInterfaceNumber; + p_cdc->bInterfaceSubClass = itf_desc->bInterfaceSubClass; + p_cdc->bInterfaceProtocol = itf_desc->bInterfaceProtocol; + p_cdc->line_state = 0; + p_cdc->serial_protocol = SERIAL_PROTOCOL_FTDI; + + // endpoint pair + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); + + // data endpoints expected to be in pairs + TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep)); + + return true; +} + +static bool ftdih_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + tusb_control_request_t const request = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_DEVICE, + .type = TUSB_REQ_TYPE_VENDOR, + .direction = TUSB_DIR_OUT + }, + .bRequest = FTDI_SIO_RESET, + .wValue = tu_htole16(FTDI_SIO_RESET_SIO), + .wIndex = 0, + .wLength = 0 + }; + + p_cdc->user_control_cb = complete_cb; + + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb, + .user_data = user_data + }; + + TU_ASSERT(tuh_control_xfer(&xfer)); + return true; +} + +#endif + + +static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) +{ uint8_t const * p_desc_end = ((uint8_t const*) itf_desc) + max_len; cdch_interface_t * p_cdc = find_new_itf(); @@ -474,6 +641,7 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_d p_cdc->bInterfaceSubClass = itf_desc->bInterfaceSubClass; p_cdc->bInterfaceProtocol = itf_desc->bInterfaceProtocol; p_cdc->line_state = 0; + p_cdc->serial_protocol = SERIAL_PROTOCOL_ACM; //------------- Control Interface -------------// uint8_t const * p_desc = tu_desc_next(itf_desc); @@ -510,35 +678,45 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_d p_desc = tu_desc_next(p_desc); // data endpoints expected to be in pairs - for(uint32_t i=0; i<2; i++) - { - tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc; - TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && - TUSB_XFER_BULK == desc_ep->bmAttributes.xfer); - - TU_ASSERT(tuh_edpt_open(daddr, desc_ep)); - - if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN ) - { - tu_edpt_stream_open(&p_cdc->stream.rx, daddr, desc_ep); - }else - { - tu_edpt_stream_open(&p_cdc->stream.tx, daddr, desc_ep); - } - - p_desc = tu_desc_next(p_desc); - } + TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const *) p_desc)); } return true; } -enum + +bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { - CONFIG_SET_CONTROL_LINE_STATE, - CONFIG_SET_LINE_CODING, - CONFIG_COMPLETE -}; + (void) rhport; + + // 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 ( 0xff == itf_desc->bInterfaceClass ) + { + uint16_t vid, pid; + TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid)); + +#if CFG_TUH_CDC_FTDI + if (TU_FTDI_VID == vid) { + uint16_t const ftdi_pids[] = {TU_FTDI_PID_LIST}; + enum { FTDI_PID_COUNT = sizeof(ftdi_pids) / sizeof(ftdi_pids[0]) }; + + for (size_t i = 0; i < FTDI_PID_COUNT; i++) { + if (ftdi_pids[i] == pid) { + return ftdih_open(daddr, itf_desc, max_len); + } + } + } +#endif + } + + return false; +} static void process_cdc_config(tuh_xfer_t* xfer) { @@ -550,9 +728,16 @@ static void process_cdc_config(tuh_xfer_t* xfer) switch(state) { + #if CFG_TUH_CDC_FTDI + // Note may need to read FTDI eeprom + case CONFIG_FTDI_RESET: + TU_ASSERT(ftdih_sio_reset(p_cdc, process_cdc_config, CONFIG_SET_CONTROL_LINE_STATE), ); + break; + #endif + case CONFIG_SET_CONTROL_LINE_STATE: #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - if (p_cdc->acm_capability.support_line_request) + if (support_line_request(p_cdc)) { TU_ASSERT( tuh_cdc_set_control_line_state(idx, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_cdc_config, CONFIG_SET_LINE_CODING), ); break; @@ -562,7 +747,7 @@ static void process_cdc_config(tuh_xfer_t* xfer) case CONFIG_SET_LINE_CODING: #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM - if (p_cdc->acm_capability.support_line_request) + if (support_line_request(p_cdc)) { cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; TU_ASSERT( tuh_cdc_set_line_coding(idx, &line_coding, process_cdc_config, CONFIG_COMPLETE), ); @@ -588,7 +773,6 @@ static void process_cdc_config(tuh_xfer_t* xfer) bool cdch_set_config(uint8_t daddr, uint8_t itf_num) { - // fake transfer to kick-off process tusb_control_request_t request; request.wIndex = tu_htole16((uint16_t) itf_num); @@ -596,7 +780,25 @@ bool cdch_set_config(uint8_t daddr, uint8_t itf_num) xfer.daddr = daddr; xfer.result = XFER_RESULT_SUCCESS; xfer.setup = &request; - xfer.user_data = CONFIG_SET_CONTROL_LINE_STATE; + + // fake transfer to kick-off process + uint8_t const idx = tuh_cdc_itf_get_index(daddr, itf_num); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc); + + switch (p_cdc->serial_protocol) { + case SERIAL_PROTOCOL_ACM: + xfer.user_data = CONFIG_SET_CONTROL_LINE_STATE; + break; + + #if CFG_TUH_CDC_FTDI + case SERIAL_PROTOCOL_FTDI: + xfer.user_data = CONFIG_FTDI_RESET; + break; + #endif + + default: return false; + } process_cdc_config(&xfer); diff --git a/src/class/cdc/serial/ftdi_host.h b/src/class/cdc/serial/ftdi_host.h new file mode 100644 index 000000000..4276d374e --- /dev/null +++ b/src/class/cdc/serial/ftdi_host.h @@ -0,0 +1,258 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2023 Ha Thach (thach@tinyusb.org) for Adafruit Industries + * + * 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. + */ + +#ifndef TUSB_FTDI_HOST_H +#define TUSB_FTDI_HOST_H + +// VID/PID for matching FTDI devices +#define TU_FTDI_VID 0x0403 +#define TU_FTDI_PID_LIST \ + 0x6001, 0x6006, 0x6010, 0x6011, 0x6014, 0x6015, 0x8372, 0xFBFA, \ + 0xcd18 + +// 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_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 + */ + +#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) + +/* + * 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 */ +#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) + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void ftdih_init (void); +bool ftdih_open (uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); +bool ftdih_set_config (uint8_t daddr, uint8_t itf_num); +bool ftdih_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +void ftdih_close (uint8_t daddr); + +#endif //TUSB_FTDI_HOST_H diff --git a/src/host/usbh.c b/src/host/usbh.c index 3be662c63..3953d5b21 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -286,7 +286,7 @@ bool tuh_vid_pid_get(uint8_t dev_addr, uint16_t* vid, uint16_t* pid) *vid = *pid = 0; usbh_device_t const* dev = get_device(dev_addr); - TU_VERIFY(dev && dev->configured); + TU_VERIFY(dev && dev->addressed && dev->vid != 0); *vid = dev->vid; *pid = dev->pid; diff --git a/src/tusb_option.h b/src/tusb_option.h index 44d036ea4..a372e9950 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -439,6 +439,11 @@ #define CFG_TUH_CDC 0 #endif +#ifndef CFG_TUH_CDC_FTDI + // FTDI is not part of CDC class, CDC is used for Serial-over-USB here + #define CFG_TUH_CDC_FTDI 0 +#endif + #ifndef CFG_TUH_HID #define CFG_TUH_HID 0 #endif From cf95b4400167f6e523d8e5d1762a78744f63e2a9 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 26 Apr 2023 22:35:28 +0700 Subject: [PATCH 03/13] enable CDC FTDI --- examples/host/cdc_msc_hid/src/tusb_config.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/host/cdc_msc_hid/src/tusb_config.h b/examples/host/cdc_msc_hid/src/tusb_config.h index 1152e4910..2baf2b1ac 100644 --- a/examples/host/cdc_msc_hid/src/tusb_config.h +++ b/examples/host/cdc_msc_hid/src/tusb_config.h @@ -96,7 +96,8 @@ #define CFG_TUH_ENUMERATION_BUFSIZE 256 #define CFG_TUH_HUB 1 // number of supported hubs -#define CFG_TUH_CDC 1 +#define CFG_TUH_CDC 1 // CDC ACM +#define CFG_TUH_CDC_FTDI 1 // FTDI UART #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 From 293a6222f81ac1085f8b34730f1907f899ce17cf Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 27 Apr 2023 11:08:42 +0700 Subject: [PATCH 04/13] rename ftdi_host to ftdi_sio --- examples/host/cdc_msc_hid/CMakeLists.txt | 2 +- src/class/cdc/cdc_host.c | 2 +- src/class/cdc/serial/{ftdi_host.h => ftdi_sio.h} | 15 +++------------ 3 files changed, 5 insertions(+), 14 deletions(-) rename src/class/cdc/serial/{ftdi_host.h => ftdi_sio.h} (92%) diff --git a/examples/host/cdc_msc_hid/CMakeLists.txt b/examples/host/cdc_msc_hid/CMakeLists.txt index d7d1a54d7..b66ff2382 100644 --- a/examples/host/cdc_msc_hid/CMakeLists.txt +++ b/examples/host/cdc_msc_hid/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.17) include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 7949ff8d3..d33f31f1a 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -34,7 +34,7 @@ #include "cdc_host.h" #if CFG_TUH_CDC_FTDI - #include "serial/ftdi_host.h" + #include "serial/ftdi_sio.h" #endif diff --git a/src/class/cdc/serial/ftdi_host.h b/src/class/cdc/serial/ftdi_sio.h similarity index 92% rename from src/class/cdc/serial/ftdi_host.h rename to src/class/cdc/serial/ftdi_sio.h index 4276d374e..6916e4031 100644 --- a/src/class/cdc/serial/ftdi_host.h +++ b/src/class/cdc/serial/ftdi_sio.h @@ -22,8 +22,8 @@ * THE SOFTWARE. */ -#ifndef TUSB_FTDI_HOST_H -#define TUSB_FTDI_HOST_H +#ifndef TUSB_FTDI_SIO_H +#define TUSB_FTDI_SIO_H // VID/PID for matching FTDI devices #define TU_FTDI_VID 0x0403 @@ -246,13 +246,4 @@ #define FTDI_RS_TEMT (1<<6) #define FTDI_RS_FIFO (1<<7) -//--------------------------------------------------------------------+ -// Internal Class Driver API -//--------------------------------------------------------------------+ -void ftdih_init (void); -bool ftdih_open (uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); -bool ftdih_set_config (uint8_t daddr, uint8_t itf_num); -bool ftdih_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); -void ftdih_close (uint8_t daddr); - -#endif //TUSB_FTDI_HOST_H +#endif //TUSB_FTDI_SIO_H From 85d9925d24feb41bd355a9248207acaf853b70ff Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 27 Apr 2023 15:20:04 +0700 Subject: [PATCH 05/13] refactor ftdi driver --- src/class/cdc/cdc_host.c | 463 +++++++++++++++++++++++++-------------- src/tusb_option.h | 7 +- 2 files changed, 307 insertions(+), 163 deletions(-) diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index d33f31f1a..fc7a7a038 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -37,6 +37,9 @@ #include "serial/ftdi_sio.h" #endif +#if CFG_TUH_CDC_CP210X + #include "serial/cp210x.h" +#endif // Debug level, TUSB_CFG_DEBUG must be at least this level for debug message #define CDCH_DEBUG 2 @@ -48,8 +51,9 @@ //--------------------------------------------------------------------+ enum { - SERIAL_PROTOCOL_ACM = 0, - SERIAL_PROTOCOL_FTDI = 1, + SERIAL_PROTOCOL_ACM = 0, + SERIAL_PROTOCOL_FTDI, + SERIAL_PROTOCOL_CP210X, }; typedef struct { @@ -111,11 +115,20 @@ static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr) } -static cdch_interface_t* find_new_itf(void) +static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const *itf_desc) { for(uint8_t i=0; idaddr = daddr; + p_cdc->bInterfaceNumber = itf_desc->bInterfaceNumber; + p_cdc->bInterfaceSubClass = itf_desc->bInterfaceSubClass; + p_cdc->bInterfaceProtocol = itf_desc->bInterfaceProtocol; + p_cdc->line_state = 0; + return p_cdc; + } } return NULL; @@ -126,6 +139,221 @@ static inline bool support_line_request(cdch_interface_t const* p_cdc) { (p_cdc->serial_protocol == SERIAL_PROTOCOL_FTDI); } +static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t const *desc_ep); +static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num); +static void cdch_internal_control_complete(tuh_xfer_t* xfer); + +//--------------------------------------------------------------------+ +// FTDI +//--------------------------------------------------------------------+ +#if CFG_TUH_CDC_FTDI + +static uint16_t const ftdi_pids[] = { TU_FTDI_PID_LIST }; +enum { + FTDI_PID_COUNT = sizeof(ftdi_pids) / sizeof(ftdi_pids[0]) +}; + +enum { + CONFIG_FTDI_RESET, + CONFIG_FTDI_MODEM_CTRL, + CONFIG_FTDI_SET_BAUDRATE, + CONFIG_FTDI_SET_DATA, + CONFIG_FTDI_COMPLETE +}; + +static bool ftdih_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { + // FTDI Interface includes 1 vendor interface + 2 bulk endpoints + TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff && itf_desc->bNumEndpoints == 2); + TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*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_protocol = SERIAL_PROTOCOL_FTDI; + + // endpoint pair + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); + + // data endpoints expected to be in pairs + return open_ep_stream_pair(p_cdc, desc_ep); +} + +static bool ftdih_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + tusb_control_request_t const request = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_DEVICE, + .type = TUSB_REQ_TYPE_VENDOR, + .direction = TUSB_DIR_OUT + }, + .bRequest = FTDI_SIO_RESET, + .wValue = tu_htole16(FTDI_SIO_RESET_SIO), + .wIndex = 0, + .wLength = 0 + }; + + p_cdc->user_control_cb = complete_cb; + + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb, + .user_data = user_data + }; + + TU_ASSERT(tuh_control_xfer(&xfer)); + return true; +} + +static bool ftdi_sio_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + tusb_control_request_t const request = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_VENDOR, + .direction = TUSB_DIR_OUT + }, + .bRequest = FTDI_SIO_MODEM_CTRL, + .wValue = tu_htole16(0x0300 | line_state), // 0x0300 is DTR and RTS enable + .wIndex = 0, // port + .wLength = 0 + }; + + p_cdc->user_control_cb = complete_cb; + + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = cdch_internal_control_complete, + .user_data = user_data + }; + + TU_ASSERT(tuh_control_xfer(&xfer)); + return true; +} + +static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + // TODO baudrate to baud divisor + (void) baudrate; + + uint16_t divisor = 0x4138; // FIXME hardcoded to 9600 baud + + tusb_control_request_t const request = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_VENDOR, + .direction = TUSB_DIR_OUT + }, + .bRequest = FTDI_SIO_SET_BAUD_RATE, + .wValue = tu_htole16(divisor), + .wIndex = 0, // port + .wLength = 0 + }; + + p_cdc->user_control_cb = complete_cb; + + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = cdch_internal_control_complete, + .user_data = user_data + }; + + TU_ASSERT(tuh_control_xfer(&xfer)); + return true; +} + +static void process_ftdi_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); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc, ); + + switch(state) { + // Note may need to read FTDI eeprom + case CONFIG_FTDI_RESET: + TU_ASSERT(ftdih_sio_reset(p_cdc, process_ftdi_config, CONFIG_FTDI_MODEM_CTRL),); + break; + + case CONFIG_FTDI_MODEM_CTRL: + #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + TU_ASSERT(ftdi_sio_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_ftdi_config, CONFIG_FTDI_SET_BAUDRATE),); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + + case CONFIG_FTDI_SET_BAUDRATE: { + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT(ftdi_sio_set_baudrate(p_cdc, line_coding.bit_rate, process_ftdi_config, CONFIG_FTDI_SET_DATA),); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + } + + case CONFIG_FTDI_SET_DATA: { + #if 0 // TODO set data format + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT(ftdi_sio_set_data(p_cdc, process_ftdi_config, CONFIG_FTDI_COMPLETE),); + break; + #endif + #endif + + TU_ATTR_FALLTHROUGH; + } + + case CONFIG_FTDI_COMPLETE: + set_config_complete(p_cdc, idx, itf_num); + break; + + default: + break; + } +} + +#endif + +//--------------------------------------------------------------------+ +// CP210x +//--------------------------------------------------------------------+ + +#if CFG_TUH_CDC_CP210X + +static uint16_t const cp210x_pids[] = { TU_CP210X_PID_LIST }; +enum { + CP210X_PID_COUNT = sizeof(cp210x_pids) / sizeof(cp210x_pids[0]) +}; + +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); + + cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); + TU_VERIFY(p_cdc); + + p_cdc->serial_protocol = SERIAL_PROTOCOL_CP210X; + + // endpoint pair + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); + + // data endpoints expected to be in pairs + return open_ep_stream_pair(p_cdc, desc_ep); +} + +#endif + //--------------------------------------------------------------------+ // APPLICATION API //--------------------------------------------------------------------+ @@ -324,10 +552,8 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c TU_LOG_CDCH("CDC Set Control Line State\r\n"); - tusb_control_request_t request; - if(p_cdc->serial_protocol == SERIAL_PROTOCOL_ACM ) { - tusb_control_request_t const acm_request = { + tusb_control_request_t const request = { .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, @@ -339,43 +565,27 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c .wLength = 0 }; - request = acm_request; + p_cdc->user_control_cb = complete_cb; + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = cdch_internal_control_complete, + .user_data = user_data + }; + + TU_ASSERT(tuh_control_xfer(&xfer)); + return true; } #if CFG_TUH_CDC_FTDI else if (p_cdc->serial_protocol == SERIAL_PROTOCOL_FTDI) { - // FTDI use vendor specific request to set control line state - tusb_control_request_t const ftdi_request = { - .bmRequestType_bit = { - .recipient = TUSB_REQ_RCPT_INTERFACE, - .type = TUSB_REQ_TYPE_VENDOR, - .direction = TUSB_DIR_OUT - }, - .bRequest = FTDI_SIO_MODEM_CTRL, - .wValue = tu_htole16(0x0300 | line_state), // 0x0300 is DTR and RTS enable - .wIndex = 0, // port - .wLength = 0 - }; - - request = ftdi_request; + return ftdi_sio_modem_ctrl(p_cdc, line_state, complete_cb, user_data); } #endif else { return false; } - - p_cdc->user_control_cb = complete_cb; - tuh_xfer_t xfer = { - .daddr = p_cdc->daddr, - .ep_addr = 0, - .setup = &request, - .buffer = NULL, - .complete_cb = cdch_internal_control_complete, - .user_data = user_data - }; - - TU_ASSERT(tuh_control_xfer(&xfer)); - - return true; } 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) @@ -385,11 +595,8 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, TU_LOG_CDCH("CDC Set Line Conding\r\n"); - tusb_control_request_t request; - uint8_t* enum_buf = NULL; - if (p_cdc->serial_protocol == SERIAL_PROTOCOL_ACM) { - tusb_control_request_t const acm_request = { + tusb_control_request_t const request = { .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, @@ -401,47 +608,32 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, .wLength = tu_htole16(sizeof(cdc_line_coding_t)) }; - request = acm_request; - // use usbh enum buf to hold line coding since user line_coding variable does not live long enough - enum_buf = usbh_get_enum_buf(); + uint8_t* enum_buf = usbh_get_enum_buf(); memcpy(enum_buf, line_coding, sizeof(cdc_line_coding_t)); + + p_cdc->user_control_cb = complete_cb; + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = enum_buf, + .complete_cb = cdch_internal_control_complete, + .user_data = user_data + }; + + TU_ASSERT(tuh_control_xfer(&xfer)); + return true; } #if CFG_TUH_CDC_FTDI else if (p_cdc->serial_protocol == SERIAL_PROTOCOL_FTDI) { // FTDI need to set baud rate and data bits, parity, stop bits separately - tusb_control_request_t const ftdi_request = { - .bmRequestType_bit = { - .recipient = TUSB_REQ_RCPT_INTERFACE, - .type = TUSB_REQ_TYPE_VENDOR, - .direction = TUSB_DIR_OUT - }, - .bRequest = FTDI_SIO_SET_BAUD_RATE, - .wValue = 0x4138, // FIXME hardcoded to 9600 baud - .wIndex = 0, // port - .wLength = tu_htole16(sizeof(cdc_line_coding_t)) - }; - - request = ftdi_request; + return ftdi_sio_set_baudrate(p_cdc, line_coding->bit_rate, complete_cb, user_data); } #endif else { return false; } - - p_cdc->user_control_cb = complete_cb; - tuh_xfer_t xfer = - { - .daddr = p_cdc->daddr, - .ep_addr = 0, - .setup = &request, - .buffer = enum_buf, - .complete_cb = cdch_internal_control_complete, - .user_data = user_data - }; - - TU_ASSERT(tuh_control_xfer(&xfer)); - return true; } //--------------------------------------------------------------------+ @@ -544,9 +736,6 @@ enum CONFIG_SET_CONTROL_LINE_STATE, CONFIG_SET_LINE_CODING, CONFIG_COMPLETE, - - // FTDI - CONFIG_FTDI_RESET }; static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t const *desc_ep) @@ -572,76 +761,14 @@ static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t c return true; } -#if CFG_TUH_CDC_FTDI -bool ftdih_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { - // FTDI configuration includes 1 vendor interface + 2 bulk endpoints - TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff && itf_desc->bNumEndpoints == 2); - TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len); - - cdch_interface_t * p_cdc = find_new_itf(); - TU_VERIFY(p_cdc); - - p_cdc->daddr = daddr; - p_cdc->bInterfaceNumber = itf_desc->bInterfaceNumber; - p_cdc->bInterfaceSubClass = itf_desc->bInterfaceSubClass; - p_cdc->bInterfaceProtocol = itf_desc->bInterfaceProtocol; - p_cdc->line_state = 0; - p_cdc->serial_protocol = SERIAL_PROTOCOL_FTDI; - - // endpoint pair - tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); - - // data endpoints expected to be in pairs - TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep)); - - return true; -} - -static bool ftdih_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) -{ - tusb_control_request_t const request = { - .bmRequestType_bit = { - .recipient = TUSB_REQ_RCPT_DEVICE, - .type = TUSB_REQ_TYPE_VENDOR, - .direction = TUSB_DIR_OUT - }, - .bRequest = FTDI_SIO_RESET, - .wValue = tu_htole16(FTDI_SIO_RESET_SIO), - .wIndex = 0, - .wLength = 0 - }; - - p_cdc->user_control_cb = complete_cb; - - tuh_xfer_t xfer = { - .daddr = p_cdc->daddr, - .ep_addr = 0, - .setup = &request, - .buffer = NULL, - .complete_cb = complete_cb, - .user_data = user_data - }; - - TU_ASSERT(tuh_control_xfer(&xfer)); - return true; -} - -#endif - - static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { uint8_t const * p_desc_end = ((uint8_t const*) itf_desc) + max_len; - cdch_interface_t * p_cdc = find_new_itf(); + cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); TU_VERIFY(p_cdc); - p_cdc->daddr = daddr; - p_cdc->bInterfaceNumber = itf_desc->bInterfaceNumber; - p_cdc->bInterfaceSubClass = itf_desc->bInterfaceSubClass; - p_cdc->bInterfaceProtocol = itf_desc->bInterfaceProtocol; - p_cdc->line_state = 0; - p_cdc->serial_protocol = SERIAL_PROTOCOL_ACM; + p_cdc->serial_protocol = SERIAL_PROTOCOL_ACM; //------------- Control Interface -------------// uint8_t const * p_desc = tu_desc_next(itf_desc); @@ -696,28 +823,47 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_d { return acm_open(daddr, itf_desc, max_len); } + #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X else if ( 0xff == itf_desc->bInterfaceClass ) { uint16_t vid, pid; TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid)); -#if CFG_TUH_CDC_FTDI + #if CFG_TUH_CDC_FTDI if (TU_FTDI_VID == vid) { - uint16_t const ftdi_pids[] = {TU_FTDI_PID_LIST}; - enum { FTDI_PID_COUNT = sizeof(ftdi_pids) / sizeof(ftdi_pids[0]) }; - for (size_t i = 0; i < FTDI_PID_COUNT; i++) { if (ftdi_pids[i] == pid) { return ftdih_open(daddr, itf_desc, max_len); } } } -#endif + #endif + + #if CFG_TUH_CDC_CP210X + if (TU_CP210X_VID == vid) { + for (size_t i = 0; i < CP210X_PID_COUNT; i++) { + if (cp210x_pids[i] == pid) { + return cp210x_open(daddr, itf_desc, max_len); + } + } + } + #endif } + #endif return false; } +static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num) { + if (tuh_cdc_mount_cb) tuh_cdc_mount_cb(idx); + + // Prepare for incoming data + tu_edpt_stream_read_xfer(&p_cdc->stream.rx); + + // notify usbh that driver enumeration is complete + usbh_driver_set_config_complete(p_cdc->daddr, itf_num); +} + static void process_cdc_config(tuh_xfer_t* xfer) { uintptr_t const state = xfer->user_data; @@ -728,16 +874,9 @@ static void process_cdc_config(tuh_xfer_t* xfer) switch(state) { - #if CFG_TUH_CDC_FTDI - // Note may need to read FTDI eeprom - case CONFIG_FTDI_RESET: - TU_ASSERT(ftdih_sio_reset(p_cdc, process_cdc_config, CONFIG_SET_CONTROL_LINE_STATE), ); - break; - #endif - case CONFIG_SET_CONTROL_LINE_STATE: #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - if (support_line_request(p_cdc)) + if (p_cdc->acm_capability.support_line_request) { TU_ASSERT( tuh_cdc_set_control_line_state(idx, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_cdc_config, CONFIG_SET_LINE_CODING), ); break; @@ -747,7 +886,7 @@ static void process_cdc_config(tuh_xfer_t* xfer) case CONFIG_SET_LINE_CODING: #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM - if (support_line_request(p_cdc)) + if (p_cdc->acm_capability.support_line_request) { cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; TU_ASSERT( tuh_cdc_set_line_coding(idx, &line_coding, process_cdc_config, CONFIG_COMPLETE), ); @@ -757,14 +896,8 @@ static void process_cdc_config(tuh_xfer_t* xfer) TU_ATTR_FALLTHROUGH; case CONFIG_COMPLETE: - if (tuh_cdc_mount_cb) tuh_cdc_mount_cb(idx); - - // Prepare for incoming data - tu_edpt_stream_read_xfer(&p_cdc->stream.rx); - - // notify usbh that driver enumeration is complete // itf_num+1 to account for data interface as well - usbh_driver_set_config_complete(xfer->daddr, itf_num+1); + set_config_complete(p_cdc, idx, itf_num+1); break; default: break; @@ -789,19 +922,25 @@ bool cdch_set_config(uint8_t daddr, uint8_t itf_num) switch (p_cdc->serial_protocol) { case SERIAL_PROTOCOL_ACM: xfer.user_data = CONFIG_SET_CONTROL_LINE_STATE; + process_cdc_config(&xfer); break; - #if CFG_TUH_CDC_FTDI + #if CFG_TUH_CDC_FTDI case SERIAL_PROTOCOL_FTDI: xfer.user_data = CONFIG_FTDI_RESET; + process_ftdi_config(&xfer); break; - #endif + #endif + + #if CFG_TUH_CDC_CP210X + case SERIAL_PROTOCOL_CP210X: + //xfer.user_data = CONFIG_SET_CONTROL_LINE_STATE; + break; + #endif default: return false; } - process_cdc_config(&xfer); - return true; } diff --git a/src/tusb_option.h b/src/tusb_option.h index a372e9950..4f3f3a985 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -440,10 +440,15 @@ #endif #ifndef CFG_TUH_CDC_FTDI - // FTDI is not part of CDC class, CDC is used for Serial-over-USB here + // FTDI is not part of CDC class, only to re-use CDC driver API #define CFG_TUH_CDC_FTDI 0 #endif +#ifndef CFG_TUH_CDC_CP210X + // CP210X is not part of CDC class, only to re-use CDC driver API + #define CFG_TUH_CDC_CP210X 0 +#endif + #ifndef CFG_TUH_HID #define CFG_TUH_HID 0 #endif From 9698a088db7616eb628b47457f717574221c9153 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 27 Apr 2023 15:29:44 +0700 Subject: [PATCH 06/13] refactor acm function --- src/class/cdc/cdc_host.c | 138 +++++++++++++++++++++------------------ 1 file changed, 73 insertions(+), 65 deletions(-) diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index fc7a7a038..00a86888c 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -545,38 +545,42 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer) xfer->complete_cb(xfer); } +static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + tusb_control_request_t const request = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT + }, + .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE, + .wValue = tu_htole16(line_state), + .wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber), + .wLength = 0 + }; + + p_cdc->user_control_cb = complete_cb; + + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = cdch_internal_control_complete, + .user_data = user_data + }; + + TU_ASSERT(tuh_control_xfer(&xfer)); + return true; +} + 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 && support_line_request(p_cdc)); - TU_LOG_CDCH("CDC Set Control Line State\r\n"); if(p_cdc->serial_protocol == SERIAL_PROTOCOL_ACM ) { - tusb_control_request_t const request = { - .bmRequestType_bit = { - .recipient = TUSB_REQ_RCPT_INTERFACE, - .type = TUSB_REQ_TYPE_CLASS, - .direction = TUSB_DIR_OUT - }, - .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE, - .wValue = tu_htole16(line_state), - .wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber), - .wLength = 0 - }; - - p_cdc->user_control_cb = complete_cb; - tuh_xfer_t xfer = { - .daddr = p_cdc->daddr, - .ep_addr = 0, - .setup = &request, - .buffer = NULL, - .complete_cb = cdch_internal_control_complete, - .user_data = user_data - }; - - TU_ASSERT(tuh_control_xfer(&xfer)); - return true; + return acm_set_control_line_state(p_cdc, line_state, complete_cb, user_data); } #if CFG_TUH_CDC_FTDI else if (p_cdc->serial_protocol == SERIAL_PROTOCOL_FTDI) { @@ -588,42 +592,45 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c } } +bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + tusb_control_request_t const request = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_OUT + }, + .bRequest = CDC_REQUEST_SET_LINE_CODING, + .wValue = 0, + .wIndex = tu_htole16(p_cdc->bInterfaceNumber), + .wLength = tu_htole16(sizeof(cdc_line_coding_t)) + }; + + // use usbh enum buf to hold line coding since user line_coding variable does not live long enough + uint8_t* enum_buf = usbh_get_enum_buf(); + memcpy(enum_buf, line_coding, sizeof(cdc_line_coding_t)); + + p_cdc->user_control_cb = complete_cb; + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = enum_buf, + .complete_cb = cdch_internal_control_complete, + .user_data = user_data + }; + + TU_ASSERT(tuh_control_xfer(&xfer)); + return true; +} + 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 && support_line_request(p_cdc)); - TU_LOG_CDCH("CDC Set Line Conding\r\n"); if (p_cdc->serial_protocol == SERIAL_PROTOCOL_ACM) { - tusb_control_request_t const request = { - .bmRequestType_bit = { - .recipient = TUSB_REQ_RCPT_INTERFACE, - .type = TUSB_REQ_TYPE_CLASS, - .direction = TUSB_DIR_OUT - }, - .bRequest = CDC_REQUEST_SET_LINE_CODING, - .wValue = 0, - .wIndex = tu_htole16(p_cdc->bInterfaceNumber), - .wLength = tu_htole16(sizeof(cdc_line_coding_t)) - }; - - // use usbh enum buf to hold line coding since user line_coding variable does not live long enough - uint8_t* enum_buf = usbh_get_enum_buf(); - memcpy(enum_buf, line_coding, sizeof(cdc_line_coding_t)); - - p_cdc->user_control_cb = complete_cb; - tuh_xfer_t xfer = { - .daddr = p_cdc->daddr, - .ep_addr = 0, - .setup = &request, - .buffer = enum_buf, - .complete_cb = cdch_internal_control_complete, - .user_data = user_data - }; - - TU_ASSERT(tuh_control_xfer(&xfer)); - return true; + return acm_set_line_coding(p_cdc, line_coding, complete_cb, user_data); } #if CFG_TUH_CDC_FTDI else if (p_cdc->serial_protocol == SERIAL_PROTOCOL_FTDI) { @@ -733,9 +740,9 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t enum { // ACM - CONFIG_SET_CONTROL_LINE_STATE, - CONFIG_SET_LINE_CODING, - CONFIG_COMPLETE, + CONFIG_ACM_SET_CONTROL_LINE_STATE, + CONFIG_ACM_SET_LINE_CODING, + CONFIG_ACM_COMPLETE, }; static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t const *desc_ep) @@ -864,7 +871,7 @@ static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t i usbh_driver_set_config_complete(p_cdc->daddr, itf_num); } -static void process_cdc_config(tuh_xfer_t* xfer) +static void process_acm_config(tuh_xfer_t* xfer) { uintptr_t const state = xfer->user_data; uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); @@ -874,28 +881,29 @@ static void process_cdc_config(tuh_xfer_t* xfer) switch(state) { - case CONFIG_SET_CONTROL_LINE_STATE: + case CONFIG_ACM_SET_CONTROL_LINE_STATE: #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM if (p_cdc->acm_capability.support_line_request) { - TU_ASSERT( tuh_cdc_set_control_line_state(idx, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_cdc_config, CONFIG_SET_LINE_CODING), ); + TU_ASSERT(tuh_cdc_set_control_line_state(idx, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_acm_config, + CONFIG_ACM_SET_LINE_CODING), ); break; } #endif TU_ATTR_FALLTHROUGH; - case CONFIG_SET_LINE_CODING: + case CONFIG_ACM_SET_LINE_CODING: #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM if (p_cdc->acm_capability.support_line_request) { cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; - TU_ASSERT( tuh_cdc_set_line_coding(idx, &line_coding, process_cdc_config, CONFIG_COMPLETE), ); + TU_ASSERT(tuh_cdc_set_line_coding(idx, &line_coding, process_acm_config, CONFIG_ACM_COMPLETE), ); break; } #endif TU_ATTR_FALLTHROUGH; - case CONFIG_COMPLETE: + case CONFIG_ACM_COMPLETE: // itf_num+1 to account for data interface as well set_config_complete(p_cdc, idx, itf_num+1); break; @@ -921,8 +929,8 @@ bool cdch_set_config(uint8_t daddr, uint8_t itf_num) switch (p_cdc->serial_protocol) { case SERIAL_PROTOCOL_ACM: - xfer.user_data = CONFIG_SET_CONTROL_LINE_STATE; - process_cdc_config(&xfer); + xfer.user_data = CONFIG_ACM_SET_CONTROL_LINE_STATE; + process_acm_config(&xfer); break; #if CFG_TUH_CDC_FTDI From a32ef1cde124251d86696ed980a87eb25739c6a8 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 27 Apr 2023 16:32:42 +0700 Subject: [PATCH 07/13] more host serial refactor --- src/class/cdc/cdc_host.c | 112 ++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 65 deletions(-) diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 00a86888c..c25ed48b0 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -154,14 +154,14 @@ enum { }; enum { - CONFIG_FTDI_RESET, + CONFIG_FTDI_RESET = 0, CONFIG_FTDI_MODEM_CTRL, CONFIG_FTDI_SET_BAUDRATE, CONFIG_FTDI_SET_DATA, CONFIG_FTDI_COMPLETE }; -static bool ftdih_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { +static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) { // FTDI Interface includes 1 vendor interface + 2 bulk endpoints TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff && itf_desc->bNumEndpoints == 2); TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len); @@ -169,6 +169,8 @@ static bool ftdih_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uin cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); TU_VERIFY(p_cdc); + TU_LOG_CDCH("FTDI opened\r\n"); + p_cdc->serial_protocol = SERIAL_PROTOCOL_FTDI; // endpoint pair @@ -178,22 +180,19 @@ static bool ftdih_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uin return open_ep_stream_pair(p_cdc, desc_ep); } -static bool ftdih_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) -{ +static bool ftdi_sio_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { tusb_control_request_t const request = { .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_DEVICE, .type = TUSB_REQ_TYPE_VENDOR, .direction = TUSB_DIR_OUT }, - .bRequest = FTDI_SIO_RESET, - .wValue = tu_htole16(FTDI_SIO_RESET_SIO), + .bRequest = command, + .wValue = tu_htole16(value), .wIndex = 0, .wLength = 0 }; - p_cdc->user_control_cb = complete_cb; - tuh_xfer_t xfer = { .daddr = p_cdc->daddr, .ep_addr = 0, @@ -203,36 +202,19 @@ static bool ftdih_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, .user_data = user_data }; - TU_ASSERT(tuh_control_xfer(&xfer)); + return tuh_control_xfer(&xfer); +} + +static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_RESET, FTDI_SIO_RESET_SIO, complete_cb, user_data)); return true; } static bool ftdi_sio_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - tusb_control_request_t const request = { - .bmRequestType_bit = { - .recipient = TUSB_REQ_RCPT_INTERFACE, - .type = TUSB_REQ_TYPE_VENDOR, - .direction = TUSB_DIR_OUT - }, - .bRequest = FTDI_SIO_MODEM_CTRL, - .wValue = tu_htole16(0x0300 | line_state), // 0x0300 is DTR and RTS enable - .wIndex = 0, // port - .wLength = 0 - }; - p_cdc->user_control_cb = complete_cb; - - tuh_xfer_t xfer = { - .daddr = p_cdc->daddr, - .ep_addr = 0, - .setup = &request, - .buffer = NULL, - .complete_cb = cdch_internal_control_complete, - .user_data = user_data - }; - - TU_ASSERT(tuh_control_xfer(&xfer)); + TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state, cdch_internal_control_complete, user_data)); return true; } @@ -240,33 +222,10 @@ static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tu { // TODO baudrate to baud divisor (void) baudrate; - uint16_t divisor = 0x4138; // FIXME hardcoded to 9600 baud - tusb_control_request_t const request = { - .bmRequestType_bit = { - .recipient = TUSB_REQ_RCPT_INTERFACE, - .type = TUSB_REQ_TYPE_VENDOR, - .direction = TUSB_DIR_OUT - }, - .bRequest = FTDI_SIO_SET_BAUD_RATE, - .wValue = tu_htole16(divisor), - .wIndex = 0, // port - .wLength = 0 - }; - p_cdc->user_control_cb = complete_cb; - - tuh_xfer_t xfer = { - .daddr = p_cdc->daddr, - .ep_addr = 0, - .setup = &request, - .buffer = NULL, - .complete_cb = cdch_internal_control_complete, - .user_data = user_data - }; - - TU_ASSERT(tuh_control_xfer(&xfer)); + TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor, cdch_internal_control_complete, user_data)); return true; } @@ -280,7 +239,7 @@ static void process_ftdi_config(tuh_xfer_t* xfer) { switch(state) { // Note may need to read FTDI eeprom case CONFIG_FTDI_RESET: - TU_ASSERT(ftdih_sio_reset(p_cdc, process_ftdi_config, CONFIG_FTDI_MODEM_CTRL),); + TU_ASSERT(ftdi_sio_reset(p_cdc, process_ftdi_config, CONFIG_FTDI_MODEM_CTRL),); break; case CONFIG_FTDI_MODEM_CTRL: @@ -335,6 +294,13 @@ enum { CP210X_PID_COUNT = sizeof(cp210x_pids) / sizeof(cp210x_pids[0]) }; +enum { + CONFIG_CP210X_IFC_ENABLE = 0, + CONFIG_CP210X_SET_BAUDRATE, + CONFIG_CP210X_SET_LINE_CTL, + CONFIG_CP210X_SET_MHS +}; + 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); @@ -343,6 +309,7 @@ static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, ui cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); TU_VERIFY(p_cdc); + TU_LOG_CDCH("CP210x opened\r\n"); p_cdc->serial_protocol = SERIAL_PROTOCOL_CP210X; // endpoint pair @@ -352,6 +319,22 @@ 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 process_cp210x_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); + cdch_interface_t *p_cdc = get_itf(idx); + TU_ASSERT(p_cdc,); + + switch (state) { + case CONFIG_CP210X_IFC_ENABLE: + + break; + + default: break; + } +} + #endif //--------------------------------------------------------------------+ @@ -740,7 +723,7 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t enum { // ACM - CONFIG_ACM_SET_CONTROL_LINE_STATE, + CONFIG_ACM_SET_CONTROL_LINE_STATE = 0, CONFIG_ACM_SET_LINE_CODING, CONFIG_ACM_COMPLETE, }; @@ -840,7 +823,7 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_d if (TU_FTDI_VID == vid) { for (size_t i = 0; i < FTDI_PID_COUNT; i++) { if (ftdi_pids[i] == pid) { - return ftdih_open(daddr, itf_desc, max_len); + return ftdi_open(daddr, itf_desc, max_len); } } } @@ -918,9 +901,10 @@ bool cdch_set_config(uint8_t daddr, uint8_t itf_num) request.wIndex = tu_htole16((uint16_t) itf_num); tuh_xfer_t xfer; - xfer.daddr = daddr; - xfer.result = XFER_RESULT_SUCCESS; - xfer.setup = &request; + xfer.daddr = daddr; + xfer.result = XFER_RESULT_SUCCESS; + xfer.setup = &request; + xfer.user_data = 0; // fake transfer to kick-off process uint8_t const idx = tuh_cdc_itf_get_index(daddr, itf_num); @@ -929,20 +913,18 @@ bool cdch_set_config(uint8_t daddr, uint8_t itf_num) switch (p_cdc->serial_protocol) { case SERIAL_PROTOCOL_ACM: - xfer.user_data = CONFIG_ACM_SET_CONTROL_LINE_STATE; process_acm_config(&xfer); break; #if CFG_TUH_CDC_FTDI case SERIAL_PROTOCOL_FTDI: - xfer.user_data = CONFIG_FTDI_RESET; process_ftdi_config(&xfer); break; #endif #if CFG_TUH_CDC_CP210X case SERIAL_PROTOCOL_CP210X: - //xfer.user_data = CONFIG_SET_CONTROL_LINE_STATE; + process_cp210x_config(&xfer); break; #endif From 86f6fd17edec73c6c7f19ece19fe1dba8575985a Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 27 Apr 2023 17:09:21 +0700 Subject: [PATCH 08/13] cp210x seems to work well --- examples/host/cdc_msc_hid/src/tusb_config.h | 3 +- src/class/cdc/cdc_host.c | 93 +++++++++++++++++++-- 2 files changed, 89 insertions(+), 7 deletions(-) diff --git a/examples/host/cdc_msc_hid/src/tusb_config.h b/examples/host/cdc_msc_hid/src/tusb_config.h index 2baf2b1ac..cc3775a76 100644 --- a/examples/host/cdc_msc_hid/src/tusb_config.h +++ b/examples/host/cdc_msc_hid/src/tusb_config.h @@ -97,7 +97,8 @@ #define CFG_TUH_HUB 1 // number of supported hubs #define CFG_TUH_CDC 1 // CDC ACM -#define CFG_TUH_CDC_FTDI 1 // FTDI UART +#define CFG_TUH_CDC_FTDI 1 // FTDI Serial +#define CFG_TUH_CDC_CP210X 1 // CP210x Serial #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 c25ed48b0..a19f66181 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -180,6 +180,7 @@ static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint return open_ep_stream_pair(p_cdc, desc_ep); } +// set request without data static bool ftdi_sio_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { tusb_control_request_t const request = { .bmRequestType_bit = { @@ -207,11 +208,10 @@ static bool ftdi_sio_set_request(cdch_interface_t* p_cdc, uint8_t command, uint1 static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_RESET, FTDI_SIO_RESET_SIO, complete_cb, user_data)); - return true; + return ftdi_sio_set_request(p_cdc, FTDI_SIO_RESET, FTDI_SIO_RESET_SIO, complete_cb, user_data); } -static bool ftdi_sio_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->user_control_cb = complete_cb; TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state, cdch_internal_control_complete, user_data)); @@ -244,7 +244,8 @@ static void process_ftdi_config(tuh_xfer_t* xfer) { case CONFIG_FTDI_MODEM_CTRL: #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - TU_ASSERT(ftdi_sio_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_ftdi_config, CONFIG_FTDI_SET_BAUDRATE),); + TU_ASSERT( + ftdi_sio_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_ftdi_config, CONFIG_FTDI_SET_BAUDRATE),); break; #else TU_ATTR_FALLTHROUGH; @@ -298,7 +299,8 @@ enum { CONFIG_CP210X_IFC_ENABLE = 0, CONFIG_CP210X_SET_BAUDRATE, CONFIG_CP210X_SET_LINE_CTL, - CONFIG_CP210X_SET_MHS + 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) { @@ -319,6 +321,54 @@ 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 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, + .type = TUSB_REQ_TYPE_VENDOR, + .direction = TUSB_DIR_OUT + }, + .bRequest = command, + .wValue = tu_htole16(value), + .wIndex = p_cdc->bInterfaceNumber, + .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(); + tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length); + } + + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = enum_buf, + .complete_cb = complete_cb, + .user_data = user_data + }; + + 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) { + return cp210x_set_request(p_cdc, CP210X_IFC_ENABLE, enabled, NULL, 0, complete_cb, user_data); +} + +static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + baudrate = tu_htole32(baudrate); + return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baudrate, 4, complete_cb, user_data); +} + +static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, 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 | line_state, NULL, 0, cdch_internal_control_complete, user_data); +} + static void process_cp210x_config(tuh_xfer_t* xfer) { uintptr_t const state = xfer->user_data; uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); @@ -328,7 +378,38 @@ static void process_cp210x_config(tuh_xfer_t* xfer) { switch (state) { case CONFIG_CP210X_IFC_ENABLE: + TU_ASSERT(cp210x_ifc_enable(p_cdc, 1, process_cp210x_config, CONFIG_CP210X_SET_BAUDRATE),); + break; + case CONFIG_CP210X_SET_BAUDRATE: { + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT(cp210x_set_baudrate(p_cdc, line_coding.bit_rate, process_cp210x_config, 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; + #else + TU_ATTR_FALLTHROUGH; + #endif + } + + case CONFIG_CP210X_SET_DTR_RTS: + #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + TU_ASSERT(cp210x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_cp210x_config, CONFIG_CP210X_COMPLETE),); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + + case CONFIG_CP210X_COMPLETE: + set_config_complete(p_cdc, idx, itf_num); break; default: break; @@ -567,7 +648,7 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c } #if CFG_TUH_CDC_FTDI else if (p_cdc->serial_protocol == SERIAL_PROTOCOL_FTDI) { - return ftdi_sio_modem_ctrl(p_cdc, line_state, complete_cb, user_data); + return ftdi_sio_set_modem_ctrl(p_cdc, line_state, complete_cb, user_data); } #endif else { From 438387be8c308c3bfed1ca952f5908a51a97b5c2 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 27 Apr 2023 17:32:56 +0700 Subject: [PATCH 09/13] more refactor --- examples/host/cdc_msc_hid/src/tusb_config.h | 4 +- src/class/cdc/cdc_host.c | 595 +++++++++++--------- 2 files changed, 317 insertions(+), 282 deletions(-) diff --git a/examples/host/cdc_msc_hid/src/tusb_config.h b/examples/host/cdc_msc_hid/src/tusb_config.h index cc3775a76..abb75f068 100644 --- a/examples/host/cdc_msc_hid/src/tusb_config.h +++ b/examples/host/cdc_msc_hid/src/tusb_config.h @@ -97,8 +97,8 @@ #define CFG_TUH_HUB 1 // number of supported hubs #define CFG_TUH_CDC 1 // CDC ACM -#define CFG_TUH_CDC_FTDI 1 // FTDI Serial -#define CFG_TUH_CDC_CP210X 1 // CP210x Serial +#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_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 a19f66181..f47ac4f10 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -143,281 +143,35 @@ static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t c static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num); static void cdch_internal_control_complete(tuh_xfer_t* xfer); -//--------------------------------------------------------------------+ -// FTDI -//--------------------------------------------------------------------+ +//------------- FTDI prototypes -------------// #if CFG_TUH_CDC_FTDI - static uint16_t const ftdi_pids[] = { TU_FTDI_PID_LIST }; enum { FTDI_PID_COUNT = sizeof(ftdi_pids) / sizeof(ftdi_pids[0]) }; -enum { - CONFIG_FTDI_RESET = 0, - CONFIG_FTDI_MODEM_CTRL, - CONFIG_FTDI_SET_BAUDRATE, - CONFIG_FTDI_SET_DATA, - CONFIG_FTDI_COMPLETE -}; - -static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) { - // FTDI Interface includes 1 vendor interface + 2 bulk endpoints - TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff && itf_desc->bNumEndpoints == 2); - TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len); - - cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); - TU_VERIFY(p_cdc); - - TU_LOG_CDCH("FTDI opened\r\n"); - - p_cdc->serial_protocol = SERIAL_PROTOCOL_FTDI; - - // endpoint pair - tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); - - // data endpoints expected to be in pairs - return open_ep_stream_pair(p_cdc, desc_ep); -} - -// set request without data -static bool ftdi_sio_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - tusb_control_request_t const request = { - .bmRequestType_bit = { - .recipient = TUSB_REQ_RCPT_DEVICE, - .type = TUSB_REQ_TYPE_VENDOR, - .direction = TUSB_DIR_OUT - }, - .bRequest = command, - .wValue = tu_htole16(value), - .wIndex = 0, - .wLength = 0 - }; - - tuh_xfer_t xfer = { - .daddr = p_cdc->daddr, - .ep_addr = 0, - .setup = &request, - .buffer = NULL, - .complete_cb = complete_cb, - .user_data = user_data - }; - - return tuh_control_xfer(&xfer); -} - -static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) -{ - return ftdi_sio_set_request(p_cdc, FTDI_SIO_RESET, FTDI_SIO_RESET_SIO, complete_cb, user_data); -} - -static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) -{ - p_cdc->user_control_cb = complete_cb; - TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state, cdch_internal_control_complete, user_data)); - return true; -} - -static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) -{ - // TODO baudrate to baud divisor - (void) baudrate; - uint16_t divisor = 0x4138; // FIXME hardcoded to 9600 baud - - p_cdc->user_control_cb = complete_cb; - TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor, cdch_internal_control_complete, user_data)); - return true; -} - -static void process_ftdi_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); - cdch_interface_t * p_cdc = get_itf(idx); - TU_ASSERT(p_cdc, ); - - switch(state) { - // Note may need to read FTDI eeprom - case CONFIG_FTDI_RESET: - TU_ASSERT(ftdi_sio_reset(p_cdc, process_ftdi_config, CONFIG_FTDI_MODEM_CTRL),); - break; - - case CONFIG_FTDI_MODEM_CTRL: - #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - TU_ASSERT( - ftdi_sio_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_ftdi_config, CONFIG_FTDI_SET_BAUDRATE),); - break; - #else - TU_ATTR_FALLTHROUGH; - #endif - - case CONFIG_FTDI_SET_BAUDRATE: { - #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM - cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; - TU_ASSERT(ftdi_sio_set_baudrate(p_cdc, line_coding.bit_rate, process_ftdi_config, CONFIG_FTDI_SET_DATA),); - break; - #else - TU_ATTR_FALLTHROUGH; - #endif - } - - case CONFIG_FTDI_SET_DATA: { - #if 0 // TODO set data format - #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM - cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; - TU_ASSERT(ftdi_sio_set_data(p_cdc, process_ftdi_config, CONFIG_FTDI_COMPLETE),); - break; - #endif - #endif - - TU_ATTR_FALLTHROUGH; - } - - case CONFIG_FTDI_COMPLETE: - set_config_complete(p_cdc, idx, itf_num); - break; - - default: - break; - } -} +static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len); +static void process_ftdi_config(tuh_xfer_t* xfer); +static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #endif -//--------------------------------------------------------------------+ -// CP210x -//--------------------------------------------------------------------+ - +//------------- CP210X prototypes -------------// #if CFG_TUH_CDC_CP210X - static uint16_t const cp210x_pids[] = { TU_CP210X_PID_LIST }; enum { CP210X_PID_COUNT = sizeof(cp210x_pids) / sizeof(cp210x_pids[0]) }; -enum { - CONFIG_CP210X_IFC_ENABLE = 0, - CONFIG_CP210X_SET_BAUDRATE, - 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) { - // 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); - - cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); - TU_VERIFY(p_cdc); - - TU_LOG_CDCH("CP210x opened\r\n"); - p_cdc->serial_protocol = SERIAL_PROTOCOL_CP210X; - - // endpoint pair - tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); - - // data endpoints expected to be in pairs - return open_ep_stream_pair(p_cdc, desc_ep); -} - -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, - .type = TUSB_REQ_TYPE_VENDOR, - .direction = TUSB_DIR_OUT - }, - .bRequest = command, - .wValue = tu_htole16(value), - .wIndex = p_cdc->bInterfaceNumber, - .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(); - tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length); - } - - tuh_xfer_t xfer = { - .daddr = p_cdc->daddr, - .ep_addr = 0, - .setup = &request, - .buffer = enum_buf, - .complete_cb = complete_cb, - .user_data = user_data - }; - - 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) { - return cp210x_set_request(p_cdc, CP210X_IFC_ENABLE, enabled, NULL, 0, complete_cb, user_data); -} - -static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - baudrate = tu_htole32(baudrate); - return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baudrate, 4, complete_cb, user_data); -} - -static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, 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 | line_state, NULL, 0, cdch_internal_control_complete, user_data); -} - -static void process_cp210x_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); - cdch_interface_t *p_cdc = get_itf(idx); - TU_ASSERT(p_cdc,); - - switch (state) { - case CONFIG_CP210X_IFC_ENABLE: - TU_ASSERT(cp210x_ifc_enable(p_cdc, 1, process_cp210x_config, CONFIG_CP210X_SET_BAUDRATE),); - break; - - case CONFIG_CP210X_SET_BAUDRATE: { - #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM - cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; - TU_ASSERT(cp210x_set_baudrate(p_cdc, line_coding.bit_rate, process_cp210x_config, 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; - #else - TU_ATTR_FALLTHROUGH; - #endif - } - - case CONFIG_CP210X_SET_DTR_RTS: - #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - TU_ASSERT(cp210x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_cp210x_config, CONFIG_CP210X_COMPLETE),); - break; - #else - TU_ATTR_FALLTHROUGH; - #endif - - case CONFIG_CP210X_COMPLETE: - set_config_complete(p_cdc, idx, itf_num); - break; - - default: break; - } -} +static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); +static void process_cp210x_config(tuh_xfer_t* xfer); +static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #endif + //--------------------------------------------------------------------+ // APPLICATION API //--------------------------------------------------------------------+ @@ -610,6 +364,7 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer) } static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + TU_LOG_CDCH("CDC ACM Set Control Line State\r\n"); tusb_control_request_t const request = { .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, @@ -641,18 +396,23 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c { cdch_interface_t* p_cdc = get_itf(idx); TU_VERIFY(p_cdc && support_line_request(p_cdc)); - TU_LOG_CDCH("CDC Set Control Line State\r\n"); - if(p_cdc->serial_protocol == SERIAL_PROTOCOL_ACM ) { - return acm_set_control_line_state(p_cdc, line_state, complete_cb, user_data); - } - #if CFG_TUH_CDC_FTDI - else if (p_cdc->serial_protocol == SERIAL_PROTOCOL_FTDI) { - return ftdi_sio_set_modem_ctrl(p_cdc, line_state, complete_cb, user_data); - } - #endif - else { - return false; + switch(p_cdc->serial_protocol) { + case SERIAL_PROTOCOL_ACM: + return acm_set_control_line_state(p_cdc, line_state, complete_cb, user_data); + + #if CFG_TUH_CDC_FTDI + case SERIAL_PROTOCOL_FTDI: + return ftdi_sio_set_modem_ctrl(p_cdc, line_state, complete_cb, user_data); + #endif + + #if CFG_TUH_CDC_CP210X + case SERIAL_PROTOCOL_CP210X: + return ftdi_sio_set_modem_ctrl(p_cdc, line_state, complete_cb, user_data); + #endif + + default: + return false; } } @@ -693,17 +453,22 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, TU_VERIFY(p_cdc && support_line_request(p_cdc)); TU_LOG_CDCH("CDC Set Line Conding\r\n"); - if (p_cdc->serial_protocol == SERIAL_PROTOCOL_ACM) { - return acm_set_line_coding(p_cdc, line_coding, complete_cb, user_data); - } - #if CFG_TUH_CDC_FTDI - else if (p_cdc->serial_protocol == SERIAL_PROTOCOL_FTDI) { - // FTDI need to set baud rate and data bits, parity, stop bits separately - return ftdi_sio_set_baudrate(p_cdc, line_coding->bit_rate, complete_cb, user_data); - } - #endif - else { - return false; + switch(p_cdc->serial_protocol) { + case SERIAL_PROTOCOL_ACM: + return acm_set_line_coding(p_cdc, line_coding, complete_cb, user_data); + + #if CFG_TUH_CDC_FTDI + case SERIAL_PROTOCOL_FTDI: + // FTDI need to set baud rate and data bits, parity, stop bits separately + return ftdi_sio_set_baudrate(p_cdc, line_coding->bit_rate, complete_cb, user_data); + #endif + + #if CFG_TUH_CDC_CP210X + case SERIAL_PROTOCOL_CP210X: + return cp210x_set_baudrate(p_cdc, line_coding->bit_rate, complete_cb, user_data); + #endif + + default: return false; } } @@ -949,7 +714,7 @@ static void process_acm_config(tuh_xfer_t* xfer) #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM if (p_cdc->acm_capability.support_line_request) { - TU_ASSERT(tuh_cdc_set_control_line_state(idx, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_acm_config, + TU_ASSERT(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_acm_config, CONFIG_ACM_SET_LINE_CODING), ); break; } @@ -1015,4 +780,274 @@ bool cdch_set_config(uint8_t daddr, uint8_t itf_num) return true; } +//--------------------------------------------------------------------+ +// FTDI +//--------------------------------------------------------------------+ +#if CFG_TUH_CDC_FTDI + +enum { + CONFIG_FTDI_RESET = 0, + CONFIG_FTDI_MODEM_CTRL, + CONFIG_FTDI_SET_BAUDRATE, + CONFIG_FTDI_SET_DATA, + CONFIG_FTDI_COMPLETE +}; + +static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) { + // FTDI Interface includes 1 vendor interface + 2 bulk endpoints + TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff && itf_desc->bNumEndpoints == 2); + TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len); + + cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); + TU_VERIFY(p_cdc); + + TU_LOG_CDCH("FTDI opened\r\n"); + + p_cdc->serial_protocol = SERIAL_PROTOCOL_FTDI; + + // endpoint pair + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); + + // data endpoints expected to be in pairs + return open_ep_stream_pair(p_cdc, desc_ep); +} + +// set request without data +static bool ftdi_sio_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + tusb_control_request_t const request = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_DEVICE, + .type = TUSB_REQ_TYPE_VENDOR, + .direction = TUSB_DIR_OUT + }, + .bRequest = command, + .wValue = tu_htole16(value), + .wIndex = 0, + .wLength = 0 + }; + + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = NULL, + .complete_cb = complete_cb, + .user_data = user_data + }; + + return tuh_control_xfer(&xfer); +} + +static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + return ftdi_sio_set_request(p_cdc, FTDI_SIO_RESET, FTDI_SIO_RESET_SIO, complete_cb, user_data); +} + +static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + TU_LOG_CDCH("CDC FTDI Set Control Line State\r\n"); + p_cdc->user_control_cb = complete_cb; + TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state, cdch_internal_control_complete, user_data)); + return true; +} + +static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + // TODO baudrate to baud divisor + (void) baudrate; + uint16_t divisor = 0x4138; // FIXME hardcoded to 9600 baud + + TU_LOG_CDCH("CDC FTDI Set BaudRate = %u, divisor = %u\n", baudrate, divisor); + + p_cdc->user_control_cb = complete_cb; + TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor, cdch_internal_control_complete, user_data)); + return true; +} + +static void process_ftdi_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); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc, ); + + switch(state) { + // Note may need to read FTDI eeprom + case CONFIG_FTDI_RESET: + TU_ASSERT(ftdi_sio_reset(p_cdc, process_ftdi_config, CONFIG_FTDI_MODEM_CTRL),); + break; + + case CONFIG_FTDI_MODEM_CTRL: + #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + TU_ASSERT( + ftdi_sio_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_ftdi_config, CONFIG_FTDI_SET_BAUDRATE),); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + + case CONFIG_FTDI_SET_BAUDRATE: { + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT(ftdi_sio_set_baudrate(p_cdc, line_coding.bit_rate, process_ftdi_config, CONFIG_FTDI_SET_DATA),); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + } + + case CONFIG_FTDI_SET_DATA: { + #if 0 // TODO set data format + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT(ftdi_sio_set_data(p_cdc, process_ftdi_config, CONFIG_FTDI_COMPLETE),); + break; + #endif + #endif + + TU_ATTR_FALLTHROUGH; + } + + case CONFIG_FTDI_COMPLETE: + set_config_complete(p_cdc, idx, itf_num); + break; + + default: + break; + } +} + +#endif + +//--------------------------------------------------------------------+ +// CP210x +//--------------------------------------------------------------------+ + +#if CFG_TUH_CDC_CP210X + +enum { + CONFIG_CP210X_IFC_ENABLE = 0, + CONFIG_CP210X_SET_BAUDRATE, + 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) { + // 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); + + cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); + TU_VERIFY(p_cdc); + + TU_LOG_CDCH("CP210x opened\r\n"); + p_cdc->serial_protocol = SERIAL_PROTOCOL_CP210X; + + // endpoint pair + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); + + // data endpoints expected to be in pairs + return open_ep_stream_pair(p_cdc, desc_ep); +} + +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, + .type = TUSB_REQ_TYPE_VENDOR, + .direction = TUSB_DIR_OUT + }, + .bRequest = command, + .wValue = tu_htole16(value), + .wIndex = p_cdc->bInterfaceNumber, + .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(); + tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length); + } + + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request, + .buffer = enum_buf, + .complete_cb = complete_cb, + .user_data = user_data + }; + + 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) { + return cp210x_set_request(p_cdc, CP210X_IFC_ENABLE, enabled, NULL, 0, complete_cb, user_data); +} + +static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + TU_LOG_CDCH("CDC CP210x Set BaudRate = %u\n", baudrate); + baudrate = tu_htole32(baudrate); + return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baudrate, 4, complete_cb, user_data); +} + +static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + TU_LOG_CDCH("CDC CP210x Set Control Line State\r\n"); + p_cdc->user_control_cb = complete_cb; + return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | line_state, NULL, 0, cdch_internal_control_complete, user_data); +} + +static void process_cp210x_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); + cdch_interface_t *p_cdc = get_itf(idx); + TU_ASSERT(p_cdc,); + + switch (state) { + case CONFIG_CP210X_IFC_ENABLE: + TU_ASSERT(cp210x_ifc_enable(p_cdc, 1, process_cp210x_config, CONFIG_CP210X_SET_BAUDRATE),); + break; + + case CONFIG_CP210X_SET_BAUDRATE: { + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT(cp210x_set_baudrate(p_cdc, line_coding.bit_rate, process_cp210x_config, 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; + #else + TU_ATTR_FALLTHROUGH; + #endif + } + + case CONFIG_CP210X_SET_DTR_RTS: + #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + TU_ASSERT(cp210x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, process_cp210x_config, CONFIG_CP210X_COMPLETE),); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + + case CONFIG_CP210X_COMPLETE: + set_config_complete(p_cdc, idx, itf_num); + break; + + default: break; + } +} + +#endif + #endif From c10f52b2375de8cca153beffb7852877d4cd7e58 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 27 Apr 2023 17:39:02 +0700 Subject: [PATCH 10/13] forgot to add cp210x header --- src/class/cdc/serial/cp210x.h | 64 +++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/class/cdc/serial/cp210x.h diff --git a/src/class/cdc/serial/cp210x.h b/src/class/cdc/serial/cp210x.h new file mode 100644 index 000000000..b01417092 --- /dev/null +++ b/src/class/cdc/serial/cp210x.h @@ -0,0 +1,64 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2023 Ha Thach (thach@tinyusb.org) for Adafruit Industries + * + * 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. + */ + +#ifndef TUSB_CP210X_H +#define TUSB_CP210X_H + +// 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 +#define TU_CP210X_PID_LIST \ + 0xEA60, 0xEA70 + +/* Config request codes */ +#define CP210X_IFC_ENABLE 0x00 +#define CP210X_SET_BAUDDIV 0x01 +#define CP210X_GET_BAUDDIV 0x02 +#define CP210X_SET_LINE_CTL 0x03 // Set parity, data bits, stop bits +#define CP210X_GET_LINE_CTL 0x04 +#define CP210X_SET_BREAK 0x05 +#define CP210X_IMM_CHAR 0x06 +#define CP210X_SET_MHS 0x07 // Set DTR, RTS +#define CP210X_GET_MDMSTS 0x08 // Get modem status (DTR, RTS, CTS, DSR, RI, DCD) +#define CP210X_SET_XON 0x09 +#define CP210X_SET_XOFF 0x0A +#define CP210X_SET_EVENTMASK 0x0B +#define CP210X_GET_EVENTMASK 0x0C +#define CP210X_SET_CHAR 0x0D +#define CP210X_GET_CHARS 0x0E +#define CP210X_GET_PROPS 0x0F +#define CP210X_GET_COMM_STATUS 0x10 +#define CP210X_RESET 0x11 +#define CP210X_PURGE 0x12 +#define CP210X_SET_FLOW 0x13 +#define CP210X_GET_FLOW 0x14 +#define CP210X_EMBED_EVENTS 0x15 +#define CP210X_GET_EVENTSTATE 0x16 +#define CP210X_SET_CHARS 0x19 +#define CP210X_GET_BAUDRATE 0x1D +#define CP210X_SET_BAUDRATE 0x1E +#define CP210X_VENDOR_SPECIFIC 0xFF // GPIO, Recipient must be Device + +#endif //TUSB_CP210X_H From 8214f0f497271116d07e874f2e5f764f1e8ebd47 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 27 Apr 2023 17:40:08 +0700 Subject: [PATCH 11/13] clean up --- src/class/cdc/cdc_host.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index f47ac4f10..ab1a10427 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -33,14 +33,6 @@ #include "cdc_host.h" -#if CFG_TUH_CDC_FTDI - #include "serial/ftdi_sio.h" -#endif - -#if CFG_TUH_CDC_CP210X - #include "serial/cp210x.h" -#endif - // Debug level, TUSB_CFG_DEBUG must be at least this level for debug message #define CDCH_DEBUG 2 @@ -145,6 +137,8 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer); //------------- FTDI prototypes -------------// #if CFG_TUH_CDC_FTDI +#include "serial/ftdi_sio.h" + static uint16_t const ftdi_pids[] = { TU_FTDI_PID_LIST }; enum { FTDI_PID_COUNT = sizeof(ftdi_pids) / sizeof(ftdi_pids[0]) @@ -159,6 +153,8 @@ static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tu //------------- CP210X prototypes -------------// #if CFG_TUH_CDC_CP210X +#include "serial/cp210x.h" + static uint16_t const cp210x_pids[] = { TU_CP210X_PID_LIST }; enum { CP210X_PID_COUNT = sizeof(cp210x_pids) / sizeof(cp210x_pids[0]) From ee58278ed2502a6e9b6d2625f2fe1bf453038716 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 27 Apr 2023 23:08:25 +0700 Subject: [PATCH 12/13] add code to calculate divisor from baudrate for ftdi --- src/class/cdc/cdc_host.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index ab1a10427..132b1ef09 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -847,13 +847,36 @@ static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state return true; } +static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base) +{ + const uint8_t divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 }; + uint32_t divisor; + + /* divisor shifted 3 bits to the left */ + uint32_t divisor3 = base / (2 * baud); + divisor = (divisor3 >> 3); + divisor |= (uint32_t) divfrac[divisor3 & 0x7] << 14; + + /* Deal with special cases for highest baud rates. */ + if (divisor == 1) { /* 1.0 */ + divisor = 0; + } + else if (divisor == 0x4001) { /* 1.5 */ + divisor = 1; + } + + return divisor; +} + +static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud) +{ + return ftdi_232bm_baud_base_to_divisor(baud, 48000000u); +} + static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - // TODO baudrate to baud divisor - (void) baudrate; - uint16_t divisor = 0x4138; // FIXME hardcoded to 9600 baud - - TU_LOG_CDCH("CDC FTDI Set BaudRate = %u, divisor = %u\n", baudrate, divisor); + uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate); + TU_LOG_CDCH("CDC FTDI Set BaudRate = %lu, divisor = 0x%04x\n", baudrate, divisor); p_cdc->user_control_cb = complete_cb; TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor, cdch_internal_control_complete, user_data)); @@ -985,7 +1008,7 @@ static bool cp210x_ifc_enable(cdch_interface_t* p_cdc, uint16_t enabled, tuh_xfe } static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - TU_LOG_CDCH("CDC CP210x Set BaudRate = %u\n", baudrate); + TU_LOG_CDCH("CDC CP210x Set BaudRate = %lu\n", baudrate); baudrate = tu_htole32(baudrate); return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baudrate, 4, complete_cb, user_data); } From 732686cc109aea9527d3133c3b8e61dc045dc233 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 27 Apr 2023 23:22:10 +0700 Subject: [PATCH 13/13] add tuh_cdc_set_baudrate() --- src/class/cdc/cdc_host.c | 43 +++++++++++++++++++++++++++++++--------- src/class/cdc/cdc_host.h | 13 ++++++++---- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 132b1ef09..4c87ff1ad 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -126,11 +126,6 @@ static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const return NULL; } -static inline bool support_line_request(cdch_interface_t const* p_cdc) { - return (p_cdc->serial_protocol == SERIAL_PROTOCOL_ACM && p_cdc->acm_capability.support_line_request) || - (p_cdc->serial_protocol == SERIAL_PROTOCOL_FTDI); -} - static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t const *desc_ep); static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num); static void cdch_internal_control_complete(tuh_xfer_t* xfer); @@ -391,10 +386,11 @@ static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_st 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 && support_line_request(p_cdc)); + TU_VERIFY(p_cdc); switch(p_cdc->serial_protocol) { case SERIAL_PROTOCOL_ACM: + TU_VERIFY(p_cdc->acm_capability.support_line_request); return acm_set_control_line_state(p_cdc, line_state, complete_cb, user_data); #if CFG_TUH_CDC_FTDI @@ -413,6 +409,8 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c } bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + TU_LOG_CDCH("CDC ACM Set Line Conding\r\n"); + tusb_control_request_t const request = { .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, @@ -446,11 +444,11 @@ bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_ 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 && support_line_request(p_cdc)); - TU_LOG_CDCH("CDC Set Line Conding\r\n"); + TU_VERIFY(p_cdc); switch(p_cdc->serial_protocol) { case SERIAL_PROTOCOL_ACM: + TU_VERIFY(p_cdc->acm_capability.support_line_request); return acm_set_line_coding(p_cdc, line_coding, complete_cb, user_data); #if CFG_TUH_CDC_FTDI @@ -468,6 +466,33 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, } } +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); + + switch(p_cdc->serial_protocol) { + case SERIAL_PROTOCOL_ACM: { + TU_VERIFY(p_cdc->acm_capability.support_line_request); + cdc_line_coding_t line_coding = p_cdc->line_coding; + line_coding.bit_rate = baudrate; + return acm_set_line_coding(p_cdc, &line_coding, complete_cb, user_data); + } + + #if CFG_TUH_CDC_FTDI + case SERIAL_PROTOCOL_FTDI: + // FTDI need to set baud rate and data bits, parity, stop bits separately + return ftdi_sio_set_baudrate(p_cdc, baudrate, complete_cb, user_data); + #endif + + #if CFG_TUH_CDC_CP210X + case SERIAL_PROTOCOL_CP210X: + return cp210x_set_baudrate(p_cdc, baudrate, complete_cb, user_data); + #endif + + default: return false; + } +} + //--------------------------------------------------------------------+ // CLASS-USBH API //--------------------------------------------------------------------+ @@ -722,7 +747,7 @@ static void process_acm_config(tuh_xfer_t* xfer) if (p_cdc->acm_capability.support_line_request) { cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; - TU_ASSERT(tuh_cdc_set_line_coding(idx, &line_coding, process_acm_config, CONFIG_ACM_COMPLETE), ); + TU_ASSERT(acm_set_line_coding(p_cdc, &line_coding, process_acm_config, CONFIG_ACM_COMPLETE), ); break; } #endif diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h index a1e78f158..971ebe2ae 100644 --- a/src/class/cdc/cdc_host.h +++ b/src/class/cdc/cdc_host.h @@ -140,22 +140,27 @@ bool tuh_cdc_read_clear (uint8_t idx); // Request to Set Control Line State: DTR (bit 0), RTS (bit 1) bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -// Request to Set Line Coding +// Request to set baudrate +bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +// Request to Set Line Coding (ACM only) 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); -// Request to Get Line Coding +// Request to Get Line Coding (ACM only) // Should only use if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding() never got invoked and // CFG_TUH_CDC_LINE_CODING_ON_ENUM is not defined // bool tuh_cdc_get_line_coding(uint8_t idx, cdc_line_coding_t* coding); // Connect by set both DTR, RTS -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 -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); }