diff options
| author | IngHK <[email protected]> | 2024-01-16 07:57:06 +0100 |
|---|---|---|
| committer | IngHK <[email protected]> | 2024-01-16 07:57:06 +0100 |
| commit | 43e655b896fab0d3dd8f2c2d1d987713d87036f6 (patch) | |
| tree | da171d665631d8ab601d39dd797a7dfb6045fcac /src/class | |
| parent | c2bbcc9f60a2b7293fad4e24468d3f045fde28ee (diff) | |
| parent | 4b3b401ce37e12eaf4c85a5184d94327c28c70a5 (diff) | |
Merge remote-tracking branch 'remotes/hathach/master' into cdc_ch34x_support
Diffstat (limited to 'src/class')
| -rw-r--r-- | src/class/cdc/cdc_debug.h.bak | 55 | ||||
| -rw-r--r-- | src/class/cdc/cdc_device.c.bak | 501 | ||||
| -rw-r--r-- | src/class/cdc/cdc_host.c | 8 | ||||
| -rw-r--r-- | src/class/cdc/cdc_host.c.bak | 1600 | ||||
| -rw-r--r-- | src/class/cdc/cdc_host.h.bak | 207 | ||||
| -rw-r--r-- | src/class/cdc/serial/ch34x.h.bak | 60 |
6 files changed, 2429 insertions, 2 deletions
diff --git a/src/class/cdc/cdc_debug.h.bak b/src/class/cdc/cdc_debug.h.bak new file mode 100644 index 000000000..dc9e8a3c8 --- /dev/null +++ b/src/class/cdc/cdc_debug.h.bak @@ -0,0 +1,55 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2023 Ha Thach (tinyusb.org) + * Copyright (c) 2023 IngHK Heiko Kuester + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_CDC_DEBUG_H_ +#define _TUSB_CDC_DEBUG_H_ + +#include "cdc.h" + +#ifdef __cplusplus + extern "C" { +#endif + +// logging of line coding +#define TU_LOG_LINE_CODING(PRE_TEXT,LINE_CODING) \ + TU_LOG_DRV(PRE_TEXT "Line Coding %luBd %u%c%s\r\n", \ + LINE_CODING.bit_rate, \ + LINE_CODING.data_bits, \ + LINE_CODING.parity == CDC_LINE_CODING_PARITY_NONE ? 'N' : \ + LINE_CODING.parity == CDC_LINE_CODING_PARITY_ODD ? 'O' : \ + LINE_CODING.parity == CDC_LINE_CODING_PARITY_EVEN ? 'E' : \ + LINE_CODING.parity == CDC_LINE_CODING_PARITY_MARK ? 'M' : \ + LINE_CODING.parity == CDC_LINE_CODING_PARITY_SPACE ? 'S' : '?', \ + LINE_CODING.stop_bits == CDC_LINE_CODING_STOP_BITS_1 ? "1" : \ + LINE_CODING.stop_bits == CDC_LINE_CODING_STOP_BITS_1_5 ? "1.5" : \ + LINE_CODING.stop_bits == CDC_LINE_CODING_STOP_BITS_2 ? "2" : "?") + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CDC_DEBUG_H_ */ diff --git a/src/class/cdc/cdc_device.c.bak b/src/class/cdc/cdc_device.c.bak new file mode 100644 index 000000000..1e3bbb2a1 --- /dev/null +++ b/src/class/cdc/cdc_device.c.bak @@ -0,0 +1,501 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (CFG_TUD_ENABLED && CFG_TUD_CDC) + +#include "device/usbd.h" +#include "device/usbd_pvt.h" + +#include "cdc_device.h" +#if 0 // TODO activate and test +#include "cdc_debug.h" +#endif + +// Level where CFG_TUSB_DEBUG must be at least for this driver is logged +#ifndef CFG_TUD_CDC_LOG_LEVEL + #define CFG_TUD_CDC_LOG_LEVEL CFG_TUD_LOG_LEVEL +#endif + +#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_CDC_LOG_LEVEL, __VA_ARGS__) + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +enum +{ + BULK_PACKET_SIZE = (TUD_OPT_HIGH_SPEED ? 512 : 64) +}; + +typedef struct +{ + uint8_t itf_num; + uint8_t ep_notif; + uint8_t ep_in; + uint8_t ep_out; + + // Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send) + uint8_t line_state; + + /*------------- From this point, data is not cleared by bus reset -------------*/ + char wanted_char; + TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding; + + // FIFO + tu_fifo_t rx_ff; + tu_fifo_t tx_ff; + + uint8_t rx_ff_buf[CFG_TUD_CDC_RX_BUFSIZE]; + uint8_t tx_ff_buf[CFG_TUD_CDC_TX_BUFSIZE]; + + OSAL_MUTEX_DEF(rx_ff_mutex); + OSAL_MUTEX_DEF(tx_ff_mutex); + + // Endpoint Transfer buffer + CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EP_BUFSIZE]; + CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EP_BUFSIZE]; + +}cdcd_interface_t; + +#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char) + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ +CFG_TUD_MEM_SECTION tu_static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC]; + +static bool _prep_out_transaction (cdcd_interface_t* p_cdc) +{ + uint8_t const rhport = 0; + uint16_t available = tu_fifo_remaining(&p_cdc->rx_ff); + + // Prepare for incoming data but only allow what we can store in the ring buffer. + // TODO Actually we can still carry out the transfer, keeping count of received bytes + // and slowly move it to the FIFO when read(). + // This pre-check reduces endpoint claiming + TU_VERIFY(available >= sizeof(p_cdc->epout_buf)); + + // claim endpoint + TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_out)); + + // fifo can be changed before endpoint is claimed + available = tu_fifo_remaining(&p_cdc->rx_ff); + + if ( available >= sizeof(p_cdc->epout_buf) ) + { + return usbd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf)); + }else + { + // Release endpoint since we don't make any transfer + usbd_edpt_release(rhport, p_cdc->ep_out); + + return false; + } +} + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ +bool tud_cdc_n_connected(uint8_t itf) +{ + // DTR (bit 0) active is considered as connected + return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0); +} + +uint8_t tud_cdc_n_get_line_state (uint8_t itf) +{ + return _cdcd_itf[itf].line_state; +} + +void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding) +{ + (*coding) = _cdcd_itf[itf].line_coding; +} + +void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted) +{ + _cdcd_itf[itf].wanted_char = wanted; +} + + +//--------------------------------------------------------------------+ +// READ API +//--------------------------------------------------------------------+ +uint32_t tud_cdc_n_available(uint8_t itf) +{ + return tu_fifo_count(&_cdcd_itf[itf].rx_ff); +} + +uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize) +{ + cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; + uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX)); + _prep_out_transaction(p_cdc); + return num_read; +} + +bool tud_cdc_n_peek(uint8_t itf, uint8_t* chr) +{ + return tu_fifo_peek(&_cdcd_itf[itf].rx_ff, chr); +} + +void tud_cdc_n_read_flush (uint8_t itf) +{ + cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; + tu_fifo_clear(&p_cdc->rx_ff); + _prep_out_transaction(p_cdc); +} + +//--------------------------------------------------------------------+ +// WRITE API +//--------------------------------------------------------------------+ +uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) +{ + cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; + uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX)); + + // flush if queue more than packet size + // may need to suppress -Wunreachable-code since most of the time CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE + if ( (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE) || ((CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE) && tu_fifo_full(&p_cdc->tx_ff)) ) + { + tud_cdc_n_write_flush(itf); + } + + return ret; +} + +uint32_t tud_cdc_n_write_flush (uint8_t itf) +{ + cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; + + // Skip if usb is not ready yet + TU_VERIFY( tud_ready(), 0 ); + + // No data to send + if ( !tu_fifo_count(&p_cdc->tx_ff) ) return 0; + + uint8_t const rhport = 0; + + // Claim the endpoint + TU_VERIFY( usbd_edpt_claim(rhport, p_cdc->ep_in), 0 ); + + // Pull data from FIFO + uint16_t const count = tu_fifo_read_n(&p_cdc->tx_ff, p_cdc->epin_buf, sizeof(p_cdc->epin_buf)); + + if ( count ) + { + TU_ASSERT( usbd_edpt_xfer(rhport, p_cdc->ep_in, p_cdc->epin_buf, count), 0 ); + return count; + }else + { + // Release endpoint since we don't make any transfer + // Note: data is dropped if terminal is not connected + usbd_edpt_release(rhport, p_cdc->ep_in); + return 0; + } +} + +uint32_t tud_cdc_n_write_available (uint8_t itf) +{ + return tu_fifo_remaining(&_cdcd_itf[itf].tx_ff); +} + +bool tud_cdc_n_write_clear (uint8_t itf) +{ + return tu_fifo_clear(&_cdcd_itf[itf].tx_ff); +} + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void cdcd_init(void) +{ + tu_memclr(_cdcd_itf, sizeof(_cdcd_itf)); + + for(uint8_t i=0; i<CFG_TUD_CDC; i++) + { + cdcd_interface_t* p_cdc = &_cdcd_itf[i]; + + p_cdc->wanted_char = (char) -1; + + // default line coding is : stop bit = 1, parity = none, data bits = 8 + p_cdc->line_coding.bit_rate = 115200; + p_cdc->line_coding.stop_bits = 0; + p_cdc->line_coding.parity = 0; + p_cdc->line_coding.data_bits = 8; + + // Config RX fifo + tu_fifo_config(&p_cdc->rx_ff, p_cdc->rx_ff_buf, TU_ARRAY_SIZE(p_cdc->rx_ff_buf), 1, false); + + // Config TX fifo as overwritable at initialization and will be changed to non-overwritable + // if terminal supports DTR bit. Without DTR we do not know if data is actually polled by terminal. + // In this way, the most current data is prioritized. + tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, true); + + tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, osal_mutex_create(&p_cdc->rx_ff_mutex)); + tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex), NULL); + } +} + +void cdcd_reset(uint8_t rhport) +{ + (void) rhport; + + for(uint8_t i=0; i<CFG_TUD_CDC; i++) + { + cdcd_interface_t* p_cdc = &_cdcd_itf[i]; + + tu_memclr(p_cdc, ITF_MEM_RESET_SIZE); + tu_fifo_clear(&p_cdc->rx_ff); + tu_fifo_clear(&p_cdc->tx_ff); + tu_fifo_set_overwritable(&p_cdc->tx_ff, true); + } +} + +uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) +{ + // Only support ACM subclass + TU_VERIFY( TUSB_CLASS_CDC == itf_desc->bInterfaceClass && + CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass, 0); + + // Find available interface + cdcd_interface_t * p_cdc = NULL; + for(uint8_t cdc_id=0; cdc_id<CFG_TUD_CDC; cdc_id++) + { + if ( _cdcd_itf[cdc_id].ep_in == 0 ) + { + p_cdc = &_cdcd_itf[cdc_id]; + break; + } + } + TU_ASSERT(p_cdc, 0); + + //------------- Control Interface -------------// + p_cdc->itf_num = itf_desc->bInterfaceNumber; + + uint16_t drv_len = sizeof(tusb_desc_interface_t); + uint8_t const * p_desc = tu_desc_next( itf_desc ); + + // Communication Functional Descriptors + while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len ) + { + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + + if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) ) + { + // notification endpoint + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; + + TU_ASSERT( usbd_edpt_open(rhport, desc_ep), 0 ); + p_cdc->ep_notif = desc_ep->bEndpointAddress; + + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + + //------------- Data Interface (if any) -------------// + if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && + (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) ) + { + // next to endpoint descriptor + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + + // Open endpoint pair + TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0 ); + + drv_len += 2*sizeof(tusb_desc_endpoint_t); + } + + // Prepare for incoming data + _prep_out_transaction(p_cdc); + + return drv_len; +} + +// Invoked when a control transfer occurred on an interface of this class +// Driver response accordingly to the request and the transfer stage (setup/data/ack) +// return false to stall control endpoint (e.g unsupported request) +bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) +{ + // Handle class request only + TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); + + uint8_t itf = 0; + cdcd_interface_t* p_cdc = _cdcd_itf; + + // Identify which interface to use + for ( ; ; itf++, p_cdc++) + { + if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false; + + if ( p_cdc->itf_num == request->wIndex ) break; + } + + switch ( request->bRequest ) + { + case CDC_REQUEST_SET_LINE_CODING: + if (stage == CONTROL_STAGE_SETUP) + { + #if 0 // TODO activate and test + TU_LOG_LINE_CODING(" Set ", p_cdc->line_coding); + #else + TU_LOG_DRV(" Set Line Coding\r\n"); + #endif + tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t)); + } + else if ( stage == CONTROL_STAGE_ACK) + { + if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding); + } + break; + + case CDC_REQUEST_GET_LINE_CODING: + if (stage == CONTROL_STAGE_SETUP) + { + #if 0 // TODO activate and test + TU_LOG_LINE_CODING(" Get ", p_cdc->line_coding); + #else + TU_LOG_DRV(" Get Line Coding\r\n"); + #endif + tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t)); + } + break; + + case CDC_REQUEST_SET_CONTROL_LINE_STATE: + if (stage == CONTROL_STAGE_SETUP) + { + tud_control_status(rhport, request); + } + else if (stage == CONTROL_STAGE_ACK) + { + // CDC PSTN v1.2 section 6.3.12 + // Bit 0: Indicates if DTE is present or not. + // This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready) + // Bit 1: Carrier control for half-duplex modems. + // This signal corresponds to V.24 signal 105 and RS-232 signal RTS (Request to Send) + bool const dtr = tu_bit_test(request->wValue, 0); + bool const rts = tu_bit_test(request->wValue, 1); + + p_cdc->line_state = (uint8_t) request->wValue; + + // Disable fifo overwriting if DTR bit is set + tu_fifo_set_overwritable(&p_cdc->tx_ff, !dtr); + + TU_LOG_DRV(" Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts); + + // Invoke callback + if ( tud_cdc_line_state_cb ) tud_cdc_line_state_cb(itf, dtr, rts); + } + break; + case CDC_REQUEST_SEND_BREAK: + if (stage == CONTROL_STAGE_SETUP) + { + tud_control_status(rhport, request); + } + else if (stage == CONTROL_STAGE_ACK) + { + TU_LOG_DRV(" Send Break\r\n"); + if ( tud_cdc_send_break_cb ) tud_cdc_send_break_cb(itf, request->wValue); + } + break; + + default: return false; // stall unsupported request + } + + return true; +} + +bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) result; + + uint8_t itf; + cdcd_interface_t* p_cdc; + + // Identify which interface to use + for (itf = 0; itf < CFG_TUD_CDC; itf++) + { + p_cdc = &_cdcd_itf[itf]; + if ( ( ep_addr == p_cdc->ep_out ) || ( ep_addr == p_cdc->ep_in ) ) break; + } + TU_ASSERT(itf < CFG_TUD_CDC); + + // Received new data + if ( ep_addr == p_cdc->ep_out ) + { + tu_fifo_write_n(&p_cdc->rx_ff, p_cdc->epout_buf, (uint16_t) xferred_bytes); + + // Check for wanted char and invoke callback if needed + if ( tud_cdc_rx_wanted_cb && (((signed char) p_cdc->wanted_char) != -1) ) + { + for ( uint32_t i = 0; i < xferred_bytes; i++ ) + { + if ( (p_cdc->wanted_char == p_cdc->epout_buf[i]) && !tu_fifo_empty(&p_cdc->rx_ff) ) + { + tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char); + } + } + } + + // invoke receive callback (if there is still data) + if (tud_cdc_rx_cb && !tu_fifo_empty(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf); + + // prepare for OUT transaction + _prep_out_transaction(p_cdc); + } + + // Data sent to host, we continue to fetch from tx fifo to send. + // Note: This will cause incorrect baudrate set in line coding. + // Though maybe the baudrate is not really important !!! + if ( ep_addr == p_cdc->ep_in ) + { + // invoke transmit callback to possibly refill tx fifo + if ( tud_cdc_tx_complete_cb ) tud_cdc_tx_complete_cb(itf); + + if ( 0 == tud_cdc_n_write_flush(itf) ) + { + // If there is no data left, a ZLP should be sent if + // xferred_bytes is multiple of EP Packet size and not zero + if ( !tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE-1))) ) + { + if ( usbd_edpt_claim(rhport, p_cdc->ep_in) ) + { + usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0); + } + } + } + } + + // nothing to do with notif endpoint for now + + return true; +} + +#endif diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 9b5186e0c..f6a6773e0 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -90,6 +90,7 @@ static cdch_interface_t cdch_data[CFG_TUH_CDC]; //--------------------------------------------------------------------+ //------------- ACM prototypes -------------// +static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); static void acm_process_config(tuh_xfer_t* xfer); static bool acm_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); @@ -649,8 +650,6 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t // Enumeration //--------------------------------------------------------------------+ -static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); - 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++) @@ -685,8 +684,13 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_d { return acm_open(daddr, itf_desc, max_len); } +<<<<<<< HEAD #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X else if ( 0xff == itf_desc->bInterfaceClass ) +======= + #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X + else if ( TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass ) +>>>>>>> remotes/hathach/master { uint16_t vid, pid; TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid)); diff --git a/src/class/cdc/cdc_host.c.bak b/src/class/cdc/cdc_host.c.bak new file mode 100644 index 000000000..2c08f2ace --- /dev/null +++ b/src/class/cdc/cdc_host.c.bak @@ -0,0 +1,1600 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Copyright (c) 2023 IngHK Heiko Kuester + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (CFG_TUH_ENABLED && CFG_TUH_CDC) + +#include "host/usbh.h" +#include "host/usbh_pvt.h" + +#include "cdc_host.h" +#include "cdc_debug.h" + +// Level where CFG_TUSB_DEBUG must be at least for this driver is logged +#ifndef CFG_TUH_CDC_LOG_LEVEL + #define CFG_TUH_CDC_LOG_LEVEL CFG_TUH_LOG_LEVEL +#endif + +#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_CDC_LOG_LEVEL, __VA_ARGS__) + +//--------------------------------------------------------------------+ +// Host CDC Interface +//--------------------------------------------------------------------+ + +typedef struct { + uint8_t daddr; + uint8_t bInterfaceNumber; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + + uint8_t serial_drid; // Serial Driver ID + cdc_acm_capability_t acm_capability; + uint8_t ep_notif; + + uint8_t line_state; // RTS, DTR (refer enums CDC_CONTROL_LINE_STATE_RTS, CDC_CONTROL_LINE_STATE_DTR) + TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding; // Baudrate, stop bits, parity, data width + + tuh_xfer_cb_t user_control_cb; + + struct { + tu_edpt_stream_t tx; + tu_edpt_stream_t rx; + + uint8_t tx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE]; + CFG_TUH_MEM_ALIGN uint8_t tx_ep_buf[CFG_TUH_CDC_TX_EPSIZE]; + + uint8_t rx_ff_buf[CFG_TUH_CDC_TX_BUFSIZE]; + CFG_TUH_MEM_ALIGN uint8_t rx_ep_buf[CFG_TUH_CDC_TX_EPSIZE]; + } stream; +#if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CH34X + uint32_t baudrate_requested; +#endif +#if CFG_TUH_CDC_CH34X + struct { + uint8_t version; + } ch34x; +#endif +} cdch_interface_t; + +CFG_TUH_MEM_SECTION +static cdch_interface_t cdch_data[CFG_TUH_CDC]; + +//--------------------------------------------------------------------+ +// Serial Driver +//--------------------------------------------------------------------+ + +//------------- ACM prototypes -------------// +static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); +static void acm_process_config(tuh_xfer_t* xfer); + +static bool acm_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); +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); +static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + +//------------- FTDI prototypes -------------// +#if CFG_TUH_CDC_FTDI +#include "serial/ftdi_sio.h" + +static uint16_t const ftdi_pids[] = { CFG_TUH_CDC_FTDI_PID_LIST }; +enum { + FTDI_PID_COUNT = sizeof(ftdi_pids) / sizeof(ftdi_pids[0]) +}; + +static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len); +static void ftdi_process_config(tuh_xfer_t* xfer); + +static bool ftdi_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); +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 prototypes -------------// +#if CFG_TUH_CDC_CP210X +#include "serial/cp210x.h" + +static uint16_t const cp210x_pids[] = { CFG_TUH_CDC_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); +static void cp210x_process_config(tuh_xfer_t* xfer); + +static bool cp210x_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); +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 + +//------------- CH34x prototypes -------------// +#if CFG_TUH_CDC_CH34X +#include "serial/ch34x.h" + +static uint16_t const ch34x_vids_pids[][2] = { CFG_TUH_CDC_CH34X_VID_PID_LIST }; +enum { + CH34X_VID_PID_COUNT = sizeof ( ch34x_vids_pids ) / sizeof ( ch34x_vids_pids[0] ) +}; + +static bool ch34x_open ( uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len ); +static void ch34x_process_config ( tuh_xfer_t* xfer ); + +static bool ch34x_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); +static bool ch34x_set_modem_ctrl ( cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data ); +static bool ch34x_set_baudrate ( cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data ); +#endif + + +enum { + SERIAL_DRIVER_ACM = 0, + +#if CFG_TUH_CDC_FTDI + SERIAL_DRIVER_FTDI, +#endif + +#if CFG_TUH_CDC_CP210X + SERIAL_DRIVER_CP210X, +#endif + +#if CFG_TUH_CDC_CH34X + SERIAL_DRIVER_CH34X, +#endif +}; + +typedef struct { + void (*const process_set_config)(tuh_xfer_t* xfer); + bool (*const set_control_line_state)(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + bool (*const set_baudrate)(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + bool (*const 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); +} cdch_serial_driver_t; + +// Note driver list must be in the same order as SERIAL_DRIVER enum +static const cdch_serial_driver_t serial_drivers[] = { + { .process_set_config = acm_process_config, + .set_control_line_state = acm_set_control_line_state, + .set_baudrate = acm_set_baudrate, + .set_line_coding = acm_set_line_coding + }, + + #if CFG_TUH_CDC_FTDI + { .process_set_config = ftdi_process_config, + .set_control_line_state = ftdi_sio_set_modem_ctrl, + .set_baudrate = ftdi_sio_set_baudrate, + .set_line_coding = ftdi_set_line_coding + }, + #endif + + #if CFG_TUH_CDC_CP210X + { .process_set_config = cp210x_process_config, + .set_control_line_state = cp210x_set_modem_ctrl, + .set_baudrate = cp210x_set_baudrate, + .set_line_coding = cp210x_set_line_coding + }, + #endif + + #if CFG_TUH_CDC_CH34X + { .process_set_config = ch34x_process_config, + .set_control_line_state = ch34x_set_modem_ctrl, + .set_baudrate = ch34x_set_baudrate, + .set_line_coding = ch34x_set_line_coding + }, + #endif +}; + +enum { + SERIAL_DRIVER_COUNT = sizeof(serial_drivers) / sizeof(serial_drivers[0]) +}; + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ + +static inline cdch_interface_t* get_itf(uint8_t idx) +{ + TU_ASSERT(idx < CFG_TUH_CDC, NULL); + cdch_interface_t* p_cdc = &cdch_data[idx]; + + return (p_cdc->daddr != 0) ? p_cdc : NULL; +} + +static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr) +{ + for(uint8_t i=0; i<CFG_TUH_CDC; i++) + { + cdch_interface_t* p_cdc = &cdch_data[i]; + if ( (p_cdc->daddr == daddr) && + (ep_addr == p_cdc->ep_notif || ep_addr == p_cdc->stream.rx.ep_addr || ep_addr == p_cdc->stream.tx.ep_addr)) + { + return i; + } + } + + return TUSB_INDEX_INVALID_8; +} + + +static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const *itf_desc) +{ + for(uint8_t i=0; i<CFG_TUH_CDC; i++) + { + if (cdch_data[i].daddr == 0) { + cdch_interface_t* p_cdc = &cdch_data[i]; + + 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; + return p_cdc; + } + } + + return NULL; +} + +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); + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ + +uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num) +{ + for(uint8_t i=0; i<CFG_TUH_CDC; i++) + { + const cdch_interface_t* p_cdc = &cdch_data[i]; + + if (p_cdc->daddr == daddr && p_cdc->bInterfaceNumber == itf_num) return i; + } + + return TUSB_INDEX_INVALID_8; +} + +bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc && info); + + info->daddr = p_cdc->daddr; + + // re-construct descriptor + tusb_desc_interface_t* desc = &info->desc; + desc->bLength = sizeof(tusb_desc_interface_t); + desc->bDescriptorType = TUSB_DESC_INTERFACE; + + desc->bInterfaceNumber = p_cdc->bInterfaceNumber; + desc->bAlternateSetting = 0; + desc->bNumEndpoints = 2u + (p_cdc->ep_notif ? 1u : 0u); + desc->bInterfaceClass = TUSB_CLASS_CDC; + desc->bInterfaceSubClass = p_cdc->bInterfaceSubClass; + desc->bInterfaceProtocol = p_cdc->bInterfaceProtocol; + desc->iInterface = 0; // not used yet + + return true; +} + +bool tuh_cdc_mounted(uint8_t idx) +{ + cdch_interface_t* p_cdc = get_itf(idx); + return p_cdc != NULL; +} + +bool tuh_cdc_get_dtr(uint8_t idx) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + TU_LOG_CONTROL_LINE_STATE("CDCh Local ", p_cdc->line_state); + + return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_DTR) ? true : false; +} + +bool tuh_cdc_get_rts(uint8_t idx) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + TU_LOG_CONTROL_LINE_STATE("CDCh Local ", p_cdc->line_state); + + return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_RTS) ? true : false; +} + +bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + *line_coding = p_cdc->line_coding; + TU_LOG_LINE_CODING("CDCh Get ", p_cdc->line_coding); + + return true; +} + +//--------------------------------------------------------------------+ +// Write +//--------------------------------------------------------------------+ + +uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_write(&p_cdc->stream.tx, buffer, bufsize); +} + +uint32_t tuh_cdc_write_flush(uint8_t idx) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_write_xfer(&p_cdc->stream.tx); +} + +bool tuh_cdc_write_clear(uint8_t idx) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_clear(&p_cdc->stream.tx); +} + +uint32_t tuh_cdc_write_available(uint8_t idx) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_write_available(&p_cdc->stream.tx); +} + +//--------------------------------------------------------------------+ +// Read +//--------------------------------------------------------------------+ + +uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_read(&p_cdc->stream.rx, buffer, bufsize); +} + +uint32_t tuh_cdc_read_available(uint8_t idx) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_read_available(&p_cdc->stream.rx); +} + +bool tuh_cdc_peek(uint8_t idx, uint8_t* ch) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + return tu_edpt_stream_peek(&p_cdc->stream.rx, ch); +} + +bool tuh_cdc_read_clear (uint8_t idx) +{ + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc); + + bool ret = tu_edpt_stream_clear(&p_cdc->stream.rx); + tu_edpt_stream_read_xfer(&p_cdc->stream.rx); + return ret; +} + +//--------------------------------------------------------------------+ +// Control Endpoint API +//--------------------------------------------------------------------+ + +// internal control complete to update state such as line state, encoding +static void cdch_internal_control_complete(tuh_xfer_t* xfer) +{ + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t* p_cdc = get_itf(idx); + TU_ASSERT(p_cdc, ); + + if (xfer->result == XFER_RESULT_SUCCESS) + { + switch (p_cdc->serial_drid) { + case SERIAL_DRIVER_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); + } + break; + + default: break; + } + break; + + #if CFG_TUH_CDC_FTDI + case SERIAL_DRIVER_FTDI: + switch (xfer->setup->bRequest) { + case FTDI_SIO_MODEM_CTRL: + p_cdc->line_state = (uint8_t) (tu_le16toh(xfer->setup->wValue) & 0x00ff); + break; + + case FTDI_SIO_SET_BAUD_RATE: + // convert from divisor to baudrate is not supported + p_cdc->line_coding.bit_rate = p_cdc->baudrate_requested; + break; + + default: break; + } + break; + #endif + + #if CFG_TUH_CDC_CP210X + case SERIAL_DRIVER_CP210X: + switch(xfer->setup->bRequest) { + case CP210X_SET_MHS: + p_cdc->line_state = (uint8_t) (tu_le16toh(xfer->setup->wValue) & 0x00ff); + break; + + case CP210X_SET_BAUDRATE: { + uint32_t baudrate; + memcpy(&baudrate, xfer->buffer, sizeof(uint32_t)); + p_cdc->line_coding.bit_rate = tu_le32toh(baudrate); + } + break; + } + break; + #endif + + default: break; + } + } + + xfer->complete_cb = p_cdc->user_control_cb; + if (xfer->complete_cb) { + xfer->complete_cb(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->serial_drid < SERIAL_DRIVER_COUNT); + cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; + + if ( complete_cb ) { + TU_VERIFY(driver->set_control_line_state(p_cdc, line_state, complete_cb, user_data)); + }else { + // blocking + xfer_result_t result = XFER_RESULT_INVALID; + bool ret = driver->set_control_line_state(p_cdc, line_state, complete_cb, (uintptr_t) &result); + + if (user_data) { + // user_data is not NULL, return result via user_data + *((xfer_result_t*) user_data) = result; + } + + TU_VERIFY(ret && result == XFER_RESULT_SUCCESS); + + p_cdc->line_state = (uint8_t) line_state; + } + TU_LOG_CONTROL_LINE_STATE("CDCh Set ", p_cdc->line_state); + + return true; +} + +bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + cdch_interface_t* p_cdc = get_itf(idx); + TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; + + if ( complete_cb ) { + TU_VERIFY(driver->set_baudrate(p_cdc, baudrate, complete_cb, user_data)); + }else { + // blocking + xfer_result_t result = XFER_RESULT_INVALID; + bool ret = driver->set_baudrate(p_cdc, baudrate, complete_cb, (uintptr_t) &result); + + if (user_data) { + // user_data is not NULL, return result via user_data + *((xfer_result_t*) user_data) = result; + } + + TU_VERIFY(ret && result == XFER_RESULT_SUCCESS); + + p_cdc->line_coding.bit_rate = baudrate; + } + TU_LOG_BAUDRATE("CDCh Set ", p_cdc->line_coding.bit_rate); + + 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->serial_drid < SERIAL_DRIVER_COUNT); + cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; + + if ( complete_cb ) { + TU_VERIFY(driver->set_line_coding(p_cdc, line_coding, complete_cb, user_data)); + } else { + // blocking + xfer_result_t result = XFER_RESULT_INVALID; + bool ret = driver->set_line_coding(p_cdc, line_coding, complete_cb, (uintptr_t) &result); + + if (user_data) { + // user_data is not NULL, return result via user_data + *((xfer_result_t*) user_data) = result; + } + + TU_VERIFY(ret && result == XFER_RESULT_SUCCESS); + + p_cdc->line_coding = *line_coding; + } + TU_LOG_LINE_CODING("CDCh Set ", p_cdc->line_coding); + + return true; +} + +//--------------------------------------------------------------------+ +// CLASS-USBH API +//--------------------------------------------------------------------+ + +void cdch_init(void) +{ + tu_memclr(cdch_data, sizeof(cdch_data)); + + for(size_t i=0; i<CFG_TUH_CDC; i++) + { + cdch_interface_t* p_cdc = &cdch_data[i]; + + tu_edpt_stream_init(&p_cdc->stream.tx, true, true, false, + p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE, + p_cdc->stream.tx_ep_buf, CFG_TUH_CDC_TX_EPSIZE); + + tu_edpt_stream_init(&p_cdc->stream.rx, true, false, false, + p_cdc->stream.rx_ff_buf, CFG_TUH_CDC_RX_BUFSIZE, + p_cdc->stream.rx_ep_buf, CFG_TUH_CDC_RX_EPSIZE); + } +} + +void cdch_close(uint8_t daddr) +{ + for(uint8_t idx=0; idx<CFG_TUH_CDC; idx++) + { + cdch_interface_t* p_cdc = &cdch_data[idx]; + if (p_cdc->daddr == daddr) + { + TU_LOG_DRV(" CDCh close addr = %u index = %u\r\n", daddr, idx); + + // Invoke application callback + if (tuh_cdc_umount_cb) tuh_cdc_umount_cb(idx); + + //tu_memclr(p_cdc, sizeof(cdch_interface_t)); + p_cdc->daddr = 0; + p_cdc->bInterfaceNumber = 0; + tu_edpt_stream_close(&p_cdc->stream.tx); + tu_edpt_stream_close(&p_cdc->stream.rx); + } + } +} + +bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) { + // TODO handle stall response, retry failed transfer ... + TU_ASSERT(event == XFER_RESULT_SUCCESS); + + uint8_t const idx = get_idx_by_ep_addr(daddr, ep_addr); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc); + + if ( ep_addr == p_cdc->stream.tx.ep_addr ) { + // invoke tx complete callback to possibly refill tx fifo + if (tuh_cdc_tx_complete_cb) tuh_cdc_tx_complete_cb(idx); + + if ( 0 == tu_edpt_stream_write_xfer(&p_cdc->stream.tx) ) { + // If there is no data left, a ZLP should be sent if: + // - xferred_bytes is multiple of EP Packet size and not zero + tu_edpt_stream_write_zlp_if_needed(&p_cdc->stream.tx, xferred_bytes); + } + } + else if ( ep_addr == p_cdc->stream.rx.ep_addr ) { + #if CFG_TUH_CDC_FTDI + if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI) { + // FTDI reserve 2 bytes for status + // FTDI status +// uint8_t status[2] = { +// p_cdc->stream.rx.ep_buf[0], +// p_cdc->stream.rx.ep_buf[1] +// }; + tu_edpt_stream_read_xfer_complete_offset(&p_cdc->stream.rx, xferred_bytes, 2); + }else + #endif + { + tu_edpt_stream_read_xfer_complete(&p_cdc->stream.rx, xferred_bytes); + } + + // invoke receive callback + if (tuh_cdc_rx_cb) tuh_cdc_rx_cb(idx); + + // prepare for next transfer if needed + tu_edpt_stream_read_xfer(&p_cdc->stream.rx); + }else if ( ep_addr == p_cdc->ep_notif ) { + // TODO handle notification endpoint + }else { + TU_ASSERT(false); + } + + return true; +} + +//--------------------------------------------------------------------+ +// Enumeration +//--------------------------------------------------------------------+ + +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; +} + +bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) +{ + (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); + } + #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X + else if ( TUSB_CLASS_VENDOR_SPECIFIC == 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) { + for (size_t i = 0; i < FTDI_PID_COUNT; i++) { + if (ftdi_pids[i] == pid) { + return ftdi_open(daddr, itf_desc, max_len); + } + } + } + #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 + + #if CFG_TUH_CDC_CH34X + for (size_t i = 0; i < CH34X_VID_PID_COUNT; i++) { + if ( ch34x_vids_pids[i][0] == vid && ch34x_vids_pids[i][1] == pid ) { + return ch34x_open(daddr, itf_desc, max_len); + } + } + #endif + } + #endif // CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X + + 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); +} + + +bool cdch_set_config(uint8_t daddr, uint8_t itf_num) +{ + tusb_control_request_t request; + request.wIndex = tu_htole16((uint16_t) itf_num); + + // fake transfer to kick-off process + tuh_xfer_t xfer; + xfer.daddr = daddr; + xfer.result = XFER_RESULT_SUCCESS; + xfer.setup = &request; + xfer.user_data = 0; // initial state + + uint8_t const idx = tuh_cdc_itf_get_index(daddr, itf_num); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + + serial_drivers[p_cdc->serial_drid].process_set_config(&xfer); + return true; +} + +//--------------------------------------------------------------------+ +// ACM +//--------------------------------------------------------------------+ + +enum { + CONFIG_ACM_SET_CONTROL_LINE_STATE = 0, + CONFIG_ACM_SET_LINE_CODING, + CONFIG_ACM_COMPLETE, +}; + +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 = make_new_itf(daddr, itf_desc); + TU_VERIFY(p_cdc); + + p_cdc->serial_drid = SERIAL_DRIVER_ACM; + + //------------- Control Interface -------------// + uint8_t const * p_desc = tu_desc_next(itf_desc); + + // Communication Functional Descriptors + while( (p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc)) ) + { + if ( CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc) ) + { + // save ACM bmCapabilities + p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities; + } + + p_desc = tu_desc_next(p_desc); + } + + // Open notification endpoint of control interface if any + if (itf_desc->bNumEndpoints == 1) + { + TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)); + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; + + TU_ASSERT( tuh_edpt_open(daddr, desc_ep) ); + p_cdc->ep_notif = desc_ep->bEndpointAddress; + + p_desc = tu_desc_next(p_desc); + } + + //------------- Data Interface (if any) -------------// + if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && + (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) ) + { + // next to endpoint descriptor + p_desc = tu_desc_next(p_desc); + + // data endpoints expected to be in pairs + TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const *) p_desc)); + } + + TU_LOG_DRV("[%u] CDCh ACM opened\r\n", daddr); + + return true; +} + +static void acm_process_config(tuh_xfer_t* xfer) +{ + uintptr_t const state = xfer->user_data; + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc, ); + + switch(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(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, acm_process_config, + CONFIG_ACM_SET_LINE_CODING), ); + break; + } + #endif + TU_ATTR_FALLTHROUGH; + + 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(acm_set_line_coding(p_cdc, &line_coding, acm_process_config, CONFIG_ACM_COMPLETE), ); + break; + } + #endif + TU_ATTR_FALLTHROUGH; + + case CONFIG_ACM_COMPLETE: + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + TU_LOG_LINE_CODING("CDCh ACM Init ", p_cdc->line_coding); + #endif + #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + TU_LOG_CONTROL_LINE_STATE("CDCh ACM Init", p_cdc->line_state); + #endif + // itf_num+1 to account for data interface as well + set_config_complete(p_cdc, idx, itf_num+1); + break; + + default: break; + } +} + +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_VERIFY(p_cdc->acm_capability.support_line_request); + TU_LOG_DRV("CDC ACM 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 + }; + + 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 ? cdch_internal_control_complete : NULL, // complete_cb is NULL for sync call + .user_data = user_data + }; + + TU_ASSERT(tuh_control_xfer(&xfer)); + return true; +} + +static 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_DRV("CDC ACM Set Line Conding\r\n"); + TU_VERIFY(p_cdc->acm_capability.support_line_request); + + 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 = complete_cb ? cdch_internal_control_complete : NULL, // complete_cb is NULL for sync call + .user_data = user_data + }; + + TU_ASSERT(tuh_control_xfer(&xfer)); + return true; +} + +static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + 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); +} + +//--------------------------------------------------------------------+ +// 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_DRV("FTDI opened\r\n"); + + p_cdc->serial_drid = SERIAL_DRIVER_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_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_ASSERT(false, false); // TODO + return false; +} + +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_DRV("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, + complete_cb ? cdch_internal_control_complete : NULL, user_data)); + 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) +{ + uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate); + TU_LOG_DRV("CDC FTDI Set BaudRate = %lu, divisor = 0x%04x\r\n", baudrate, divisor); + + p_cdc->user_control_cb = complete_cb; + p_cdc->baudrate_requested = baudrate; + TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor, + complete_cb ? cdch_internal_control_complete : NULL, user_data)); + + return true; +} + +static void ftdi_process_config(tuh_xfer_t* xfer) { + uintptr_t const state = xfer->user_data; + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + 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, ftdi_process_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, ftdi_process_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, ftdi_process_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); + #if 0 // TODO set data format + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + TU_LOG_LINE_CODING("CDCh FTDI Init ", p_cdc->line_coding); + #endif + #endif + #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + TU_LOG_CONTROL_LINE_STATE("CDCh FTDI Init ", p_cdc->line_state); + #endif + 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_DRV("CP210x opened\r\n"); + p_cdc->serial_drid = SERIAL_DRIVER_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_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_ASSERT(false, false); // TODO + return false; +} + +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_DRV("CDC CP210x Set BaudRate = %lu\r\n", baudrate); + uint32_t baud_le = tu_htole32(baudrate); + p_cdc->user_control_cb = complete_cb; + return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4, + complete_cb ? cdch_internal_control_complete : NULL, 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_DRV("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, + complete_cb ? cdch_internal_control_complete : NULL, user_data); +} + +static void cp210x_process_config(tuh_xfer_t* xfer) { + uintptr_t const state = xfer->user_data; + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + 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, cp210x_process_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, cp210x_process_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, cp210x_process_config, CONFIG_CP210X_COMPLETE),); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + + case CONFIG_CP210X_COMPLETE: + set_config_complete(p_cdc, idx, itf_num); + #if defined(CFG_TUH_CDC_LINE_CODING_ON_ENUM) && 0 // skip for now + TU_LOG_LINE_CODING("CDCh CP210x Init ", p_cdc->line_coding); + #endif + #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + TU_LOG_CONTROL_LINE_STATE("CDCh CP210x Init ", p_cdc->line_state); + #endif + break; + + default: break; + } +} + +#endif + +//--------------------------------------------------------------------+ +// CH34x (CH340 & CH341) +//--------------------------------------------------------------------+ + +#if CFG_TUH_CDC_CH34X + +#define CH34X_LOGS false + +enum { + CONFIG_CH34X_READ_VERSION = 0, + CONFIG_CH34X_SERIAL_INIT, + CONFIG_CH34X_SPECIAL_REG_WRITE, + CONFIG_CH34X_FLOW_CONTROL, + CONFIG_CH34X_MODEM_CONTROL, + CONFIG_CH34X_COMPLETE +}; + +static bool ch34x_open ( uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len ) +{ + // CH34x Interface includes 1 vendor interface + 3 bulk endpoints + TU_VERIFY ( itf_desc->bNumEndpoints == 3 ); + 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_drid = SERIAL_DRIVER_CH34X; + + // 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), false); + + TU_LOG_DRV("[%u] CDCh CH34x opened\r\n", daddr); + + return true; +} + +static bool ch34x_set_request ( cdch_interface_t* p_cdc, uint8_t direction, uint8_t request, uint16_t value, uint16_t index, + uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data ) +{ + tusb_control_request_t const request_setup = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_DEVICE, + .type = TUSB_REQ_TYPE_VENDOR, + .direction = direction + }, + .bRequest = request, + .wValue = tu_htole16 ( value ), + .wIndex = tu_htole16 ( index ), + .wLength = tu_htole16 ( length ) + }; + + // use usbh enum buf since application variable does not live long enough + uint8_t* enum_buf = NULL; + + if ( buffer && length > 0 ) { + enum_buf = usbh_get_enum_buf(); + tu_memcpy_s ( enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length ); + } + + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request_setup, + .buffer = enum_buf, + .complete_cb = complete_cb, + .user_data = user_data + }; + + return tuh_control_xfer ( &xfer ); +} + +static bool ch34x_control_out ( cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index, tuh_xfer_cb_t complete_cb, uintptr_t user_data ) +{ + return ch34x_set_request ( p_cdc, TUSB_DIR_OUT, request, value, index, /* buffer */ NULL, /* length */ 0, complete_cb, user_data ); +} + +static bool ch34x_control_in ( cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index, + uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data ) +{ + return ch34x_set_request ( p_cdc, TUSB_DIR_IN, request, value, index, buffer, buffersize, complete_cb, user_data ); +} + +static bool ch34x_write_reg ( cdch_interface_t* p_cdc, uint16_t reg, uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data ) +{ + return ch34x_control_out ( p_cdc, CH34X_REQ_WRITE_REG, /* value */ reg, /* index */ value, complete_cb, user_data ); +} + +//static bool ch34x_read_reg_request ( cdch_interface_t* p_cdc, uint16_t reg, +// uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data ) +//{ +// return ch34x_control_in ( p_cdc, CH34X_REQ_READ_REG, reg, /* index */ 0, buffer, buffersize, complete_cb, user_data ); +//} + +uint8_t ch34x_xfer_get_itf_num ( tuh_xfer_t* xfer ) +// CH34x needs a special handling to get bInterfaceNumber, because wIndex is used for other purposes and not for bInterfaceNumber +// CH340 and CH341 derivates have always only one interface, so it's OK to check only daddr +{ + for ( uint8_t i=0; i<CFG_TUH_CDC; i++ ) + { + const cdch_interface_t* p_cdc = &cdch_data[i]; + if ( p_cdc->daddr == xfer->daddr ) return p_cdc->bInterfaceNumber; + } + + return INTERFACE_INVALID_NUMBER; +} + +// internal control complete to update state such as line state, encoding +static void ch34x_control_complete ( tuh_xfer_t* xfer ) +{ + uint8_t const itf_num = ch34x_xfer_get_itf_num ( xfer ); + uint8_t const idx = tuh_cdc_itf_get_index ( xfer->daddr, itf_num ); + cdch_interface_t *p_cdc = get_itf ( idx ); + uint16_t value = tu_le16toh ( xfer->setup->wValue ); + TU_ASSERT ( p_cdc, ); + TU_ASSERT ( p_cdc->serial_drid == SERIAL_DRIVER_CH34X, ); // ch34x_control_complete is only used for CH34x + + if ( xfer->result == XFER_RESULT_SUCCESS ) { + switch ( xfer->setup->bRequest ) { + case CH34X_REQ_WRITE_REG: { // register write request + switch ( value ) { + case ( 0x1312 ): { // baudrate write + p_cdc->line_coding.bit_rate = p_cdc->baudrate_requested; + #if CH34X_LOGS + TU_LOG_BAUDRATE("CDCh CH34x Control Complete ", p_cdc->line_coding.bit_rate); + #endif + break; + } + default: { + TU_ASSERT(false, ); // unexpected register write + break; + } + } + break; + } + case CH34X_REQ_MODEM_CTRL: { // set modem controls RTS/DTR request + if ( ~value & CH34X_BIT_RTS ) + p_cdc->line_state |= CDC_CONTROL_LINE_STATE_RTS; + else + p_cdc->line_state &= ~CDC_CONTROL_LINE_STATE_RTS; + if ( ~value & CH34X_BIT_DTR ) + p_cdc->line_state |= CDC_CONTROL_LINE_STATE_DTR; + else + p_cdc->line_state &= ~CDC_CONTROL_LINE_STATE_DTR; + #if CH34X_LOGS + TU_LOG_CONTROL_LINE_STATE("CDCh CH34x Control Complete ", p_cdc->line_state); + #endif + break; + } + default: { + TU_ASSERT(false, ); // unexpected request + break; + } + } + xfer->complete_cb = p_cdc->user_control_cb; + if (xfer->complete_cb) + xfer->complete_cb(xfer); + } +} + +static bool ch34x_get_factor_divisor ( uint32_t baval, uint8_t *factor, uint8_t *divisor ) +{ // calc baudrate factor and divisor + uint8_t a; + uint8_t b; + uint32_t c; + + switch (baval) { + case 921600: + a = 0xf3; + b = 7; + break; + case 307200: + a = 0xd9; + b = 7; + break; + default: + if (baval > 6000000 / 255) { + b = 3; + c = 6000000; + } else if (baval > 750000 / 255) { + b = 2; + c = 750000; + } else if (baval > 93750 / 255) { + b = 1; + c = 93750; + } else { + b = 0; + c = 11719; + } + a = (unsigned char)(c / baval); + if (a == 0 || a == 0xFF) + return false; + if ((c / a - baval) > (baval - c / (a + 1))) + a++; + a = 256 - a; + break; + } + *factor = a; + *divisor = b; + + return true; +} + +static bool ch34x_get_lcr ( cdc_line_coding_t const* line_coding, uint8_t *lcr ) +{ // calc lcr register value (data bits, parity, stop bits) + *lcr = CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX; + switch ( line_coding->data_bits ) { + case 5: + *lcr |= CH34X_LCR_CS5; + break; + case 6: + *lcr |= CH34X_LCR_CS6; + break; + case 7: + *lcr |= CH34X_LCR_CS7; + break; + case 8: + *lcr |= CH34X_LCR_CS8; + break; + default: + TU_ASSERT ( false, ); // not supported data_bits + *lcr |= CH34X_LCR_CS8; + break; + } + if ( line_coding.parity != CDC_LINE_CODING_PARITY_NONE ) { + *lcr |= CH34X_LCR_ENABLE_PAR; + if ( line_coding.parity == CDC_LINE_CODING_PARITY_EVEN || line_coding.parity == CDC_LINE_CODING_PARITY_SPACE ) + *lcr |= CH34X_LCR_PAR_EVEN; + if ( line_coding.parity == CDC_LINE_CODING_PARITY_MARK || line_coding.parity == CDC_LINE_CODING_PARITY_SPACE ) + *lcr |= CH34X_LCR_MARK_SPACE; + } + TU_ASSERT ( line_coding.stop_bits == CDC_LINE_CODING_STOP_BITS_1 || line_coding.stop_bits == CDC_LINE_CODING_STOP_BITS_2, ); // not supported 1.5 stop bits + if ( line_coding.stop_bits == CDC_LINE_CODING_STOP_BITS_2 ) + *lcr |= CH34X_LCR_STOP_BITS_2; + + return true; +} + +static bool ch34x_set_baudrate ( cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data ) +{ + p_cdc->baudrate_requested = baudrate; + p_cdc->user_control_cb = complete_cb; + uint8_t factor, divisor; + TU_ASSERT ( ch34x_get_factor_divisor ( baudrate, &factor, &divisor ), false ); + TU_ASSERT ( ch34x_write_reg ( p_cdc, /* reg */ 0x1312, /* value */ (uint16_t)factor << 8 | 0x80 | divisor, + complete_cb ? ch34x_control_complete : NULL, user_data ), false ); + return true; +} + +static bool ch34x_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_ASSERT(false, false); // TODO + return false; +} + +static bool ch34x_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; + uint16_t control = 0; + if ( line_state & CDC_CONTROL_LINE_STATE_RTS ) + control |= CH34X_BIT_RTS; + if ( line_state & CDC_CONTROL_LINE_STATE_DTR ) + control |= CH34X_BIT_DTR; + TU_ASSERT ( ch34x_control_out ( p_cdc, /* request */ CH34X_REQ_MODEM_CTRL, /* value */ (uint8_t)~control, + /* index */ 0, complete_cb ? ch34x_control_complete : NULL, user_data ), false ); + return true; +} + +static void ch34x_process_config ( tuh_xfer_t* xfer ) +{ + uint8_t const itf_num = ch34x_xfer_get_itf_num ( xfer ); + uintptr_t const state = xfer->user_data; + uint8_t const idx = tuh_cdc_itf_get_index ( xfer->daddr, itf_num ); + cdch_interface_t *p_cdc = get_itf ( idx ); + cdc_line_coding_t const line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X; + uint8_t buffer[2]; + TU_ASSERT ( p_cdc, ); + + switch ( state ) { + case CONFIG_CH34X_READ_VERSION: // request version read + #if CH34X_LOGS + TU_LOG_DRV ( "[%u] CDCh CH34x Process Config started\r\n", p_cdc->daddr ); + #endif + p_cdc->ch34x.version = 0; + TU_ASSERT ( ch34x_control_in ( p_cdc, /* request */ CH34X_REQ_READ_VERSION, /* value */ 0, + /* index */ 0, buffer, 2, ch34x_process_config, CONFIG_CH34X_SERIAL_INIT ), ); + break; + case CONFIG_CH34X_SERIAL_INIT: { // handle version read data, request to init CH34x with line_coding and baudrate + p_cdc->ch34x.version = xfer->buffer[0]; + #if CH34X_LOGS + TU_LOG_DRV ( "[%u] CDCh CH34x Chip version=%02x\r\n", p_cdc->daddr, p_cdc->ch34x.version ); + #endif + // only versions >= 0x30 are tested, below 0x30 seems having other programming, see WCH vendor, linux kernel and FreeBSD drivers + TU_ASSERT ( p_cdc->ch34x.version >= 0x30, ); + uint8_t factor, divisor; + TU_ASSERT ( ch34x_get_factor_divisor ( line_coding.bit_rate, &factor, &divisor ), ); + TU_ASSERT ( ch34x_control_out ( p_cdc, /* request */ CH34X_REQ_SERIAL_INIT, /* value */ (uint16_t)lcr << 8 | 0x9c, + /* index */ (uint16_t)factor << 8 | 0x80 | divisor, ch34x_process_config, CONFIG_CH34X_SPECIAL_REG_WRITE ), ); + break; + } + case CONFIG_CH34X_SPECIAL_REG_WRITE: // do special reg write, purpose unknown, overtaken from WCH driver + TU_ASSERT ( ch34x_write_reg ( p_cdc, /* reg */ 0x0f2c, /* value */ 0x0007, ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL ), ); + break; + case CONFIG_CH34X_FLOW_CONTROL: // no hardware flow control + TU_ASSERT ( ch34x_write_reg ( p_cdc, /* reg */ 0x2727, /* value */ 0x0000, ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL ), ); + break; + case CONFIG_CH34X_MODEM_CONTROL: // !always! set modem controls RTS/DTR (CH34x has no reset state after CH34X_REQ_SERIAL_INIT) + TU_ASSERT ( ch34x_set_modem_ctrl ( p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ch34x_process_config, CONFIG_CH34X_COMPLETE ), ); + break; + case CONFIG_CH34X_COMPLETE: + p_cdc->line_coding = line_coding; // CONFIG_CH34X_SERIAL_INIT not handled by ch34x_control_complete + #if CH34X_LOGS + TU_LOG_DRV("CDCh CH34x Process Config Complete\r\n"); + TU_LOG_LINE_CODING(" ", p_cdc->line_coding); + TU_LOG_CONTROL_LINE_STATE(" ", p_cdc->line_state); + #endif + set_config_complete ( p_cdc, idx, itf_num ); + break; + default: + TU_ASSERT ( false, ); + break; + } +} + +#endif // CFG_TUH_CDC_CH34X + +#endif // (CFG_TUH_ENABLED && CFG_TUH_CDC) diff --git a/src/class/cdc/cdc_host.h.bak b/src/class/cdc/cdc_host.h.bak new file mode 100644 index 000000000..7b0d2a392 --- /dev/null +++ b/src/class/cdc/cdc_host.h.bak @@ -0,0 +1,207 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_CDC_HOST_H_ +#define _TUSB_CDC_HOST_H_ + +#include "cdc.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Class Driver Configuration +//--------------------------------------------------------------------+ + +// Set Line Control state on enumeration/mounted, refer enums CDC_CONTROL_LINE_STATE_RTS, CDC_CONTROL_LINE_STATE_DTR +#ifndef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM +#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM 0 +#endif + +// Set Line Coding on enumeration/mounted, value for cdc_line_coding_t +//#ifndef CFG_TUH_CDC_LINE_CODING_ON_ENUM +//#define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 } +//#endif + +// RX FIFO size +#ifndef CFG_TUH_CDC_RX_BUFSIZE +#define CFG_TUH_CDC_RX_BUFSIZE USBH_EPSIZE_BULK_MAX +#endif + +// RX Endpoint size +#ifndef CFG_TUH_CDC_RX_EPSIZE +#define CFG_TUH_CDC_RX_EPSIZE USBH_EPSIZE_BULK_MAX +#endif + +// TX FIFO size +#ifndef CFG_TUH_CDC_TX_BUFSIZE +#define CFG_TUH_CDC_TX_BUFSIZE USBH_EPSIZE_BULK_MAX +#endif + +// TX Endpoint size +#ifndef CFG_TUH_CDC_TX_EPSIZE +#define CFG_TUH_CDC_TX_EPSIZE USBH_EPSIZE_BULK_MAX +#endif + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +// Get Interface index from device address + interface number +// return TUSB_INDEX_INVALID_8 (0xFF) if not found +uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num); + +// Get Interface information +// return true if index is correct and interface is currently mounted +bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info); + +// Check if a interface is mounted +bool tuh_cdc_mounted(uint8_t idx); + +// Get current DTR status +bool tuh_cdc_get_dtr(uint8_t idx); + +// Get current RTS status +bool tuh_cdc_get_rts(uint8_t idx); + +// Get current line status +bool tuh_cdc_get_control_line_state(uint8_t idx, uint8_t* line_state); + +// Check if interface is connected (DTR active) +TU_ATTR_ALWAYS_INLINE static inline bool tuh_cdc_connected(uint8_t idx) +{ + return tuh_cdc_get_dtr(idx); +} + +// Get local (saved/cached) version of line coding. +// This function should return correct values if tuh_cdc_set_line_coding() / tuh_cdc_get_line_coding() +// are invoked previously or CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined. +// NOTE: This function does not make any USB transfer request to device. +bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding); + +//--------------------------------------------------------------------+ +// Write API +//--------------------------------------------------------------------+ + +// Get the number of bytes available for writing +uint32_t tuh_cdc_write_available(uint8_t idx); + +// Write to cdc interface +uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize); + +// Force sending data if possible, return number of forced bytes +uint32_t tuh_cdc_write_flush(uint8_t idx); + +// Clear the transmit FIFO +bool tuh_cdc_write_clear(uint8_t idx); + +//--------------------------------------------------------------------+ +// Read API +//--------------------------------------------------------------------+ + +// Get the number of bytes available for reading +uint32_t tuh_cdc_read_available(uint8_t idx); + +// Read from cdc interface +uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize); + +// Get a byte from RX FIFO without removing it +bool tuh_cdc_peek(uint8_t idx, uint8_t* ch); + +// Clear the received FIFO +bool tuh_cdc_read_clear (uint8_t idx); + +//--------------------------------------------------------------------+ +// Control Endpoint (Request) API +// Each Function will make a USB control transfer request to/from device +// - If complete_cb is provided, the function will return immediately and invoke +// the callback when request is complete. +// - If complete_cb is NULL, the function will block until request is complete. +// - In this case, user_data should be pointed to xfer_result_t to hold the transfer result. +// - The function will return true if transfer is successful, false otherwise. +//--------------------------------------------------------------------+ + +// Request to Set Control Line State, refer enums CDC_CONTROL_LINE_STATE_RTS, CDC_CONTROL_LINE_STATE_DTR +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 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) +// Should only use if you don't work with serial devices such as FTDI/CP210x +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 (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 +TU_ATTR_ALWAYS_INLINE static inline +bool tuh_cdc_connect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + return tuh_cdc_set_control_line_state(idx, CDC_CONTROL_LINE_STATE_DTR | CDC_CONTROL_LINE_STATE_RTS, complete_cb, user_data); +} + +// Disconnect by clear both DTR, RTS +TU_ATTR_ALWAYS_INLINE static inline +bool tuh_cdc_disconnect(uint8_t idx, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + return tuh_cdc_set_control_line_state(idx, 0x00, complete_cb, user_data); +} + +//--------------------------------------------------------------------+ +// CDC APPLICATION CALLBACKS +//--------------------------------------------------------------------+ + +// Invoked when a device with CDC interface is mounted +// idx is index of cdc interface in the internal pool. +TU_ATTR_WEAK extern void tuh_cdc_mount_cb(uint8_t idx); + +// Invoked when a device with CDC interface is unmounted +TU_ATTR_WEAK extern void tuh_cdc_umount_cb(uint8_t idx); + +// Invoked when received new data +TU_ATTR_WEAK extern void tuh_cdc_rx_cb(uint8_t idx); + +// Invoked when a TX is complete and therefore space becomes available in TX buffer +TU_ATTR_WEAK extern void tuh_cdc_tx_complete_cb(uint8_t idx); + +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void cdch_init (void); +bool cdch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); +bool cdch_set_config (uint8_t dev_addr, uint8_t itf_num); +bool cdch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes); +void cdch_close (uint8_t dev_addr); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CDC_HOST_H_ */ diff --git a/src/class/cdc/serial/ch34x.h.bak b/src/class/cdc/serial/ch34x.h.bak new file mode 100644 index 000000000..f77434b31 --- /dev/null +++ b/src/class/cdc/serial/ch34x.h.bak @@ -0,0 +1,60 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2023 Heiko Kuester (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _CH34X_H_ +#define _CH34X_H_ + +// set line_coding +#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM +#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH341 CFG_TUH_CDC_LINE_CODING_ON_ENUM +#else // this default is necessary to work properly +#define CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH341 { 9600, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 } +#endif + +// USB requests +#define CH341_REQ_READ_VERSION 0x5F // dec 95 +#define CH341_REQ_WRITE_REG 0x9A // dec 154 +#define CH341_REQ_READ_REG 0x95 // dec 149 +#define CH341_REQ_SERIAL_INIT 0xA1 // dec 161 +#define CH341_REQ_MODEM_CTRL 0xA4 // dev 164 + +// modem control bits +#define CH341_BIT_RTS ( 1 << 6 ) +#define CH341_BIT_DTR ( 1 << 5 ) + +// line control bits +#define CH341_LCR_ENABLE_RX 0x80 +#define CH341_LCR_ENABLE_TX 0x40 +#define CH341_LCR_MARK_SPACE 0x20 +#define CH341_LCR_PAR_EVEN 0x10 +#define CH341_LCR_ENABLE_PAR 0x08 +#define CH341_LCR_STOP_BITS_2 0x04 +#define CH341_LCR_CS8 0x03 +#define CH341_LCR_CS7 0x02 +#define CH341_LCR_CS6 0x01 +#define CH341_LCR_CS5 0x00 + +#endif /* _CH34X_H_ */ |
