From 478361529cb27eaa715ac4ab446040573d24e3bc Mon Sep 17 00:00:00 2001 From: TravisRo Date: Fri, 10 Jun 2022 01:27:07 -0600 Subject: [PATCH 01/15] Add CH341 class driver Add CH341 class driver. This is not really a class but rather CH341 emulation firmware. It allows any supported general purpose microcontroller to appear to the host as a CH341 device (IC) --- src/class/ch341/ch341.h | 95 ++++ src/class/ch341/ch341_device.c | 912 +++++++++++++++++++++++++++++++++ src/class/ch341/ch341_device.h | 295 +++++++++++ 3 files changed, 1302 insertions(+) create mode 100644 src/class/ch341/ch341.h create mode 100644 src/class/ch341/ch341_device.c create mode 100644 src/class/ch341/ch341_device.h diff --git a/src/class/ch341/ch341.h b/src/class/ch341/ch341.h new file mode 100644 index 0000000000..ae81d50714 --- /dev/null +++ b/src/class/ch341/ch341.h @@ -0,0 +1,95 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Portions Copyright (c) 2022 Travis Robinson (libusbdotnet@gmail.com) + * + * 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. + */ + +/** \ingroup group_class + * \defgroup ClassDriver_CH341 Communication Device Class (CH341) + * Currently only Abstract Control Model subclass is supported + * @{ */ + +#ifndef _TUSB_CH341_H__ +#define _TUSB_CH341_H__ + +#include "common/tusb_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup ClassDriver_CH341_Common Common Definitions + * @{ */ + +/// CH341 Pipe ID, used to indicate which pipe the API is addressing to (Notification, Out, In) +typedef enum +{ + CH341_PIPE_DATA_IN, ///< Data in pipe + CH341_PIPE_DATA_OUT, ///< Data out pipe + CH341_PIPE_NOTIFICATION, ///< Notification pipe + CH341_PIPE_ERROR, ///< Invalid Pipe ID +} ch341_pipeid_t; + +typedef struct +{ + uint32_t bit_rate; + uint8_t stop_bits; ///< 0: 1 stop bit - 2: 2 stop bits + uint8_t parity; ///< 0: None - 1: Odd - 2: Even - 3: Mark - 4: Space + uint8_t data_bits; ///< can be 5, 6, 7, 8 + bool tx_en; + bool rx_en; +} ch341_line_coding_t; + +typedef enum +{ + CH341_LINE_STATE_DTR_ACTIVE = (1 << 0), + CH341_LINE_STATE_RTS_ACTIVE = (1 << 1), +} ch341_line_state_t; + +typedef enum +{ + CH341_MODEM_STATE_CTS_ACTIVE = 0x01, + CH341_MODEM_STATE_DSR_ACTIVE = 0x02, + CH341_MODEM_STATE_RNG_ACTIVE = 0x04, + CH341_MODEM_STATE_DCD_ACTIVE = 0x08, + + CH341_MODEM_STATE_ALL = 0x0F, +} ch341_modem_state_t; + +// The CH34x doesn't use any structures so we don't wave anything to pack +// Start of all packed definitions for compiler without per-type packed +/* + TU_ATTR_PACKED_BEGIN + TU_ATTR_BIT_FIELD_ORDER_BEGIN + + TU_ATTR_PACKED_END // End of all packed definitions + TU_ATTR_BIT_FIELD_ORDER_END + */ +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ diff --git a/src/class/ch341/ch341_device.c b/src/class/ch341/ch341_device.c new file mode 100644 index 0000000000..b7f3a0bb30 --- /dev/null +++ b/src/class/ch341/ch341_device.c @@ -0,0 +1,912 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Portions Copyright (c) 2022 Travis Robinson (libusbdotnet@gmail.com) + * + * 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 (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_CH341) + +#include "device/usbd.h" +#include "device/usbd_pvt.h" + +#include "ch341_device.h" +#include + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +////////////////////////////////////////////////////////////////////////////// +// CH341 defines directly from Linux host driver. The CH341 works basically +// the same as a CH340G. In-fact, they share the same driver on Windows. +// These are formatted and laid out much better than the older CH340.c so I'm +// using them even though the descriptors are from a CH340G. +// https://github.com/torvalds/linux/blob/master/drivers/usb/serial/ch341.c +////////////////////////////////////////////////////////////////////////////// + +#define CH341_CLKRATE 48000000.0F + +/* flags for IO-Bits */ +#define CH341_BIT_RTS (1 << 6) +#define CH341_BIT_DTR (1 << 5) + +/******************************/ +/* interrupt pipe definitions */ +/******************************/ +/* always 4 interrupt bytes */ +/* first irq byte normally 0x08 */ +/* second irq byte base 0x7d + below */ +/* third irq byte base 0x94 + below */ +/* fourth irq byte normally 0xee */ + +/* second interrupt byte */ +#define CH341_MULT_STAT 0x04 /* multiple status since last interrupt event */ + +/* status returned in third interrupt answer byte, inverted in data + from irq */ +#define CH341_BIT_CTS 0x01 +#define CH341_BIT_DSR 0x02 +#define CH341_BIT_RI 0x04 +#define CH341_BIT_DCD 0x08 +#define CH341_BITS_MODEM_STAT 0x0f /* all bits */ + +#define CH341_REQ_READ_VERSION 0x5F +#define CH341_REQ_WRITE_REG 0x9A +#define CH341_REQ_READ_REG 0x95 +#define CH341_REQ_SERIAL_INIT 0xA1 +#define CH341_REQ_MODEM_CTRL 0xA4 + +#define CH341_REG_BREAK 0x05 +#define CH341_REG_PRESCALER 0x12 +#define CH341_REG_DIVISOR 0x13 +#define CH341_REG_LCR 0x18 +#define CH341_REG_LCR2 0x25 + +// undocumented register. init val:0 +#define CH341_REG_0x0F 0x0F +// undocumented register. init val:4 +#define CH341_REG_0x2C 0x2C +// undocumented register. init val:0 +#define CH341_REG_0x27 0x27 + +#define CH341_REG_MCR_MSR 0x06 +#define CH341_REG_MCR_MSR2 0x07 + +#define CH341_NBREAK_BITS 0x01 + +#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 + +// The CH340G stores it's data in 8 bit registers. +// REG_DIDX is used as our indicies to emulate these. +typedef enum +{ + REG_DIDX_BREAK, + REG_DIDX_PRESCALER, + REG_DIDX_DIVISOR, + REG_DIDX_LCR, + REG_DIDX_LCR2, + REG_DIDX_MCR_MSR, + REG_DIDX_MCR_MSR2, + REG_DIDX_0x0F, + REG_DIDX_0x2C, + REG_DIDX_0x27, + + REG_DIDX_MAX, +}REG_DIDX; + +typedef struct +{ + uint8_t itf_num; + uint8_t ep_notif; + uint8_t ep_in; + uint8_t ep_out; + + // There is no way to sense if the serial port is closed without using DTR/RTS. + // This driver does not make any assumptions as to how the user might want to + // use signals hence we do not do this. + bool connected; + bool line_coding_changed; + bool line_state_changed; + bool break_state_changed; + + /*------------- From this point, data is not cleared by bus reset -------------*/ + char wanted_char; + ch341_line_coding_t line_coding; + ch341_line_state_t line_state; + + // FIFO + tu_fifo_t rx_ff; + tu_fifo_t tx_ff; + tu_fifo_t txnotify_ff; + + uint8_t rx_ff_buf[CFG_TUD_CH341_RX_BUFSIZE]; + uint8_t tx_ff_buf[CFG_TUD_CH341_TX_BUFSIZE]; + uint8_t txnotify_ff_buf[CFG_TUD_CH341_TXNOTIFY_BUFSIZE]; + +#if CFG_FIFO_MUTEX + osal_mutex_def_t rx_ff_mutex; + osal_mutex_def_t tx_ff_mutex; + osal_mutex_def_t txnotify_ff_mutex; +#endif + + // Endpoint 0 Transfer buffer + // Note that we are NOT using CFG_TUD_ENDPOINT0_SIZE here! This is because + // this is merely a temporary buffer we use to send data to the host and + // we never send more than 2 bytes. + CFG_TUSB_MEM_ALIGN uint8_t ep0_in_buf[4]; + + // Endpoint Transfer buffer + CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CH341_EP_BUFSIZE]; + CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CH341_EP_BUFSIZE]; + + uint8_t register_data[REG_DIDX_MAX]; + +}ch341d_interface_t; + + +#define ITF_MEM_RESET_SIZE offsetof(ch341d_interface_t, wanted_char) + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ + +// The CH341 cannot coexist with other interfaces so there can be only 1. +// This is because it sends vendor class control messages directly to the +// device asnd uses wIndex of the control packet to transfer vendor +// specific data. +CFG_TUSB_MEM_SECTION static ch341d_interface_t _ch341d_itf[CFG_TUD_CH341]; + + +const static uint32_t ch341_known_baud_rates[] = { 50, 75, 100, 110, 150, 300, 600, 900, 1200, 1800, 2400, 3600, 4800, 9600, 14400, 19200, 28800, 33600, 38400, 56000, 57600, 76800, 115200, 128000, 153600, 230400, 460800, 921600, 1500000, 2000000}; +static inline uint16_t __get_unaligned_le16(const uint8_t *p) +{ + return p[0] | p[1] << 8; +} + +static inline void __put_unaligned_le16(uint16_t val, uint8_t *p) +{ + *p++ = val; + *p++ = val >> 8; +} + +static inline void ch341_decode_bit_rate(ch341d_interface_t *p_ch341) +{ + uint16_t baud_div = 0x100 - p_ch341->register_data[REG_DIDX_DIVISOR]; + uint8_t baud_fact = (p_ch341->register_data[REG_DIDX_PRESCALER] >> 2) & 0x01; + uint8_t baud_ps = p_ch341->register_data[REG_DIDX_PRESCALER] & 0x03; + TU_LOG1("ch341_decode_bit_rate: baud_div=%u baud_fact=%u baud_ps=%u\r\n", baud_div, baud_fact, baud_ps); + uint32_t calc_bit_rate = (uint32_t)lroundf(CH341_CLKRATE / (pow(2.0F, (12.0F - 3.0F * (float)baud_ps - (float)baud_fact)) * (float)baud_div)); + int index; + + for (index = 0; index < (sizeof(ch341_known_baud_rates) / sizeof(uint32_t)); index++) + { + int max_diff = lroundf((float)ch341_known_baud_rates[index] * 0.002F); + if (calc_bit_rate >= ch341_known_baud_rates[index] - max_diff && calc_bit_rate <= ch341_known_baud_rates[index] + max_diff) + { + p_ch341->line_coding.bit_rate = ch341_known_baud_rates[index]; + return; + } + if (calc_bit_rate < ch341_known_baud_rates[index]) + break; + } + p_ch341->line_coding.bit_rate = calc_bit_rate; +} + +static inline void ch341_decode_lcr(ch341d_interface_t *p_ch341) +{ + // decode rx/tx enable + uint8_t lcr = p_ch341->register_data[REG_DIDX_LCR]; + TU_LOG1("ch341_decode_lcr: lcr=%02X\r\n", lcr); + p_ch341->line_coding.tx_en = (lcr & CH341_LCR_ENABLE_TX) ? true : false; + p_ch341->line_coding.rx_en = (lcr & CH341_LCR_ENABLE_RX) ? true : false; + + // decode parity (0=None, 1=Odd, 2=Even, 3=Mark, 4=Space) + if (lcr & CH341_LCR_ENABLE_PAR) + { + if ((lcr & (CH341_LCR_PAR_EVEN)) == 0) + p_ch341->line_coding.parity = 1; // Odd + else + p_ch341->line_coding.parity = 2; // Even + + if ((lcr & (CH341_LCR_MARK_SPACE))) + p_ch341->line_coding.parity+=2; + } + else + { + p_ch341->line_coding.parity = 0; // None + } + + // decode stop bits (0=1 stop bit, 2=2 stop bits) + p_ch341->line_coding.stop_bits = (lcr & CH341_LCR_STOP_BITS_2) ? 2 : 0; + + // decode data bits + if ((lcr & 0x3) == CH341_LCR_CS5) + p_ch341->line_coding.data_bits = 5; + else if ((lcr & 0x3) == CH341_LCR_CS6) + p_ch341->line_coding.data_bits = 6; + else if ((lcr & 0x3) == CH341_LCR_CS7) + p_ch341->line_coding.data_bits = 7; + else + p_ch341->line_coding.data_bits = 8; +} + +static inline ch341_line_state_t ch341_decode_mcr(ch341d_interface_t *p_ch341, uint16_t mcr) +{ + p_ch341->register_data[REG_DIDX_MCR_MSR] = (p_ch341->register_data[REG_DIDX_MCR_MSR] & (~(CH341_BIT_DTR | CH341_BIT_RTS))) | (mcr & (CH341_BIT_DTR | CH341_BIT_RTS)); + + ch341_line_state_t line_state = 0; + if (mcr & CH341_BIT_DTR) + line_state &= ~CH341_LINE_STATE_DTR_ACTIVE; + else + line_state |= CH341_LINE_STATE_DTR_ACTIVE; + + if (mcr & CH341_BIT_RTS) + line_state &= ~CH341_LINE_STATE_RTS_ACTIVE; + else + line_state |= CH341_LINE_STATE_RTS_ACTIVE; + + return line_state; +} + +static inline void ch341_write_regs(ch341d_interface_t *p_ch341, uint16_t wValue, uint16_t wIndex) +{ + int i; + + for (i = 0; i < 2; i++) + { + uint8_t reg = wValue & 0xFF; + uint8_t regval = wIndex & 0xFF; + TU_LOG1("ch341_write_regs: %02X=%02X\r\n", reg, regval); + + switch (reg) + { + case CH341_REG_BREAK: + p_ch341->register_data[REG_DIDX_BREAK] = regval; + p_ch341->break_state_changed = true; + break; + case CH341_REG_DIVISOR: + p_ch341->register_data[REG_DIDX_DIVISOR] = regval; + p_ch341->line_coding_changed = true; + break; + case CH341_REG_LCR: + p_ch341->register_data[REG_DIDX_LCR] = regval; + p_ch341->line_coding_changed = true; + break; + case CH341_REG_LCR2: + p_ch341->register_data[REG_DIDX_LCR2] = regval; + break; + case CH341_REG_PRESCALER: + regval &= 0x7; + p_ch341->register_data[REG_DIDX_PRESCALER] = regval; + p_ch341->line_coding_changed = true; + break; + case CH341_REG_0x0F: + p_ch341->register_data[REG_DIDX_0x0F] = regval; + break; + case CH341_REG_0x27: + p_ch341->register_data[REG_DIDX_0x27] = regval; + break; + case CH341_REG_0x2C: + p_ch341->register_data[REG_DIDX_0x2C] = regval; + break; + default: + break; + } + + wValue >>= 8; + wIndex >>= 8; + } +} + +static inline void ch341_read_regs(ch341d_interface_t *p_ch341, uint8_t* data, uint16_t wValue) +{ + int i; + + for (i = 0; i < 2; i++) + { + uint8_t reg = wValue & 0xFF; + TU_LOG1("ch341_read_regs: %02X\r\n", reg); + switch (wValue & 0xFF) + { + case CH341_REG_BREAK: + data[i] = p_ch341->register_data[REG_DIDX_BREAK]; + break; + case CH341_REG_DIVISOR: + data[i] = p_ch341->register_data[REG_DIDX_DIVISOR]; + break; + case CH341_REG_LCR: + data[i] = p_ch341->register_data[REG_DIDX_LCR]; + break; + case CH341_REG_LCR2: + data[i] = p_ch341->register_data[REG_DIDX_LCR2]; + break; + case CH341_REG_PRESCALER: + data[i] = p_ch341->register_data[REG_DIDX_PRESCALER]; + break; + case CH341_REG_0x0F: + data[i] = p_ch341->register_data[REG_DIDX_0x0F]; + break; + case CH341_REG_0x27: + data[i] = p_ch341->register_data[REG_DIDX_0x27]; + break; + case CH341_REG_0x2C: + data[i] = p_ch341->register_data[REG_DIDX_0x2C]; + break; + case CH341_REG_MCR_MSR: + data[i] = p_ch341->register_data[REG_DIDX_MCR_MSR]; + break; + case CH341_REG_MCR_MSR2: + data[i] = p_ch341->register_data[REG_DIDX_MCR_MSR2]; + break; + default: + break; + } + wValue >>= 8; + } +} + +static void _prep_out_transaction (ch341d_interface_t* p_ch341) +{ + uint8_t const rhport = TUD_OPT_RHPORT; + uint16_t available = tu_fifo_remaining(&p_ch341->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_ch341->epout_buf), ); + + // claim endpoint + TU_VERIFY(usbd_edpt_claim(rhport, p_ch341->ep_out), ); + + // fifo can be changed before endpoint is claimed + available = tu_fifo_remaining(&p_ch341->rx_ff); + + if ( available >= sizeof(p_ch341->epout_buf) ) + { + usbd_edpt_xfer(rhport, p_ch341->ep_out, p_ch341->epout_buf, sizeof(p_ch341->epout_buf)); + } + else + { + // Release endpoint since we don't make any transfer + usbd_edpt_release(rhport, p_ch341->ep_out); + } +} + +//--------------------------------------------------------------------+ +// APPLICATION API +//--------------------------------------------------------------------+ +bool tud_ch341_n_connected(uint8_t itf) +{ + return tud_ready() && _ch341d_itf[itf].connected; +} + +ch341_line_state_t tud_ch341_n_get_line_state (uint8_t itf) +{ + return _ch341d_itf[itf].line_state; +} + +uint32_t tud_ch341_n_set_modem_state(uint8_t itf, ch341_modem_state_t modem_states) +{ + ch341d_interface_t *p_ch341 = &_ch341d_itf[itf]; + uint8_t buffer[4]; + + modem_states &= CH341_MODEM_STATE_ALL; + + // FIXME? I beleive these signals are all active=low. I can only test the CTS line with + // my CH340G breakout board and I know that with nothing connected or the CTS line shorted + // to positive, the value transferred is 0xF (all 1's). With the CTS line shorted to ground, + // the value is 0xE (all 1's except CTS) + modem_states = (~modem_states) & CH341_MODEM_STATE_ALL; + + p_ch341->register_data[REG_DIDX_MCR_MSR] = (p_ch341->register_data[REG_DIDX_MCR_MSR] & 0xF0) | (modem_states); + return 1; + + buffer[0] = 0x08; + + // This is sometimes 0x3F and other times 0xBF but I beleieve that's because when I short + // CTS to ground there is no de-bounce so it flickers. + buffer[1] = 0x3F; + + buffer[2] = 0x90 | modem_states; + + // Register MCR_MSR2 is presumably for future use as it's always 0xEE but I beleive it to be + // for additional mcr/msr status. + buffer[3] = p_ch341->register_data[REG_DIDX_MCR_MSR2]; + + // write fifo + uint16_t ret = tu_fifo_write_n(&p_ch341->txnotify_ff, buffer, 4); + + if (ret == 4) + { + // start transfer + ret = tud_ch341_n_notify_flush(itf); + } + return ret; +} + +ch341_modem_state_t tud_ch341_n_get_modem_state(uint8_t itf) +{ + ch341d_interface_t *p_ch341 = &_ch341d_itf[itf]; + ch341_modem_state_t modem_states = p_ch341->register_data[REG_DIDX_MCR_MSR]; + modem_states = (~modem_states) & CH341_MODEM_STATE_ALL; + + return modem_states; +} + +void tud_ch341_n_get_line_coding (uint8_t itf, ch341_line_coding_t* coding) +{ + (*coding) = _ch341d_itf[itf].line_coding; +} + +void tud_ch341_n_set_wanted_char (uint8_t itf, char wanted) +{ + _ch341d_itf[itf].wanted_char = wanted; +} + + +//--------------------------------------------------------------------+ +// READ API +//--------------------------------------------------------------------+ +uint32_t tud_ch341_n_available(uint8_t itf) +{ + return tu_fifo_count(&_ch341d_itf[itf].rx_ff); +} + +uint32_t tud_ch341_n_read(uint8_t itf, void* buffer, uint32_t bufsize) +{ + ch341d_interface_t* p_ch341 = &_ch341d_itf[itf]; + uint32_t num_read = tu_fifo_read_n(&p_ch341->rx_ff, buffer, bufsize); + _prep_out_transaction(p_ch341); + return num_read; +} + +bool tud_ch341_n_peek(uint8_t itf, uint8_t* chr) +{ + return tu_fifo_peek(&_ch341d_itf[itf].rx_ff, chr); +} + +void tud_ch341_n_read_flush (uint8_t itf) +{ + ch341d_interface_t* p_ch341 = &_ch341d_itf[itf]; + tu_fifo_clear(&p_ch341->rx_ff); + _prep_out_transaction(p_ch341); +} + +//--------------------------------------------------------------------+ +// WRITE API +//--------------------------------------------------------------------+ +uint32_t tud_ch341_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) +{ + ch341d_interface_t* p_ch341 = &_ch341d_itf[itf]; + uint16_t ret = tu_fifo_write_n(&p_ch341->tx_ff, buffer, bufsize); + + // flush if queue more than packet size + if (tu_fifo_count(&p_ch341->tx_ff) >= CFG_TUD_CH341_EP_BUFSIZE ) + { + tud_ch341_n_write_flush(itf); + } + + return ret; +} + +uint32_t tud_ch341_n_write_flush (uint8_t itf) +{ + ch341d_interface_t* p_ch341 = &_ch341d_itf[itf]; + + // Skip if usb is not ready yet + TU_VERIFY( tud_ready(), 0 ); + + // No data to send + if ( !tu_fifo_count(&p_ch341->tx_ff) ) return 0; + + uint8_t const rhport = TUD_OPT_RHPORT; + + // Claim the endpoint + TU_VERIFY( usbd_edpt_claim(rhport, p_ch341->ep_in), 0 ); + + // Pull data from FIFO + uint16_t const count = tu_fifo_read_n(&p_ch341->tx_ff, p_ch341->epin_buf, sizeof(p_ch341->epin_buf)); + + if ( count ) + { + TU_ASSERT( usbd_edpt_xfer(rhport, p_ch341->ep_in, p_ch341->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_ch341->ep_in); + return 0; + } +} +uint32_t tud_ch341_n_notify_flush(uint8_t itf) +{ + ch341d_interface_t *p_ch341 = &_ch341d_itf[itf]; + + // Skip if usb is not ready yet + TU_VERIFY(tud_ready(), 0); + + // No data to send + if (!tu_fifo_count(&p_ch341->txnotify_ff)) + return 0; + + uint8_t const rhport = TUD_OPT_RHPORT; + + // Claim the endpoint + TU_VERIFY(usbd_edpt_claim(rhport, p_ch341->ep_notif), 0); + + // Pull data from FIFO + uint16_t const count = tu_fifo_read_n(&p_ch341->txnotify_ff, p_ch341->txnotify_ff_buf, sizeof(p_ch341->txnotify_ff_buf)); + + if (count) + { + TU_ASSERT(usbd_edpt_xfer(rhport, p_ch341->ep_notif, p_ch341->txnotify_ff_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_ch341->ep_notif); + return 0; + } +} + +uint32_t tud_ch341_n_write_available (uint8_t itf) +{ + return tu_fifo_remaining(&_ch341d_itf[itf].tx_ff); +} + +bool tud_ch341_n_write_clear (uint8_t itf) +{ + return tu_fifo_clear(&_ch341d_itf[itf].tx_ff); +} + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ +void ch341d_init(void) +{ + TU_LOG1("ch341d_init:begin\r\n"); + tu_memclr(_ch341d_itf, sizeof(_ch341d_itf)); + + for(uint8_t i=0; iwanted_char = -1; + + // default line coding is : stop bit = 1, parity = none, data bits = 8 + p_ch341->line_coding.bit_rate = 115200; + p_ch341->line_coding.stop_bits = 0; + p_ch341->line_coding.parity = 0; + p_ch341->line_coding.data_bits = 8; + p_ch341->line_coding.rx_en = false; + p_ch341->line_coding.tx_en = false; + + p_ch341->line_state = 0; + + p_ch341->register_data[REG_DIDX_0x0F] = 0xD2; + p_ch341->register_data[REG_DIDX_0x27] = 0x00; + p_ch341->register_data[REG_DIDX_0x2C] = 0x0B; + p_ch341->register_data[REG_DIDX_BREAK] = 0xBF; + p_ch341->register_data[REG_DIDX_DIVISOR] = 0xCC; + p_ch341->register_data[REG_DIDX_LCR] = 0xC3; + p_ch341->register_data[REG_DIDX_LCR2] = 0x00; + p_ch341->register_data[REG_DIDX_PRESCALER] = 0x03; + p_ch341->register_data[REG_DIDX_MCR_MSR] = 0xFF; + p_ch341->register_data[REG_DIDX_MCR_MSR2] = 0xEE; + + // Config RX fifo + tu_fifo_config(&p_ch341->rx_ff, p_ch341->rx_ff_buf, TU_ARRAY_SIZE(p_ch341->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_ch341->tx_ff, p_ch341->tx_ff_buf, TU_ARRAY_SIZE(p_ch341->tx_ff_buf), 1, true); + + // Config TX notify 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_ch341->txnotify_ff, p_ch341->txnotify_ff_buf, TU_ARRAY_SIZE(p_ch341->txnotify_ff_buf), 1, true); + +#if CFG_FIFO_MUTEX + tu_fifo_config_mutex(&p_ch341->rx_ff, NULL, osal_mutex_create(&p_ch341->rx_ff_mutex)); + tu_fifo_config_mutex(&p_ch341->tx_ff, osal_mutex_create(&p_ch341->tx_ff_mutex), NULL); + tu_fifo_config_mutex(&p_ch341->txnotify_ff, osal_mutex_create(&p_ch341->txnotify_ff_mutex), NULL); +#endif + } + + TU_LOG1("ch341d_init:end\r\n"); +} + +void ch341d_reset(uint8_t rhport) +{ + (void) rhport; + + for(uint8_t i=0; irx_ff); + tu_fifo_clear(&p_ch341->tx_ff); + tu_fifo_clear(&p_ch341->txnotify_ff); + tu_fifo_set_overwritable(&p_ch341->tx_ff, false); + tu_fifo_set_overwritable(&p_ch341->txnotify_ff, false); + } +} + +uint16_t ch341d_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) +{ + // CH340G interface class, subclass, and protocol + TU_VERIFY(0xFF == itf_desc->bInterfaceClass && + 0x01 == itf_desc->bInterfaceSubClass && + 0x02 == itf_desc->bInterfaceProtocol, 0); + + // Find available interface + ch341d_interface_t * p_ch341 = NULL; + for(uint8_t ch341_id=0; ch341_iditf_num = itf_desc->bInterfaceNumber; + + uint16_t drv_len = sizeof(tusb_desc_interface_t); + uint8_t const * p_desc = tu_desc_next( itf_desc ); + while (p_desc && drv_len < max_len) + { + TU_LOG1("ch341d_open:drv_len=%u, max_len=%u\r\n", drv_len, max_len); + if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) + { + tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *)p_desc; + if (desc_ep->bmAttributes.xfer == TUSB_XFER_BULK) + { + if (p_ch341->ep_in == 0) + { + // Open endpoint pair + TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_ch341->ep_out, &p_ch341->ep_in), 0); + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + } + else if (desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT) + { + if (p_ch341->ep_notif == 0) + { + // Open notification endpoint + TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0); + p_ch341->ep_notif = desc_ep->bEndpointAddress; + } + } + } + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + + // Prepare for incoming data + _prep_out_transaction(p_ch341); + + TU_LOG1("ch341d_open:end\r\n"); + return drv_len; +} + +// The CH341 driver sends vendor requests only. we will pipe these into the ch341d_control_xfer_cb so it can handle everything +bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) +{ + return ch341d_control_xfer_cb(rhport, stage, request); +} + +// 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 ch341d_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) +{ + + uint8_t itf = 0; + ch341d_interface_t* p_ch341 = _ch341d_itf; + + if ((request->bmRequestType_bit.type) == TUSB_REQ_TYPE_VENDOR && ((request->bmRequestType_bit.recipient) == TUSB_REQ_RCPT_DEVICE)) + { + switch(request->bRequest) + { + case CH341_REQ_READ_VERSION: // (0x5F) + if (stage == CONTROL_STAGE_SETUP) + { + p_ch341->ep0_in_buf[0] = 0x31; + p_ch341->ep0_in_buf[1] = 0; + tud_control_xfer(rhport, request, p_ch341->ep0_in_buf, 2); + } + break; + + case CH341_REQ_READ_REG: // (0x95) + if (stage == CONTROL_STAGE_SETUP) + { + ch341_read_regs(p_ch341, p_ch341->ep0_in_buf, request->wValue); + tud_control_xfer(rhport, request, p_ch341->ep0_in_buf, 2); + } + break; + + case CH341_REQ_WRITE_REG: // (0x9A) + if (stage == CONTROL_STAGE_SETUP) + { + tud_control_status(rhport, request); + } + else if (stage == CONTROL_STAGE_ACK) + { + ch341_write_regs(p_ch341, request->wValue, request->wIndex); + } + break; + + + case CH341_REQ_SERIAL_INIT: // (0xA1) + if (stage == CONTROL_STAGE_SETUP) + { + tud_control_status(rhport, request); + } + else if (stage == CONTROL_STAGE_ACK) + { + // wValue = LCR/LCR2 + // wIndex = BAUDDIV/PRESCALAR + if (request->wValue && request->wIndex) + { + ch341_write_regs(p_ch341, CH341_REG_LCR << 8 | CH341_REG_LCR2, request->wValue); + ch341_write_regs(p_ch341, CH341_REG_DIVISOR << 8 | CH341_REG_PRESCALER, request->wIndex); + p_ch341->line_state_changed = true; + p_ch341->connected = true; + } + } + break; + + case CH341_REQ_MODEM_CTRL: // (0xA4) + if (stage == CONTROL_STAGE_SETUP) + { + tud_control_status(rhport, request); + } + else if (stage == CONTROL_STAGE_ACK) + { + p_ch341->line_state = ch341_decode_mcr(p_ch341, request->wValue); + TU_LOG1("CH341_REQ_MODEM_CTRL DTR=%u RTS=%u\r\n", (p_ch341->line_state & CH341_LINE_STATE_BIT_DTR) ? 1 : 0, (p_ch341->line_state & CH341_LINE_STATE_BIT_RTS) ? 1 : 0); + p_ch341->line_state_changed = true; + } + break; + + default: + TU_LOG1("ch341d: bRequest=%02X wValue=%04X wIndex=%04X wLength=%u\r\n", request->bRequest, request->wValue, request->wIndex, request->wLength); + return false; + } + + if (p_ch341->line_state_changed) + { + p_ch341->line_state_changed = false; + if (tud_ch341_line_state_cb) + tud_ch341_line_state_cb(itf, p_ch341->line_state); + } + if (p_ch341->line_coding_changed) + { + p_ch341->line_coding_changed = false; + ch341_decode_bit_rate(p_ch341); + ch341_decode_lcr(p_ch341); + if (tud_ch341_line_coding_cb) + tud_ch341_line_coding_cb(itf, &p_ch341->line_coding); + TU_LOG1("BPS:%lu P:%u D:%u S:%u RX:%d, TX:%d\r\n", p_ch341->line_coding.bit_rate, p_ch341->line_coding.parity, p_ch341->line_coding.data_bits, p_ch341->line_coding.stop_bits, p_ch341->line_coding.rx_en, p_ch341->line_coding.tx_en); + } + if (p_ch341->break_state_changed) + { + p_ch341->break_state_changed = false; + if (tud_ch341_send_break_cb) + tud_ch341_send_break_cb(itf, p_ch341->register_data[REG_DIDX_BREAK]); + } + + return true; + } + TU_LOG1("ch341d: bRequest=%02X wValue=%04X wIndex=%04X wLength=%u\r\n", request->bRequest, request->wValue, request->wIndex, request->wLength); + return false; +} + +bool ch341d_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void) result; + + uint8_t itf; + ch341d_interface_t* p_ch341; + + // Identify which interface to use + for (itf = 0; itf < CFG_TUD_CH341; itf++) + { + p_ch341 = &_ch341d_itf[itf]; + if ( ( ep_addr == p_ch341->ep_out ) || ( ep_addr == p_ch341->ep_in ) ) break; + } + TU_ASSERT(itf < CFG_TUD_CH341); + + // Received new data + if ( ep_addr == p_ch341->ep_out ) + { + tu_fifo_write_n(&p_ch341->rx_ff, &p_ch341->epout_buf, xferred_bytes); + + // Check for wanted char and invoke callback if needed + if ( tud_ch341_rx_wanted_cb && (((signed char) p_ch341->wanted_char) != -1) ) + { + for ( uint32_t i = 0; i < xferred_bytes; i++ ) + { + if ( (p_ch341->wanted_char == p_ch341->epout_buf[i]) && !tu_fifo_empty(&p_ch341->rx_ff) ) + { + tud_ch341_rx_wanted_cb(itf, p_ch341->wanted_char); + } + } + } + + // invoke receive callback (if there is still data) + if (tud_ch341_rx_cb && !tu_fifo_empty(&p_ch341->rx_ff) ) tud_ch341_rx_cb(itf); + + // prepare for OUT transaction + _prep_out_transaction(p_ch341); + } + + // 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_ch341->ep_in ) + { + // invoke transmit callback to possibly refill tx fifo + if ( tud_ch341_tx_complete_cb ) tud_ch341_tx_complete_cb(itf); + + if ( 0 == tud_ch341_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_ch341->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (CFG_TUD_CH341_EP_BUFSIZE-1))) ) + { + if ( usbd_edpt_claim(rhport, p_ch341->ep_in) ) + { + usbd_edpt_xfer(rhport, p_ch341->ep_in, NULL, 0); + } + } + } + } + + return true; +} + +#endif diff --git a/src/class/ch341/ch341_device.h b/src/class/ch341/ch341_device.h new file mode 100644 index 0000000000..c5fd6b0a69 --- /dev/null +++ b/src/class/ch341/ch341_device.h @@ -0,0 +1,295 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Portions Copyright (c) 2022 Travis Robinson (libusbdotnet@gmail.com) + * + * 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_CH341_DEVICE_H_ +#define _TUSB_CH341_DEVICE_H_ + +#include "common/tusb_common.h" +#include "ch341.h" + +//////////////////////////////////////////////////////////////////////////////// +// TR 06-09-22 NOTE: /////////////////////////////////////////////////////////// +// I've left the "_n_" functions in to maintain some API compatibility with the +// CDC implementation but there is no way to have more than one CH341 interface +// due to the nature of how host and device communicate. +//////////////////////////////////////////////////////////////////////////////// + +//--------------------------------------------------------------------+ +// Class Driver Configuration +//--------------------------------------------------------------------+ +#if !defined(CFG_TUD_CH341_EP_BUFSIZE) && defined(CFG_TUD_CH341_EPSIZE) + #warning CFG_TUD_CH341_EPSIZE is renamed to CFG_TUD_CH341_EP_BUFSIZE, please update to use the new name + #define CFG_TUD_CH341_EP_BUFSIZE CFG_TUD_CH341_EPSIZE +#endif + +#ifndef CFG_TUD_CH341_EP_BUFSIZE + #define CFG_TUD_CH341_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/** \addtogroup CH341_Serial Serial + * @{ + * \defgroup CH341_Serial_Device Device + * @{ */ + +//--------------------------------------------------------------------+ +// Application API (Multiple Ports) +// CFG_TUD_CH341 > 1 +//--------------------------------------------------------------------+ + +// Check if terminal is connected to this port +bool tud_ch341_n_connected (uint8_t itf); + +// Get current line state. Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send) +ch341_line_state_t tud_ch341_n_get_line_state (uint8_t itf); + +// Get current line encoding: bit rate, stop bits parity etc .. +void tud_ch341_n_get_line_coding (uint8_t itf, ch341_line_coding_t* coding); + +// Set special character that will trigger tud_ch341_rx_wanted_cb() callback on receiving +void tud_ch341_n_set_wanted_char (uint8_t itf, char wanted); + +// Get the number of bytes available for reading +uint32_t tud_ch341_n_available (uint8_t itf); + +// Read received bytes +uint32_t tud_ch341_n_read (uint8_t itf, void* buffer, uint32_t bufsize); + +// Read a byte, return -1 if there is none +static inline +int32_t tud_ch341_n_read_char (uint8_t itf); + +// Clear the received FIFO +void tud_ch341_n_read_flush (uint8_t itf); + +// Get a byte from FIFO at the specified position without removing it +bool tud_ch341_n_peek (uint8_t itf, uint8_t* ui8); + +// Write bytes to TX FIFO, data may remain in the FIFO for a while +uint32_t tud_ch341_n_write (uint8_t itf, void const* buffer, uint32_t bufsize); + +// Write a byte +static inline +uint32_t tud_ch341_n_write_char (uint8_t itf, char ch); + +// Write a null-terminated string +static inline +uint32_t tud_ch341_n_write_str (uint8_t itf, char const* str); + +// Force sending data if possible, return number of forced bytes +uint32_t tud_ch341_n_write_flush (uint8_t itf); + +// Return the number of bytes (characters) available for writing to TX FIFO buffer in a single n_write operation. +uint32_t tud_ch341_n_write_available (uint8_t itf); + +// Clear the transmit FIFO +bool tud_ch341_n_write_clear (uint8_t itf); + +// Send line events to host. (IE: CTS, DSR, RI, DCD) +uint32_t tud_ch341_n_set_modem_state(uint8_t itf, ch341_modem_state_t modem_states); + +ch341_modem_state_t tud_ch341_n_get_modem_state(uint8_t itf); + +uint32_t tud_ch341_n_set_break(uint8_t itf, bool on); + +uint32_t tud_ch341_n_notify_flush(uint8_t itf); + +//--------------------------------------------------------------------+ +// Application API (Single Port) +//--------------------------------------------------------------------+ +static inline bool tud_ch341_connected (void); +static inline ch341_line_state_t tud_ch341_get_line_state (void); +static inline void tud_ch341_get_line_coding (ch341_line_coding_t* coding); +static inline void tud_ch341_set_wanted_char (char wanted); + +static inline uint32_t tud_ch341_available (void); +static inline int32_t tud_ch341_read_char (void); +static inline uint32_t tud_ch341_read (void* buffer, uint32_t bufsize); +static inline void tud_ch341_read_flush (void); +static inline bool tud_ch341_peek (uint8_t* ui8); + +static inline uint32_t tud_ch341_write_char (char ch); +static inline uint32_t tud_ch341_write (void const* buffer, uint32_t bufsize); +static inline uint32_t tud_ch341_write_str (char const* str); +static inline uint32_t tud_ch341_write_flush (void); +static inline uint32_t tud_ch341_write_available (void); +static inline bool tud_ch341_write_clear (void); +static inline uint32_t tud_ch341_set_modem_state(ch341_modem_state_t modem_states); +static inline ch341_modem_state_t tud_ch341_get_modem_state(void); +static inline uint32_t tud_ch341_notify_flush(void); + +//--------------------------------------------------------------------+ +// Application Callback API (weak is optional) +//--------------------------------------------------------------------+ + +// Invoked when received new data +TU_ATTR_WEAK void tud_ch341_rx_cb(uint8_t itf); + +// Invoked when received `wanted_char` +TU_ATTR_WEAK void tud_ch341_rx_wanted_cb(uint8_t itf, char wanted_char); + +// Invoked when space becomes available in TX buffer +TU_ATTR_WEAK void tud_ch341_tx_complete_cb(uint8_t itf); + +// Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE +TU_ATTR_WEAK void tud_ch341_line_state_cb(uint8_t itf, ch341_line_state_t line_state); + +// Invoked when line coding is change via SET_LINE_CODING +TU_ATTR_WEAK void tud_ch341_line_coding_cb(uint8_t itf, ch341_line_coding_t const* p_line_coding); + +// Invoked when received send break +TU_ATTR_WEAK void tud_ch341_send_break_cb(uint8_t itf, bool is_break_active); + +//--------------------------------------------------------------------+ +// Inline Functions +//--------------------------------------------------------------------+ +static inline int32_t tud_ch341_n_read_char (uint8_t itf) +{ + uint8_t ch; + return tud_ch341_n_read(itf, &ch, 1) ? (int32_t) ch : -1; +} + +static inline uint32_t tud_ch341_n_write_char(uint8_t itf, char ch) +{ + return tud_ch341_n_write(itf, &ch, 1); +} + +static inline uint32_t tud_ch341_n_write_str (uint8_t itf, char const* str) +{ + return tud_ch341_n_write(itf, str, strlen(str)); +} + +static inline bool tud_ch341_connected (void) +{ + return tud_ch341_n_connected(0); +} + +static inline ch341_line_state_t tud_ch341_get_line_state (void) +{ + return tud_ch341_n_get_line_state(0); +} + +static inline void tud_ch341_get_line_coding (ch341_line_coding_t* coding) +{ + tud_ch341_n_get_line_coding(0, coding); +} + +static inline void tud_ch341_set_wanted_char (char wanted) +{ + tud_ch341_n_set_wanted_char(0, wanted); +} + +static inline uint32_t tud_ch341_available (void) +{ + return tud_ch341_n_available(0); +} + +static inline int32_t tud_ch341_read_char (void) +{ + return tud_ch341_n_read_char(0); +} + +static inline uint32_t tud_ch341_read (void* buffer, uint32_t bufsize) +{ + return tud_ch341_n_read(0, buffer, bufsize); +} + +static inline void tud_ch341_read_flush (void) +{ + tud_ch341_n_read_flush(0); +} + +static inline bool tud_ch341_peek (uint8_t* ui8) +{ + return tud_ch341_n_peek(0, ui8); +} + +static inline uint32_t tud_ch341_write_char (char ch) +{ + return tud_ch341_n_write_char(0, ch); +} + +static inline uint32_t tud_ch341_write (void const* buffer, uint32_t bufsize) +{ + return tud_ch341_n_write(0, buffer, bufsize); +} + +static inline uint32_t tud_ch341_write_str (char const* str) +{ + return tud_ch341_n_write_str(0, str); +} + +static inline uint32_t tud_ch341_write_flush (void) +{ + return tud_ch341_n_write_flush(0); +} + +static inline uint32_t tud_ch341_write_available(void) +{ + return tud_ch341_n_write_available(0); +} + +static inline bool tud_ch341_write_clear(void) +{ + return tud_ch341_n_write_clear(0); +} + +static inline uint32_t tud_ch341_set_modem_state(ch341_modem_state_t modem_states) +{ + return tud_ch341_n_set_modem_state(0, modem_states); +} + +static inline ch341_modem_state_t tud_ch341_get_modem_state(void) +{ + return tud_ch341_n_get_modem_state(0); +} + +static inline uint32_t tud_ch341_notify_flush(void) +{ + return tud_ch341_n_notify_flush(0); +} + +/** @} */ +/** @} */ + +//--------------------------------------------------------------------+ +// INTERNAL USBD-CLASS DRIVER API +//--------------------------------------------------------------------+ +void ch341d_init (void); +void ch341d_reset (uint8_t rhport); +uint16_t ch341d_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); +bool ch341d_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); +bool ch341d_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CH341_DEVICE_H_ */ From 85ad2c68ba6b9fb59c79d9f4b4f5fb9378c44378 Mon Sep 17 00:00:00 2001 From: TravisRo Date: Fri, 10 Jun 2022 01:27:55 -0600 Subject: [PATCH 02/15] Add CH341 to built-in driver list Add CH341 to built-in driver list (CFG_TUD_CH341) --- src/device/usbd.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/device/usbd.c b/src/device/usbd.c index ec598e49f8..c9a337fff1 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -233,6 +233,18 @@ static usbd_class_driver_t const _usbd_driver[] = .sof = NULL }, #endif + + #if CFG_TUD_CH341 + { + DRIVER_NAME("CH341") + .init = ch341d_init, + .reset = ch341d_reset, + .open = ch341d_open, + .control_xfer_cb = ch341d_control_xfer_cb, + .xfer_cb = ch341d_xfer_cb, + .sof_isr = NULL + }, + #endif }; enum { BUILTIN_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) }; From 08447d9ed37dab232bd487cfc111c7d4fc34dd36 Mon Sep 17 00:00:00 2001 From: TravisRo Date: Fri, 10 Jun 2022 01:28:34 -0600 Subject: [PATCH 03/15] Add CH341 example Add CH341 example --- examples/device/ch341_serial/CMakeLists.txt | 28 +++ examples/device/ch341_serial/Makefile | 12 ++ examples/device/ch341_serial/src/main.c | 116 +++++++++++ .../device/ch341_serial/src/tusb_config.h | 114 ++++++++++ .../device/ch341_serial/src/usb_descriptors.c | 194 ++++++++++++++++++ 5 files changed, 464 insertions(+) create mode 100644 examples/device/ch341_serial/CMakeLists.txt create mode 100644 examples/device/ch341_serial/Makefile create mode 100644 examples/device/ch341_serial/src/main.c create mode 100644 examples/device/ch341_serial/src/tusb_config.h create mode 100644 examples/device/ch341_serial/src/usb_descriptors.c diff --git a/examples/device/ch341_serial/CMakeLists.txt b/examples/device/ch341_serial/CMakeLists.txt new file mode 100644 index 0000000000..abc4d91da9 --- /dev/null +++ b/examples/device/ch341_serial/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.5) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) + +# gets PROJECT name for the example (e.g. -) +family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) + +project(${PROJECT}) + +# Checks this example is valid for the family and initializes the project +family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) + +add_executable(${PROJECT}) + +# Example source +target_sources(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c + ) + +# Example include +target_include_directories(${PROJECT} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + +# Configure compilation flags and libraries for the example... see the corresponding function +# in hw/bsp/FAMILY/family.cmake for details. +family_configure_device_example(${PROJECT}) \ No newline at end of file diff --git a/examples/device/ch341_serial/Makefile b/examples/device/ch341_serial/Makefile new file mode 100644 index 0000000000..5a455078e1 --- /dev/null +++ b/examples/device/ch341_serial/Makefile @@ -0,0 +1,12 @@ +include ../../../tools/top.mk +include ../../make.mk + +INC += \ + src \ + $(TOP)/hw \ + +# Example source +EXAMPLE_SOURCE += $(wildcard src/*.c) +SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) + +include ../../rules.mk diff --git a/examples/device/ch341_serial/src/main.c b/examples/device/ch341_serial/src/main.c new file mode 100644 index 0000000000..a2e1656860 --- /dev/null +++ b/examples/device/ch341_serial/src/main.c @@ -0,0 +1,116 @@ +/* + * 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. + * + */ + +#include +#include +#include +#include + +#include "bsp/board.h" +#include "tusb.h" + +static const char * _par_str[] = +{ + "N", + "O", + "E", + "M", + "S" +}; + +//------------- prototypes -------------// +static void ch341_task(void); + +/*------------- MAIN -------------*/ +int main(void) +{ + board_init(); + + // init device stack on configured roothub port + tud_init(BOARD_TUD_RHPORT); + + while (1) + { + tud_task(); // tinyusb device task + ch341_task(); + } + + return 0; +} + +// Echo back to terminal +static void echo_serial_port( uint8_t buf[], uint32_t count) +{ + tud_ch341_write(buf, count); + tud_ch341_write_flush(); +} + +//--------------------------------------------------------------------+ +// USB CH341 +//--------------------------------------------------------------------+ +static void ch341_task(void) +{ + // connected() The serial port has been opened atleast once + // Will continue to return true even after the serial port is closed. + if ( tud_ch341_connected()) + { + if ( tud_ch341_available() ) + { + uint8_t buf[64]; + + uint32_t count = tud_ch341_read(buf, sizeof(buf)); + + // echo back to terminal + echo_serial_port(buf, count); + } + } +} + +// Invoked when DTR/RTS changes +void tud_ch341_line_state_cb(uint8_t itf, ch341_line_state_t line_state) +{ + printf("DTR=%u, RTS=%u\r\n", + line_state & CH341_LINE_STATE_DTR_ACTIVE ? 1 : 0, + line_state & CH341_LINE_STATE_RTS_ACTIVE ? 1 : 0); +} + +// Invoked when line coding changes +void tud_ch341_line_coding_cb(uint8_t itf, ch341_line_coding_t const* p_line_coding) +{ + printf("BITRATE=%lu (%s%u%u) RX:%s TX:%s\r\n", + p_line_coding->bit_rate, + _par_str[p_line_coding->parity], + p_line_coding->data_bits, + p_line_coding->stop_bits ? 2 : 1, + p_line_coding->rx_en ? "ON":"OFF", + p_line_coding->tx_en ? "ON":"OFF"); + +} + +// Invoked when a break signal is received +void tud_ch341_send_break_cb(uint8_t itf, bool is_break_active) +{ + printf("RCV BREAK=%u\r\n", is_break_active); +} diff --git a/examples/device/ch341_serial/src/tusb_config.h b/examples/device/ch341_serial/src/tusb_config.h new file mode 100644 index 0000000000..bdb8b076ea --- /dev/null +++ b/examples/device/ch341_serial/src/tusb_config.h @@ -0,0 +1,114 @@ +/* + * 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. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + + +//--------------------------------------------------------------------+ +// Board Specific Configuration +//--------------------------------------------------------------------+ + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif + +// RHPort max operational speed can defined by board.mk +#ifndef BOARD_TUD_MAX_SPEED +#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by board.mk +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +// Enable Device stack +#define CFG_TUD_ENABLED 1 + +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_CDC 0 +#define CFG_TUD_MSC 0 +#define CFG_TUD_HID 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 +#define CFG_TUD_CH341 1 + +// CDC FIFO size of TX and RX +#define CFG_TUD_CH341_TX_BUFSIZE (64) +#define CFG_TUD_CH341_RX_BUFSIZE (64) +#define CFG_TUD_CH341_TXNOTIFY_BUFSIZE (8) + +// CDC Endpoint transfer buffer size, more is faster +#define CFG_TUD_CH341_EP_BUFSIZE (64) + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/examples/device/ch341_serial/src/usb_descriptors.c b/examples/device/ch341_serial/src/usb_descriptors.c new file mode 100644 index 0000000000..777d294c3f --- /dev/null +++ b/examples/device/ch341_serial/src/usb_descriptors.c @@ -0,0 +1,194 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * Portions Copyright (c) 2022 Travis Robinson (libusbdotnet@gmail.com) + * + * 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. + * + */ + +#include "tusb.h" + +// QinHeng Electronics +#define USB_VID 0x1A86 +// CH341 serial converter +#define USB_PID 0x7523 +#define USB_BCD 0x0110 + +//--------------------------------------------------------------------+ +// Generic Descriptor Templates +//--------------------------------------------------------------------+ + +#define TUD_GEN_INTERFACE_DESCRIPTOR(_itfnum, _altsetting, _num_endpoints, _itfclass, _itfsubclass, _itfprotocol, _itfstridx) \ + 9, TUSB_DESC_INTERFACE, _itfnum, _altsetting, _num_endpoints, _itfclass, _itfsubclass, _itfprotocol, _itfstridx + +#define TUD_GEN_BULK_EP_DESCRIPTOR(_epnum, _epsize) \ + 7, TUSB_DESC_ENDPOINT, _epnum, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 + +#define TUD_GEN_INTERRUPT_EP_DESCRIPTOR(_epnum, _epsize, _interval) \ + 7, TUSB_DESC_ENDPOINT, _epnum, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_epsize), _interval + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +tusb_desc_device_t const desc_device = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = USB_BCD, + + .bDeviceClass = 0xFF, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = 0x0263, // CH340G + + // A CH340G only has a product string. All other string indexes are 0. + // The driver doesn't seen to care so we will specify all of them. + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const * tud_descriptor_device_cb(void) +{ + return (uint8_t const *) &desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ +enum +{ + ITF_NUM_CH341, + ITF_NUM_TOTAL +}; + +// interface + 3 endpoints +#define TUD_CH341_DESC_LEN (9+7+7+7) +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CH341_DESC_LEN) + +#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX + // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number + // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ... + #define EPNUM_CH341_0_NOTIF 0x81 + #define EPNUM_CH341_0_OUT 0x02 + #define EPNUM_CH341_0_IN 0x82 +#elif CFG_TUSB_MCU == OPT_MCU_SAMG || CFG_TUSB_MCU == OPT_MCU_SAMX7X + // SAMG & SAME70 don't support a same endpoint number with different direction IN and OUT + // e.g EP1 OUT & EP1 IN cannot exist together + #define EPNUM_CH341_0_NOTIF 0x81 + #define EPNUM_CH341_0_OUT 0x02 + #define EPNUM_CH341_0_IN 0x83 +#else + #define EPNUM_CH341_0_NOTIF 0x81 + #define EPNUM_CH341_0_OUT 0x02 + #define EPNUM_CH341_0_IN 0x82 +#endif + +uint8_t const desc_fs_configuration[] = +{ + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x80, 100), + TUD_GEN_INTERFACE_DESCRIPTOR(0, 0, 3, 0xFF, 1, 2 , 0), + TUD_GEN_BULK_EP_DESCRIPTOR(EPNUM_CH341_0_IN, CFG_TUD_CH341_EP_BUFSIZE), + TUD_GEN_BULK_EP_DESCRIPTOR(EPNUM_CH341_0_OUT, CFG_TUD_CH341_EP_BUFSIZE), + TUD_GEN_INTERRUPT_EP_DESCRIPTOR(EPNUM_CH341_0_NOTIF, 8, 1) + }; + +#if TUD_OPT_HIGH_SPEED +#error "High speed not supported" +// TODO: Im not sure if the ch341 windows driver would allow high speed descriptors. Its certainly possible but unlikely +#endif // highspeed + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) +{ + (void) index; // for multiple configurations + +#if TUD_OPT_HIGH_SPEED + // Although we are highspeed, host may be fullspeed. + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration; +#else + return desc_fs_configuration; +#endif +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const* string_desc_arr [] = +{ + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "CH341 serial converter", // 2: Product + "123456", // 3: Serials, should use chip ID +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) +{ + (void) langid; + + uint8_t chr_count; + + if ( index == 0) + { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + }else + { + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; + + const char* str = string_desc_arr[index]; + + // Cap at max char + chr_count = strlen(str); + if ( chr_count > 31 ) chr_count = 31; + + // Convert ASCII string into UTF-16 + for(uint8_t i=0; i Date: Fri, 10 Jun 2022 01:29:28 -0600 Subject: [PATCH 04/15] Add CH341 example to CMakelist and rules --- examples/device/CMakeLists.txt | 1 + examples/rules.mk | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/device/CMakeLists.txt b/examples/device/CMakeLists.txt index edf5ab8054..78bc98903b 100644 --- a/examples/device/CMakeLists.txt +++ b/examples/device/CMakeLists.txt @@ -26,3 +26,4 @@ family_add_subdirectory(uac2_headset) family_add_subdirectory(usbtmc) family_add_subdirectory(video_capture) family_add_subdirectory(webusb_serial) +family_add_subdirectory(ch341_serial) diff --git a/examples/rules.mk b/examples/rules.mk index 538dffbcfa..bfb582a650 100644 --- a/examples/rules.mk +++ b/examples/rules.mk @@ -38,7 +38,8 @@ SRC_C += \ src/class/net/ncm_device.c \ src/class/usbtmc/usbtmc_device.c \ src/class/video/video_device.c \ - src/class/vendor/vendor_device.c + src/class/vendor/vendor_device.c \ + src/class/ch341/ch341_device.c # TinyUSB stack include INC += $(TOP)/src From 3b84243061892c890680c6168e4b1ab9e568970c Mon Sep 17 00:00:00 2001 From: TravisRo Date: Fri, 10 Jun 2022 08:58:17 -0600 Subject: [PATCH 05/15] include ch341_device Add ch441_device.c to rp2040/family.cmake. Add ch341_device.h include to tusb.h --- hw/bsp/rp2040/family.cmake | 1 + src/tusb.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/hw/bsp/rp2040/family.cmake b/hw/bsp/rp2040/family.cmake index df3a6a91e9..fcad10f0eb 100644 --- a/hw/bsp/rp2040/family.cmake +++ b/hw/bsp/rp2040/family.cmake @@ -79,6 +79,7 @@ if (NOT TARGET _rp2040_family_inclusion_marker) ${TOP}/src/class/usbtmc/usbtmc_device.c ${TOP}/src/class/vendor/vendor_device.c ${TOP}/src/class/video/video_device.c + ${TOP}/src/class/ch341/ch341_device.c ) #------------------------------------ diff --git a/src/tusb.h b/src/tusb.h index b776d7d010..9431334e84 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -113,6 +113,10 @@ #if CFG_TUD_BTH #include "class/bth/bth_device.h" #endif + + #if CFG_TUD_CH341 + #include "class/ch341/ch341_device.h" + #endif #endif From 42ed9a52af85a30d4f8a126dff5f4d06a444d9ec Mon Sep 17 00:00:00 2001 From: TravisRo Date: Fri, 10 Jun 2022 08:59:06 -0600 Subject: [PATCH 06/15] Fix compile errors when using latest branch --- examples/device/ch341_serial/src/tusb_config.h | 4 ++++ src/class/ch341/ch341_device.c | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/device/ch341_serial/src/tusb_config.h b/examples/device/ch341_serial/src/tusb_config.h index bdb8b076ea..8cb3fe80fb 100644 --- a/examples/device/ch341_serial/src/tusb_config.h +++ b/examples/device/ch341_serial/src/tusb_config.h @@ -68,6 +68,10 @@ // Default is max speed that hardware controller could support with on-chip PHY #define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED +#ifndef CFG_TUSB_RHPORT0_MODE +#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE +#endif + /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. * Tinyusb use follows macros to declare transferring memory so that they can be put * into those specific section. diff --git a/src/class/ch341/ch341_device.c b/src/class/ch341/ch341_device.c index b7f3a0bb30..9446bd335f 100644 --- a/src/class/ch341/ch341_device.c +++ b/src/class/ch341/ch341_device.c @@ -379,7 +379,7 @@ static inline void ch341_read_regs(ch341d_interface_t *p_ch341, uint8_t* data, u static void _prep_out_transaction (ch341d_interface_t* p_ch341) { - uint8_t const rhport = TUD_OPT_RHPORT; + uint8_t const rhport = BOARD_TUD_RHPORT; uint16_t available = tu_fifo_remaining(&p_ch341->rx_ff); // Prepare for incoming data but only allow what we can store in the ring buffer. @@ -532,7 +532,7 @@ uint32_t tud_ch341_n_write_flush (uint8_t itf) // No data to send if ( !tu_fifo_count(&p_ch341->tx_ff) ) return 0; - uint8_t const rhport = TUD_OPT_RHPORT; + uint8_t const rhport = BOARD_TUD_RHPORT; // Claim the endpoint TU_VERIFY( usbd_edpt_claim(rhport, p_ch341->ep_in), 0 ); @@ -563,7 +563,7 @@ uint32_t tud_ch341_n_notify_flush(uint8_t itf) if (!tu_fifo_count(&p_ch341->txnotify_ff)) return 0; - uint8_t const rhport = TUD_OPT_RHPORT; + uint8_t const rhport = BOARD_TUD_RHPORT; // Claim the endpoint TU_VERIFY(usbd_edpt_claim(rhport, p_ch341->ep_notif), 0); From d63da661341088df237b9faefb74a0f5f018b977 Mon Sep 17 00:00:00 2001 From: TravisRo Date: Fri, 10 Jun 2022 08:59:44 -0600 Subject: [PATCH 07/15] Fix compile errors related to sof/sof_isr renaming --- src/device/usbd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/device/usbd.c b/src/device/usbd.c index c9a337fff1..2112850177 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -242,7 +242,7 @@ static usbd_class_driver_t const _usbd_driver[] = .open = ch341d_open, .control_xfer_cb = ch341d_control_xfer_cb, .xfer_cb = ch341d_xfer_cb, - .sof_isr = NULL + .sof = NULL }, #endif }; From 4c66812422ef0c9002d129088d47a99020c5109d Mon Sep 17 00:00:00 2001 From: TravisRo Date: Thu, 16 Jun 2022 04:55:13 -0600 Subject: [PATCH 08/15] Add missing CFG_TUD_CH341 define --- src/tusb_option.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tusb_option.h b/src/tusb_option.h index 206d23e72c..efd9a7ab75 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -350,6 +350,10 @@ typedef int make_iso_compilers_happy; #define CFG_TUD_BTH 0 #endif +#ifndef CFG_TUD_CH341 + #define CFG_TUD_CH341 0 +#endif + #ifndef CFG_TUD_ECM_RNDIS #ifdef CFG_TUD_NET #warning "CFG_TUD_NET is renamed to CFG_TUD_ECM_RNDIS" From 103153e9761b11872bb6412c8371b35655c7bdba Mon Sep 17 00:00:00 2001 From: TravisRo Date: Thu, 16 Jun 2022 09:06:19 -0600 Subject: [PATCH 09/15] Fixed compiler errors unused parameters const static / static const TUSB_OPT_DEVICE_ENABLED / CFG_TUD_ENABLED CFG_TUD_MAX_SPEED=TUSB_SPEED_FULL --- examples/device/ch341_serial/src/main.c | 3 +++ examples/device/ch341_serial/src/tusb_config.h | 6 +++--- src/class/ch341/ch341_device.c | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/device/ch341_serial/src/main.c b/examples/device/ch341_serial/src/main.c index a2e1656860..674ac18143 100644 --- a/examples/device/ch341_serial/src/main.c +++ b/examples/device/ch341_serial/src/main.c @@ -91,6 +91,7 @@ static void ch341_task(void) // Invoked when DTR/RTS changes void tud_ch341_line_state_cb(uint8_t itf, ch341_line_state_t line_state) { + (void)(itf); printf("DTR=%u, RTS=%u\r\n", line_state & CH341_LINE_STATE_DTR_ACTIVE ? 1 : 0, line_state & CH341_LINE_STATE_RTS_ACTIVE ? 1 : 0); @@ -99,6 +100,7 @@ void tud_ch341_line_state_cb(uint8_t itf, ch341_line_state_t line_state) // Invoked when line coding changes void tud_ch341_line_coding_cb(uint8_t itf, ch341_line_coding_t const* p_line_coding) { + (void)(itf); printf("BITRATE=%lu (%s%u%u) RX:%s TX:%s\r\n", p_line_coding->bit_rate, _par_str[p_line_coding->parity], @@ -112,5 +114,6 @@ void tud_ch341_line_coding_cb(uint8_t itf, ch341_line_coding_t const* p_line_cod // Invoked when a break signal is received void tud_ch341_send_break_cb(uint8_t itf, bool is_break_active) { + (void)(itf); printf("RCV BREAK=%u\r\n", is_break_active); } diff --git a/examples/device/ch341_serial/src/tusb_config.h b/examples/device/ch341_serial/src/tusb_config.h index 8cb3fe80fb..ad8fcf2c74 100644 --- a/examples/device/ch341_serial/src/tusb_config.h +++ b/examples/device/ch341_serial/src/tusb_config.h @@ -65,11 +65,11 @@ // Enable Device stack #define CFG_TUD_ENABLED 1 -// Default is max speed that hardware controller could support with on-chip PHY -#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED +// CH340G is a full speed USB device. +#define CFG_TUD_MAX_SPEED TUSB_SPEED_FULL #ifndef CFG_TUSB_RHPORT0_MODE -#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE +#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED #endif /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. diff --git a/src/class/ch341/ch341_device.c b/src/class/ch341/ch341_device.c index 9446bd335f..c56cb7e4ce 100644 --- a/src/class/ch341/ch341_device.c +++ b/src/class/ch341/ch341_device.c @@ -27,7 +27,7 @@ #include "tusb_option.h" -#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_CH341) +#if (CFG_TUD_ENABLED && CFG_TUD_CH341) #include "device/usbd.h" #include "device/usbd_pvt.h" @@ -189,7 +189,7 @@ typedef struct CFG_TUSB_MEM_SECTION static ch341d_interface_t _ch341d_itf[CFG_TUD_CH341]; -const static uint32_t ch341_known_baud_rates[] = { 50, 75, 100, 110, 150, 300, 600, 900, 1200, 1800, 2400, 3600, 4800, 9600, 14400, 19200, 28800, 33600, 38400, 56000, 57600, 76800, 115200, 128000, 153600, 230400, 460800, 921600, 1500000, 2000000}; +static const uint32_t ch341_known_baud_rates[] = { 50, 75, 100, 110, 150, 300, 600, 900, 1200, 1800, 2400, 3600, 4800, 9600, 14400, 19200, 28800, 33600, 38400, 56000, 57600, 76800, 115200, 128000, 153600, 230400, 460800, 921600, 1500000, 2000000}; static inline uint16_t __get_unaligned_le16(const uint8_t *p) { return p[0] | p[1] << 8; From 8f8990aa58c35d5101f880fb59b7fb3d0d80710a Mon Sep 17 00:00:00 2001 From: TravisRo Date: Thu, 16 Jun 2022 12:20:09 -0600 Subject: [PATCH 10/15] Fix compile errors for high speed devices Added high speed descriptors though I'm not sure if the CH341 driver will work as a high speed device. Changed "buffer size" define names in tusb_config to clarify whether we are specifying fifo buffer sizes or endpoint max packet sizes --- .../device/ch341_serial/src/tusb_config.h | 34 +++------- .../device/ch341_serial/src/usb_descriptors.c | 66 +++++++++++++++++-- src/class/ch341/ch341_device.c | 28 ++++---- src/class/ch341/ch341_device.h | 17 +++-- 4 files changed, 92 insertions(+), 53 deletions(-) diff --git a/examples/device/ch341_serial/src/tusb_config.h b/examples/device/ch341_serial/src/tusb_config.h index ad8fcf2c74..ad5a3ce04d 100644 --- a/examples/device/ch341_serial/src/tusb_config.h +++ b/examples/device/ch341_serial/src/tusb_config.h @@ -65,28 +65,13 @@ // Enable Device stack #define CFG_TUD_ENABLED 1 -// CH340G is a full speed USB device. -#define CFG_TUD_MAX_SPEED TUSB_SPEED_FULL +// Default is max speed that hardware controller could support with on-chip PHY +#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED #ifndef CFG_TUSB_RHPORT0_MODE #define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED #endif -/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. - * Tinyusb use follows macros to declare transferring memory so that they can be put - * into those specific section. - * e.g - * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) - * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) - */ -#ifndef CFG_TUSB_MEM_SECTION -#define CFG_TUSB_MEM_SECTION -#endif - -#ifndef CFG_TUSB_MEM_ALIGN -#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) -#endif - //-------------------------------------------------------------------- // DEVICE CONFIGURATION //-------------------------------------------------------------------- @@ -102,14 +87,15 @@ #define CFG_TUD_MIDI 0 #define CFG_TUD_VENDOR 0 #define CFG_TUD_CH341 1 - -// CDC FIFO size of TX and RX -#define CFG_TUD_CH341_TX_BUFSIZE (64) -#define CFG_TUD_CH341_RX_BUFSIZE (64) -#define CFG_TUD_CH341_TXNOTIFY_BUFSIZE (8) -// CDC Endpoint transfer buffer size, more is faster -#define CFG_TUD_CH341_EP_BUFSIZE (64) +// CH341 Endpoint max packet sizes (should not change) +#define CFG_TUD_CH341_EP_RX_MAX_PACKET (TUD_OPT_HIGH_SPEED ? 512 : 64) +#define CFG_TUD_CH341_EP_TX_MAX_PACKET CFG_TUD_CH341_EP_RX_MAX_PACKET +#define CFG_TUD_CH341_EP_TXNOTIFY_MAX_PACKET (8) + +// CH341 buffer size for TX and RX data (must be an interval of max packet size) +// more is faster +#define CFG_TUD_CH341_FIFO_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #ifdef __cplusplus } diff --git a/examples/device/ch341_serial/src/usb_descriptors.c b/examples/device/ch341_serial/src/usb_descriptors.c index 777d294c3f..ea44e9412c 100644 --- a/examples/device/ch341_serial/src/usb_descriptors.c +++ b/examples/device/ch341_serial/src/usb_descriptors.c @@ -30,7 +30,6 @@ #define USB_VID 0x1A86 // CH341 serial converter #define USB_PID 0x7523 -#define USB_BCD 0x0110 //--------------------------------------------------------------------+ // Generic Descriptor Templates @@ -52,8 +51,13 @@ tusb_desc_device_t const desc_device = { .bLength = sizeof(tusb_desc_device_t), .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = USB_BCD, - +#if TUD_OPT_HIGH_SPEED + // atleast 0x0200 required for HS + .bcdUSB = 0x0200, +#else + // this is what's in a genuine CH340G's descriptor + .bcdUSB = 0x0110, +#endif .bDeviceClass = 0xFF, .bDeviceSubClass = 0x00, .bDeviceProtocol = 0x00, @@ -115,14 +119,62 @@ uint8_t const desc_fs_configuration[] = // Config number, interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x80, 100), TUD_GEN_INTERFACE_DESCRIPTOR(0, 0, 3, 0xFF, 1, 2 , 0), - TUD_GEN_BULK_EP_DESCRIPTOR(EPNUM_CH341_0_IN, CFG_TUD_CH341_EP_BUFSIZE), - TUD_GEN_BULK_EP_DESCRIPTOR(EPNUM_CH341_0_OUT, CFG_TUD_CH341_EP_BUFSIZE), + TUD_GEN_BULK_EP_DESCRIPTOR(EPNUM_CH341_0_IN, CFG_TUD_CH341_EP_TX_MAX_PACKET), + TUD_GEN_BULK_EP_DESCRIPTOR(EPNUM_CH341_0_OUT, CFG_TUD_CH341_EP_RX_MAX_PACKET), TUD_GEN_INTERRUPT_EP_DESCRIPTOR(EPNUM_CH341_0_NOTIF, 8, 1) }; #if TUD_OPT_HIGH_SPEED -#error "High speed not supported" -// TODO: Im not sure if the ch341 windows driver would allow high speed descriptors. Its certainly possible but unlikely + +// TEST: Im not sure if the ch341 windows driver will allow high speed descriptors. +// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration + +uint8_t const desc_hs_configuration[] = +{ + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x80, 100), + TUD_GEN_INTERFACE_DESCRIPTOR(0, 0, 3, 0xFF, 1, 2 , 0), + TUD_GEN_BULK_EP_DESCRIPTOR(EPNUM_CH341_0_IN, CFG_TUD_CH341_EP_TX_MAX_PACKET), + TUD_GEN_BULK_EP_DESCRIPTOR(EPNUM_CH341_0_OUT, CFG_TUD_CH341_EP_RX_MAX_PACKET), + TUD_GEN_INTERRUPT_EP_DESCRIPTOR(EPNUM_CH341_0_NOTIF, 8, 1) +}; + +// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed +tusb_desc_device_qualifier_t const desc_device_qualifier = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + + .bDeviceClass = 0xFF, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0x00 +}; + +// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. +// device_qualifier descriptor describes information about a high-speed capable device that would +// change if the device were operating at the other speed. If not highspeed capable stall this request. +uint8_t const* tud_descriptor_device_qualifier_cb(void) +{ + return (uint8_t const*) &desc_device_qualifier; +} + +// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa +uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) +{ + (void) index; // for multiple configurations + + // if link speed is high return fullspeed config, and vice versa + return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration; +} + #endif // highspeed // Invoked when received GET CONFIGURATION DESCRIPTOR diff --git a/src/class/ch341/ch341_device.c b/src/class/ch341/ch341_device.c index c56cb7e4ce..fd93abd0c8 100644 --- a/src/class/ch341/ch341_device.c +++ b/src/class/ch341/ch341_device.c @@ -151,9 +151,9 @@ typedef struct tu_fifo_t tx_ff; tu_fifo_t txnotify_ff; - uint8_t rx_ff_buf[CFG_TUD_CH341_RX_BUFSIZE]; - uint8_t tx_ff_buf[CFG_TUD_CH341_TX_BUFSIZE]; - uint8_t txnotify_ff_buf[CFG_TUD_CH341_TXNOTIFY_BUFSIZE]; + uint8_t rx_ff_buf[CFG_TUD_CH341_FIFO_SIZE]; + uint8_t tx_ff_buf[CFG_TUD_CH341_FIFO_SIZE]; + uint8_t txnotify_ff_buf[CFG_TUD_CH341_EP_TXNOTIFY_MAX_PACKET]; #if CFG_FIFO_MUTEX osal_mutex_def_t rx_ff_mutex; @@ -168,8 +168,8 @@ typedef struct CFG_TUSB_MEM_ALIGN uint8_t ep0_in_buf[4]; // Endpoint Transfer buffer - CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CH341_EP_BUFSIZE]; - CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CH341_EP_BUFSIZE]; + CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CH341_EP_RX_MAX_PACKET]; + CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CH341_EP_TX_MAX_PACKET]; uint8_t register_data[REG_DIDX_MAX]; @@ -514,7 +514,7 @@ uint32_t tud_ch341_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) uint16_t ret = tu_fifo_write_n(&p_ch341->tx_ff, buffer, bufsize); // flush if queue more than packet size - if (tu_fifo_count(&p_ch341->tx_ff) >= CFG_TUD_CH341_EP_BUFSIZE ) + if (tu_fifo_count(&p_ch341->tx_ff) >= sizeof(p_ch341->epin_buf)) { tud_ch341_n_write_flush(itf); } @@ -632,16 +632,10 @@ void ch341d_init(void) // Config RX fifo tu_fifo_config(&p_ch341->rx_ff, p_ch341->rx_ff_buf, TU_ARRAY_SIZE(p_ch341->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_ch341->tx_ff, p_ch341->tx_ff_buf, TU_ARRAY_SIZE(p_ch341->tx_ff_buf), 1, true); - - // Config TX notify 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_ch341->txnotify_ff, p_ch341->txnotify_ff_buf, TU_ARRAY_SIZE(p_ch341->txnotify_ff_buf), 1, true); + // Config TX fifo + tu_fifo_config(&p_ch341->tx_ff, p_ch341->tx_ff_buf, TU_ARRAY_SIZE(p_ch341->tx_ff_buf), 1, false); + // Config TX NOTIFY fifo + tu_fifo_config(&p_ch341->txnotify_ff, p_ch341->txnotify_ff_buf, TU_ARRAY_SIZE(p_ch341->txnotify_ff_buf), 1, false); #if CFG_FIFO_MUTEX tu_fifo_config_mutex(&p_ch341->rx_ff, NULL, osal_mutex_create(&p_ch341->rx_ff_mutex)); @@ -896,7 +890,7 @@ bool ch341d_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 { // 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_ch341->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (CFG_TUD_CH341_EP_BUFSIZE-1))) ) + if (!tu_fifo_count(&p_ch341->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (CFG_TUD_CH341_EP_TX_MAX_PACKET-1))) ) { if ( usbd_edpt_claim(rhport, p_ch341->ep_in) ) { diff --git a/src/class/ch341/ch341_device.h b/src/class/ch341/ch341_device.h index c5fd6b0a69..1bda60746d 100644 --- a/src/class/ch341/ch341_device.h +++ b/src/class/ch341/ch341_device.h @@ -41,13 +41,20 @@ //--------------------------------------------------------------------+ // Class Driver Configuration //--------------------------------------------------------------------+ -#if !defined(CFG_TUD_CH341_EP_BUFSIZE) && defined(CFG_TUD_CH341_EPSIZE) - #warning CFG_TUD_CH341_EPSIZE is renamed to CFG_TUD_CH341_EP_BUFSIZE, please update to use the new name - #define CFG_TUD_CH341_EP_BUFSIZE CFG_TUD_CH341_EPSIZE +#ifndef CFG_TUD_CH341_EP_RX_MAX_PACKET + #define CFG_TUD_CH341_EP_RX_MAX_PACKET (TUD_OPT_HIGH_SPEED ? 512 : 64) #endif -#ifndef CFG_TUD_CH341_EP_BUFSIZE - #define CFG_TUD_CH341_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) +#ifndef CFG_TUD_CH341_EP_TX_MAX_PACKET + #define CFG_TUD_CH341_EP_TX_MAX_PACKET CFG_TUD_CH341_EP_RX_MAX_PACKET +#endif + +#ifndef CFG_TUD_CH341_EP_TXNOTIFY_MAX_PACKET + #define CFG_TUD_CH341_EP_TXNOTIFY_MAX_PACKET (8) +#endif + +#ifndef CFG_TUD_CH341_FIFO_SIZE + #define CFG_TUD_CH341_FIFO_SIZE CFG_TUD_CH341_EP_RX_MAX_PACKET #endif #ifdef __cplusplus From e6e36f026c63dbdd247df49804910c2f94239565 Mon Sep 17 00:00:00 2001 From: TravisRo Date: Thu, 16 Jun 2022 16:11:40 -0600 Subject: [PATCH 11/15] fix unused variable and conversion compile errors --- src/class/ch341/ch341_device.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/class/ch341/ch341_device.c b/src/class/ch341/ch341_device.c index fd93abd0c8..8956f4605c 100644 --- a/src/class/ch341/ch341_device.c +++ b/src/class/ch341/ch341_device.c @@ -207,10 +207,10 @@ static inline void ch341_decode_bit_rate(ch341d_interface_t *p_ch341) uint8_t baud_fact = (p_ch341->register_data[REG_DIDX_PRESCALER] >> 2) & 0x01; uint8_t baud_ps = p_ch341->register_data[REG_DIDX_PRESCALER] & 0x03; TU_LOG1("ch341_decode_bit_rate: baud_div=%u baud_fact=%u baud_ps=%u\r\n", baud_div, baud_fact, baud_ps); - uint32_t calc_bit_rate = (uint32_t)lroundf(CH341_CLKRATE / (pow(2.0F, (12.0F - 3.0F * (float)baud_ps - (float)baud_fact)) * (float)baud_div)); + uint32_t calc_bit_rate = (uint32_t)lroundf(CH341_CLKRATE / ((float)pow(2.0F, (12.0F - 3.0F * (float)baud_ps - (float)baud_fact)) * (float)baud_div)); int index; - for (index = 0; index < (sizeof(ch341_known_baud_rates) / sizeof(uint32_t)); index++) + for (index = 0; index < (int)((sizeof(ch341_known_baud_rates) / sizeof(uint32_t))); index++) { int max_diff = lroundf((float)ch341_known_baud_rates[index] * 0.002F); if (calc_bit_rate >= ch341_known_baud_rates[index] - max_diff && calc_bit_rate <= ch341_known_baud_rates[index] + max_diff) @@ -338,7 +338,7 @@ static inline void ch341_read_regs(ch341d_interface_t *p_ch341, uint8_t* data, u { uint8_t reg = wValue & 0xFF; TU_LOG1("ch341_read_regs: %02X\r\n", reg); - switch (wValue & 0xFF) + switch (reg) { case CH341_REG_BREAK: data[i] = p_ch341->register_data[REG_DIDX_BREAK]; From 89eb35d60b9f0a74eda712da867c0605cc607019 Mon Sep 17 00:00:00 2001 From: TravisRo Date: Fri, 17 Jun 2022 16:26:35 -0600 Subject: [PATCH 12/15] Misc. improvements Remove floating point math Remove multi-interface support Remove un-needed logging Add missing comments Reduce memory footprint --- examples/device/ch341_serial/src/main.c | 9 +- src/class/ch341/ch341_device.c | 167 ++++++++------------ src/class/ch341/ch341_device.h | 193 ++++-------------------- 3 files changed, 91 insertions(+), 278 deletions(-) diff --git a/examples/device/ch341_serial/src/main.c b/examples/device/ch341_serial/src/main.c index 674ac18143..66941e334c 100644 --- a/examples/device/ch341_serial/src/main.c +++ b/examples/device/ch341_serial/src/main.c @@ -89,18 +89,16 @@ static void ch341_task(void) } // Invoked when DTR/RTS changes -void tud_ch341_line_state_cb(uint8_t itf, ch341_line_state_t line_state) +void tud_ch341_line_state_cb(ch341_line_state_t line_state) { - (void)(itf); printf("DTR=%u, RTS=%u\r\n", line_state & CH341_LINE_STATE_DTR_ACTIVE ? 1 : 0, line_state & CH341_LINE_STATE_RTS_ACTIVE ? 1 : 0); } // Invoked when line coding changes -void tud_ch341_line_coding_cb(uint8_t itf, ch341_line_coding_t const* p_line_coding) +void tud_ch341_line_coding_cb(ch341_line_coding_t const* p_line_coding) { - (void)(itf); printf("BITRATE=%lu (%s%u%u) RX:%s TX:%s\r\n", p_line_coding->bit_rate, _par_str[p_line_coding->parity], @@ -112,8 +110,7 @@ void tud_ch341_line_coding_cb(uint8_t itf, ch341_line_coding_t const* p_line_cod } // Invoked when a break signal is received -void tud_ch341_send_break_cb(uint8_t itf, bool is_break_active) +void tud_ch341_send_break_cb(bool is_break_active) { - (void)(itf); printf("RCV BREAK=%u\r\n", is_break_active); } diff --git a/src/class/ch341/ch341_device.c b/src/class/ch341/ch341_device.c index 8956f4605c..645a0ab7e7 100644 --- a/src/class/ch341/ch341_device.c +++ b/src/class/ch341/ch341_device.c @@ -33,7 +33,6 @@ #include "device/usbd_pvt.h" #include "ch341_device.h" -#include //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF @@ -47,7 +46,7 @@ // https://github.com/torvalds/linux/blob/master/drivers/usb/serial/ch341.c ////////////////////////////////////////////////////////////////////////////// -#define CH341_CLKRATE 48000000.0F +#define CH341_CLKRATE 48000000UL /* flags for IO-Bits */ #define CH341_BIT_RTS (1 << 6) @@ -186,33 +185,22 @@ typedef struct // This is because it sends vendor class control messages directly to the // device asnd uses wIndex of the control packet to transfer vendor // specific data. -CFG_TUSB_MEM_SECTION static ch341d_interface_t _ch341d_itf[CFG_TUD_CH341]; +CFG_TUSB_MEM_SECTION static ch341d_interface_t _ch341d_itf[1]; static const uint32_t ch341_known_baud_rates[] = { 50, 75, 100, 110, 150, 300, 600, 900, 1200, 1800, 2400, 3600, 4800, 9600, 14400, 19200, 28800, 33600, 38400, 56000, 57600, 76800, 115200, 128000, 153600, 230400, 460800, 921600, 1500000, 2000000}; -static inline uint16_t __get_unaligned_le16(const uint8_t *p) -{ - return p[0] | p[1] << 8; -} - -static inline void __put_unaligned_le16(uint16_t val, uint8_t *p) -{ - *p++ = val; - *p++ = val >> 8; -} static inline void ch341_decode_bit_rate(ch341d_interface_t *p_ch341) { uint16_t baud_div = 0x100 - p_ch341->register_data[REG_DIDX_DIVISOR]; uint8_t baud_fact = (p_ch341->register_data[REG_DIDX_PRESCALER] >> 2) & 0x01; uint8_t baud_ps = p_ch341->register_data[REG_DIDX_PRESCALER] & 0x03; - TU_LOG1("ch341_decode_bit_rate: baud_div=%u baud_fact=%u baud_ps=%u\r\n", baud_div, baud_fact, baud_ps); - uint32_t calc_bit_rate = (uint32_t)lroundf(CH341_CLKRATE / ((float)pow(2.0F, (12.0F - 3.0F * (float)baud_ps - (float)baud_fact)) * (float)baud_div)); + uint32_t calc_bit_rate = (uint32_t)(CH341_CLKRATE / ((1 << (12 - 3 * baud_ps - baud_fact)) * baud_div)); int index; for (index = 0; index < (int)((sizeof(ch341_known_baud_rates) / sizeof(uint32_t))); index++) { - int max_diff = lroundf((float)ch341_known_baud_rates[index] * 0.002F); + int max_diff = (int)((ch341_known_baud_rates[index] * 2) / 1000); if (calc_bit_rate >= ch341_known_baud_rates[index] - max_diff && calc_bit_rate <= ch341_known_baud_rates[index] + max_diff) { p_ch341->line_coding.bit_rate = ch341_known_baud_rates[index]; @@ -228,7 +216,6 @@ static inline void ch341_decode_lcr(ch341d_interface_t *p_ch341) { // decode rx/tx enable uint8_t lcr = p_ch341->register_data[REG_DIDX_LCR]; - TU_LOG1("ch341_decode_lcr: lcr=%02X\r\n", lcr); p_ch341->line_coding.tx_en = (lcr & CH341_LCR_ENABLE_TX) ? true : false; p_ch341->line_coding.rx_en = (lcr & CH341_LCR_ENABLE_RX) ? true : false; @@ -280,7 +267,7 @@ static inline ch341_line_state_t ch341_decode_mcr(ch341d_interface_t *p_ch341, u return line_state; } -static inline void ch341_write_regs(ch341d_interface_t *p_ch341, uint16_t wValue, uint16_t wIndex) +static void ch341_write_regs(ch341d_interface_t *p_ch341, uint16_t wValue, uint16_t wIndex) { int i; @@ -288,8 +275,6 @@ static inline void ch341_write_regs(ch341d_interface_t *p_ch341, uint16_t wValue { uint8_t reg = wValue & 0xFF; uint8_t regval = wIndex & 0xFF; - TU_LOG1("ch341_write_regs: %02X=%02X\r\n", reg, regval); - switch (reg) { case CH341_REG_BREAK: @@ -330,14 +315,13 @@ static inline void ch341_write_regs(ch341d_interface_t *p_ch341, uint16_t wValue } } -static inline void ch341_read_regs(ch341d_interface_t *p_ch341, uint8_t* data, uint16_t wValue) +static void ch341_read_regs(ch341d_interface_t *p_ch341, uint8_t* data, uint16_t wValue) { int i; for (i = 0; i < 2; i++) { uint8_t reg = wValue & 0xFF; - TU_LOG1("ch341_read_regs: %02X\r\n", reg); switch (reg) { case CH341_REG_BREAK: @@ -408,19 +392,19 @@ static void _prep_out_transaction (ch341d_interface_t* p_ch341) //--------------------------------------------------------------------+ // APPLICATION API //--------------------------------------------------------------------+ -bool tud_ch341_n_connected(uint8_t itf) +bool tud_ch341_connected(void) { - return tud_ready() && _ch341d_itf[itf].connected; + return tud_ready() && _ch341d_itf[0].connected; } -ch341_line_state_t tud_ch341_n_get_line_state (uint8_t itf) +ch341_line_state_t tud_ch341_get_line_state (void) { - return _ch341d_itf[itf].line_state; + return _ch341d_itf[0].line_state; } -uint32_t tud_ch341_n_set_modem_state(uint8_t itf, ch341_modem_state_t modem_states) +uint32_t tud_ch341_set_modem_state(ch341_modem_state_t modem_states) { - ch341d_interface_t *p_ch341 = &_ch341d_itf[itf]; + ch341d_interface_t *p_ch341 = &_ch341d_itf[0]; uint8_t buffer[4]; modem_states &= CH341_MODEM_STATE_ALL; @@ -452,55 +436,55 @@ uint32_t tud_ch341_n_set_modem_state(uint8_t itf, ch341_modem_state_t modem_stat if (ret == 4) { // start transfer - ret = tud_ch341_n_notify_flush(itf); + ret = tud_ch341_notify_flush(); } return ret; } -ch341_modem_state_t tud_ch341_n_get_modem_state(uint8_t itf) +ch341_modem_state_t tud_ch341_get_modem_state(void) { - ch341d_interface_t *p_ch341 = &_ch341d_itf[itf]; + ch341d_interface_t *p_ch341 = &_ch341d_itf[0]; ch341_modem_state_t modem_states = p_ch341->register_data[REG_DIDX_MCR_MSR]; modem_states = (~modem_states) & CH341_MODEM_STATE_ALL; return modem_states; } -void tud_ch341_n_get_line_coding (uint8_t itf, ch341_line_coding_t* coding) +void tud_ch341_get_line_coding (ch341_line_coding_t* coding) { - (*coding) = _ch341d_itf[itf].line_coding; + (*coding) = _ch341d_itf[0].line_coding; } -void tud_ch341_n_set_wanted_char (uint8_t itf, char wanted) +void tud_ch341_set_wanted_char (char wanted) { - _ch341d_itf[itf].wanted_char = wanted; + _ch341d_itf[0].wanted_char = wanted; } //--------------------------------------------------------------------+ // READ API //--------------------------------------------------------------------+ -uint32_t tud_ch341_n_available(uint8_t itf) +uint32_t tud_ch341_available(void) { - return tu_fifo_count(&_ch341d_itf[itf].rx_ff); + return tu_fifo_count(&_ch341d_itf[0].rx_ff); } -uint32_t tud_ch341_n_read(uint8_t itf, void* buffer, uint32_t bufsize) +uint32_t tud_ch341_read(void* buffer, uint32_t bufsize) { - ch341d_interface_t* p_ch341 = &_ch341d_itf[itf]; + ch341d_interface_t* p_ch341 = &_ch341d_itf[0]; uint32_t num_read = tu_fifo_read_n(&p_ch341->rx_ff, buffer, bufsize); _prep_out_transaction(p_ch341); return num_read; } -bool tud_ch341_n_peek(uint8_t itf, uint8_t* chr) +bool tud_ch341_peek(uint8_t* chr) { - return tu_fifo_peek(&_ch341d_itf[itf].rx_ff, chr); + return tu_fifo_peek(&_ch341d_itf[0].rx_ff, chr); } -void tud_ch341_n_read_flush (uint8_t itf) +void tud_ch341_read_flush (void) { - ch341d_interface_t* p_ch341 = &_ch341d_itf[itf]; + ch341d_interface_t* p_ch341 = &_ch341d_itf[0]; tu_fifo_clear(&p_ch341->rx_ff); _prep_out_transaction(p_ch341); } @@ -508,23 +492,23 @@ void tud_ch341_n_read_flush (uint8_t itf) //--------------------------------------------------------------------+ // WRITE API //--------------------------------------------------------------------+ -uint32_t tud_ch341_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) +uint32_t tud_ch341_write(void const* buffer, uint32_t bufsize) { - ch341d_interface_t* p_ch341 = &_ch341d_itf[itf]; + ch341d_interface_t* p_ch341 = &_ch341d_itf[0]; uint16_t ret = tu_fifo_write_n(&p_ch341->tx_ff, buffer, bufsize); // flush if queue more than packet size if (tu_fifo_count(&p_ch341->tx_ff) >= sizeof(p_ch341->epin_buf)) { - tud_ch341_n_write_flush(itf); + tud_ch341_write_flush(); } return ret; } -uint32_t tud_ch341_n_write_flush (uint8_t itf) +uint32_t tud_ch341_write_flush (void) { - ch341d_interface_t* p_ch341 = &_ch341d_itf[itf]; + ch341d_interface_t* p_ch341 = &_ch341d_itf[0]; // Skip if usb is not ready yet TU_VERIFY( tud_ready(), 0 ); @@ -552,9 +536,9 @@ uint32_t tud_ch341_n_write_flush (uint8_t itf) return 0; } } -uint32_t tud_ch341_n_notify_flush(uint8_t itf) +uint32_t tud_ch341_notify_flush(void) { - ch341d_interface_t *p_ch341 = &_ch341d_itf[itf]; + ch341d_interface_t *p_ch341 = &_ch341d_itf[0]; // Skip if usb is not ready yet TU_VERIFY(tud_ready(), 0); @@ -585,14 +569,14 @@ uint32_t tud_ch341_n_notify_flush(uint8_t itf) } } -uint32_t tud_ch341_n_write_available (uint8_t itf) +uint32_t tud_ch341_write_available (void) { - return tu_fifo_remaining(&_ch341d_itf[itf].tx_ff); + return tu_fifo_remaining(&_ch341d_itf[0].tx_ff); } -bool tud_ch341_n_write_clear (uint8_t itf) +bool tud_ch341_write_clear (void) { - return tu_fifo_clear(&_ch341d_itf[itf].tx_ff); + return tu_fifo_clear(&_ch341d_itf[0].tx_ff); } //--------------------------------------------------------------------+ @@ -600,12 +584,9 @@ bool tud_ch341_n_write_clear (uint8_t itf) //--------------------------------------------------------------------+ void ch341d_init(void) { - TU_LOG1("ch341d_init:begin\r\n"); - tu_memclr(_ch341d_itf, sizeof(_ch341d_itf)); + tu_memclr(_ch341d_itf, sizeof(_ch341d_itf)); - for(uint8_t i=0; iwanted_char = -1; @@ -642,26 +623,20 @@ void ch341d_init(void) tu_fifo_config_mutex(&p_ch341->tx_ff, osal_mutex_create(&p_ch341->tx_ff_mutex), NULL); tu_fifo_config_mutex(&p_ch341->txnotify_ff, osal_mutex_create(&p_ch341->txnotify_ff_mutex), NULL); #endif - } - - TU_LOG1("ch341d_init:end\r\n"); } void ch341d_reset(uint8_t rhport) { (void) rhport; - for(uint8_t i=0; irx_ff); - tu_fifo_clear(&p_ch341->tx_ff); - tu_fifo_clear(&p_ch341->txnotify_ff); - tu_fifo_set_overwritable(&p_ch341->tx_ff, false); - tu_fifo_set_overwritable(&p_ch341->txnotify_ff, false); - } + ch341d_interface_t* p_ch341 = &_ch341d_itf[0]; + + tu_memclr(p_ch341, ITF_MEM_RESET_SIZE); + tu_fifo_clear(&p_ch341->rx_ff); + tu_fifo_clear(&p_ch341->tx_ff); + tu_fifo_clear(&p_ch341->txnotify_ff); + tu_fifo_set_overwritable(&p_ch341->tx_ff, false); + tu_fifo_set_overwritable(&p_ch341->txnotify_ff, false); } uint16_t ch341d_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) @@ -671,19 +646,8 @@ uint16_t ch341d_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin 0x01 == itf_desc->bInterfaceSubClass && 0x02 == itf_desc->bInterfaceProtocol, 0); - // Find available interface - ch341d_interface_t * p_ch341 = NULL; - for(uint8_t ch341_id=0; ch341_iditf_num = itf_desc->bInterfaceNumber; @@ -738,8 +702,7 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ bool ch341d_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) { - uint8_t itf = 0; - ch341d_interface_t* p_ch341 = _ch341d_itf; + ch341d_interface_t* p_ch341 = &_ch341d_itf[0]; if ((request->bmRequestType_bit.type) == TUSB_REQ_TYPE_VENDOR && ((request->bmRequestType_bit.recipient) == TUSB_REQ_RCPT_DEVICE)) { @@ -807,7 +770,6 @@ bool ch341d_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_ break; default: - TU_LOG1("ch341d: bRequest=%02X wValue=%04X wIndex=%04X wLength=%u\r\n", request->bRequest, request->wValue, request->wIndex, request->wLength); return false; } @@ -815,7 +777,7 @@ bool ch341d_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_ { p_ch341->line_state_changed = false; if (tud_ch341_line_state_cb) - tud_ch341_line_state_cb(itf, p_ch341->line_state); + tud_ch341_line_state_cb(p_ch341->line_state); } if (p_ch341->line_coding_changed) { @@ -823,36 +785,25 @@ bool ch341d_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_ ch341_decode_bit_rate(p_ch341); ch341_decode_lcr(p_ch341); if (tud_ch341_line_coding_cb) - tud_ch341_line_coding_cb(itf, &p_ch341->line_coding); - TU_LOG1("BPS:%lu P:%u D:%u S:%u RX:%d, TX:%d\r\n", p_ch341->line_coding.bit_rate, p_ch341->line_coding.parity, p_ch341->line_coding.data_bits, p_ch341->line_coding.stop_bits, p_ch341->line_coding.rx_en, p_ch341->line_coding.tx_en); + tud_ch341_line_coding_cb(&p_ch341->line_coding); } if (p_ch341->break_state_changed) { p_ch341->break_state_changed = false; if (tud_ch341_send_break_cb) - tud_ch341_send_break_cb(itf, p_ch341->register_data[REG_DIDX_BREAK]); + tud_ch341_send_break_cb(p_ch341->register_data[REG_DIDX_BREAK]); } return true; } - TU_LOG1("ch341d: bRequest=%02X wValue=%04X wIndex=%04X wLength=%u\r\n", request->bRequest, request->wValue, request->wIndex, request->wLength); - return false; + return false; } bool ch341d_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) { (void) result; - uint8_t itf; - ch341d_interface_t* p_ch341; - - // Identify which interface to use - for (itf = 0; itf < CFG_TUD_CH341; itf++) - { - p_ch341 = &_ch341d_itf[itf]; - if ( ( ep_addr == p_ch341->ep_out ) || ( ep_addr == p_ch341->ep_in ) ) break; - } - TU_ASSERT(itf < CFG_TUD_CH341); + ch341d_interface_t* p_ch341 = &_ch341d_itf[0]; // Received new data if ( ep_addr == p_ch341->ep_out ) @@ -866,13 +817,13 @@ bool ch341d_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 { if ( (p_ch341->wanted_char == p_ch341->epout_buf[i]) && !tu_fifo_empty(&p_ch341->rx_ff) ) { - tud_ch341_rx_wanted_cb(itf, p_ch341->wanted_char); + tud_ch341_rx_wanted_cb(p_ch341->wanted_char); } } } // invoke receive callback (if there is still data) - if (tud_ch341_rx_cb && !tu_fifo_empty(&p_ch341->rx_ff) ) tud_ch341_rx_cb(itf); + if (tud_ch341_rx_cb && !tu_fifo_empty(&p_ch341->rx_ff) ) tud_ch341_rx_cb(); // prepare for OUT transaction _prep_out_transaction(p_ch341); @@ -884,9 +835,9 @@ bool ch341d_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 if ( ep_addr == p_ch341->ep_in ) { // invoke transmit callback to possibly refill tx fifo - if ( tud_ch341_tx_complete_cb ) tud_ch341_tx_complete_cb(itf); + if ( tud_ch341_tx_complete_cb ) tud_ch341_tx_complete_cb(); - if ( 0 == tud_ch341_n_write_flush(itf) ) + if ( 0 == tud_ch341_write_flush() ) { // If there is no data left, a ZLP should be sent if // xferred_bytes is multiple of EP Packet size and not zero diff --git a/src/class/ch341/ch341_device.h b/src/class/ch341/ch341_device.h index 1bda60746d..5d03a8be87 100644 --- a/src/class/ch341/ch341_device.h +++ b/src/class/ch341/ch341_device.h @@ -67,221 +67,86 @@ * @{ */ //--------------------------------------------------------------------+ -// Application API (Multiple Ports) -// CFG_TUD_CH341 > 1 +// Application API (Single Port) +// The CH341 is a vendor class device that sends it's requests directly +// to the device, hence there can be only 1 CH341 interface per device. //--------------------------------------------------------------------+ // Check if terminal is connected to this port -bool tud_ch341_n_connected (uint8_t itf); +bool tud_ch341_connected (void); // Get current line state. Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send) -ch341_line_state_t tud_ch341_n_get_line_state (uint8_t itf); +ch341_line_state_t tud_ch341_get_line_state (void); // Get current line encoding: bit rate, stop bits parity etc .. -void tud_ch341_n_get_line_coding (uint8_t itf, ch341_line_coding_t* coding); +void tud_ch341_get_line_coding (ch341_line_coding_t* coding); // Set special character that will trigger tud_ch341_rx_wanted_cb() callback on receiving -void tud_ch341_n_set_wanted_char (uint8_t itf, char wanted); +void tud_ch341_set_wanted_char (char wanted); // Get the number of bytes available for reading -uint32_t tud_ch341_n_available (uint8_t itf); +uint32_t tud_ch341_available (void); // Read received bytes -uint32_t tud_ch341_n_read (uint8_t itf, void* buffer, uint32_t bufsize); +uint32_t tud_ch341_read (void* buffer, uint32_t bufsize); // Read a byte, return -1 if there is none -static inline -int32_t tud_ch341_n_read_char (uint8_t itf); +int32_t tud_ch341_read_char (void); // Clear the received FIFO -void tud_ch341_n_read_flush (uint8_t itf); +void tud_ch341_read_flush (void); // Get a byte from FIFO at the specified position without removing it -bool tud_ch341_n_peek (uint8_t itf, uint8_t* ui8); +bool tud_ch341_peek (uint8_t* ui8); // Write bytes to TX FIFO, data may remain in the FIFO for a while -uint32_t tud_ch341_n_write (uint8_t itf, void const* buffer, uint32_t bufsize); +uint32_t tud_ch341_write (void const* buffer, uint32_t bufsize); // Write a byte -static inline -uint32_t tud_ch341_n_write_char (uint8_t itf, char ch); +uint32_t tud_ch341_write_char (char ch); // Write a null-terminated string -static inline -uint32_t tud_ch341_n_write_str (uint8_t itf, char const* str); +uint32_t tud_ch341_write_str (char const* str); // Force sending data if possible, return number of forced bytes -uint32_t tud_ch341_n_write_flush (uint8_t itf); +uint32_t tud_ch341_write_flush (void); // Return the number of bytes (characters) available for writing to TX FIFO buffer in a single n_write operation. -uint32_t tud_ch341_n_write_available (uint8_t itf); +uint32_t tud_ch341_write_available (void); // Clear the transmit FIFO -bool tud_ch341_n_write_clear (uint8_t itf); +bool tud_ch341_write_clear (void); // Send line events to host. (IE: CTS, DSR, RI, DCD) -uint32_t tud_ch341_n_set_modem_state(uint8_t itf, ch341_modem_state_t modem_states); - -ch341_modem_state_t tud_ch341_n_get_modem_state(uint8_t itf); +uint32_t tud_ch341_set_modem_state(ch341_modem_state_t modem_states); -uint32_t tud_ch341_n_set_break(uint8_t itf, bool on); +// Returns the current state of the modem. (IE: CTS, DSR, RI, DCD) +ch341_modem_state_t tud_ch341_get_modem_state(void); -uint32_t tud_ch341_n_notify_flush(uint8_t itf); - -//--------------------------------------------------------------------+ -// Application API (Single Port) -//--------------------------------------------------------------------+ -static inline bool tud_ch341_connected (void); -static inline ch341_line_state_t tud_ch341_get_line_state (void); -static inline void tud_ch341_get_line_coding (ch341_line_coding_t* coding); -static inline void tud_ch341_set_wanted_char (char wanted); - -static inline uint32_t tud_ch341_available (void); -static inline int32_t tud_ch341_read_char (void); -static inline uint32_t tud_ch341_read (void* buffer, uint32_t bufsize); -static inline void tud_ch341_read_flush (void); -static inline bool tud_ch341_peek (uint8_t* ui8); - -static inline uint32_t tud_ch341_write_char (char ch); -static inline uint32_t tud_ch341_write (void const* buffer, uint32_t bufsize); -static inline uint32_t tud_ch341_write_str (char const* str); -static inline uint32_t tud_ch341_write_flush (void); -static inline uint32_t tud_ch341_write_available (void); -static inline bool tud_ch341_write_clear (void); -static inline uint32_t tud_ch341_set_modem_state(ch341_modem_state_t modem_states); -static inline ch341_modem_state_t tud_ch341_get_modem_state(void); -static inline uint32_t tud_ch341_notify_flush(void); +// Force sending notify data if possible, return number of forced bytes +uint32_t tud_ch341_notify_flush(void); //--------------------------------------------------------------------+ // Application Callback API (weak is optional) //--------------------------------------------------------------------+ // Invoked when received new data -TU_ATTR_WEAK void tud_ch341_rx_cb(uint8_t itf); +TU_ATTR_WEAK void tud_ch341_rx_cb(void); // Invoked when received `wanted_char` -TU_ATTR_WEAK void tud_ch341_rx_wanted_cb(uint8_t itf, char wanted_char); +TU_ATTR_WEAK void tud_ch341_rx_wanted_cb(char wanted_char); // Invoked when space becomes available in TX buffer -TU_ATTR_WEAK void tud_ch341_tx_complete_cb(uint8_t itf); +TU_ATTR_WEAK void tud_ch341_tx_complete_cb(void); // Invoked when line state DTR & RTS are changed via SET_CONTROL_LINE_STATE -TU_ATTR_WEAK void tud_ch341_line_state_cb(uint8_t itf, ch341_line_state_t line_state); +TU_ATTR_WEAK void tud_ch341_line_state_cb(ch341_line_state_t line_state); // Invoked when line coding is change via SET_LINE_CODING -TU_ATTR_WEAK void tud_ch341_line_coding_cb(uint8_t itf, ch341_line_coding_t const* p_line_coding); +TU_ATTR_WEAK void tud_ch341_line_coding_cb(ch341_line_coding_t const* p_line_coding); // Invoked when received send break -TU_ATTR_WEAK void tud_ch341_send_break_cb(uint8_t itf, bool is_break_active); - -//--------------------------------------------------------------------+ -// Inline Functions -//--------------------------------------------------------------------+ -static inline int32_t tud_ch341_n_read_char (uint8_t itf) -{ - uint8_t ch; - return tud_ch341_n_read(itf, &ch, 1) ? (int32_t) ch : -1; -} - -static inline uint32_t tud_ch341_n_write_char(uint8_t itf, char ch) -{ - return tud_ch341_n_write(itf, &ch, 1); -} - -static inline uint32_t tud_ch341_n_write_str (uint8_t itf, char const* str) -{ - return tud_ch341_n_write(itf, str, strlen(str)); -} - -static inline bool tud_ch341_connected (void) -{ - return tud_ch341_n_connected(0); -} - -static inline ch341_line_state_t tud_ch341_get_line_state (void) -{ - return tud_ch341_n_get_line_state(0); -} - -static inline void tud_ch341_get_line_coding (ch341_line_coding_t* coding) -{ - tud_ch341_n_get_line_coding(0, coding); -} - -static inline void tud_ch341_set_wanted_char (char wanted) -{ - tud_ch341_n_set_wanted_char(0, wanted); -} - -static inline uint32_t tud_ch341_available (void) -{ - return tud_ch341_n_available(0); -} - -static inline int32_t tud_ch341_read_char (void) -{ - return tud_ch341_n_read_char(0); -} - -static inline uint32_t tud_ch341_read (void* buffer, uint32_t bufsize) -{ - return tud_ch341_n_read(0, buffer, bufsize); -} - -static inline void tud_ch341_read_flush (void) -{ - tud_ch341_n_read_flush(0); -} - -static inline bool tud_ch341_peek (uint8_t* ui8) -{ - return tud_ch341_n_peek(0, ui8); -} - -static inline uint32_t tud_ch341_write_char (char ch) -{ - return tud_ch341_n_write_char(0, ch); -} - -static inline uint32_t tud_ch341_write (void const* buffer, uint32_t bufsize) -{ - return tud_ch341_n_write(0, buffer, bufsize); -} - -static inline uint32_t tud_ch341_write_str (char const* str) -{ - return tud_ch341_n_write_str(0, str); -} - -static inline uint32_t tud_ch341_write_flush (void) -{ - return tud_ch341_n_write_flush(0); -} - -static inline uint32_t tud_ch341_write_available(void) -{ - return tud_ch341_n_write_available(0); -} - -static inline bool tud_ch341_write_clear(void) -{ - return tud_ch341_n_write_clear(0); -} - -static inline uint32_t tud_ch341_set_modem_state(ch341_modem_state_t modem_states) -{ - return tud_ch341_n_set_modem_state(0, modem_states); -} - -static inline ch341_modem_state_t tud_ch341_get_modem_state(void) -{ - return tud_ch341_n_get_modem_state(0); -} - -static inline uint32_t tud_ch341_notify_flush(void) -{ - return tud_ch341_n_notify_flush(0); -} +TU_ATTR_WEAK void tud_ch341_send_break_cb(bool is_break_active); /** @} */ /** @} */ From 6510e87911d10bd67ed8d69dea564fbbfb53b806 Mon Sep 17 00:00:00 2001 From: TravisRo Date: Fri, 17 Jun 2022 21:18:49 -0600 Subject: [PATCH 13/15] logging cleanup. remove printfs from example --- examples/device/ch341_serial/src/main.c | 22 +++++++++++++++++++--- src/class/ch341/ch341_device.c | 3 --- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/examples/device/ch341_serial/src/main.c b/examples/device/ch341_serial/src/main.c index 66941e334c..cd2133edcd 100644 --- a/examples/device/ch341_serial/src/main.c +++ b/examples/device/ch341_serial/src/main.c @@ -31,6 +31,10 @@ #include "bsp/board.h" #include "tusb.h" +// Change this to 1 and event callback will be logged to stdout +#define CH341_LOG_EVENTS 0 + +#if (CH341_LOG_EVENTS) static const char * _par_str[] = { "N", @@ -39,6 +43,7 @@ static const char * _par_str[] = "M", "S" }; +#endif //------------- prototypes -------------// static void ch341_task(void); @@ -91,26 +96,37 @@ static void ch341_task(void) // Invoked when DTR/RTS changes void tud_ch341_line_state_cb(ch341_line_state_t line_state) { +#if (CH341_LOG_EVENTS) printf("DTR=%u, RTS=%u\r\n", line_state & CH341_LINE_STATE_DTR_ACTIVE ? 1 : 0, line_state & CH341_LINE_STATE_RTS_ACTIVE ? 1 : 0); +#else + (void)(line_state); +#endif } // Invoked when line coding changes void tud_ch341_line_coding_cb(ch341_line_coding_t const* p_line_coding) { +#if (CH341_LOG_EVENTS) printf("BITRATE=%lu (%s%u%u) RX:%s TX:%s\r\n", - p_line_coding->bit_rate, + (unsigned long)p_line_coding->bit_rate, _par_str[p_line_coding->parity], p_line_coding->data_bits, p_line_coding->stop_bits ? 2 : 1, p_line_coding->rx_en ? "ON":"OFF", p_line_coding->tx_en ? "ON":"OFF"); - +#else + (void)(p_line_coding); +#endif } // Invoked when a break signal is received void tud_ch341_send_break_cb(bool is_break_active) { - printf("RCV BREAK=%u\r\n", is_break_active); +#if (CH341_LOG_EVENTS) + printf("RCV BREAK=%s\r\n", is_break_active ? "ON" : "OFF"); +#else + (void)(is_break_active); +#endif } diff --git a/src/class/ch341/ch341_device.c b/src/class/ch341/ch341_device.c index 645a0ab7e7..d40fdc272e 100644 --- a/src/class/ch341/ch341_device.c +++ b/src/class/ch341/ch341_device.c @@ -655,7 +655,6 @@ uint16_t ch341d_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin uint8_t const * p_desc = tu_desc_next( itf_desc ); while (p_desc && drv_len < max_len) { - TU_LOG1("ch341d_open:drv_len=%u, max_len=%u\r\n", drv_len, max_len); if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) { tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *)p_desc; @@ -686,7 +685,6 @@ uint16_t ch341d_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin // Prepare for incoming data _prep_out_transaction(p_ch341); - TU_LOG1("ch341d_open:end\r\n"); return drv_len; } @@ -764,7 +762,6 @@ bool ch341d_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_ else if (stage == CONTROL_STAGE_ACK) { p_ch341->line_state = ch341_decode_mcr(p_ch341, request->wValue); - TU_LOG1("CH341_REQ_MODEM_CTRL DTR=%u RTS=%u\r\n", (p_ch341->line_state & CH341_LINE_STATE_BIT_DTR) ? 1 : 0, (p_ch341->line_state & CH341_LINE_STATE_BIT_RTS) ? 1 : 0); p_ch341->line_state_changed = true; } break; From d8e8057a5794fb9a642657a4568f7f58db106e18 Mon Sep 17 00:00:00 2001 From: TravisRo Date: Mon, 20 Jun 2022 19:21:24 -0600 Subject: [PATCH 14/15] Add missing static inline class functions tud_ch341_read_char tud_ch341_write_char tud_ch341_write_str --- src/class/ch341/ch341_device.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/class/ch341/ch341_device.h b/src/class/ch341/ch341_device.h index 5d03a8be87..9ba74bbbbf 100644 --- a/src/class/ch341/ch341_device.h +++ b/src/class/ch341/ch341_device.h @@ -91,6 +91,7 @@ uint32_t tud_ch341_available (void); uint32_t tud_ch341_read (void* buffer, uint32_t bufsize); // Read a byte, return -1 if there is none +static inline int32_t tud_ch341_read_char (void); // Clear the received FIFO @@ -103,9 +104,11 @@ bool tud_ch341_peek (uint8_t* ui8); uint32_t tud_ch341_write (void const* buffer, uint32_t bufsize); // Write a byte +static inline uint32_t tud_ch341_write_char (char ch); // Write a null-terminated string +static inline uint32_t tud_ch341_write_str (char const* str); // Force sending data if possible, return number of forced bytes @@ -148,6 +151,25 @@ TU_ATTR_WEAK void tud_ch341_line_coding_cb(ch341_line_coding_t const* p_line_cod // Invoked when received send break TU_ATTR_WEAK void tud_ch341_send_break_cb(bool is_break_active); +//--------------------------------------------------------------------+ +// Inline functions +//--------------------------------------------------------------------+ +static inline int32_t tud_ch341_read_char (void) +{ + uint8_t ch; + return tud_ch341_read(&ch, 1) ? (int32_t) ch : -1; +} + +static inline uint32_t tud_ch341_write_char (char ch) +{ + return tud_ch341_write((void const*)&ch, 1); +} + +static inline uint32_t tud_ch341_write_str (char const* str) +{ + return tud_ch341_write((uint8_t const*)str, strlen(str)); +} + /** @} */ /** @} */ From ee5a1443fda3f074d1d69df7b6e27a498bba22e6 Mon Sep 17 00:00:00 2001 From: TravisRo Date: Thu, 11 Aug 2022 21:58:42 -0600 Subject: [PATCH 15/15] Allow setting CFG_TUSB_OS from parent cmakefile --- hw/bsp/rp2040/family.cmake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/bsp/rp2040/family.cmake b/hw/bsp/rp2040/family.cmake index e853d1b56b..aaf0fee9c2 100644 --- a/hw/bsp/rp2040/family.cmake +++ b/hw/bsp/rp2040/family.cmake @@ -52,9 +52,13 @@ if (NOT TARGET _rp2040_family_inclusion_marker) set(TINYUSB_DEBUG_LEVEL 1) endif() + if (NOT CFG_TUSB_OS) + set(CFG_TUSB_OS OPT_OS_PICO) + endif() + target_compile_definitions(tinyusb_common_base INTERFACE CFG_TUSB_MCU=OPT_MCU_RP2040 - CFG_TUSB_OS=OPT_OS_PICO + CFG_TUSB_OS=${CFG_TUSB_OS} CFG_TUSB_DEBUG=${TINYUSB_DEBUG_LEVEL} )