From 2ddd74fada9b2f272923fce9c3b1926a6e9f5c80 Mon Sep 17 00:00:00 2001 From: rppicomidi Date: Fri, 12 Aug 2022 11:06:36 -0700 Subject: Add MIDI host support to tinyusb --- src/class/midi/README_midi_host.md | 111 +++++ src/class/midi/midi.h | 19 +- src/class/midi/midi_host.c | 944 +++++++++++++++++++++++++++++++++++++ src/class/midi/midi_host.h | 137 ++++++ src/host/hcd.h | 2 +- src/host/usbh.c | 11 + src/tusb.h | 4 + 7 files changed, 1225 insertions(+), 3 deletions(-) create mode 100644 src/class/midi/README_midi_host.md create mode 100644 src/class/midi/midi_host.c create mode 100644 src/class/midi/midi_host.h (limited to 'src') diff --git a/src/class/midi/README_midi_host.md b/src/class/midi/README_midi_host.md new file mode 100644 index 000000000..9df2249b3 --- /dev/null +++ b/src/class/midi/README_midi_host.md @@ -0,0 +1,111 @@ +# MIDI HOST DRIVER +This README file contains the design notes and limitations of the +MIDI host driver. + +# MAXIMUM NUMBER OF MIDI DEVICES ATTACHED TO HOST +In this version of the driver, only one MIDI device is supported. This +constraint may change in the future. + +# MAXIMUM NUMBER OF ENDPOINTS +Although the USB MIDI 1.0 Class specification allows an arbitrary number +of endpoints, this driver supports at most one USB BULK DATA IN endpoint +and one USB BULK DATA OUT endpoint. Each endpoint can support up to 16 +virtual cables. If a device has multiple IN endpoints or multiple OUT +endpoints, it will fail to enumerate. + +Most USB MIDI devices contain both an IN endpoint and an OUT endpoint, +but not all do. For example, some USB pedals only support an OUT endpoint. +This driver allows that. + +# PUBLIC API +Applications interact with this driver via 8-bit buffers of MIDI messages +formed using the rules for sending bytes on a 5-pin DIN cable per the +original MIDI 1.0 specification. + +To send a message to a device, the Host application composes a sequence +of status and data bytes in a byte array and calls the API function. +The arguments of the function are a pointer to the byte array, the number +of bytes in the array, and the target virtual cable number 0-15. + +When the host driver receives a message from the device, the host driver +will call a callback function that the host application registers. This +callback function contains a pointer to a message buffer, a message length, +and the virtual cable number of the message buffer. One complete bulk IN +endpoint transfer might contain multiple messages targeted to different +virtual cables. + +# SUBCLASS AUDIO CONTROL +A MIDI device does not absolutely need to have an Audio Control Interface, +unless it adheres to the USB Audio Class 2 spec, but many devices +have them even if the devices do not have an audio streaming interface. +Because this driver does not support audio streaming, the descriptor parser +will skip past any audio control interface and audio streaming interface +and open only the MIDI interface. + +An audio streaming host driver can use this driver by passing a pointer +to the MIDI interface descriptor that is found after the audio streaming +interface to the midih_open() function. That is, an audio streaming host +driver would parse the audio control interface descriptor and then the +audio streaming interface and endpoint descriptors. When the next descriptor +pointer points to a MIDI interface descriptor, call midih_open() with that +descriptor pointer. + +# CLASS SPECIFIC INTERFACE AND REQUESTS +The host driver does not make use of the informaton in the class specific +interface descriptors. In the future, a public API could be created to +retrieve the string descriptors for the names of each ELEMENT, +IN JACK and OUT JACK, and how the device describes the connections. + +This driver also does not support class specific requests to control +ELEMENT items, nor does it support non-MIDI Streaming bulk endpoints. + +# MIDI CLASS SPECIFIC DESCRIPTOR TOTAL LENGTH FIELD IGNORED +I have observed at least one keyboard by a leading manufacturer that +sets the wTotalLength field of the Class-Specific MS Interface Header +Descriptor to include the length of the MIDIStreaming Endpoint +Descriptors. This is wrong per my reading of the specification. + +# MESSAGE BUFFER DETAILS +Messages buffers composed from USB data received on the IN endpoint will never contain +running status because USB MIDI 1.0 class does not support that. Messages +buffers to be sent to the device on the OUT endpont may contain running status +(the message might come from a UART data stream from a 5-pin DIN MIDI IN +cable on the host, for example). The driver may in the future correctly compose +4-byte USB MIDI Class packets using the running status if need be. However, +it does not currently do that. Also, use of running status is not a good idea +overall because a single byte error can really mess up the data stream with no +way to recover until the next non-real time status byte is in the message buffer. + +Message buffers to be sent to the device may contain Real time messages +such as MIDI clock. Real time messages may be inserted in the message +byte stream between status and data bytes of another message without disrupting +the running status. However, because MIDI 1.0 class messages are sent +as four byte packets, a real-time message so inserted will be re-ordered +to be sent to the device in a new 4-byte packet immediately before the +interrupted data stream. + +Real time messages the device sends to the host can only appear between +the status byte and data bytes of the message in System Exclusive messages +that are longer than 3 bytes. + +# POORLY FORMED USB MIDI DATA PACKETS FROM THE DEVICE +Some devices do not properly encode the code index number (CIN) for the +MIDI message status byte even though the 3-byte data payload correctly encodes +the MIDI message. This driver looks to the byte after the CIN byte to decide +how many bytes to place in the message buffer. + +Some devices do not properly encode the virtual cable number. If the virtual +cable number in the CIN data byte of the packet is not less than bNumEmbMIDIJack +for that endpoint, then the host driver assumes virtual cable 0 and does not +report an error. + +Some MIDI devices will always send back exactly wMaxPacketSize bytes on +every endpoint even if only one 4-byte packet is required (e.g., NOTE ON). +These devices send packets with 4 packet bytes 0. This driver ignores all +zero packets without reporting an error. + +# ENUMERATION FAILURES +The host may fail to enumerate a device if it has too many endpoints, if it has +if it has a Standard MS Transfer Bulk Data Endpoint Descriptor (not supported), +if it has a poorly formed descriptor, or if the descriptor is too long for +the host to read the whole thing. diff --git a/src/class/midi/midi.h b/src/class/midi/midi.h index 74dc41749..ea2cfec75 100644 --- a/src/class/midi/midi.h +++ b/src/class/midi/midi.h @@ -71,8 +71,8 @@ typedef enum MIDI_CIN_SYSEX_END_1BYTE = 5, // SysEx ends with 1 data, or 1 byte system common message MIDI_CIN_SYSEX_END_2BYTE = 6, // SysEx ends with 2 data MIDI_CIN_SYSEX_END_3BYTE = 7, // SysEx ends with 3 data - MIDI_CIN_NOTE_ON = 8, - MIDI_CIN_NOTE_OFF = 9, + MIDI_CIN_NOTE_ON = 9, + MIDI_CIN_NOTE_OFF = 8, MIDI_CIN_POLY_KEYPRESS = 10, MIDI_CIN_CONTROL_CHANGE = 11, MIDI_CIN_PROGRAM_CHANGE = 12, @@ -106,6 +106,11 @@ enum MIDI_STATUS_SYSREAL_SYSTEM_RESET = 0xFF, }; +enum +{ + MIDI_MAX_DATA_VAL = 0x7F, +}; + /// MIDI Interface Header Descriptor typedef struct TU_ATTR_PACKED { @@ -201,6 +206,16 @@ typedef struct TU_ATTR_PACKED uint8_t iElement; \ } +// This descriptor follows the standard bulk data endpoint descriptor +typedef struct +{ + uint8_t bLength ; ///< Size of this descriptor in bytes (4+bNumEmbMIDIJack) + uint8_t bDescriptorType ; ///< Descriptor Type, must be CS_ENDPOINT + uint8_t bDescriptorSubType ; ///< Descriptor SubType, must be MS_GENERAL + uint8_t bNumEmbMIDIJack; ; ///< Number of embedded MIDI jacks associated with this endpoint + uint8_t baAssocJackID[]; ; ///< A list of associated jacks +} midi_cs_desc_endpoint_t; + /** @} */ #ifdef __cplusplus diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c new file mode 100644 index 000000000..e482f5265 --- /dev/null +++ b/src/class/midi/midi_host.c @@ -0,0 +1,944 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if (TUSB_OPT_HOST_ENABLED && CFG_TUH_MIDI) + +#include "host/usbh.h" +#include "host/usbh_classdriver.h" + +#include "midi_host.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ +#ifndef CFG_TUH_MAX_CABLES + #define CFG_TUH_MAX_CABLES 16 +#endif +#define CFG_TUH_MIDI_RX_BUFSIZE 64 +#define CFG_TUH_MIDI_TX_BUFSIZE 64 +#ifndef CFG_TUH_MIDI_EP_BUFSIZE + #define CFG_TUH_MIDI_EP_BUFSIZE 64 +#endif + +// TODO: refactor to share code with the MIDI Device driver +typedef struct +{ + uint8_t buffer[4]; + uint8_t index; + uint8_t total; +}midi_stream_t; + +typedef struct +{ + uint8_t dev_addr; + uint8_t itf_num; + + uint8_t ep_in; // IN endpoint address + uint8_t ep_out; // OUT endpoint address + uint16_t ep_in_max; // min( CFG_TUH_MIDI_RX_BUFSIZE, wMaxPacketSize of the IN endpoint) + uint16_t ep_out_max; // min( CFG_TUH_MIDI_TX_BUFSIZE, wMaxPacketSize of the OUT endpoint) + + uint8_t num_cables_rx; // IN endpoint CS descriptor bNumEmbMIDIJack value + uint8_t num_cables_tx; // OUT endpoint CS descriptor bNumEmbMIDIJack value + + // For Stream read()/write() API + // Messages are always 4 bytes long, queue them for reading and writing so the + // callers can use the Stream interface with single-byte read/write calls. + midi_stream_t stream_write; + midi_stream_t stream_read; + + /*------------- From this point, data is not cleared by bus reset -------------*/ + // Endpoint FIFOs + tu_fifo_t rx_ff; + tu_fifo_t tx_ff; + + + uint8_t rx_ff_buf[CFG_TUH_MIDI_RX_BUFSIZE]; + uint8_t tx_ff_buf[CFG_TUH_MIDI_TX_BUFSIZE]; + + #if CFG_FIFO_MUTEX + osal_mutex_def_t rx_ff_mutex; + osal_mutex_def_t tx_ff_mutex; + #endif + + // Endpoint Transfer buffer + CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUH_MIDI_EP_BUFSIZE]; + CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUH_MIDI_EP_BUFSIZE]; + + bool configured; + +#if CFG_MIDI_HOST_DEVSTRINGS +#define MAX_STRING_INDICES 32 + uint8_t all_string_indices[MAX_STRING_INDICES]; + uint8_t num_string_indices; +#define MAX_IN_JACKS 8 +#define MAX_OUT_JACKS 8 + struct { + uint8_t jack_id; + int8_t jack_type; + uint8_t string_index; + } in_jack_info[MAX_IN_JACKS]; + uint8_t next_in_jack; + struct { + uint8_t jack_id; + uint8_t jack_type; + uint8_t num_source_ids; + uint8_t source_ids[MAX_IN_JACKS/4]; + uint8_t string_index; + } out_jack_info[MAX_OUT_JACKS]; + uint8_t next_out_jack; + uint8_t ep_in_associated_jacks[MAX_OUT_JACKS/2]; + uint8_t ep_out_associated_jacks[MAX_IN_JACKS/2]; +#endif +}midih_interface_t; + +static midih_interface_t _midi_host[CFG_TUH_DEVICE_MAX]; + +static midih_interface_t *get_midi_host(uint8_t dev_addr) +{ + TU_VERIFY(dev_addr >0 && dev_addr <= CFG_TUH_DEVICE_MAX); + return (_midi_host + dev_addr - 1); +} + +//------------- Internal prototypes -------------// +static uint32_t write_flush(uint8_t dev_addr, midih_interface_t* midi); + +//--------------------------------------------------------------------+ +// USBH API +//--------------------------------------------------------------------+ +void midih_init(void) +{ + tu_memclr(&_midi_host, sizeof(_midi_host)); + // config fifos + for (int inst = 0; inst < CFG_TUH_DEVICE_MAX; inst++) + { + midih_interface_t *p_midi_host = &_midi_host[inst]; + tu_fifo_config(&p_midi_host->rx_ff, p_midi_host->rx_ff_buf, CFG_TUH_MIDI_RX_BUFSIZE, 1, false); // true, true + tu_fifo_config(&p_midi_host->tx_ff, p_midi_host->tx_ff_buf, CFG_TUH_MIDI_TX_BUFSIZE, 1, false); // OBVS. + + #if CFG_FIFO_MUTEX + tu_fifo_config_mutex(&p_midi_host->rx_ff, NULL, osal_mutex_create(&p_midi_host->rx_ff_mutex)); + tu_fifo_config_mutex(&p_midi_host->tx_ff, osal_mutex_create(&p_midi_host->tx_ff_mutex), NULL); + #endif + } +} + +bool midih_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +{ + (void)result; + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + TU_VERIFY(p_midi_host != NULL); + if ( ep_addr == p_midi_host->ep_in) + { + if (0 == xferred_bytes) + { + return true; // No data to handle + } + + // receive new data if available + uint32_t packets_queued = 0; + if (xferred_bytes) + { + // put in the RX FIFO only non-zero MIDI IN 4-byte packets + uint8_t* buf = p_midi_host->epin_buf; + uint32_t npackets = xferred_bytes / 4; + uint32_t packet_num; + for (packet_num = 0; packet_num < npackets; packet_num++) + { + // some devices send back all zero packets even if there is no data ready + uint32_t packet = (uint32_t)((*buf)<<24) | ((uint32_t)(*(buf+1))<<16) | ((uint32_t)(*(buf+2))<<8) | ((uint32_t)(*(buf+3))); + if (packet != 0) + { + tu_fifo_write_n(&p_midi_host->rx_ff, buf, 4); + ++packets_queued; + TU_LOG3("MIDI RX=%08x\r\n", packet); + } + buf += 4; + } + } + // invoke receive callback if available + if (tuh_midi_rx_cb) + { + tuh_midi_rx_cb(dev_addr, packets_queued); + } + } + else if ( ep_addr == p_midi_host->ep_out ) + { + if (0 == write_flush(dev_addr, p_midi_host)) + { + // If there is no data left, a ZLP should be sent if + // xferred_bytes is multiple of EP size and not zero + if ( !tu_fifo_count(&p_midi_host->tx_ff) && xferred_bytes && (0 == (xferred_bytes % p_midi_host->ep_out_max)) ) + { + if ( usbh_edpt_claim(dev_addr, p_midi_host->ep_out) ) + { + TU_ASSERT(usbh_edpt_xfer(dev_addr, p_midi_host->ep_out, XFER_RESULT_SUCCESS, 0)); + } + } + } + if (tuh_midi_tx_cb) + { + tuh_midi_tx_cb(dev_addr); + } + } + + return true; +} + +void midih_close(uint8_t dev_addr) +{ + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + if (p_midi_host == NULL) + return; + if (tuh_midi_umount_cb) + tuh_midi_umount_cb(dev_addr, 0); + tu_fifo_clear(&p_midi_host->rx_ff); + tu_fifo_clear(&p_midi_host->tx_ff); + p_midi_host->ep_in = 0; + p_midi_host->ep_in_max = 0; + p_midi_host->ep_out = 0; + p_midi_host->ep_out_max = 0; + p_midi_host->itf_num = 0; + p_midi_host->num_cables_rx = 0; + p_midi_host->num_cables_tx = 0; + p_midi_host->dev_addr = 255; // invalid + p_midi_host->configured = false; + tu_memclr(&p_midi_host->stream_read, sizeof(p_midi_host->stream_read)); + tu_memclr(&p_midi_host->stream_write, sizeof(p_midi_host->stream_write)); +} + +//--------------------------------------------------------------------+ +// Enumeration +//--------------------------------------------------------------------+ +bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len) +{ + (void) rhport; + + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + + TU_VERIFY(p_midi_host != NULL); + p_midi_host->num_string_indices = 0; + TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass); + // There can be just a MIDI interface or an audio and a MIDI interface. Only open the MIDI interface + uint8_t const *p_desc = (uint8_t const *) desc_itf; + uint16_t len_parsed = 0; + if (AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass) + { + // Keep track of any string descriptor that might be here + if (desc_itf->iInterface != 0) + p_midi_host->all_string_indices[p_midi_host->num_string_indices++] = desc_itf->iInterface; + // This driver does not support audio streaming. However, if this is the audio control interface + // there might be a MIDI interface following it. Search through every descriptor until a MIDI + // interface is found or the end of the descriptor is found + while (len_parsed < max_len && (desc_itf->bInterfaceClass != TUSB_CLASS_AUDIO || desc_itf->bInterfaceSubClass != AUDIO_SUBCLASS_MIDI_STREAMING)) + { + len_parsed += desc_itf->bLength; + p_desc = tu_desc_next(p_desc); + desc_itf = (tusb_desc_interface_t const *)p_desc; + } + + TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass); + } + TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == desc_itf->bInterfaceSubClass); + len_parsed += desc_itf->bLength; + + // Keep track of any string descriptor that might be here + if (desc_itf->iInterface != 0) + p_midi_host->all_string_indices[p_midi_host->num_string_indices++] = desc_itf->iInterface; + + p_desc = tu_desc_next(p_desc); + TU_LOG1("MIDI opening Interface %u (addr = %u)\r\n", desc_itf->bInterfaceNumber, dev_addr); + // Find out if getting the MIDI class specific interface header or an endpoint descriptor + // or a class-specific endpoint descriptor + // Jack descriptors or element descriptors must follow the cs interface header, + // but this driver does not support devices that contain element descriptors + + // assume it is an interface header + midi_desc_header_t const *p_mdh = (midi_desc_header_t const *)p_desc; + TU_VERIFY((p_mdh->bDescriptorType == TUSB_DESC_CS_INTERFACE && p_mdh->bDescriptorSubType == MIDI_CS_INTERFACE_HEADER) || + (p_mdh->bDescriptorType == TUSB_DESC_CS_ENDPOINT && p_mdh->bDescriptorSubType == MIDI_CS_ENDPOINT_GENERAL) || + p_mdh->bDescriptorType == TUSB_DESC_ENDPOINT); + + uint8_t prev_ep_addr = 0; // the CS endpoint descriptor is associated with the previous endpoint descrptor + p_midi_host->itf_num = desc_itf->bInterfaceNumber; + tusb_desc_endpoint_t const* in_desc = NULL; + tusb_desc_endpoint_t const* out_desc = NULL; + while (len_parsed < max_len) + { + TU_VERIFY((p_mdh->bDescriptorType == TUSB_DESC_CS_INTERFACE) || + (p_mdh->bDescriptorType == TUSB_DESC_CS_ENDPOINT && p_mdh->bDescriptorSubType == MIDI_CS_ENDPOINT_GENERAL) || + p_mdh->bDescriptorType == TUSB_DESC_ENDPOINT); + + if (p_mdh->bDescriptorType == TUSB_DESC_CS_INTERFACE) { + // The USB host doesn't really need this information unless it uses + // the string descriptor for a jack or Element + + // assume it is an input jack + midi_desc_in_jack_t const *p_mdij = (midi_desc_in_jack_t const *)p_desc; + if (p_mdij->bDescriptorSubType == MIDI_CS_INTERFACE_HEADER) + { + TU_LOG2("Found MIDI Interface Header\r\b"); + } + else if (p_mdij->bDescriptorSubType == MIDI_CS_INTERFACE_IN_JACK) + { + // Then it is an in jack. + TU_LOG2("Found in jack\r\n"); +#if CFG_MIDI_HOST_DEVSTRINGS + if (p_midi_host->next_in_jack < MAX_IN_JACKS) + { + p_midi_host->in_jack_info[p_midi_host->next_in_jack].jack_id = p_mdij->bJackID; + p_midi_host->in_jack_info[p_midi_host->next_in_jack].jack_type = p_mdij->bJackType; + p_midi_host->in_jack_info[p_midi_host->next_in_jack].string_index = p_mdij->iJack; + ++p_midi_host->next_in_jack; + // Keep track of any string descriptor that might be here + if (p_mdij->iJack != 0) + p_midi_host->all_string_indices[p_midi_host->num_string_indices++] = p_mdij->iJack; + + } +#endif + } + else if (p_mdij->bDescriptorSubType == MIDI_CS_INTERFACE_OUT_JACK) + { + // then it is an out jack + TU_LOG2("Found out jack\r\n"); +#if CFG_MIDI_HOST_DEVSTRINGS + if (p_midi_host->next_out_jack < MAX_OUT_JACKS) + { + midi_desc_out_jack_t const *p_mdoj = (midi_desc_out_jack_t const *)p_desc; + p_midi_host->out_jack_info[p_midi_host->next_out_jack].jack_id = p_mdoj->bJackID; + p_midi_host->out_jack_info[p_midi_host->next_out_jack].jack_type = p_mdoj->bJackType; + p_midi_host->out_jack_info[p_midi_host->next_out_jack].num_source_ids = p_mdoj->bNrInputPins; + struct associated_jack_s { + uint8_t id; + uint8_t pin; + } *associated_jack = (struct associated_jack_s *)(p_desc+6); + int jack; + for (jack = 0; jack < p_mdoj->bNrInputPins; jack++) + { + p_midi_host->out_jack_info[p_midi_host->next_out_jack].source_ids[jack] = associated_jack->id; + } + p_midi_host->out_jack_info[p_midi_host->next_out_jack].string_index = *(p_desc+6+p_mdoj->bNrInputPins*2); + ++p_midi_host->next_out_jack; + if (p_mdoj->iJack != 0) + p_midi_host->all_string_indices[p_midi_host->num_string_indices++] = p_mdoj->iJack; + } +#endif + } + else if (p_mdij->bDescriptorSubType == MIDI_CS_INTERFACE_ELEMENT) + { + // the it is an element; + #if CFG_MIDI_HOST_DEVSTRINGS + TU_LOG1("Found element; strings not supported\r\n"); + #else + TU_LOG2("Found element\r\n"); + #endif + } + else + { + TU_LOG2("Unknown CS Interface sub-type %u\r\n", p_mdij->bDescriptorSubType); + TU_VERIFY(false); // unknown CS Interface sub-type + } + len_parsed += p_mdij->bLength; + } + else if (p_mdh->bDescriptorType == TUSB_DESC_CS_ENDPOINT) + { + TU_LOG2("found CS_ENDPOINT Descriptor for %u\r\n", prev_ep_addr); + TU_VERIFY(prev_ep_addr != 0); + // parse out the mapping between the device's embedded jacks and the endpoints + // Each embedded IN jack is assocated with an OUT endpoint + midi_cs_desc_endpoint_t const* p_csep = (midi_cs_desc_endpoint_t const*)p_mdh; + if (tu_edpt_dir(prev_ep_addr) == TUSB_DIR_OUT) + { + TU_VERIFY(p_midi_host->ep_out == prev_ep_addr); + TU_VERIFY(p_midi_host->num_cables_tx == 0); + p_midi_host->num_cables_tx = p_csep->bNumEmbMIDIJack; +#if CFG_MIDI_HOST_DEVSTRINGS + uint8_t jack; + uint8_t max_jack = p_midi_host->num_cables_tx; + if (max_jack > sizeof(p_midi_host->ep_out_associated_jacks)) + { + max_jack = sizeof(p_midi_host->ep_out_associated_jacks); + } + for (jack = 0; jack < max_jack; jack++) + { + p_midi_host->ep_out_associated_jacks[jack] = p_csep->baAssocJackID[jack]; + } +#endif + } + else + { + TU_VERIFY(p_midi_host->ep_in == prev_ep_addr); + TU_VERIFY(p_midi_host->num_cables_rx == 0); + p_midi_host->num_cables_rx = p_csep->bNumEmbMIDIJack; +#if CFG_MIDI_HOST_DEVSTRINGS + uint8_t jack; + uint8_t max_jack = p_midi_host->num_cables_rx; + if (max_jack > sizeof(p_midi_host->ep_in_associated_jacks)) + { + max_jack = sizeof(p_midi_host->ep_in_associated_jacks); + } + for (jack = 0; jack < max_jack; jack++) + { + p_midi_host->ep_in_associated_jacks[jack] = p_csep->baAssocJackID[jack]; + } +#endif + } + len_parsed += p_csep->bLength; + prev_ep_addr = 0; + } + else if (p_mdh->bDescriptorType == TUSB_DESC_ENDPOINT) { + // parse out the bulk endpoint info + tusb_desc_endpoint_t const *p_ep = (tusb_desc_endpoint_t const *)p_mdh; + TU_LOG2("found ENDPOINT Descriptor for %u\r\n", p_ep->bEndpointAddress); + if (tu_edpt_dir(p_ep->bEndpointAddress) == TUSB_DIR_OUT) + { + TU_VERIFY(p_midi_host->ep_out == 0); + TU_VERIFY(p_midi_host->num_cables_tx == 0); + p_midi_host->ep_out = p_ep->bEndpointAddress; + p_midi_host->ep_out_max = p_ep->wMaxPacketSize; + if (p_midi_host->ep_out_max > CFG_TUH_MIDI_TX_BUFSIZE) + p_midi_host->ep_out_max = CFG_TUH_MIDI_TX_BUFSIZE; + prev_ep_addr = p_midi_host->ep_out; + out_desc = p_ep; + } + else + { + TU_VERIFY(p_midi_host->ep_in == 0); + TU_VERIFY(p_midi_host->num_cables_rx == 0); + p_midi_host->ep_in = p_ep->bEndpointAddress; + p_midi_host->ep_in_max = p_ep->wMaxPacketSize; + if (p_midi_host->ep_in_max > CFG_TUH_MIDI_RX_BUFSIZE) + p_midi_host->ep_in_max = CFG_TUH_MIDI_RX_BUFSIZE; + prev_ep_addr = p_midi_host->ep_in; + in_desc = p_ep; + } + len_parsed += p_mdh->bLength; + } + p_desc = tu_desc_next(p_desc); + p_mdh = (midi_desc_header_t const *)p_desc; + } + TU_VERIFY((p_midi_host->ep_out != 0 && p_midi_host->num_cables_tx != 0) || + (p_midi_host->ep_in != 0 && p_midi_host->num_cables_rx != 0)); + TU_LOG1("MIDI descriptor parsed successfully\r\n"); + // remove duplicate string indices + for (int idx=0; idx < p_midi_host->num_string_indices; idx++) { + for (int jdx = idx+1; jdx < p_midi_host->num_string_indices; jdx++) { + while (jdx < p_midi_host->num_string_indices && p_midi_host->all_string_indices[idx] == p_midi_host->all_string_indices[jdx]) { + // delete the duplicate by overwriting it with the last entry and reducing the number of entries by 1 + p_midi_host->all_string_indices[jdx] = p_midi_host->all_string_indices[p_midi_host->num_string_indices-1]; + --p_midi_host->num_string_indices; + } + } + } + if (in_desc) + { + TU_ASSERT(tuh_edpt_open(dev_addr, in_desc)); + // Some devices always return exactly the request length so transfers won't complete + // unless you assume every transfer is the last one. + // TODO usbh_edpt_force_last_buffer(dev_addr, p_midi_host->ep_in, true); + } + if (out_desc) + { + TU_ASSERT(tuh_edpt_open(dev_addr, out_desc)); + } + p_midi_host->dev_addr = dev_addr; + + if (tuh_midi_mount_cb) + { + tuh_midi_mount_cb(dev_addr, p_midi_host->ep_in, p_midi_host->ep_out, p_midi_host->num_cables_rx, p_midi_host->num_cables_tx); + } + return true; +} + +bool tuh_midi_configured(uint8_t dev_addr) +{ + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + TU_VERIFY(p_midi_host != NULL); + return p_midi_host->configured; +} + +bool midih_set_config(uint8_t dev_addr, uint8_t itf_num) +{ + (void) itf_num; + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + TU_VERIFY(p_midi_host != NULL); + p_midi_host->configured = true; + + // TODO I don't think there are any special config things to do for MIDI + + return true; +} + +//--------------------------------------------------------------------+ +// Stream API +//--------------------------------------------------------------------+ +static uint32_t write_flush(uint8_t dev_addr, midih_interface_t* midi) +{ + // No data to send + if ( !tu_fifo_count(&midi->tx_ff) ) return 0; + + // skip if previous transfer not complete + TU_VERIFY( usbh_edpt_claim(dev_addr, midi->ep_out) ); + + uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epout_buf, midi->ep_out_max); + + if (count) + { + TU_ASSERT( usbh_edpt_xfer(dev_addr, midi->ep_out, midi->epout_buf, count), 0 ); + return count; + }else + { + // Release endpoint since we don't make any transfer + usbh_edpt_release(dev_addr, midi->ep_out); + return 0; + } +} + +bool tuh_midi_read_poll( uint8_t dev_addr ) +{ + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + TU_VERIFY(p_midi_host != NULL); + bool result = false; + + bool in_edpt_not_busy = !usbh_edpt_busy(dev_addr, p_midi_host->ep_in); + if (in_edpt_not_busy) + { + TU_LOG2("Requesting poll IN endpoint %d\r\n", p_midi_host->ep_in); + TU_ASSERT(usbh_edpt_xfer(p_midi_host->dev_addr, p_midi_host->ep_in, _midi_host->epin_buf, _midi_host->ep_in_max), 0); + result = true; + } + else + { + // Maybe the IN endpoint is only busy because the RP2040 host hardware + // is retrying a NAK'd IN transfer forever. Try aborting the NAK'd + // transfer to allow other transfers to happen on the one shared + // epx endpoint. + // TODO for RP2040 USB shared endpoint: usbh_edpt_clear_in_on_nak(p_midi_host->dev_addr, p_midi_host->ep_in); + } + return result; +} + +uint32_t tuh_midi_stream_write (uint8_t dev_addr, uint8_t cable_num, uint8_t const* buffer, uint32_t bufsize) +{ + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + TU_VERIFY(p_midi_host != NULL); + TU_VERIFY(cable_num < p_midi_host->num_cables_tx); + midi_stream_t *stream = &p_midi_host->stream_write; + + uint32_t i = 0; + while ( (i < bufsize) && (tu_fifo_remaining(&p_midi_host->tx_ff) >= 4) ) + { + uint8_t const data = buffer[i]; + i++; + if (data >= MIDI_STATUS_SYSREAL_TIMING_CLOCK) + { + // real-time messages need to be sent right away + midi_stream_t streamrt; + streamrt.buffer[0] = MIDI_CIN_SYSEX_END_1BYTE; + streamrt.buffer[1] = data; + streamrt.index = 2; + streamrt.total = 2; + uint16_t const count = tu_fifo_write_n(&p_midi_host->tx_ff, streamrt.buffer, 4); + // FIFO overflown, since we already check fifo remaining. It is probably race condition + TU_ASSERT(count == 4, i); + } + else if ( stream->index == 0 ) + { + //------------- New event packet -------------// + + uint8_t const msg = data >> 4; + + stream->index = 2; + stream->buffer[1] = data; + + // Check to see if we're still in a SysEx transmit. + if ( stream->buffer[0] == MIDI_CIN_SYSEX_START ) + { + if ( data == MIDI_STATUS_SYSEX_END ) + { + stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE; + stream->total = 2; + } + else + { + stream->total = 4; + } + } + else if ( (msg >= 0x8 && msg <= 0xB) || msg == 0xE ) + { + // Channel Voice Messages + stream->buffer[0] = (cable_num << 4) | msg; + stream->total = 4; + } + else if ( msg == 0xC || msg == 0xD) + { + // Channel Voice Messages, two-byte variants (Program Change and Channel Pressure) + stream->buffer[0] = (cable_num << 4) | msg; + stream->total = 3; + } + else if ( msg == 0xf ) + { + // System message + if ( data == MIDI_STATUS_SYSEX_START ) + { + stream->buffer[0] = MIDI_CIN_SYSEX_START; + stream->total = 4; + } + else if ( data == MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME || data == MIDI_STATUS_SYSCOM_SONG_SELECT ) + { + stream->buffer[0] = MIDI_CIN_SYSCOM_2BYTE; + stream->total = 3; + } + else if ( data == MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER ) + { + stream->buffer[0] = MIDI_CIN_SYSCOM_3BYTE; + stream->total = 4; + } + else + { + stream->buffer[0] = MIDI_CIN_SYSEX_END_1BYTE; + stream->total = 2; + } + } + else + { + // Pack individual bytes if we don't support packing them into words. + stream->buffer[0] = cable_num << 4 | 0xf; + stream->buffer[2] = 0; + stream->buffer[3] = 0; + stream->index = 2; + stream->total = 2; + } + } + else + { + //------------- On-going (buffering) packet -------------// + + TU_ASSERT(stream->index < 4, i); + stream->buffer[stream->index] = data; + stream->index++; + // See if this byte ends a SysEx. + if ( stream->buffer[0] == MIDI_CIN_SYSEX_START && data == MIDI_STATUS_SYSEX_END ) + { + stream->buffer[0] = MIDI_CIN_SYSEX_START + (stream->index - 1); + stream->total = stream->index; + } + } + + // Send out packet + if ( stream->index >= 2 && stream->index == stream->total ) + { + // zeroes unused bytes + for(uint8_t idx = stream->total; idx < 4; idx++) stream->buffer[idx] = 0; + TU_LOG3_MEM(stream->buffer, 4, 2); + + uint16_t const count = tu_fifo_write_n(&p_midi_host->tx_ff, stream->buffer, 4); + + // complete current event packet, reset stream + stream->index = 0; + stream->total = 0; + + // FIFO overflown, since we already check fifo remaining. It is probably race condition + TU_ASSERT(count == 4, i); + } + } + return i; +} + +bool tuh_midi_packet_write (uint8_t dev_addr, uint8_t const packet[4]) +{ + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + TU_VERIFY(p_midi_host != NULL); + + if (tu_fifo_remaining(&p_midi_host->tx_ff) < 4) + { + return false; + } + + tu_fifo_write_n(&p_midi_host->tx_ff, packet, 4); + + return true; +} + +uint32_t tuh_midi_stream_flush( uint8_t dev_addr ) +{ + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + TU_VERIFY(p_midi_host != NULL); + + uint32_t bytes_flushed = 0; + if (!usbh_edpt_busy(p_midi_host->dev_addr, p_midi_host->ep_out)) + { + bytes_flushed = write_flush(dev_addr, p_midi_host); + } + return bytes_flushed; +} +//--------------------------------------------------------------------+ +// Helper +//--------------------------------------------------------------------+ +uint8_t tuh_midih_get_num_tx_cables (uint8_t dev_addr) +{ + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + TU_VERIFY(p_midi_host != NULL); + TU_VERIFY(p_midi_host->ep_out != 0); // returns 0 if fails + return p_midi_host->num_cables_tx; +} + +uint8_t tuh_midih_get_num_rx_cables (uint8_t dev_addr) +{ + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + TU_VERIFY(p_midi_host != NULL); + TU_VERIFY(p_midi_host->ep_in != 0); // returns 0 if fails + return p_midi_host->num_cables_rx; +} + +bool tuh_midi_packet_read (uint8_t dev_addr, uint8_t packet[4]) +{ + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + TU_VERIFY(p_midi_host != NULL); + TU_VERIFY(tu_fifo_count(&p_midi_host->rx_ff) >= 4); + return tu_fifo_read_n(&p_midi_host->rx_ff, packet, 4) == 4; +} + +uint32_t tuh_midi_stream_read (uint8_t dev_addr, uint8_t *p_cable_num, uint8_t *p_buffer, uint16_t bufsize) +{ + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + TU_VERIFY(p_midi_host != NULL); + uint32_t bytes_buffered = 0; + TU_ASSERT(p_cable_num); + TU_ASSERT(p_buffer); + TU_ASSERT(bufsize); + uint8_t one_byte; + if (!tu_fifo_peek(&p_midi_host->rx_ff, &one_byte)) + { + return 0; + } + *p_cable_num = (one_byte >> 4) & 0xf; + uint32_t nread = tu_fifo_read_n(&p_midi_host->rx_ff, p_midi_host->stream_read.buffer, 4); + static uint16_t cable_sysex_in_progress; // bit i is set if received MIDI_STATUS_SYSEX_START but not MIDI_STATUS_SYSEX_END + while (nread == 4 && bytes_buffered < bufsize) + { + *p_cable_num=(p_midi_host->stream_read.buffer[0] >> 4) & 0x0f; + uint8_t bytes_to_add_to_stream = 0; + if (*p_cable_num < p_midi_host->num_cables_rx) + { + // ignore the CIN field; too many devices out there encode this wrong + uint8_t status = p_midi_host->stream_read.buffer[1]; + uint16_t cable_mask = 1 << *p_cable_num; + if (status <= MIDI_MAX_DATA_VAL || status == MIDI_STATUS_SYSEX_START) + { + if (status == MIDI_STATUS_SYSEX_START) + { + cable_sysex_in_progress |= cable_mask; + } + // only add the packet if a sysex message is in progress + if (cable_sysex_in_progress & cable_mask) + { + ++bytes_to_add_to_stream; + uint8_t idx; + for (idx = 2; idx < 4; idx++) + { + if (p_midi_host->stream_read.buffer[idx] <= MIDI_MAX_DATA_VAL) + { + ++bytes_to_add_to_stream; + } + else if (p_midi_host->stream_read.buffer[idx] == MIDI_STATUS_SYSEX_END) + { + ++bytes_to_add_to_stream; + cable_sysex_in_progress &= ~cable_mask; + idx = 4; // force the loop to exit; I hate break statements in loops + } + } + } + } + else if (status < MIDI_STATUS_SYSEX_START) + { + // then it is a channel message either three bytes or two + uint8_t fake_cin = (status & 0xf0) >> 4; + switch (fake_cin) + { + case MIDI_CIN_NOTE_OFF: + case MIDI_CIN_NOTE_ON: + case MIDI_CIN_POLY_KEYPRESS: + case MIDI_CIN_CONTROL_CHANGE: + case MIDI_CIN_PITCH_BEND_CHANGE: + bytes_to_add_to_stream = 3; + break; + case MIDI_CIN_PROGRAM_CHANGE: + case MIDI_CIN_CHANNEL_PRESSURE: + bytes_to_add_to_stream = 2; + break; + default: + break; // Should not get this + } + cable_sysex_in_progress &= ~cable_mask; + } + else if (status < MIDI_STATUS_SYSREAL_TIMING_CLOCK) + { + switch (status) + { + case MIDI_STATUS_SYSCOM_TIME_CODE_QUARTER_FRAME: + case MIDI_STATUS_SYSCOM_SONG_SELECT: + bytes_to_add_to_stream = 2; + break; + case MIDI_STATUS_SYSCOM_SONG_POSITION_POINTER: + bytes_to_add_to_stream = 3; + break; + case MIDI_STATUS_SYSCOM_TUNE_REQUEST: + case MIDI_STATUS_SYSEX_END: + bytes_to_add_to_stream = 1; + break; + default: + break; + cable_sysex_in_progress &= ~cable_mask; + } + } + else + { + // Real-time message: can be inserted into a sysex message, + // so do don't clear cable_sysex_in_progress bit + bytes_to_add_to_stream = 1; + } + } + uint8_t idx; + for (idx = 1; idx <= bytes_to_add_to_stream; idx++) + { + *p_buffer++ = p_midi_host->stream_read.buffer[idx]; + } + bytes_buffered += bytes_to_add_to_stream; + nread = 0; + if (tu_fifo_peek(&p_midi_host->rx_ff, &one_byte)) + { + uint8_t new_cable = (one_byte >> 4) & 0xf; + if (new_cable == *p_cable_num) + { + // still on the same cable. Continue reading the stream + nread = tu_fifo_read_n(&p_midi_host->rx_ff, p_midi_host->stream_read.buffer, 4); + } + } + } + + return bytes_buffered; +} + +uint8_t tuh_midi_get_num_rx_cables(uint8_t dev_addr) +{ + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + TU_VERIFY(p_midi_host != NULL); + uint8_t num_cables = 0; + if (p_midi_host) + { + num_cables = p_midi_host->num_cables_rx; + } + return num_cables; +} + +uint8_t tuh_midi_get_num_tx_cables(uint8_t dev_addr) +{ + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + TU_VERIFY(p_midi_host != NULL); + uint8_t num_cables = 0; + if (p_midi_host) + { + num_cables = p_midi_host->num_cables_tx; + } + return num_cables; +} + +#if CFG_MIDI_HOST_DEVSTRINGS +static uint8_t find_string_index(midih_interface_t *ptr, uint8_t jack_id) +{ + uint8_t index = 0; + uint8_t assoc; + for (assoc = 0; index == 0 && assoc < ptr->next_in_jack; assoc++) + { + if (jack_id == ptr->in_jack_info[assoc].jack_id) + { + index = ptr->in_jack_info[assoc].string_index; + } + } + for (assoc = 0; index == 0 && assoc < ptr->next_out_jack; assoc++) + { + if (jack_id == ptr->out_jack_info[assoc].jack_id) + { + index = ptr->out_jack_info[assoc].string_index; + } + } + return index; +} +#endif + +#if CFG_MIDI_HOST_DEVSTRINGS +uint8_t tuh_midi_get_rx_cable_istrings(uint8_t dev_addr, uint8_t* istrings, uint8_t max_istrings) +{ + uint8_t nstrings = 0; + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + TU_VERIFY(p_midi_host != NULL); + nstrings = p_midi_host->num_cables_rx; + if (nstrings > max_istrings) + { + nstrings = max_istrings; + } + uint8_t jack; + for (jack=0; jackep_in_associated_jacks[jack]; + istrings[jack] = find_string_index(p_midi_host, jack_id); + } + return nstrings; +} + +uint8_t tuh_midi_get_tx_cable_istrings(uint8_t dev_addr, uint8_t* istrings, uint8_t max_istrings) +{ + uint8_t nstrings = 0; + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + TU_VERIFY(p_midi_host != NULL); + nstrings = p_midi_host->num_cables_tx; + if (nstrings > max_istrings) + { + nstrings = max_istrings; + } + uint8_t jack; + for (jack=0; jackep_out_associated_jacks[jack]; + istrings[jack] = find_string_index(p_midi_host, jack_id); + } + return nstrings; +} +#endif + +uint8_t tuh_midi_get_all_istrings(uint8_t dev_addr, const uint8_t** istrings) +{ + midih_interface_t *p_midi_host = get_midi_host(dev_addr); + TU_VERIFY(p_midi_host != NULL); + uint8_t nstrings = p_midi_host->num_string_indices; + if (nstrings) + *istrings = p_midi_host->all_string_indices; + return nstrings; +} +#endif diff --git a/src/class/midi/midi_host.h b/src/class/midi/midi_host.h new file mode 100644 index 000000000..fc661939a --- /dev/null +++ b/src/class/midi/midi_host.h @@ -0,0 +1,137 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _TUSB_MIDI_HOST_H_ +#define _TUSB_MIDI_HOST_H_ + +#include "class/audio/audio.h" +#include "midi.h" + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Class Driver Configuration +//--------------------------------------------------------------------+ + +// TODO Highspeed bulk transfer can be up to 512 bytes +#ifndef CFG_TUH_HID_EPIN_BUFSIZE +#define CFG_TUH_HID_EPIN_BUFSIZE 64 +#endif + +#ifndef CFG_TUH_HID_EPOUT_BUFSIZE +#define CFG_TUH_HID_EPOUT_BUFSIZE 64 +#endif + + +//--------------------------------------------------------------------+ +// Application API (Single Interface) +//--------------------------------------------------------------------+ +bool tuh_midi_configured (uint8_t dev_addr); +uint32_t tuh_midi_available (uint8_t dev_addr); + +// return the number of virtual midi cables on the device's OUT endpoint +uint8_t tuh_midih_get_num_tx_cables (uint8_t dev_addr); + +// return the number of virtual midi cables on the device's IN endpoint +uint8_t tuh_midih_get_num_rx_cables (uint8_t dev_addr); + +// request available data from the device. tuh_midi_message_received_cb() will +// be called if the device has any data to send. Otherwise, the device will +// respond NAK. This function blocks until the transfer completes or the +// devices sends NAK. +// This function will return false if the hardware is busy. +bool tuh_midi_read_poll( uint8_t dev_addr ); + +// Queue a packet to the device. The application +// must call tuh_midi_stream_flush to actually have the +// data go out. It is up to the application to properly +// format this packet; this function does not check. +// Returns true if the packet was successfully queued. +bool tuh_midi_packet_write (uint8_t dev_addr, uint8_t const packet[4]); + +// Queue a message to the device. The application +// must call tuh_midi_stream_flush to actually have the +// data go out. +uint32_t tuh_midi_stream_write (uint8_t dev_addr, uint8_t cable_num, uint8_t const* p_buffer, uint32_t bufsize); + +// Send any queued packets to the device if the host hardware is able to do it +// Returns the number of bytes flushed to the host hardware or 0 if +// the host hardware is busy or there is nothing in queue to send. +uint32_t tuh_midi_stream_flush( uint8_t dev_addr); + +// Get the MIDI stream from the device. Set the value pointed +// to by p_cable_num to the MIDI cable number intended to receive it. +// The MIDI stream will be stored in the buffer pointed to by p_buffer. +// Return the number of bytes added to the buffer. +// Note that this function ignores the CIN field of the MIDI packet +// because a number of commercial devices out there do not encode +// it properly. +uint32_t tuh_midi_stream_read (uint8_t dev_addr, uint8_t *p_cable_num, uint8_t *p_buffer, uint16_t bufsize); + +// Read a raw MIDI packet from the connected device +// This function does not parse the packet format +// Return true if a packet was returned +bool tuh_midi_packet_read (uint8_t dev_addr, uint8_t packet[4]); + +uint8_t tuh_midi_get_num_rx_cables(uint8_t dev_addr); +uint8_t tuh_midi_get_num_tx_cables(uint8_t dev_addr); +#if CFG_MIDI_HOST_DEVSTRINGS +uint8_t tuh_midi_get_rx_cable_istrings(uint8_t dev_addr, uint8_t* istrings, uint8_t max_istrings); +uint8_t tuh_midi_get_tx_cable_istrings(uint8_t dev_addr, uint8_t* istrings, uint8_t max_istrings); +uint8_t tuh_midi_get_all_istrings(uint8_t dev_addr, const uint8_t** istrings); +#endif +//--------------------------------------------------------------------+ +// Internal Class Driver API +//--------------------------------------------------------------------+ +void midih_init (void); +bool midih_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len); +bool midih_set_config (uint8_t dev_addr, uint8_t itf_num); +bool midih_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); +void midih_close (uint8_t dev_addr); + +//--------------------------------------------------------------------+ +// Callbacks (Weak is optional) +//--------------------------------------------------------------------+ + +// Invoked when device with MIDI interface is mounted. +// If the MIDI host application requires MIDI IN, it should requst an +// IN transfer here. The device will likely NAK this transfer. How the driver +// handles the NAK is hardware dependent. +TU_ATTR_WEAK void tuh_midi_mount_cb(uint8_t dev_addr, uint8_t in_ep, uint8_t out_ep, uint8_t num_cables_rx, uint16_t num_cables_tx); + +// Invoked when device with MIDI interface is un-mounted +// For now, the instance parameter is always 0 and can be ignored +TU_ATTR_WEAK void tuh_midi_umount_cb(uint8_t dev_addr, uint8_t instance); + +TU_ATTR_WEAK void tuh_midi_rx_cb(uint8_t dev_addr, uint32_t num_packets); +TU_ATTR_WEAK void tuh_midi_tx_cb(uint8_t dev_addr); +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_MIDI_HOST_H_ */ diff --git a/src/host/hcd.h b/src/host/hcd.h index deebc59d4..97b668d79 100644 --- a/src/host/hcd.h +++ b/src/host/hcd.h @@ -40,7 +40,7 @@ //--------------------------------------------------------------------+ #ifndef CFG_TUH_ENDPOINT_MAX - #define CFG_TUH_ENDPOINT_MAX (CFG_TUH_HUB + CFG_TUH_HID*2 + CFG_TUH_MSC*2 + CFG_TUH_CDC*3) + #define CFG_TUH_ENDPOINT_MAX (CFG_TUH_HUB + CFG_TUH_HID*2 + CFG_TUH_MSC*2 + CFG_TUH_CDC*3 + CFG_TUH_MIDI*2) // #ifdef TUP_HCD_ENDPOINT_MAX // #define CFG_TUH_ENDPPOINT_MAX TUP_HCD_ENDPOINT_MAX // #else diff --git a/src/host/usbh.c b/src/host/usbh.c index 9d618db92..c59f31596 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -166,6 +166,17 @@ static usbh_class_driver_t const usbh_class_drivers[] = }, #endif + #if CFG_TUH_MIDI + { + DRIVER_NAME("MIDI") + .init = midih_init, + .open = midih_open, + .set_config = midih_set_config, + .xfer_cb = midih_xfer_cb, + .close = midih_close + }, + #endif + #if CFG_TUH_HUB { DRIVER_NAME("HUB") diff --git a/src/tusb.h b/src/tusb.h index b776d7d01..093c053f7 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -56,6 +56,10 @@ #include "class/cdc/cdc_host.h" #endif + #if CFG_TUH_MIDI + #include "class/midi/midi_host.h" + #endif + #if CFG_TUH_VENDOR #include "class/vendor/vendor_host.h" #endif -- cgit v1.3.1 From 70eefcb849542f386a1349da7a4944d2d02dcd40 Mon Sep 17 00:00:00 2001 From: rppicomidi Date: Thu, 25 Aug 2022 15:08:00 -0700 Subject: Add hooks to allow cloning an attached USB device descriptor --- src/device/usbd_control.c | 2 +- src/host/usbh.c | 7 +++++-- src/host/usbh.h | 6 +++++- src/tusb_option.h | 6 +++--- 4 files changed, 14 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c index 0995ef669..187547ae3 100644 --- a/src/device/usbd_control.c +++ b/src/device/usbd_control.c @@ -56,7 +56,7 @@ typedef struct static usbd_control_xfer_t _ctrl_xfer; CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN -static uint8_t _usbd_ctrl_buf[CFG_TUD_ENDPOINT0_SIZE]; +static uint8_t _usbd_ctrl_buf[/*CFG_TUD_ENDPOINT0_SIZE*/ 64]; //--------------------------------------------------------------------+ // Application API diff --git a/src/host/usbh.c b/src/host/usbh.c index c59f31596..44b4d3217 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -1323,7 +1323,7 @@ static void process_enumeration(tuh_xfer_t* xfer) dev->i_product = desc_device->iProduct; dev->i_serial = desc_device->iSerialNumber; - // if (tuh_attach_cb) tuh_attach_cb((tusb_desc_device_t*) _usbh_ctrl_buf); + if (tuh_desc_device_cb) tuh_desc_device_cb(daddr, (tusb_desc_device_t const*) _usbh_ctrl_buf); // Get 9-byte for total length uint8_t const config_idx = CONFIG_NUM - 1; @@ -1350,9 +1350,12 @@ static void process_enumeration(tuh_xfer_t* xfer) break; case ENUM_SET_CONFIG: + // Got the whole configuration descriptor. Make a copy + if (tuh_desc_config_cb) tuh_desc_config_cb(daddr, (const tusb_desc_configuration_t*) _usbh_ctrl_buf); + // Parse configuration & set up drivers // Driver open aren't allowed to make any usb transfer yet - TU_ASSERT( _parse_configuration_descriptor(daddr, (tusb_desc_configuration_t*) _usbh_ctrl_buf), ); + TU_ASSERT( _parse_configuration_descriptor(daddr, (const tusb_desc_configuration_t* ) _usbh_ctrl_buf), ); TU_ASSERT( tuh_configuration_set(daddr, CONFIG_NUM, process_enumeration, ENUM_CONFIG_DRIVER), ); break; diff --git a/src/host/usbh.h b/src/host/usbh.h index 560a1ea23..7355c83ee 100644 --- a/src/host/usbh.h +++ b/src/host/usbh.h @@ -78,7 +78,11 @@ enum // APPLICATION CALLBACK //--------------------------------------------------------------------+ -//TU_ATTR_WEAK uint8_t tuh_attach_cb (tusb_desc_device_t const *desc_device); +// Give the application an opportunity to grab the device descriptor +TU_ATTR_WEAK void tuh_desc_device_cb(uint8_t daddr, const tusb_desc_device_t *desc_device); + +// Give the application an opportunity to grab the configuration descriptor +TU_ATTR_WEAK void tuh_desc_config_cb(uint8_t daddr, const tusb_desc_configuration_t *desc_config); // Invoked when device is mounted (configured) TU_ATTR_WEAK void tuh_mount_cb (uint8_t daddr); diff --git a/src/tusb_option.h b/src/tusb_option.h index f95ae6273..b905fc7ce 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -427,9 +427,9 @@ typedef int make_iso_compilers_happy; //------------------------------------------------------------------ // Configuration Validation //------------------------------------------------------------------ -#if CFG_TUD_ENDPOINT0_SIZE > 64 - #error Control Endpoint Max Packet Size cannot be larger than 64 -#endif +//#if CFG_TUD_ENDPOINT0_SIZE > 64 +// #error Control Endpoint Max Packet Size cannot be larger than 64 +//#endif #endif /* _TUSB_OPTION_H_ */ -- cgit v1.3.1 From 3325e2613d4bc68ac2443445e62520baa4bbe6cb Mon Sep 17 00:00:00 2001 From: rppicomidi Date: Thu, 25 Aug 2022 15:09:27 -0700 Subject: Make USB MIDI device code allow a device with no Audio Control interface --- src/class/midi/midi_device.c | 26 ++++++++++++++++---------- src/device/usbd.c | 10 +++++++++- 2 files changed, 25 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/class/midi/midi_device.c b/src/class/midi/midi_device.c index de41706e8..c6c99db29 100644 --- a/src/class/midi/midi_device.c +++ b/src/class/midi/midi_device.c @@ -406,19 +406,25 @@ void midid_reset(uint8_t rhport) uint16_t midid_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len) { + uint16_t drv_len = 0; + uint8_t const * p_desc = (uint8_t const *)desc_itf; // 1st Interface is Audio Control v1 - TU_VERIFY(TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass && + if (TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass && AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass && - AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol, 0); - - uint16_t drv_len = tu_desc_len(desc_itf); - uint8_t const * p_desc = tu_desc_next(desc_itf); - - // Skip Class Specific descriptors - while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len ) + AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) { - drv_len += tu_desc_len(p_desc); - p_desc = tu_desc_next(p_desc); + drv_len = tu_desc_len(desc_itf); + p_desc = tu_desc_next(desc_itf); + // Skip Class Specific descriptors + while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len ) + { + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + } + else + { + TU_LOG1("Warning: MIDI Device has no Audio Control Interface"); } // 2nd Interface is MIDI Streaming diff --git a/src/device/usbd.c b/src/device/usbd.c index c199e647e..1cede1289 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -920,7 +920,15 @@ static bool process_set_config(uint8_t rhport, uint8_t cfg_num) #endif #if CFG_TUD_MIDI - if ( driver->open == midid_open ) assoc_itf_count = 2; + if ( driver->open == midid_open ) + { + // If there is a class-compliant Audio Control Class, then 2 interfaces + // Otherwise, only one + if (TUSB_CLASS_AUDIO == desc_itf->bInterfaceClass && + AUDIO_SUBCLASS_CONTROL == desc_itf->bInterfaceSubClass && + AUDIO_FUNC_PROTOCOL_CODE_UNDEF == desc_itf->bInterfaceProtocol) + assoc_itf_count = 2; + } #endif #if CFG_TUD_BTH && CFG_TUD_BTH_ISO_ALT_COUNT -- cgit v1.3.1 From 4fbf99621d5a5b8b5de698a792578a2066c667f3 Mon Sep 17 00:00:00 2001 From: atok Date: Thu, 8 Sep 2022 17:51:01 +0200 Subject: Get rid of implicit uint conversions --- src/class/midi/midi_host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c index e482f5265..a6ad1303a 100644 --- a/src/class/midi/midi_host.c +++ b/src/class/midi/midi_host.c @@ -100,7 +100,7 @@ typedef struct #define MAX_OUT_JACKS 8 struct { uint8_t jack_id; - int8_t jack_type; + uint8_t jack_type; uint8_t string_index; } in_jack_info[MAX_IN_JACKS]; uint8_t next_in_jack; -- cgit v1.3.1 From 19563b441463aed9b3d622f2d57d61082199fbdd Mon Sep 17 00:00:00 2001 From: atok Date: Thu, 8 Sep 2022 21:13:55 +0200 Subject: Simple MIDI rx example --- examples/host/CMakeLists.txt | 1 + examples/host/midi_rx/CMakeLists.txt | 27 ++++++ examples/host/midi_rx/only.txt | 1 + examples/host/midi_rx/src/main.c | 152 ++++++++++++++++++++++++++++++++ examples/host/midi_rx/src/tusb_config.h | 96 ++++++++++++++++++++ src/class/midi/midi_host.c | 4 +- 6 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 examples/host/midi_rx/CMakeLists.txt create mode 100644 examples/host/midi_rx/only.txt create mode 100644 examples/host/midi_rx/src/main.c create mode 100644 examples/host/midi_rx/src/tusb_config.h (limited to 'src') diff --git a/examples/host/CMakeLists.txt b/examples/host/CMakeLists.txt index 5c63ec0c0..39a41c4ae 100644 --- a/examples/host/CMakeLists.txt +++ b/examples/host/CMakeLists.txt @@ -8,3 +8,4 @@ family_initialize_project(tinyusb_host_examples ${CMAKE_CURRENT_LIST_DIR}) # family_add_subdirectory will filter what to actually add based on selected FAMILY family_add_subdirectory(cdc_msc_hid) family_add_subdirectory(hid_controller) +family_add_subdirectory(midi_rx) \ No newline at end of file diff --git a/examples/host/midi_rx/CMakeLists.txt b/examples/host/midi_rx/CMakeLists.txt new file mode 100644 index 000000000..4856e0a47 --- /dev/null +++ b/examples/host/midi_rx/CMakeLists.txt @@ -0,0 +1,27 @@ +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 + ) + +# 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_host_example(${PROJECT}) \ No newline at end of file diff --git a/examples/host/midi_rx/only.txt b/examples/host/midi_rx/only.txt new file mode 100644 index 000000000..2ba4438fd --- /dev/null +++ b/examples/host/midi_rx/only.txt @@ -0,0 +1 @@ +mcu:RP2040 diff --git a/examples/host/midi_rx/src/main.c b/examples/host/midi_rx/src/main.c new file mode 100644 index 000000000..64428932f --- /dev/null +++ b/examples/host/midi_rx/src/main.c @@ -0,0 +1,152 @@ +/* + * 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 "bsp/board.h" +#include "tusb.h" +#include "class/midi/midi_host.h" + + +#if CFG_TUH_MIDI + +//--------------------------------------------------------------------+ +// STATIC GLOBALS DECLARATION +//--------------------------------------------------------------------+ +static uint8_t midi_dev_addr = 0; + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF PROTYPES +//--------------------------------------------------------------------+ +void led_blinking_task(void); +void midi_host_rx_task(void); + +/*------------- MAIN -------------*/ +int main(void) +{ + board_init(); + + printf("TinyUSB Host MIDI Example\r\n"); + + tusb_init(); + + while (1) + { + tuh_task(); + led_blinking_task(); + midi_host_rx_task(); + } + + return 0; +} + +#endif + +//--------------------------------------------------------------------+ +// Blinking Task +//--------------------------------------------------------------------+ +void led_blinking_task(void) +{ + const uint32_t interval_ms = 1000; + static uint32_t start_ms = 0; + + static bool led_state = false; + + // Blink every interval ms + if ( board_millis() - start_ms < interval_ms) return; // not enough time + start_ms += interval_ms; + + board_led_write(led_state); + led_state = 1 - led_state; // toggle +} + +//--------------------------------------------------------------------+ +// MIDI host receive task +//--------------------------------------------------------------------+ +void midi_host_rx_task(void) +{ + // device must be attached and have at least one endpoint ready to receive a message + if (!midi_dev_addr || !tuh_midi_configured(midi_dev_addr)) + { + return; + } + if (tuh_midih_get_num_rx_cables(midi_dev_addr) < 1) + { + return; + } + tuh_midi_read_poll(midi_dev_addr); +} + +//--------------------------------------------------------------------+ +// TinyUSB Callbacks +//--------------------------------------------------------------------+ + +// Invoked when device with hid interface is mounted +// Report descriptor is also available for use. tuh_hid_parse_report_descriptor() +// can be used to parse common/simple enough descriptor. +// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped +// therefore report_desc = NULL, desc_len = 0 +void tuh_midi_mount_cb(uint8_t dev_addr, uint8_t in_ep, uint8_t out_ep, uint8_t num_cables_rx, uint16_t num_cables_tx) +{ + TU_LOG1("MIDI device address = %u, IN endpoint %u has %u cables, OUT endpoint %u has %u cables\r\n", + dev_addr, in_ep & 0xf, num_cables_rx, out_ep & 0xf, num_cables_tx); + + midi_dev_addr = dev_addr; +} + +// Invoked when device with hid interface is un-mounted +void tuh_midi_umount_cb(uint8_t dev_addr, uint8_t instance) +{ + TU_LOG1("MIDI device address = %d, instance = %d is unmounted\r\n", dev_addr, instance); + + midi_dev_addr = 0; +} + +void tuh_midi_rx_cb(uint8_t dev_addr, uint32_t num_packets) +{ + if (midi_dev_addr != dev_addr) + { + return; + } + + if(num_packets == 0) + { + return; + } + + uint8_t cable_num; + uint8_t buffer[48]; + uint32_t bytes_read = tuh_midi_stream_read(dev_addr, &cable_num, buffer, sizeof(buffer)); + + TU_LOG1("Read bytes %u cable %u", bytes_read, cable_num); + TU_LOG1_MEM(buffer, bytes_read, 2); +} + +void tuh_midi_tx_cb(uint8_t dev_addr) +{ + (void ) dev_addr; +} \ No newline at end of file diff --git a/examples/host/midi_rx/src/tusb_config.h b/examples/host/midi_rx/src/tusb_config.h new file mode 100644 index 000000000..5cc354ea9 --- /dev/null +++ b/examples/host/midi_rx/src/tusb_config.h @@ -0,0 +1,96 @@ +/* + * 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 + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_HOST | OPT_MODE_HIGH_SPEED) +#else +#define CFG_TUSB_RHPORT0_MODE OPT_MODE_HOST +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +// CFG_TUSB_DEBUG is defined by compiler in DEBUG build +// #define CFG_TUSB_DEBUG 0 + +/* 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 + +//-------------------------------------------------------------------- +// CONFIGURATION +//-------------------------------------------------------------------- + +// Size of buffer to hold descriptors and other data used for enumeration +#define CFG_TUH_ENUMERATION_BUFSIZE 256 + +#define CFG_TUH_HUB 1 +#define CFG_TUH_CDC 0 +#define CFG_TUH_HID 0 // typical keyboard + mouse device can have 3-4 HID interfaces +#define CFG_TUH_MIDI 1 // there will be at most one MIDIStreaming Interface descriptor +#define CFG_TUH_MSC 0 +#define CFG_TUH_VENDOR 0 + +// max device support (excluding hub device) +#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports +#define CFG_MIDI_HOST_DEVSTRINGS 1 + +//------------- HID -------------// +#define CFG_TUH_HID_EPIN_BUFSIZE 64 +#define CFG_TUH_HID_EPOUT_BUFSIZE 64 + +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c index a6ad1303a..aafa340cd 100644 --- a/src/class/midi/midi_host.c +++ b/src/class/midi/midi_host.c @@ -333,10 +333,10 @@ bool midih_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *d p_midi_host->out_jack_info[p_midi_host->next_out_jack].jack_id = p_mdoj->bJackID; p_midi_host->out_jack_info[p_midi_host->next_out_jack].jack_type = p_mdoj->bJackType; p_midi_host->out_jack_info[p_midi_host->next_out_jack].num_source_ids = p_mdoj->bNrInputPins; - struct associated_jack_s { + const struct associated_jack_s { uint8_t id; uint8_t pin; - } *associated_jack = (struct associated_jack_s *)(p_desc+6); + } *associated_jack = (const struct associated_jack_s *)(p_desc+6); int jack; for (jack = 0; jack < p_mdoj->bNrInputPins; jack++) { -- cgit v1.3.1 From a6d4b64beccc586d1bfb6e4c171d560762221645 Mon Sep 17 00:00:00 2001 From: atok Date: Sun, 2 Oct 2022 09:53:37 +0200 Subject: Removed unused function prototype --- src/class/midi/midi_host.h | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/class/midi/midi_host.h b/src/class/midi/midi_host.h index fc661939a..8180e3309 100644 --- a/src/class/midi/midi_host.h +++ b/src/class/midi/midi_host.h @@ -52,7 +52,6 @@ // Application API (Single Interface) //--------------------------------------------------------------------+ bool tuh_midi_configured (uint8_t dev_addr); -uint32_t tuh_midi_available (uint8_t dev_addr); // return the number of virtual midi cables on the device's OUT endpoint uint8_t tuh_midih_get_num_tx_cables (uint8_t dev_addr); -- cgit v1.3.1 From 21e003af2234b3211fb9b4da42202c79949e4caa Mon Sep 17 00:00:00 2001 From: atoktoto Date: Sun, 13 Nov 2022 14:38:33 +0100 Subject: Added explicit conversions --- src/class/midi/midi_host.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c index aafa340cd..d242d33c5 100644 --- a/src/class/midi/midi_host.c +++ b/src/class/midi/midi_host.c @@ -592,13 +592,13 @@ uint32_t tuh_midi_stream_write (uint8_t dev_addr, uint8_t cable_num, uint8_t con else if ( (msg >= 0x8 && msg <= 0xB) || msg == 0xE ) { // Channel Voice Messages - stream->buffer[0] = (cable_num << 4) | msg; + stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg); stream->total = 4; } else if ( msg == 0xC || msg == 0xD) { // Channel Voice Messages, two-byte variants (Program Change and Channel Pressure) - stream->buffer[0] = (cable_num << 4) | msg; + stream->buffer[0] = (uint8_t) ((cable_num << 4) | msg); stream->total = 3; } else if ( msg == 0xf ) @@ -628,7 +628,7 @@ uint32_t tuh_midi_stream_write (uint8_t dev_addr, uint8_t cable_num, uint8_t con else { // Pack individual bytes if we don't support packing them into words. - stream->buffer[0] = cable_num << 4 | 0xf; + stream->buffer[0] = (uint8_t) (cable_num << 4 | 0xf); stream->buffer[2] = 0; stream->buffer[3] = 0; stream->index = 2; @@ -748,7 +748,7 @@ uint32_t tuh_midi_stream_read (uint8_t dev_addr, uint8_t *p_cable_num, uint8_t * { // ignore the CIN field; too many devices out there encode this wrong uint8_t status = p_midi_host->stream_read.buffer[1]; - uint16_t cable_mask = 1 << *p_cable_num; + uint16_t cable_mask = (uint16_t) (1 << *p_cable_num); if (status <= MIDI_MAX_DATA_VAL || status == MIDI_STATUS_SYSEX_START) { if (status == MIDI_STATUS_SYSEX_START) -- cgit v1.3.1 From 0a844f2a3019df04a346232b0e54a9fc89e4c551 Mon Sep 17 00:00:00 2001 From: atoktoto Date: Sat, 19 Nov 2022 18:08:31 +0100 Subject: Fix usb hubs --- src/class/midi/midi_host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c index d242d33c5..bc87898cc 100644 --- a/src/class/midi/midi_host.c +++ b/src/class/midi/midi_host.c @@ -490,7 +490,7 @@ bool midih_set_config(uint8_t dev_addr, uint8_t itf_num) p_midi_host->configured = true; // TODO I don't think there are any special config things to do for MIDI - + usbh_driver_set_config_complete(dev_addr, p_midi_host->itf_num); return true; } -- cgit v1.3.1 From 34729b1dc46ef9cee3446e22c88e8484395f1fa7 Mon Sep 17 00:00:00 2001 From: atoktoto Date: Mon, 21 Nov 2022 10:29:26 +0100 Subject: Fix usb-hub data transfer problems by AndrewCapon --- src/class/midi/midi_host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c index bc87898cc..8df1b2005 100644 --- a/src/class/midi/midi_host.c +++ b/src/class/midi/midi_host.c @@ -529,7 +529,7 @@ bool tuh_midi_read_poll( uint8_t dev_addr ) if (in_edpt_not_busy) { TU_LOG2("Requesting poll IN endpoint %d\r\n", p_midi_host->ep_in); - TU_ASSERT(usbh_edpt_xfer(p_midi_host->dev_addr, p_midi_host->ep_in, _midi_host->epin_buf, _midi_host->ep_in_max), 0); + TU_ASSERT(usbh_edpt_xfer(p_midi_host->dev_addr, p_midi_host->ep_in, p_midi_host->epin_buf, p_midi_host->ep_in_max), 0); result = true; } else -- cgit v1.3.1 From 3ba03110782747dead880f334f4fd65c03cbca9d Mon Sep 17 00:00:00 2001 From: atoktoto Date: Mon, 21 Nov 2022 12:36:50 +0100 Subject: Explicit type conversion --- src/class/midi/midi_host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c index 8df1b2005..308120ea2 100644 --- a/src/class/midi/midi_host.c +++ b/src/class/midi/midi_host.c @@ -769,7 +769,7 @@ uint32_t tuh_midi_stream_read (uint8_t dev_addr, uint8_t *p_cable_num, uint8_t * else if (p_midi_host->stream_read.buffer[idx] == MIDI_STATUS_SYSEX_END) { ++bytes_to_add_to_stream; - cable_sysex_in_progress &= ~cable_mask; + cable_sysex_in_progress &= (uint16_t) ~cable_mask; idx = 4; // force the loop to exit; I hate break statements in loops } } -- cgit v1.3.1 From 89eac75085134d570dc26dffaf3d8f204aef5359 Mon Sep 17 00:00:00 2001 From: atoktoto Date: Mon, 21 Nov 2022 13:49:43 +0100 Subject: Explicit type conversion --- src/class/midi/midi_host.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/class/midi/midi_host.c b/src/class/midi/midi_host.c index 308120ea2..32311556e 100644 --- a/src/class/midi/midi_host.c +++ b/src/class/midi/midi_host.c @@ -795,7 +795,7 @@ uint32_t tuh_midi_stream_read (uint8_t dev_addr, uint8_t *p_cable_num, uint8_t * default: break; // Should not get this } - cable_sysex_in_progress &= ~cable_mask; + cable_sysex_in_progress &= (uint16_t)~cable_mask; } else if (status < MIDI_STATUS_SYSREAL_TIMING_CLOCK) { @@ -814,7 +814,7 @@ uint32_t tuh_midi_stream_read (uint8_t dev_addr, uint8_t *p_cable_num, uint8_t * break; default: break; - cable_sysex_in_progress &= ~cable_mask; + cable_sysex_in_progress &= (uint16_t)~cable_mask; } } else -- cgit v1.3.1 From 069c68ad04bd6210fc50dbc2aeb2c62448c1731d Mon Sep 17 00:00:00 2001 From: IngHK Date: Fri, 23 Feb 2024 23:27:38 +0100 Subject: sorted driver functions into Control Request, Driver API, Enumeration and Helper. no functional changes --- src/class/cdc/cdc_host.c | 359 +++++++++++++++++++++++++---------------------- 1 file changed, 189 insertions(+), 170 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 2fb37d835..26eb70c9b 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -782,92 +782,7 @@ bool cdch_set_config(uint8_t daddr, uint8_t itf_num) { // ACM //--------------------------------------------------------------------+ -enum { - CONFIG_ACM_SET_CONTROL_LINE_STATE = 0, - CONFIG_ACM_SET_LINE_CODING, - CONFIG_ACM_COMPLETE, -}; - -static bool acm_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) { - uint8_t const* p_desc_end = ((uint8_t const*) itf_desc) + max_len; - - cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc); - TU_VERIFY(p_cdc); - p_cdc->serial_drid = SERIAL_DRIVER_ACM; - - //------------- Control Interface -------------// - uint8_t const* p_desc = tu_desc_next(itf_desc); - - // Communication Functional Descriptors - while ((p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc))) { - if (CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc)) { - // save ACM bmCapabilities - p_cdc->acm_capability = ((cdc_desc_func_acm_t const*) p_desc)->bmCapabilities; - } - - p_desc = tu_desc_next(p_desc); - } - - // Open notification endpoint of control interface if any - if (itf_desc->bNumEndpoints == 1) { - TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)); - tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc; - - TU_ASSERT(tuh_edpt_open(daddr, desc_ep)); - p_cdc->ep_notif = desc_ep->bEndpointAddress; - - p_desc = tu_desc_next(p_desc); - } - - //------------- Data Interface (if any) -------------// - if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && - (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const*) p_desc)->bInterfaceClass)) { - // next to endpoint descriptor - p_desc = tu_desc_next(p_desc); - - // data endpoints expected to be in pairs - TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const*) p_desc)); - } - - return true; -} - -static void acm_process_config(tuh_xfer_t* xfer) { - uintptr_t const state = xfer->user_data; - uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); - uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); - cdch_interface_t* p_cdc = get_itf(idx); - TU_ASSERT(p_cdc,); - - switch (state) { - case CONFIG_ACM_SET_CONTROL_LINE_STATE: - #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - if (p_cdc->acm_capability.support_line_request) { - TU_ASSERT(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, acm_process_config, CONFIG_ACM_SET_LINE_CODING),); - break; - } - #endif - TU_ATTR_FALLTHROUGH; - - case CONFIG_ACM_SET_LINE_CODING: - #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM - if (p_cdc->acm_capability.support_line_request) { - cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; - TU_ASSERT(acm_set_line_coding(p_cdc, &line_coding, acm_process_config, CONFIG_ACM_COMPLETE),); - break; - } - #endif - TU_ATTR_FALLTHROUGH; - - case CONFIG_ACM_COMPLETE: - // itf_num+1 to account for data interface as well - set_config_complete(p_cdc, idx, itf_num + 1); - break; - - default: - break; - } -} +//------------- Driver API -------------// static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { TU_VERIFY(p_cdc->acm_capability.support_line_request); @@ -953,37 +868,104 @@ static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfe return acm_set_line_coding(p_cdc, &line_coding, complete_cb, user_data); } -//--------------------------------------------------------------------+ -// FTDI -//--------------------------------------------------------------------+ -#if CFG_TUH_CDC_FTDI +//------------- Enumeration -------------// enum { - CONFIG_FTDI_RESET = 0, - CONFIG_FTDI_MODEM_CTRL, - CONFIG_FTDI_SET_BAUDRATE, - CONFIG_FTDI_SET_DATA, - CONFIG_FTDI_COMPLETE + CONFIG_ACM_SET_CONTROL_LINE_STATE = 0, + CONFIG_ACM_SET_LINE_CODING, + CONFIG_ACM_COMPLETE, }; -static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) { - // FTDI Interface includes 1 vendor interface + 2 bulk endpoints - TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff && itf_desc->bNumEndpoints == 2); - TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len); +static bool acm_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) { + uint8_t const* p_desc_end = ((uint8_t const*) itf_desc) + max_len; - cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); + cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc); TU_VERIFY(p_cdc); + p_cdc->serial_drid = SERIAL_DRIVER_ACM; - TU_LOG_DRV("FTDI opened\r\n"); - p_cdc->serial_drid = SERIAL_DRIVER_FTDI; + //------------- Control Interface -------------// + uint8_t const* p_desc = tu_desc_next(itf_desc); - // endpoint pair - tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); + // Communication Functional Descriptors + while ((p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc))) { + if (CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc)) { + // save ACM bmCapabilities + p_cdc->acm_capability = ((cdc_desc_func_acm_t const*) p_desc)->bmCapabilities; + } - // data endpoints expected to be in pairs - return open_ep_stream_pair(p_cdc, desc_ep); + p_desc = tu_desc_next(p_desc); + } + + // Open notification endpoint of control interface if any + if (itf_desc->bNumEndpoints == 1) { + TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)); + tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc; + + TU_ASSERT(tuh_edpt_open(daddr, desc_ep)); + p_cdc->ep_notif = desc_ep->bEndpointAddress; + + p_desc = tu_desc_next(p_desc); + } + + //------------- Data Interface (if any) -------------// + if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && + (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const*) p_desc)->bInterfaceClass)) { + // next to endpoint descriptor + p_desc = tu_desc_next(p_desc); + + // data endpoints expected to be in pairs + TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const*) p_desc)); + } + + return true; } +static void acm_process_config(tuh_xfer_t* xfer) { + uintptr_t const state = xfer->user_data; + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t* p_cdc = get_itf(idx); + TU_ASSERT(p_cdc,); + + switch (state) { + case CONFIG_ACM_SET_CONTROL_LINE_STATE: + #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + if (p_cdc->acm_capability.support_line_request) { + TU_ASSERT(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, acm_process_config, CONFIG_ACM_SET_LINE_CODING),); + break; + } + #endif + TU_ATTR_FALLTHROUGH; + + case CONFIG_ACM_SET_LINE_CODING: + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + if (p_cdc->acm_capability.support_line_request) { + cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT(acm_set_line_coding(p_cdc, &line_coding, acm_process_config, CONFIG_ACM_COMPLETE),); + break; + } + #endif + TU_ATTR_FALLTHROUGH; + + case CONFIG_ACM_COMPLETE: + // itf_num+1 to account for data interface as well + set_config_complete(p_cdc, idx, itf_num + 1); + break; + + default: + break; + } +} + +//--------------------------------------------------------------------+ +// FTDI +//--------------------------------------------------------------------+ +#if CFG_TUH_CDC_FTDI + +static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud); + +//------------- Control Request -------------// + // set request without data static bool ftdi_sio_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { tusb_control_request_t const request = { @@ -1014,6 +996,20 @@ static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, u return ftdi_sio_set_request(p_cdc, FTDI_SIO_RESET, FTDI_SIO_RESET_SIO, complete_cb, user_data); } +//------------- Driver API -------------// + +static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate); + TU_LOG_DRV("CDC FTDI Set BaudRate = %lu, divisor = 0x%04x\r\n", baudrate, divisor); + + p_cdc->user_control_cb = complete_cb; + p_cdc->requested_line_coding.bit_rate = baudrate; + TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor, + complete_cb ? cdch_internal_control_complete : NULL, user_data)); + + return true; +} + static bool ftdi_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { (void) p_cdc; @@ -1043,40 +1039,32 @@ static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state return true; } -static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base) { - const uint8_t divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 }; - uint32_t divisor; - - /* divisor shifted 3 bits to the left */ - uint32_t divisor3 = base / (2 * baud); - divisor = (divisor3 >> 3); - divisor |= (uint32_t) divfrac[divisor3 & 0x7] << 14; +//------------- Enumeration -------------// - /* Deal with special cases for highest baud rates. */ - if (divisor == 1) { /* 1.0 */ - divisor = 0; - } - else if (divisor == 0x4001) { /* 1.5 */ - divisor = 1; - } +enum { + CONFIG_FTDI_RESET = 0, + CONFIG_FTDI_MODEM_CTRL, + CONFIG_FTDI_SET_BAUDRATE, + CONFIG_FTDI_SET_DATA, + CONFIG_FTDI_COMPLETE +}; - return divisor; -} +static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) { + // FTDI Interface includes 1 vendor interface + 2 bulk endpoints + TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff && itf_desc->bNumEndpoints == 2); + TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len); -static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud) { - return ftdi_232bm_baud_base_to_divisor(baud, 48000000u); -} + cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); + TU_VERIFY(p_cdc); -static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate); - TU_LOG_DRV("CDC FTDI Set BaudRate = %lu, divisor = 0x%04x\r\n", baudrate, divisor); + TU_LOG_DRV("FTDI opened\r\n"); + p_cdc->serial_drid = SERIAL_DRIVER_FTDI; - p_cdc->user_control_cb = complete_cb; - p_cdc->requested_line_coding.bit_rate = baudrate; - TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor, - complete_cb ? cdch_internal_control_complete : NULL, user_data)); + // endpoint pair + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); - return true; + // data endpoints expected to be in pairs + return open_ep_stream_pair(p_cdc, desc_ep); } static void ftdi_process_config(tuh_xfer_t* xfer) { @@ -1131,39 +1119,40 @@ static void ftdi_process_config(tuh_xfer_t* xfer) { } } -#endif +//------------- Helper -------------// -//--------------------------------------------------------------------+ -// CP210x -//--------------------------------------------------------------------+ +static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base) { + const uint8_t divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 }; + uint32_t divisor; -#if CFG_TUH_CDC_CP210X + /* divisor shifted 3 bits to the left */ + uint32_t divisor3 = base / (2 * baud); + divisor = (divisor3 >> 3); + divisor |= (uint32_t) divfrac[divisor3 & 0x7] << 14; -enum { - CONFIG_CP210X_IFC_ENABLE = 0, - CONFIG_CP210X_SET_BAUDRATE, - CONFIG_CP210X_SET_LINE_CTL, - CONFIG_CP210X_SET_DTR_RTS, - CONFIG_CP210X_COMPLETE -}; + /* Deal with special cases for highest baud rates. */ + if (divisor == 1) { /* 1.0 */ + divisor = 0; + } + else if (divisor == 0x4001) { /* 1.5 */ + divisor = 1; + } -static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { - // CP210x Interface includes 1 vendor interface + 2 bulk endpoints - TU_VERIFY(itf_desc->bInterfaceSubClass == 0 && itf_desc->bInterfaceProtocol == 0 && itf_desc->bNumEndpoints == 2); - TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len); + return divisor; +} - cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); - TU_VERIFY(p_cdc); +static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud) { + return ftdi_232bm_baud_base_to_divisor(baud, 48000000u); +} - TU_LOG_DRV("CP210x opened\r\n"); - p_cdc->serial_drid = SERIAL_DRIVER_CP210X; +#endif - // endpoint pair - tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); +//--------------------------------------------------------------------+ +// CP210x +//--------------------------------------------------------------------+ +#if CFG_TUH_CDC_CP210X - // data endpoints expected to be in pairs - return open_ep_stream_pair(p_cdc, desc_ep); -} +//------------- Control Request -------------// static bool cp210x_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { tusb_control_request_t const request = { @@ -1202,14 +1191,7 @@ static bool cp210x_ifc_enable(cdch_interface_t* p_cdc, uint16_t enabled, tuh_xfe return cp210x_set_request(p_cdc, CP210X_IFC_ENABLE, enabled, NULL, 0, complete_cb, user_data); } -static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - // TODO implement later - (void) p_cdc; - (void) line_coding; - (void) complete_cb; - (void) user_data; - return false; -} +//------------- Driver API -------------// static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { TU_LOG_DRV("CDC CP210x Set BaudRate = %lu\r\n", baudrate); @@ -1231,6 +1213,15 @@ static bool cp210x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, u return false; } +static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + // TODO implement later + (void) p_cdc; + (void) line_coding; + (void) complete_cb; + (void) user_data; + return false; +} + static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { TU_LOG_DRV("CDC CP210x Set Control Line State\r\n"); p_cdc->user_control_cb = complete_cb; @@ -1238,6 +1229,34 @@ static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, complete_cb ? cdch_internal_control_complete : NULL, user_data); } +//------------- Enumeration -------------// + +enum { + CONFIG_CP210X_IFC_ENABLE = 0, + CONFIG_CP210X_SET_BAUDRATE, + CONFIG_CP210X_SET_LINE_CTL, + CONFIG_CP210X_SET_DTR_RTS, + CONFIG_CP210X_COMPLETE +}; + +static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { + // CP210x Interface includes 1 vendor interface + 2 bulk endpoints + TU_VERIFY(itf_desc->bInterfaceSubClass == 0 && itf_desc->bInterfaceProtocol == 0 && itf_desc->bNumEndpoints == 2); + TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len); + + cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); + TU_VERIFY(p_cdc); + + TU_LOG_DRV("CP210x opened\r\n"); + p_cdc->serial_drid = SERIAL_DRIVER_CP210X; + + // endpoint pair + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); + + // data endpoints expected to be in pairs + return open_ep_stream_pair(p_cdc, desc_ep); +} + static void cp210x_process_config(tuh_xfer_t* xfer) { uintptr_t const state = xfer->user_data; uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); @@ -1296,7 +1315,7 @@ static void cp210x_process_config(tuh_xfer_t* xfer) { static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits); static uint16_t ch34x_get_divisor_prescaler(uint32_t baval); -//------------- control request -------------// +//------------- Control Request -------------// static bool ch34x_set_request(cdch_interface_t* p_cdc, uint8_t direction, uint8_t request, uint16_t value, uint16_t index, uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { @@ -1471,6 +1490,7 @@ static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, } //------------- Enumeration -------------// + enum { CONFIG_CH34X_READ_VERSION = 0, CONFIG_CH34X_SERIAL_INIT, @@ -1565,7 +1585,7 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { } } -//------------- CH34x helper -------------// +//------------- Helper -------------// // calculate divisor and prescaler for baudrate, return it as 16-bit combined value static uint16_t ch34x_get_divisor_prescaler(uint32_t baval) { @@ -1654,7 +1674,6 @@ static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bit return lcr; } - #endif // CFG_TUH_CDC_CH34X #endif -- cgit v1.3.1 From 47777a63050f9438d79364436c25d0f6a80dfd55 Mon Sep 17 00:00:00 2001 From: IngHK Date: Tue, 20 Feb 2024 22:08:51 +0100 Subject: improved TU_LOGs --- src/class/cdc/cdc_host.c | 86 +++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 26eb70c9b..856db32ff 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -41,7 +41,11 @@ #define CFG_TUH_CDC_LOG_LEVEL CFG_TUH_LOG_LEVEL #endif -#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_CDC_LOG_LEVEL, __VA_ARGS__) +#define TU_LOG_DRV(...) TU_LOG(CFG_TUH_CDC_LOG_LEVEL, __VA_ARGS__) +#define TU_LOG_CDC(TXT,DADDR,ITF_NUM,NAME,...) TU_LOG_DRV("[:%u:%u] CDCh %s " TXT "\r\n", \ + DADDR, ITF_NUM, NAME, ##__VA_ARGS__) +#define TU_LOG_P_CDC(TXT,...) TU_LOG_CDC(TXT, p_cdc->daddr, p_cdc->bInterfaceNumber, \ + serial_drivers[p_cdc->serial_drid].name, ##__VA_ARGS__) //--------------------------------------------------------------------+ // Host CDC Interface @@ -169,6 +173,9 @@ typedef struct { bool (*const set_baudrate)(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); bool (*const set_data_format)(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data); bool (*const set_line_coding)(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL + uint8_t const * name; + #endif } cdch_serial_driver_t; // Note driver list must be in the same order as SERIAL_DRIVER enum @@ -181,7 +188,10 @@ static const cdch_serial_driver_t serial_drivers[] = { .set_control_line_state = acm_set_control_line_state, .set_baudrate = acm_set_baudrate, .set_data_format = acm_set_data_format, - .set_line_coding = acm_set_line_coding + .set_line_coding = acm_set_line_coding, + #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL + .name = (uint8_t const *) "ACM" + #endif }, #if CFG_TUH_CDC_FTDI @@ -193,7 +203,10 @@ static const cdch_serial_driver_t serial_drivers[] = { .set_control_line_state = ftdi_sio_set_modem_ctrl, .set_baudrate = ftdi_sio_set_baudrate, .set_data_format = ftdi_set_data_format, - .set_line_coding = ftdi_set_line_coding + .set_line_coding = ftdi_set_line_coding, + #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL + .name = (uint8_t const *) "FTDI" + #endif }, #endif @@ -206,7 +219,10 @@ static const cdch_serial_driver_t serial_drivers[] = { .set_control_line_state = cp210x_set_modem_ctrl, .set_baudrate = cp210x_set_baudrate, .set_data_format = cp210x_set_data_format, - .set_line_coding = cp210x_set_line_coding + .set_line_coding = cp210x_set_line_coding, + #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL + .name = (uint8_t const *) "CP210x" + #endif }, #endif @@ -219,7 +235,10 @@ static const cdch_serial_driver_t serial_drivers[] = { .set_control_line_state = ch34x_set_modem_ctrl, .set_baudrate = ch34x_set_baudrate, .set_data_format = ch34x_set_data_format, - .set_line_coding = ch34x_set_line_coding + .set_line_coding = ch34x_set_line_coding, + #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL + .name = (uint8_t const *) "CH34x" + #endif }, #endif }; @@ -408,8 +427,10 @@ static void process_internal_control_complete(tuh_xfer_t* xfer, uint8_t itf_num) cdch_interface_t* p_cdc = get_itf(idx); TU_ASSERT(p_cdc, ); uint16_t const value = tu_le16toh(xfer->setup->wValue); + bool const success = (xfer->result == XFER_RESULT_SUCCESS); + TU_LOG_P_CDC("control complete success = %u", success); - if (xfer->result == XFER_RESULT_SUCCESS) { + if (success) { switch (p_cdc->serial_drid) { case SERIAL_DRIVER_ACM: switch (xfer->setup->bRequest) { @@ -525,6 +546,7 @@ static void cdch_internal_control_complete(tuh_xfer_t* xfer) { bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { cdch_interface_t* p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + TU_LOG_P_CDC("set control line state line_state = %u", line_state); cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; if (complete_cb) { @@ -548,6 +570,7 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { cdch_interface_t* p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + TU_LOG_P_CDC("set baudrate = %lu", baudrate); cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; if (complete_cb) { @@ -572,6 +595,8 @@ bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uin tuh_xfer_cb_t complete_cb, uintptr_t user_data) { cdch_interface_t* p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + TU_LOG_P_CDC("set data format data_bits = %u parity = %u stop_bits = %u (indexes!)", + data_bits, parity, stop_bits); cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; if (complete_cb) { @@ -597,6 +622,8 @@ bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uin bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { cdch_interface_t* p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + TU_LOG_P_CDC("set line coding baudrate = %lu data_bits = %u parity = %u stop_bits = %u (indexes!)", + line_coding->bit_rate, line_coding->data_bits, line_coding->parity, line_coding->stop_bits); cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; if ( complete_cb ) { @@ -641,7 +668,7 @@ void cdch_close(uint8_t daddr) { for (uint8_t idx = 0; idx < CFG_TUH_CDC; idx++) { cdch_interface_t* p_cdc = &cdch_data[idx]; if (p_cdc->daddr == daddr) { - TU_LOG_DRV(" CDCh close addr = %u index = %u\r\n", daddr, idx); + TU_LOG_P_CDC("close"); // Invoke application callback if (tuh_cdc_umount_cb) tuh_cdc_umount_cb(idx); @@ -722,33 +749,42 @@ static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t co bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { (void) rhport; + cdch_serial_driver_t const * driver_detected = NULL; // For CDC: only support ACM subclass // Note: Protocol 0xFF can be RNDIS device if (TUSB_CLASS_CDC == itf_desc->bInterfaceClass && CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass) { - return acm_open(daddr, itf_desc, max_len); - } - else if (SERIAL_DRIVER_COUNT > 1 && - TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass) { + driver_detected = &serial_drivers[0]; + } else if (SERIAL_DRIVER_COUNT > 1 && TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass) { uint16_t vid, pid; TU_VERIFY(tuh_vid_pid_get(daddr, &vid, &pid)); for (size_t dr = 1; dr < SERIAL_DRIVER_COUNT; dr++) { - cdch_serial_driver_t const* driver = &serial_drivers[dr]; + cdch_serial_driver_t const * driver = &serial_drivers[dr]; for (size_t i = 0; i < driver->vid_pid_count; i++) { if (driver->vid_pid_list[i][0] == vid && driver->vid_pid_list[i][1] == pid) { - return driver->open(daddr, itf_desc, max_len); + driver_detected = driver; + break; } } + if (driver_detected) { + break; + } } } + if (driver_detected) { + TU_LOG_CDC("open", daddr, itf_desc->bInterfaceNumber, driver_detected->name); + bool ret = driver_detected->open(daddr, itf_desc, max_len); + return ret; + } + return false; } static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num) { - TU_LOG_DRV("CDCh Set Configure complete\r\n"); + TU_LOG_P_CDC("set config complete"); p_cdc->mounted = true; if (tuh_cdc_mount_cb) tuh_cdc_mount_cb(idx); @@ -762,6 +798,10 @@ static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t i bool cdch_set_config(uint8_t daddr, uint8_t itf_num) { tusb_control_request_t request; request.wIndex = tu_htole16((uint16_t) itf_num); + uint8_t const idx = tuh_cdc_itf_get_index(daddr, itf_num); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + TU_LOG_P_CDC("set config"); // fake transfer to kick-off process tuh_xfer_t xfer; @@ -770,10 +810,6 @@ bool cdch_set_config(uint8_t daddr, uint8_t itf_num) { xfer.setup = &request; xfer.user_data = 0; // initial state - uint8_t const idx = tuh_cdc_itf_get_index(daddr, itf_num); - cdch_interface_t * p_cdc = get_itf(idx); - TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); - serial_drivers[p_cdc->serial_drid].process_set_config(&xfer); return true; } @@ -786,7 +822,6 @@ bool cdch_set_config(uint8_t daddr, uint8_t itf_num) { static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { TU_VERIFY(p_cdc->acm_capability.support_line_request); - TU_LOG_DRV("CDC ACM Set Control Line State\r\n"); tusb_control_request_t const request = { .bmRequestType_bit = { @@ -816,8 +851,6 @@ static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_st } static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - TU_LOG_DRV("CDC ACM Set Line Conding\r\n"); - tusb_control_request_t const request = { .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, @@ -850,7 +883,6 @@ static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const static bool acm_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - TU_LOG_DRV("CDC ACM Set Data Format\r\n"); cdc_line_coding_t line_coding; line_coding.bit_rate = p_cdc->line_coding.bit_rate; @@ -1000,7 +1032,6 @@ static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, u static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate); - TU_LOG_DRV("CDC FTDI Set BaudRate = %lu, divisor = 0x%04x\r\n", baudrate, divisor); p_cdc->user_control_cb = complete_cb; p_cdc->requested_line_coding.bit_rate = baudrate; @@ -1032,7 +1063,6 @@ static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t cons } static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - TU_LOG_DRV("CDC FTDI Set Control Line State\r\n"); p_cdc->user_control_cb = complete_cb; TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state, complete_cb ? cdch_internal_control_complete : NULL, user_data)); @@ -1057,7 +1087,6 @@ static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); TU_VERIFY(p_cdc); - TU_LOG_DRV("FTDI opened\r\n"); p_cdc->serial_drid = SERIAL_DRIVER_FTDI; // endpoint pair @@ -1194,7 +1223,6 @@ static bool cp210x_ifc_enable(cdch_interface_t* p_cdc, uint16_t enabled, tuh_xfe //------------- Driver API -------------// static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - TU_LOG_DRV("CDC CP210x Set BaudRate = %lu\r\n", baudrate); uint32_t baud_le = tu_htole32(baudrate); p_cdc->user_control_cb = complete_cb; return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4, @@ -1223,7 +1251,6 @@ static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t co } static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - TU_LOG_DRV("CDC CP210x Set Control Line State\r\n"); p_cdc->user_control_cb = complete_cb; return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | line_state, NULL, 0, complete_cb ? cdch_internal_control_complete : NULL, user_data); @@ -1247,7 +1274,6 @@ static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, ui cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); TU_VERIFY(p_cdc); - TU_LOG_DRV("CP210x opened\r\n"); p_cdc->serial_drid = SERIAL_DRIVER_CP210X; // endpoint pair @@ -1508,7 +1534,6 @@ static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uin cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc); TU_VERIFY (p_cdc); - TU_LOG_DRV ("CH34x opened\r\n"); p_cdc->serial_drid = SERIAL_DRIVER_CH34X; tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) tu_desc_next(itf_desc); @@ -1538,14 +1563,13 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { switch (state) { case CONFIG_CH34X_READ_VERSION: - TU_LOG_DRV("[%u] CDCh CH34x attempt to read Chip Version\r\n", p_cdc->daddr); TU_ASSERT (ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, buffer, 2, ch34x_process_config, CONFIG_CH34X_SERIAL_INIT),); break; case CONFIG_CH34X_SERIAL_INIT: { // handle version read data, set CH34x line coding (incl. baudrate) uint8_t const version = xfer->buffer[0]; - TU_LOG_DRV("[%u] CDCh CH34x Chip Version = %02x\r\n", p_cdc->daddr, version); + TU_LOG_P_CDC("Chip Version = %02x", version); // only versions >= 0x30 are tested, below 0x30 seems having other programming, see drivers from WCH vendor, Linux kernel and FreeBSD TU_ASSERT (version >= 0x30,); // init CH34x with line coding -- cgit v1.3.1 From 829ea52873387f6fe269c44089c339cfd6d7c64e Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 22 Feb 2024 08:56:15 +0100 Subject: splitted cdch_internal_control_complete() into driver's _internal_control_complete() and moved them into driver's sections. no functional change --- src/class/cdc/cdc_host.c | 289 +++++++++++++++++++++++++---------------------- 1 file changed, 154 insertions(+), 135 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 856db32ff..e39122e8f 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -286,7 +286,6 @@ static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t const *desc_ep); static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num); -static void cdch_internal_control_complete(tuh_xfer_t* xfer); //--------------------------------------------------------------------+ // APPLICATION API @@ -422,127 +421,6 @@ bool tuh_cdc_read_clear (uint8_t idx) { // Control Endpoint API //--------------------------------------------------------------------+ -static void process_internal_control_complete(tuh_xfer_t* xfer, uint8_t itf_num) { - uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); - cdch_interface_t* p_cdc = get_itf(idx); - TU_ASSERT(p_cdc, ); - uint16_t const value = tu_le16toh(xfer->setup->wValue); - bool const success = (xfer->result == XFER_RESULT_SUCCESS); - TU_LOG_P_CDC("control complete success = %u", success); - - if (success) { - switch (p_cdc->serial_drid) { - case SERIAL_DRIVER_ACM: - switch (xfer->setup->bRequest) { - case CDC_REQUEST_SET_CONTROL_LINE_STATE: - p_cdc->line_state = (uint8_t) value; - break; - - case CDC_REQUEST_SET_LINE_CODING: { - uint16_t const len = tu_min16(sizeof(cdc_line_coding_t), tu_le16toh(xfer->setup->wLength)); - memcpy(&p_cdc->line_coding, xfer->buffer, len); - break; - } - - default: break; - } - break; - - #if CFG_TUH_CDC_FTDI - case SERIAL_DRIVER_FTDI: - switch (xfer->setup->bRequest) { - case FTDI_SIO_MODEM_CTRL: - p_cdc->line_state = (uint8_t) value; - break; - - case FTDI_SIO_SET_BAUD_RATE: - p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; - break; - - default: break; - } - break; - #endif - - #if CFG_TUH_CDC_CP210X - case SERIAL_DRIVER_CP210X: - switch(xfer->setup->bRequest) { - case CP210X_SET_MHS: - p_cdc->line_state = (uint8_t) value; - break; - - case CP210X_SET_BAUDRATE: { - uint32_t baudrate; - memcpy(&baudrate, xfer->buffer, sizeof(uint32_t)); - p_cdc->line_coding.bit_rate = tu_le32toh(baudrate); - break; - } - - default: break; - } - break; - #endif - - #if CFG_TUH_CDC_CH34X - case SERIAL_DRIVER_CH34X: - switch (xfer->setup->bRequest) { - case CH34X_REQ_WRITE_REG: - // register write request - switch (value) { - case CH34X_REG16_DIVISOR_PRESCALER: - // baudrate - p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; - break; - - case CH32X_REG16_LCR2_LCR: - // data format - p_cdc->line_coding.stop_bits = p_cdc->requested_line_coding.stop_bits; - p_cdc->line_coding.parity = p_cdc->requested_line_coding.parity; - p_cdc->line_coding.data_bits = p_cdc->requested_line_coding.data_bits; - break; - - default: break; - } - break; - - case CH34X_REQ_MODEM_CTRL: { - // set modem controls RTS/DTR request. Note: signals are inverted - uint16_t const modem_signal = ~value; - if (modem_signal & CH34X_BIT_RTS) { - p_cdc->line_state |= CDC_CONTROL_LINE_STATE_RTS; - } else { - p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_RTS; - } - - if (modem_signal & CH34X_BIT_DTR) { - p_cdc->line_state |= CDC_CONTROL_LINE_STATE_DTR; - } else { - p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_DTR; - } - break; - } - - default: break; - } - break; - #endif - - default: break; - } - } - - xfer->complete_cb = p_cdc->user_control_cb; - if (xfer->complete_cb) { - xfer->complete_cb(xfer); - } -} - -// internal control complete to update state such as line state, encoding -static void cdch_internal_control_complete(tuh_xfer_t* xfer) { - uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); - process_internal_control_complete(xfer, itf_num); -} - bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { cdch_interface_t* p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); @@ -820,6 +698,36 @@ bool cdch_set_config(uint8_t daddr, uint8_t itf_num) { //------------- Driver API -------------// +// internal control complete to update state such as line state, encoding +static void acm_internal_control_complete(tuh_xfer_t * xfer) { + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc,); + bool const success = (xfer->result == XFER_RESULT_SUCCESS); + TU_LOG_P_CDC("control complete success = %u", success); + + if (success) { + switch (xfer->setup->bRequest) { + case CDC_REQUEST_SET_CONTROL_LINE_STATE: + p_cdc->line_state = (uint8_t) tu_le16toh(xfer->setup->wValue); + break; + + case CDC_REQUEST_SET_LINE_CODING: + uint16_t const len = tu_min16(sizeof(cdc_line_coding_t), tu_le16toh(xfer->setup->wLength)); + memcpy(&p_cdc->line_coding, xfer->buffer, len); + break; + + default: break; + } + } + + xfer->complete_cb = p_cdc->user_control_cb; + if (xfer->complete_cb) { + xfer->complete_cb(xfer); + } +} + static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { TU_VERIFY(p_cdc->acm_capability.support_line_request); @@ -842,7 +750,7 @@ static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_st .ep_addr = 0, .setup = &request, .buffer = NULL, - .complete_cb = complete_cb ? cdch_internal_control_complete : NULL, // complete_cb is NULL for sync call + .complete_cb = complete_cb ? acm_internal_control_complete : NULL, // complete_cb is NULL for sync call .user_data = user_data }; @@ -873,7 +781,7 @@ static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const .ep_addr = 0, .setup = &request, .buffer = enum_buf, - .complete_cb = complete_cb ? cdch_internal_control_complete : NULL, // complete_cb is NULL for sync call + .complete_cb = complete_cb ? acm_internal_control_complete : NULL, // complete_cb is NULL for sync call .user_data = user_data }; @@ -1030,13 +938,42 @@ static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, u //------------- Driver API -------------// +// internal control complete to update state such as line state, line_coding +static void ftdi_internal_control_complete(tuh_xfer_t * xfer) { + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc,); + bool const success = (xfer->result == XFER_RESULT_SUCCESS); + TU_LOG_P_CDC("control complete success = %u", success); + + if (success) { + switch (xfer->setup->bRequest) { + case FTDI_SIO_MODEM_CTRL: + p_cdc->line_state = (uint8_t) tu_le16toh(xfer->setup->wValue); + break; + + case FTDI_SIO_SET_BAUD_RATE: + p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; + break; + + default: break; + } + } + + xfer->complete_cb = p_cdc->user_control_cb; + if (xfer->complete_cb) { + xfer->complete_cb(xfer); + } +} + static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate); p_cdc->user_control_cb = complete_cb; p_cdc->requested_line_coding.bit_rate = baudrate; TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor, - complete_cb ? cdch_internal_control_complete : NULL, user_data)); + complete_cb ? ftdi_internal_control_complete : NULL, user_data)); return true; } @@ -1065,7 +1002,7 @@ static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t cons static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->user_control_cb = complete_cb; TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state, - complete_cb ? cdch_internal_control_complete : NULL, user_data)); + complete_cb ? ftdi_internal_control_complete : NULL, user_data)); return true; } @@ -1222,11 +1159,42 @@ static bool cp210x_ifc_enable(cdch_interface_t* p_cdc, uint16_t enabled, tuh_xfe //------------- Driver API -------------// +// internal control complete to update state such as line state, encoding +static void cp210x_internal_control_complete(tuh_xfer_t * xfer) { + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc,); + bool const success = (xfer->result == XFER_RESULT_SUCCESS); + TU_LOG_P_CDC("control complete success = %u", success); + + if (success) { + switch(xfer->setup->bRequest) { + case CP210X_SET_MHS: + p_cdc->line_state = (uint8_t) tu_le16toh(xfer->setup->wValue); + break; + + case CP210X_SET_BAUDRATE: + uint32_t baudrate; + memcpy(&baudrate, xfer->buffer, sizeof(uint32_t)); + p_cdc->line_coding.bit_rate = tu_le32toh(baudrate); + break; + + default: break; + } + } + + xfer->complete_cb = p_cdc->user_control_cb; + if (xfer->complete_cb) { + xfer->complete_cb(xfer); + } +} + static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { uint32_t baud_le = tu_htole32(baudrate); p_cdc->user_control_cb = complete_cb; return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4, - complete_cb ? cdch_internal_control_complete : NULL, user_data); + complete_cb ? cp210x_internal_control_complete : NULL, user_data); } static bool cp210x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, @@ -1253,7 +1221,7 @@ static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t co static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->user_control_cb = complete_cb; return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | line_state, NULL, 0, - complete_cb ? cdch_internal_control_complete : NULL, user_data); + complete_cb ? cp210x_internal_control_complete : NULL, user_data); } //------------- Enumeration -------------// @@ -1412,9 +1380,60 @@ static bool ch34x_write_reg_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, //------------- Driver API -------------// // internal control complete to update state such as line state, encoding -static void ch34x_control_complete(tuh_xfer_t* xfer) { - // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber - process_internal_control_complete(xfer, 0); +static void ch34x_internal_control_complete(tuh_xfer_t * xfer) { + // CH34x has only interface 0, because wIndex is used as payload and not for bInterfaceNumber + uint8_t const itf_num = 0; + uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc,); + bool const success = (xfer->result == XFER_RESULT_SUCCESS); + TU_LOG_P_CDC("control complete success = %u", success); + + if (success) { + switch (xfer->setup->bRequest) { + case CH34X_REQ_WRITE_REG: + // register write request + switch (tu_le16toh(xfer->setup->wValue)) { + case CH34X_REG16_DIVISOR_PRESCALER: + // baudrate + p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; + break; + + case CH32X_REG16_LCR2_LCR: + // data format + p_cdc->line_coding.stop_bits = p_cdc->requested_line_coding.stop_bits; + p_cdc->line_coding.parity = p_cdc->requested_line_coding.parity; + p_cdc->line_coding.data_bits = p_cdc->requested_line_coding.data_bits; + break; + + default: break; + } + break; + + case CH34X_REQ_MODEM_CTRL: + // set modem controls RTS/DTR request. Note: signals are inverted + uint16_t const modem_signal = ~tu_le16toh(xfer->setup->wValue); + if (modem_signal & CH34X_BIT_RTS) { + p_cdc->line_state |= CDC_CONTROL_LINE_STATE_RTS; + } else { + p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_RTS; + } + + if (modem_signal & CH34X_BIT_DTR) { + p_cdc->line_state |= CDC_CONTROL_LINE_STATE_DTR; + } else { + p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_DTR; + } + break; + + default: break; + } + } + + xfer->complete_cb = p_cdc->user_control_cb; + if (xfer->complete_cb) { + xfer->complete_cb(xfer); + } } static bool ch34x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, @@ -1426,7 +1445,7 @@ static bool ch34x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, ui uint8_t const lcr = ch34x_get_lcr(stop_bits, parity, data_bits); TU_VERIFY(lcr); TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, CH32X_REG16_LCR2_LCR, lcr, - complete_cb ? ch34x_control_complete : NULL, user_data)); + complete_cb ? ch34x_internal_control_complete : NULL, user_data)); return true; } @@ -1435,7 +1454,7 @@ static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, p_cdc->requested_line_coding.bit_rate = baudrate; p_cdc->user_control_cb = complete_cb; TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, baudrate, - complete_cb ? ch34x_control_complete : NULL, user_data)); + complete_cb ? ch34x_internal_control_complete : NULL, user_data)); return true; } @@ -1450,7 +1469,7 @@ static void ch34x_set_line_coding_stage1_complete(tuh_xfer_t* xfer) { // stage 1 success, continue to stage 2 p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; TU_ASSERT(ch34x_set_data_format(p_cdc, p_cdc->requested_line_coding.stop_bits, p_cdc->requested_line_coding.parity, - p_cdc->requested_line_coding.data_bits, ch34x_control_complete, xfer->user_data), ); + p_cdc->requested_line_coding.data_bits, ch34x_internal_control_complete, xfer->user_data), ); } else { // stage 1 failed, notify user xfer->complete_cb = p_cdc->user_control_cb; @@ -1511,7 +1530,7 @@ static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, p_cdc->user_control_cb = complete_cb; TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_MODEM_CTRL, control, 0, - complete_cb ? ch34x_control_complete : NULL, user_data)); + complete_cb ? ch34x_internal_control_complete : NULL, user_data)); return true; } -- cgit v1.3.1 From 2f50f5a4263dc3d65f3bf96b593edac5ac271a5a Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 22 Feb 2024 14:59:16 +0100 Subject: changed to use of p_cdc->requested_line_coding --- src/class/cdc/cdc_host.c | 215 +++++++++++++++++++++-------------------------- 1 file changed, 96 insertions(+), 119 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index e39122e8f..b2ba7c9ea 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -63,12 +63,10 @@ typedef struct { cdc_acm_capability_t acm_capability; TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding; // Baudrate, stop bits, parity, data width - uint8_t line_state; // DTR (bit0), RTS (bit1) - - #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X - cdc_line_coding_t requested_line_coding; + TU_ATTR_ALIGNED(4) cdc_line_coding_t requested_line_coding; // 1 byte padding - #endif + + uint8_t line_state; // DTR (bit0), RTS (bit1) tuh_xfer_cb_t user_control_cb; @@ -95,9 +93,9 @@ static cdch_interface_t cdch_data[CFG_TUH_CDC]; static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); static void acm_process_config(tuh_xfer_t* xfer); -static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool acm_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool acm_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool acm_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool acm_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); //------------- FTDI prototypes -------------// @@ -109,9 +107,9 @@ static uint16_t const ftdi_vid_pid_list[][2] = {CFG_TUH_CDC_FTDI_VID_PID_LIST}; static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len); static void ftdi_process_config(tuh_xfer_t* xfer); -static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool ftdi_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ftdi_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #endif @@ -124,9 +122,9 @@ static uint16_t const cp210x_vid_pid_list[][2] = {CFG_TUH_CDC_CP210X_VID_PID_LIS static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); static void cp210x_process_config(tuh_xfer_t* xfer); -static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool cp210x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool cp210x_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #endif @@ -139,9 +137,9 @@ static uint16_t const ch34x_vid_pid_list[][2] = {CFG_TUH_CDC_CH34X_VID_PID_LIST} static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len); static void ch34x_process_config(tuh_xfer_t* xfer); -static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool ch34x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ch34x_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #endif @@ -170,9 +168,9 @@ typedef struct { bool (*const open)(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len); void (*const process_set_config)(tuh_xfer_t* xfer); bool (*const set_control_line_state)(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); - bool (*const set_baudrate)(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); - bool (*const set_data_format)(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data); - bool (*const set_line_coding)(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + bool (*const set_baudrate)(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + bool (*const set_data_format)(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + bool (*const set_line_coding)(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL uint8_t const * name; #endif @@ -276,6 +274,7 @@ static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const p_cdc->bInterfaceNumber = itf_desc->bInterfaceNumber; p_cdc->bInterfaceSubClass = itf_desc->bInterfaceSubClass; p_cdc->bInterfaceProtocol = itf_desc->bInterfaceProtocol; + p_cdc->line_coding = (cdc_line_coding_t) { 0, 0, 0, 0 }; p_cdc->line_state = 0; return p_cdc; } @@ -451,12 +450,14 @@ bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete TU_LOG_P_CDC("set baudrate = %lu", baudrate); cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; + p_cdc->requested_line_coding.bit_rate = baudrate; + if (complete_cb) { - return driver->set_baudrate(p_cdc, baudrate, complete_cb, user_data); + return driver->set_baudrate(p_cdc, complete_cb, user_data); } else { // blocking xfer_result_t result = XFER_RESULT_INVALID; - bool ret = driver->set_baudrate(p_cdc, baudrate, complete_cb, (uintptr_t) &result); + bool ret = driver->set_baudrate(p_cdc, complete_cb, (uintptr_t) &result); if (user_data) { // user_data is not NULL, return result via user_data @@ -477,12 +478,16 @@ bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uin data_bits, parity, stop_bits); cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; + p_cdc->requested_line_coding.stop_bits = stop_bits; + p_cdc->requested_line_coding.parity = parity; + p_cdc->requested_line_coding.data_bits = data_bits; + if (complete_cb) { - return driver->set_data_format(p_cdc, stop_bits, parity, data_bits, complete_cb, user_data); + return driver->set_data_format(p_cdc, complete_cb, user_data); } else { // blocking xfer_result_t result = XFER_RESULT_INVALID; - bool ret = driver->set_data_format(p_cdc, stop_bits, parity, data_bits, complete_cb, (uintptr_t) &result); + bool ret = driver->set_data_format(p_cdc, complete_cb, (uintptr_t) &result); if (user_data) { // user_data is not NULL, return result via user_data @@ -491,7 +496,7 @@ bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uin TU_VERIFY(ret && result == XFER_RESULT_SUCCESS); p_cdc->line_coding.stop_bits = stop_bits; - p_cdc->line_coding.parity = parity; + p_cdc->line_coding.parity = parity; p_cdc->line_coding.data_bits = data_bits; return true; } @@ -504,12 +509,14 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, line_coding->bit_rate, line_coding->data_bits, line_coding->parity, line_coding->stop_bits); cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; + p_cdc->requested_line_coding = *line_coding; + if ( complete_cb ) { - return driver->set_line_coding(p_cdc, line_coding, complete_cb, user_data); + return driver->set_line_coding(p_cdc, complete_cb, user_data); } else { // blocking xfer_result_t result = XFER_RESULT_INVALID; - bool ret = driver->set_line_coding(p_cdc, line_coding, complete_cb, (uintptr_t) &result); + bool ret = driver->set_line_coding(p_cdc, complete_cb, (uintptr_t) &result); if (user_data) { // user_data is not NULL, return result via user_data @@ -714,8 +721,7 @@ static void acm_internal_control_complete(tuh_xfer_t * xfer) { break; case CDC_REQUEST_SET_LINE_CODING: - uint16_t const len = tu_min16(sizeof(cdc_line_coding_t), tu_le16toh(xfer->setup->wLength)); - memcpy(&p_cdc->line_coding, xfer->buffer, len); + p_cdc->line_coding = p_cdc->requested_line_coding; break; default: break; @@ -758,7 +764,7 @@ static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_st return true; } -static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool acm_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { tusb_control_request_t const request = { .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, @@ -773,7 +779,7 @@ static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const // use usbh enum buf to hold line coding since user line_coding variable does not live long enough uint8_t* enum_buf = usbh_get_enum_buf(); - memcpy(enum_buf, line_coding, sizeof(cdc_line_coding_t)); + memcpy(enum_buf, &p_cdc->requested_line_coding, sizeof(cdc_line_coding_t)); p_cdc->user_control_cb = complete_cb; tuh_xfer_t xfer = { @@ -789,23 +795,19 @@ static bool acm_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const return true; } -static bool acm_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, - tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - - cdc_line_coding_t line_coding; - line_coding.bit_rate = p_cdc->line_coding.bit_rate; - line_coding.stop_bits = stop_bits; - line_coding.parity = parity; - line_coding.data_bits = data_bits; +static bool acm_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + p_cdc->requested_line_coding.bit_rate = p_cdc->line_coding.bit_rate; - return acm_set_line_coding(p_cdc, &line_coding, complete_cb, user_data); + return acm_set_line_coding(p_cdc, complete_cb, user_data); } -static bool acm_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool acm_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + p_cdc->requested_line_coding.stop_bits = p_cdc->line_coding.stop_bits; + p_cdc->requested_line_coding.parity = p_cdc->line_coding.parity; + p_cdc->requested_line_coding.data_bits = p_cdc->line_coding.data_bits; TU_VERIFY(p_cdc->acm_capability.support_line_request); - cdc_line_coding_t line_coding = p_cdc->line_coding; - line_coding.bit_rate = baudrate; - return acm_set_line_coding(p_cdc, &line_coding, complete_cb, user_data); + + return acm_set_line_coding(p_cdc, complete_cb, user_data); } //------------- Enumeration -------------// @@ -879,11 +881,11 @@ static void acm_process_config(tuh_xfer_t* xfer) { case CONFIG_ACM_SET_LINE_CODING: #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM - if (p_cdc->acm_capability.support_line_request) { - cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; - TU_ASSERT(acm_set_line_coding(p_cdc, &line_coding, acm_process_config, CONFIG_ACM_COMPLETE),); - break; - } + if (p_cdc->acm_capability.support_line_request) { + p_cdc->requested_line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT(acm_set_line_coding(p_cdc, acm_process_config, CONFIG_ACM_COMPLETE),); + break; + } #endif TU_ATTR_FALLTHROUGH; @@ -902,7 +904,7 @@ static void acm_process_config(tuh_xfer_t* xfer) { //--------------------------------------------------------------------+ #if CFG_TUH_CDC_FTDI -static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud); +static uint32_t ftdi_232bm_baud_to_divisor(cdch_interface_t* p_cdc); //------------- Control Request -------------// @@ -967,32 +969,26 @@ static void ftdi_internal_control_complete(tuh_xfer_t * xfer) { } } -static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate); +static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(p_cdc); p_cdc->user_control_cb = complete_cb; - p_cdc->requested_line_coding.bit_rate = baudrate; TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor, complete_cb ? ftdi_internal_control_complete : NULL, user_data)); return true; } -static bool ftdi_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, - tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool ftdi_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { (void) p_cdc; - (void) stop_bits; - (void) parity; - (void) data_bits; (void) complete_cb; (void) user_data; // TODO not implemented yet return false; } -static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { (void) p_cdc; - (void) line_coding; (void) complete_cb; (void) user_data; // TODO not implemented yet @@ -1056,11 +1052,11 @@ static void ftdi_process_config(tuh_xfer_t* xfer) { case CONFIG_FTDI_SET_BAUDRATE: { #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM - cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; - TU_ASSERT(ftdi_sio_set_baudrate(p_cdc, line_coding.bit_rate, ftdi_process_config, CONFIG_FTDI_SET_DATA),); - break; + p_cdc->requested_line_coding.bit_rate = ((cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM).bit_rate; + TU_ASSERT(ftdi_sio_set_baudrate(p_cdc, ftdi_process_config, CONFIG_FTDI_SET_DATA),); + break; #else - TU_ATTR_FALLTHROUGH; + TU_ATTR_FALLTHROUGH; #endif } @@ -1107,8 +1103,8 @@ static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base) { return divisor; } -static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud) { - return ftdi_232bm_baud_base_to_divisor(baud, 48000000u); +static uint32_t ftdi_232bm_baud_to_divisor(cdch_interface_t* p_cdc) { + return ftdi_232bm_baud_base_to_divisor(p_cdc->requested_line_coding.bit_rate, 48000000u); } #endif @@ -1175,9 +1171,7 @@ static void cp210x_internal_control_complete(tuh_xfer_t * xfer) { break; case CP210X_SET_BAUDRATE: - uint32_t baudrate; - memcpy(&baudrate, xfer->buffer, sizeof(uint32_t)); - p_cdc->line_coding.bit_rate = tu_le32toh(baudrate); + p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; break; default: break; @@ -1190,29 +1184,24 @@ static void cp210x_internal_control_complete(tuh_xfer_t * xfer) { } } -static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - uint32_t baud_le = tu_htole32(baudrate); +static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + uint32_t baud_le = tu_htole32(p_cdc->requested_line_coding.bit_rate); p_cdc->user_control_cb = complete_cb; return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4, complete_cb ? cp210x_internal_control_complete : NULL, user_data); } -static bool cp210x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, - tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool cp210x_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { (void) p_cdc; - (void) stop_bits; - (void) parity; - (void) data_bits; (void) complete_cb; (void) user_data; // TODO not implemented yet return false; } -static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { // TODO implement later (void) p_cdc; - (void) line_coding; (void) complete_cb; (void) user_data; return false; @@ -1265,11 +1254,11 @@ static void cp210x_process_config(tuh_xfer_t* xfer) { case CONFIG_CP210X_SET_BAUDRATE: { #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM - cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; - TU_ASSERT(cp210x_set_baudrate(p_cdc, line_coding.bit_rate, cp210x_process_config, CONFIG_CP210X_SET_LINE_CTL),); - break; + p_cdc->requested_line_coding.bit_rate = ((cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM).bit_rate; + TU_ASSERT(cp210x_set_baudrate(p_cdc, cp210x_process_config, CONFIG_CP210X_SET_LINE_CTL),); + break; #else - TU_ATTR_FALLTHROUGH; + TU_ATTR_FALLTHROUGH; #endif } @@ -1306,8 +1295,8 @@ static void cp210x_process_config(tuh_xfer_t* xfer) { #if CFG_TUH_CDC_CH34X -static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits); -static uint16_t ch34x_get_divisor_prescaler(uint32_t baval); +static uint8_t ch34x_get_lcr(cdch_interface_t * p_cdc); +static uint16_t ch34x_get_divisor_prescaler(cdch_interface_t * p_cdc); //------------- Control Request -------------// @@ -1368,9 +1357,8 @@ static inline bool ch34x_write_reg(cdch_interface_t* p_cdc, uint16_t reg, uint16 // return ch34x_control_in ( p_cdc, CH34X_REQ_READ_REG, reg, 0, buffer, buffersize, complete_cb, user_data ); //} -static bool ch34x_write_reg_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, - tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - uint16_t const div_ps = ch34x_get_divisor_prescaler(baudrate); +static bool ch34x_write_reg_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + uint16_t const div_ps = ch34x_get_divisor_prescaler(p_cdc); TU_VERIFY(div_ps); TU_ASSERT(ch34x_write_reg(p_cdc, CH34X_REG16_DIVISOR_PRESCALER, div_ps, complete_cb, user_data)); @@ -1436,25 +1424,17 @@ static void ch34x_internal_control_complete(tuh_xfer_t * xfer) { } } -static bool ch34x_set_data_format(cdch_interface_t* p_cdc, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, - tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - p_cdc->requested_line_coding.stop_bits = stop_bits; - p_cdc->requested_line_coding.parity = parity; - p_cdc->requested_line_coding.data_bits = data_bits; - - uint8_t const lcr = ch34x_get_lcr(stop_bits, parity, data_bits); +static bool ch34x_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + uint8_t const lcr = ch34x_get_lcr(p_cdc); TU_VERIFY(lcr); TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, CH32X_REG16_LCR2_LCR, lcr, complete_cb ? ch34x_internal_control_complete : NULL, user_data)); return true; } -static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, - tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - p_cdc->requested_line_coding.bit_rate = baudrate; +static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->user_control_cb = complete_cb; - TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, baudrate, - complete_cb ? ch34x_internal_control_complete : NULL, user_data)); + TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, complete_cb ? ch34x_internal_control_complete : NULL, user_data)); return true; } @@ -1468,8 +1448,7 @@ static void ch34x_set_line_coding_stage1_complete(tuh_xfer_t* xfer) { if (xfer->result == XFER_RESULT_SUCCESS) { // stage 1 success, continue to stage 2 p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; - TU_ASSERT(ch34x_set_data_format(p_cdc, p_cdc->requested_line_coding.stop_bits, p_cdc->requested_line_coding.parity, - p_cdc->requested_line_coding.data_bits, ch34x_internal_control_complete, xfer->user_data), ); + TU_ASSERT(ch34x_set_data_format(p_cdc, ch34x_internal_control_complete, xfer->user_data), ); } else { // stage 1 failed, notify user xfer->complete_cb = p_cdc->user_control_cb; @@ -1480,31 +1459,24 @@ static void ch34x_set_line_coding_stage1_complete(tuh_xfer_t* xfer) { } // 2 stages: set baudrate (stage1) + set data format (stage2) -static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t const* line_coding, - tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - p_cdc->requested_line_coding = *line_coding; +static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->user_control_cb = complete_cb; if (complete_cb) { // stage 1 set baudrate - TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, line_coding->bit_rate, - ch34x_set_line_coding_stage1_complete, user_data)); + TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, ch34x_set_line_coding_stage1_complete, user_data)); } else { // sync call xfer_result_t result; // stage 1 set baudrate - TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, line_coding->bit_rate, NULL, (uintptr_t) &result)); + TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, NULL, (uintptr_t) &result)); TU_VERIFY(result == XFER_RESULT_SUCCESS); - p_cdc->line_coding.bit_rate = line_coding->bit_rate; + p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; // stage 2 set data format - TU_ASSERT(ch34x_set_data_format(p_cdc, line_coding->stop_bits, line_coding->parity, line_coding->data_bits, - NULL, (uintptr_t) &result)); + TU_ASSERT(ch34x_set_data_format(p_cdc, NULL, (uintptr_t) &result)); TU_VERIFY(result == XFER_RESULT_SUCCESS); - p_cdc->line_coding.stop_bits = line_coding->stop_bits; - p_cdc->line_coding.parity = line_coding->parity; - p_cdc->line_coding.data_bits = line_coding->data_bits; // update transfer result, user_data is expected to point to xfer_result_t if (user_data) { @@ -1592,10 +1564,10 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { // only versions >= 0x30 are tested, below 0x30 seems having other programming, see drivers from WCH vendor, Linux kernel and FreeBSD TU_ASSERT (version >= 0x30,); // init CH34x with line coding - cdc_line_coding_t const line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X; - uint16_t const div_ps = ch34x_get_divisor_prescaler(line_coding.bit_rate); + p_cdc->requested_line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X; + uint16_t const div_ps = ch34x_get_divisor_prescaler(p_cdc); TU_ASSERT(div_ps, ); - uint8_t const lcr = ch34x_get_lcr(line_coding.stop_bits, line_coding.parity, line_coding.data_bits); + uint8_t const lcr = ch34x_get_lcr(p_cdc); TU_ASSERT(lcr, ); TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_SERIAL_INIT, tu_u16(lcr, 0x9c), div_ps, ch34x_process_config, CONFIG_CH34X_SPECIAL_REG_WRITE),); @@ -1604,7 +1576,7 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { case CONFIG_CH34X_SPECIAL_REG_WRITE: // overtake line coding and do special reg write, purpose unknown, overtaken from WCH driver - p_cdc->line_coding = ((cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X); + p_cdc->line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X; TU_ASSERT (ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x0F, CH341_REG_0x2C), 0x0007, ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL),); break; @@ -1631,7 +1603,8 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { //------------- Helper -------------// // calculate divisor and prescaler for baudrate, return it as 16-bit combined value -static uint16_t ch34x_get_divisor_prescaler(uint32_t baval) { +static uint16_t ch34x_get_divisor_prescaler(cdch_interface_t * p_cdc) { + uint32_t const baval = p_cdc->requested_line_coding.bit_rate; uint8_t a; uint8_t b; uint32_t c; @@ -1680,7 +1653,11 @@ static uint16_t ch34x_get_divisor_prescaler(uint32_t baval) { } // calculate lcr value from data coding -static uint8_t ch34x_get_lcr(uint8_t stop_bits, uint8_t parity, uint8_t data_bits) { +static uint8_t ch34x_get_lcr(cdch_interface_t * p_cdc) { + uint8_t const stop_bits = p_cdc->requested_line_coding.stop_bits; + uint8_t const parity = p_cdc->requested_line_coding.parity; + uint8_t const data_bits = p_cdc->requested_line_coding.data_bits; + uint8_t lcr = CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX; TU_VERIFY(data_bits >= 5 && data_bits <= 8, 0); lcr |= (uint8_t) (data_bits - 5); -- cgit v1.3.1 From 7dd435cb877f5d3e0b9576dfcdb8c40e9a03c399 Mon Sep 17 00:00:00 2001 From: IngHK Date: Tue, 20 Feb 2024 20:45:50 +0100 Subject: changed to use of p_cdc->requested_line_state --- src/class/cdc/cdc_host.c | 82 ++++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index b2ba7c9ea..0af429e4b 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -67,6 +67,7 @@ typedef struct { // 1 byte padding uint8_t line_state; // DTR (bit0), RTS (bit1) + uint8_t requested_line_state; tuh_xfer_cb_t user_control_cb; @@ -96,7 +97,7 @@ static void acm_process_config(tuh_xfer_t* xfer); static bool acm_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool acm_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool acm_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool acm_set_control_line_state(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); //------------- FTDI prototypes -------------// #if CFG_TUH_CDC_FTDI @@ -110,7 +111,7 @@ static void ftdi_process_config(tuh_xfer_t* xfer); static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool ftdi_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #endif //------------- CP210X prototypes -------------// @@ -125,7 +126,7 @@ static void cp210x_process_config(tuh_xfer_t* xfer); static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool cp210x_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #endif //------------- CH34x prototypes -------------// @@ -140,7 +141,7 @@ static void ch34x_process_config(tuh_xfer_t* xfer); static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool ch34x_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #endif //------------- Common -------------// @@ -167,7 +168,7 @@ typedef struct { uint16_t const vid_pid_count; bool (*const open)(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len); void (*const process_set_config)(tuh_xfer_t* xfer); - bool (*const set_control_line_state)(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + bool (*const set_control_line_state)(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); bool (*const set_baudrate)(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); bool (*const set_data_format)(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); bool (*const set_line_coding)(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); @@ -426,12 +427,14 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c TU_LOG_P_CDC("set control line state line_state = %u", line_state); cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; + p_cdc->requested_line_state = (uint8_t) line_state; + if (complete_cb) { - return driver->set_control_line_state(p_cdc, line_state, complete_cb, user_data); + return driver->set_control_line_state(p_cdc, complete_cb, user_data); } else { // blocking xfer_result_t result = XFER_RESULT_INVALID; - bool ret = driver->set_control_line_state(p_cdc, line_state, complete_cb, (uintptr_t) &result); + bool ret = driver->set_control_line_state(p_cdc, complete_cb, (uintptr_t) &result); if (user_data) { // user_data is not NULL, return result via user_data @@ -717,7 +720,7 @@ static void acm_internal_control_complete(tuh_xfer_t * xfer) { if (success) { switch (xfer->setup->bRequest) { case CDC_REQUEST_SET_CONTROL_LINE_STATE: - p_cdc->line_state = (uint8_t) tu_le16toh(xfer->setup->wValue); + p_cdc->line_state = p_cdc->requested_line_state; break; case CDC_REQUEST_SET_LINE_CODING: @@ -734,7 +737,7 @@ static void acm_internal_control_complete(tuh_xfer_t * xfer) { } } -static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool acm_set_control_line_state(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { TU_VERIFY(p_cdc->acm_capability.support_line_request); tusb_control_request_t const request = { @@ -744,7 +747,7 @@ static bool acm_set_control_line_state(cdch_interface_t* p_cdc, uint16_t line_st .direction = TUSB_DIR_OUT }, .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE, - .wValue = tu_htole16(line_state), + .wValue = tu_htole16(p_cdc->requested_line_state), .wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber), .wLength = 0 }; @@ -872,10 +875,11 @@ static void acm_process_config(tuh_xfer_t* xfer) { switch (state) { case CONFIG_ACM_SET_CONTROL_LINE_STATE: #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - if (p_cdc->acm_capability.support_line_request) { - TU_ASSERT(acm_set_control_line_state(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, acm_process_config, CONFIG_ACM_SET_LINE_CODING),); - break; - } + if (p_cdc->acm_capability.support_line_request) { + p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + TU_ASSERT(acm_set_control_line_state(p_cdc, acm_process_config, CONFIG_ACM_SET_LINE_CODING),); + break; + } #endif TU_ATTR_FALLTHROUGH; @@ -952,7 +956,7 @@ static void ftdi_internal_control_complete(tuh_xfer_t * xfer) { if (success) { switch (xfer->setup->bRequest) { case FTDI_SIO_MODEM_CTRL: - p_cdc->line_state = (uint8_t) tu_le16toh(xfer->setup->wValue); + p_cdc->line_state = p_cdc->requested_line_state; break; case FTDI_SIO_SET_BAUD_RATE: @@ -995,9 +999,9 @@ static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete return false; } -static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->user_control_cb = complete_cb; - TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | line_state, + TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | p_cdc->requested_line_state, complete_cb ? ftdi_internal_control_complete : NULL, user_data)); return true; } @@ -1044,10 +1048,11 @@ static void ftdi_process_config(tuh_xfer_t* xfer) { case CONFIG_FTDI_MODEM_CTRL: #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - TU_ASSERT(ftdi_sio_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE),); - break; + p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + TU_ASSERT(ftdi_sio_set_modem_ctrl(p_cdc, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE),); + break; #else - TU_ATTR_FALLTHROUGH; + TU_ATTR_FALLTHROUGH; #endif case CONFIG_FTDI_SET_BAUDRATE: { @@ -1167,7 +1172,7 @@ static void cp210x_internal_control_complete(tuh_xfer_t * xfer) { if (success) { switch(xfer->setup->bRequest) { case CP210X_SET_MHS: - p_cdc->line_state = (uint8_t) tu_le16toh(xfer->setup->wValue); + p_cdc->line_state = p_cdc->requested_line_state; break; case CP210X_SET_BAUDRATE: @@ -1207,9 +1212,9 @@ static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t comple return false; } -static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->user_control_cb = complete_cb; - return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | line_state, NULL, 0, + return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | p_cdc->requested_line_state, NULL, 0, complete_cb ? cp210x_internal_control_complete : NULL, user_data); } @@ -1273,10 +1278,11 @@ static void cp210x_process_config(tuh_xfer_t* xfer) { case CONFIG_CP210X_SET_DTR_RTS: #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - TU_ASSERT(cp210x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, cp210x_process_config, CONFIG_CP210X_COMPLETE),); - break; + p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + TU_ASSERT(cp210x_set_modem_ctrl(p_cdc, cp210x_process_config, CONFIG_CP210X_COMPLETE),); + break; #else - TU_ATTR_FALLTHROUGH; + TU_ATTR_FALLTHROUGH; #endif case CONFIG_CP210X_COMPLETE: @@ -1399,19 +1405,7 @@ static void ch34x_internal_control_complete(tuh_xfer_t * xfer) { break; case CH34X_REQ_MODEM_CTRL: - // set modem controls RTS/DTR request. Note: signals are inverted - uint16_t const modem_signal = ~tu_le16toh(xfer->setup->wValue); - if (modem_signal & CH34X_BIT_RTS) { - p_cdc->line_state |= CDC_CONTROL_LINE_STATE_RTS; - } else { - p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_RTS; - } - - if (modem_signal & CH34X_BIT_DTR) { - p_cdc->line_state |= CDC_CONTROL_LINE_STATE_DTR; - } else { - p_cdc->line_state &= (uint8_t) ~CDC_CONTROL_LINE_STATE_DTR; - } + p_cdc->line_state = p_cdc->requested_line_state; break; default: break; @@ -1487,13 +1481,12 @@ static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complet return true; } -static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, uint16_t line_state, - tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { uint8_t control = 0; - if (line_state & CDC_CONTROL_LINE_STATE_RTS) { + if (p_cdc->requested_line_state & CDC_CONTROL_LINE_STATE_RTS) { control |= CH34X_BIT_RTS; } - if (line_state & CDC_CONTROL_LINE_STATE_DTR) { + if (p_cdc->requested_line_state & CDC_CONTROL_LINE_STATE_DTR) { control |= CH34X_BIT_DTR; } @@ -1587,7 +1580,8 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { case CONFIG_CH34X_MODEM_CONTROL: // !always! set modem controls RTS/DTR (CH34x has no reset state after CH34X_REQ_SERIAL_INIT) - TU_ASSERT (ch34x_set_modem_ctrl(p_cdc, CFG_TUH_CDC_LINE_CONTROL_ON_ENUM, ch34x_process_config, CONFIG_CH34X_COMPLETE),); + p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + TU_ASSERT (ch34x_set_modem_ctrl(p_cdc, ch34x_process_config, CONFIG_CH34X_COMPLETE),); break; case CONFIG_CH34X_COMPLETE: -- cgit v1.3.1 From dcadf8c2a2a79b838cfccf32612fadcb15216c77 Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 22 Feb 2024 15:04:24 +0100 Subject: created set_function_call() --- src/class/cdc/cdc_host.c | 108 +++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 0af429e4b..6a650071d 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -421,115 +421,101 @@ bool tuh_cdc_read_clear (uint8_t idx) { // Control Endpoint API //--------------------------------------------------------------------+ -bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - cdch_interface_t* p_cdc = get_itf(idx); - TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); - TU_LOG_P_CDC("set control line state line_state = %u", line_state); - cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; - - p_cdc->requested_line_state = (uint8_t) line_state; - +// call of (non-)blocking set-functions (to set line state, baudrate, ...) +static bool set_function_call ( + cdch_interface_t * p_cdc, + bool (*set_function)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data), + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { if (complete_cb) { - return driver->set_control_line_state(p_cdc, complete_cb, user_data); + // non-blocking with call back + return set_function(p_cdc, complete_cb, user_data); } else { // blocking - xfer_result_t result = XFER_RESULT_INVALID; - bool ret = driver->set_control_line_state(p_cdc, complete_cb, (uintptr_t) &result); + xfer_result_t result = XFER_RESULT_INVALID; // use local result, because user_data ptr may be NULL + bool ret = set_function(p_cdc, NULL, (uintptr_t) &result); if (user_data) { - // user_data is not NULL, return result via user_data - *((xfer_result_t*) user_data) = result; + *((xfer_result_t *) user_data) = result; } - TU_VERIFY(ret && result == XFER_RESULT_SUCCESS); + return (ret && result == XFER_RESULT_SUCCESS); + } +} + +bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + cdch_interface_t * p_cdc = get_itf(idx); + TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + TU_LOG_P_CDC("set control line state line_state = %u", line_state); + cdch_serial_driver_t const * driver = &serial_drivers[p_cdc->serial_drid]; + + p_cdc->requested_line_state = (uint8_t) line_state; + + bool ret = set_function_call(p_cdc, driver->set_control_line_state, complete_cb, user_data); + + if (ret && !complete_cb) { p_cdc->line_state = (uint8_t) line_state; - return true; } + + return ret; } bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - cdch_interface_t* p_cdc = get_itf(idx); + cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); TU_LOG_P_CDC("set baudrate = %lu", baudrate); - cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; + cdch_serial_driver_t const * driver = &serial_drivers[p_cdc->serial_drid]; p_cdc->requested_line_coding.bit_rate = baudrate; - if (complete_cb) { - return driver->set_baudrate(p_cdc, complete_cb, user_data); - } else { - // blocking - xfer_result_t result = XFER_RESULT_INVALID; - bool ret = driver->set_baudrate(p_cdc, complete_cb, (uintptr_t) &result); - - if (user_data) { - // user_data is not NULL, return result via user_data - *((xfer_result_t*) user_data) = result; - } + bool ret = set_function_call(p_cdc, driver->set_baudrate, complete_cb, user_data); - TU_VERIFY(ret && result == XFER_RESULT_SUCCESS); + if (ret && !complete_cb) { p_cdc->line_coding.bit_rate = baudrate; - return true; } + + return ret; } bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uint8_t data_bits, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - cdch_interface_t* p_cdc = get_itf(idx); + cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); TU_LOG_P_CDC("set data format data_bits = %u parity = %u stop_bits = %u (indexes!)", data_bits, parity, stop_bits); - cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; + cdch_serial_driver_t const * driver = &serial_drivers[p_cdc->serial_drid]; p_cdc->requested_line_coding.stop_bits = stop_bits; p_cdc->requested_line_coding.parity = parity; p_cdc->requested_line_coding.data_bits = data_bits; - if (complete_cb) { - return driver->set_data_format(p_cdc, complete_cb, user_data); - } else { - // blocking - xfer_result_t result = XFER_RESULT_INVALID; - bool ret = driver->set_data_format(p_cdc, complete_cb, (uintptr_t) &result); - - if (user_data) { - // user_data is not NULL, return result via user_data - *((xfer_result_t*) user_data) = result; - } + bool ret = set_function_call(p_cdc, driver->set_data_format, complete_cb, user_data); - TU_VERIFY(ret && result == XFER_RESULT_SUCCESS); + if (ret && !complete_cb) { p_cdc->line_coding.stop_bits = stop_bits; p_cdc->line_coding.parity = parity; p_cdc->line_coding.data_bits = data_bits; - return true; } + + return ret; } -bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const* line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - cdch_interface_t* p_cdc = get_itf(idx); +bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const * line_coding, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); TU_LOG_P_CDC("set line coding baudrate = %lu data_bits = %u parity = %u stop_bits = %u (indexes!)", line_coding->bit_rate, line_coding->data_bits, line_coding->parity, line_coding->stop_bits); - cdch_serial_driver_t const* driver = &serial_drivers[p_cdc->serial_drid]; + cdch_serial_driver_t const * driver = &serial_drivers[p_cdc->serial_drid]; p_cdc->requested_line_coding = *line_coding; - if ( complete_cb ) { - return driver->set_line_coding(p_cdc, complete_cb, user_data); - } else { - // blocking - xfer_result_t result = XFER_RESULT_INVALID; - bool ret = driver->set_line_coding(p_cdc, complete_cb, (uintptr_t) &result); + bool ret = set_function_call(p_cdc, driver->set_line_coding, complete_cb, user_data); - if (user_data) { - // user_data is not NULL, return result via user_data - *((xfer_result_t*) user_data) = result; - } - - TU_VERIFY(ret && result == XFER_RESULT_SUCCESS); + if (ret && !complete_cb) { p_cdc->line_coding = *line_coding; - return true; } + + return ret; } //--------------------------------------------------------------------+ -- cgit v1.3.1 From ea86bbe5f742344b307e52945c0977143522810b Mon Sep 17 00:00:00 2001 From: IngHK Date: Wed, 21 Feb 2024 16:30:02 +0100 Subject: added continue enum after config fail --- src/class/cdc/cdc_host.c | 109 +++++++++++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 6a650071d..226f389e3 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -47,6 +47,16 @@ #define TU_LOG_P_CDC(TXT,...) TU_LOG_CDC(TXT, p_cdc->daddr, p_cdc->bInterfaceNumber, \ serial_drivers[p_cdc->serial_drid].name, ##__VA_ARGS__) +#define TU_ASSERT_COMPLETE_DEFINE(_cond, _itf_offset) \ + do { \ + if (!(_cond)) { _MESS_FAILED(); TU_BREAKPOINT(); set_config_complete(idx, _itf_offset, false); } \ + } while(0) + +#define TU_ASSERT_COMPLETE_1ARGS(_cond) TU_ASSERT_COMPLETE_DEFINE(_cond, 0) +#define TU_ASSERT_COMPLETE_2ARGS(_cond, _itf_offset) TU_ASSERT_COMPLETE_DEFINE(_cond, _itf_offset) + +#define TU_ASSERT_COMPLETE(...) _GET_3RD_ARG(__VA_ARGS__, TU_ASSERT_COMPLETE_2ARGS, TU_ASSERT_COMPLETE_1ARGS, _dummy)(__VA_ARGS__) + //--------------------------------------------------------------------+ // Host CDC Interface //--------------------------------------------------------------------+ @@ -285,7 +295,6 @@ static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const } static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t const *desc_ep); -static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num); //--------------------------------------------------------------------+ // APPLICATION API @@ -657,16 +666,26 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_d return false; } -static void set_config_complete(cdch_interface_t * p_cdc, uint8_t idx, uint8_t itf_num) { - TU_LOG_P_CDC("set config complete"); - p_cdc->mounted = true; - if (tuh_cdc_mount_cb) tuh_cdc_mount_cb(idx); +static void set_config_complete(uint8_t idx, uint8_t itf_offset, bool success) { + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc,); + TU_LOG_P_CDC("set config complete success = %u", success); - // Prepare for incoming data - tu_edpt_stream_read_xfer(&p_cdc->stream.rx); + if (success) { + p_cdc->mounted = true; + if (tuh_cdc_mount_cb) { + tuh_cdc_mount_cb(idx); + } + // Prepare for incoming data + tu_edpt_stream_read_xfer(&p_cdc->stream.rx); + } else { + // clear the interface entry + p_cdc->daddr = 0; + p_cdc->bInterfaceNumber = 0; + } // notify usbh that driver enumeration is complete - usbh_driver_set_config_complete(p_cdc->daddr, itf_num); + usbh_driver_set_config_complete(p_cdc->daddr, p_cdc->bInterfaceNumber + itf_offset); } bool cdch_set_config(uint8_t daddr, uint8_t itf_num) { @@ -856,14 +875,14 @@ static void acm_process_config(tuh_xfer_t* xfer) { uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); cdch_interface_t* p_cdc = get_itf(idx); - TU_ASSERT(p_cdc,); + TU_ASSERT_COMPLETE(p_cdc && xfer->result == XFER_RESULT_SUCCESS, 1); switch (state) { case CONFIG_ACM_SET_CONTROL_LINE_STATE: #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM if (p_cdc->acm_capability.support_line_request) { p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; - TU_ASSERT(acm_set_control_line_state(p_cdc, acm_process_config, CONFIG_ACM_SET_LINE_CODING),); + TU_ASSERT_COMPLETE(acm_set_control_line_state(p_cdc, acm_process_config, CONFIG_ACM_SET_LINE_CODING), 1); break; } #endif @@ -873,7 +892,7 @@ static void acm_process_config(tuh_xfer_t* xfer) { #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM if (p_cdc->acm_capability.support_line_request) { p_cdc->requested_line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM; - TU_ASSERT(acm_set_line_coding(p_cdc, acm_process_config, CONFIG_ACM_COMPLETE),); + TU_ASSERT_COMPLETE(acm_set_line_coding(p_cdc, acm_process_config, CONFIG_ACM_COMPLETE), 1); break; } #endif @@ -881,10 +900,11 @@ static void acm_process_config(tuh_xfer_t* xfer) { case CONFIG_ACM_COMPLETE: // itf_num+1 to account for data interface as well - set_config_complete(p_cdc, idx, itf_num + 1); + set_config_complete(idx, 1, true); break; default: + set_config_complete(idx, 1, false); break; } } @@ -1024,18 +1044,18 @@ static void ftdi_process_config(tuh_xfer_t* xfer) { uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); cdch_interface_t * p_cdc = get_itf(idx); - TU_ASSERT(p_cdc, ); + TU_ASSERT_COMPLETE(p_cdc && xfer->result == XFER_RESULT_SUCCESS); switch(state) { // Note may need to read FTDI eeprom case CONFIG_FTDI_RESET: - TU_ASSERT(ftdi_sio_reset(p_cdc, ftdi_process_config, CONFIG_FTDI_MODEM_CTRL),); + TU_ASSERT_COMPLETE(ftdi_sio_reset(p_cdc, ftdi_process_config, CONFIG_FTDI_MODEM_CTRL)); break; case CONFIG_FTDI_MODEM_CTRL: #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; - TU_ASSERT(ftdi_sio_set_modem_ctrl(p_cdc, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE),); + TU_ASSERT_COMPLETE(ftdi_sio_set_modem_ctrl(p_cdc, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE)); break; #else TU_ATTR_FALLTHROUGH; @@ -1044,7 +1064,7 @@ static void ftdi_process_config(tuh_xfer_t* xfer) { case CONFIG_FTDI_SET_BAUDRATE: { #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM p_cdc->requested_line_coding.bit_rate = ((cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM).bit_rate; - TU_ASSERT(ftdi_sio_set_baudrate(p_cdc, ftdi_process_config, CONFIG_FTDI_SET_DATA),); + TU_ASSERT_COMPLETE(ftdi_sio_set_baudrate(p_cdc, ftdi_process_config, CONFIG_FTDI_SET_DATA)); break; #else TU_ATTR_FALLTHROUGH; @@ -1055,7 +1075,7 @@ static void ftdi_process_config(tuh_xfer_t* xfer) { #if 0 // TODO set data format #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; - TU_ASSERT(ftdi_sio_set_data(p_cdc, process_ftdi_config, CONFIG_FTDI_COMPLETE),); + TU_ASSERT_COMPLETE(ftdi_sio_set_data(p_cdc, process_ftdi_config, CONFIG_FTDI_COMPLETE)); break; #endif #endif @@ -1064,10 +1084,11 @@ static void ftdi_process_config(tuh_xfer_t* xfer) { } case CONFIG_FTDI_COMPLETE: - set_config_complete(p_cdc, idx, itf_num); + set_config_complete(idx, 0, true); break; default: + set_config_complete(idx, 0, false); break; } } @@ -1150,8 +1171,8 @@ static bool cp210x_ifc_enable(cdch_interface_t* p_cdc, uint16_t enabled, tuh_xfe static void cp210x_internal_control_complete(tuh_xfer_t * xfer) { uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); - cdch_interface_t * p_cdc = get_itf(idx); - TU_ASSERT(p_cdc,); + cdch_interface_t* p_cdc = get_itf(idx); + TU_ASSERT(p_cdc, ); bool const success = (xfer->result == XFER_RESULT_SUCCESS); TU_LOG_P_CDC("control complete success = %u", success); @@ -1236,17 +1257,17 @@ static void cp210x_process_config(tuh_xfer_t* xfer) { uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); cdch_interface_t *p_cdc = get_itf(idx); - TU_ASSERT(p_cdc,); + TU_ASSERT_COMPLETE(p_cdc && xfer->result == XFER_RESULT_SUCCESS); switch (state) { case CONFIG_CP210X_IFC_ENABLE: - TU_ASSERT(cp210x_ifc_enable(p_cdc, 1, cp210x_process_config, CONFIG_CP210X_SET_BAUDRATE),); + TU_ASSERT_COMPLETE(cp210x_ifc_enable(p_cdc, 1, cp210x_process_config, CONFIG_CP210X_SET_BAUDRATE)); break; case CONFIG_CP210X_SET_BAUDRATE: { #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM p_cdc->requested_line_coding.bit_rate = ((cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM).bit_rate; - TU_ASSERT(cp210x_set_baudrate(p_cdc, cp210x_process_config, CONFIG_CP210X_SET_LINE_CTL),); + TU_ASSERT_COMPLETE(cp210x_set_baudrate(p_cdc, cp210x_process_config, CONFIG_CP210X_SET_LINE_CTL)); break; #else TU_ATTR_FALLTHROUGH; @@ -1265,17 +1286,19 @@ static void cp210x_process_config(tuh_xfer_t* xfer) { case CONFIG_CP210X_SET_DTR_RTS: #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; - TU_ASSERT(cp210x_set_modem_ctrl(p_cdc, cp210x_process_config, CONFIG_CP210X_COMPLETE),); + TU_ASSERT_COMPLETE(cp210x_set_modem_ctrl(p_cdc, cp210x_process_config, CONFIG_CP210X_COMPLETE)); break; #else TU_ATTR_FALLTHROUGH; #endif case CONFIG_CP210X_COMPLETE: - set_config_complete(p_cdc, idx, itf_num); + set_config_complete(idx, 0, true); break; - default: break; + default: + set_config_complete(idx, 0, false); + break; } } @@ -1361,11 +1384,11 @@ static bool ch34x_write_reg_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t comp // internal control complete to update state such as line state, encoding static void ch34x_internal_control_complete(tuh_xfer_t * xfer) { - // CH34x has only interface 0, because wIndex is used as payload and not for bInterfaceNumber + // CH34x only has 1 interface and wIndex used as payload and not for bInterfaceNumber uint8_t const itf_num = 0; uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); - cdch_interface_t * p_cdc = get_itf(idx); - TU_ASSERT(p_cdc,); + cdch_interface_t* p_cdc = get_itf(idx); + TU_ASSERT(p_cdc, ); bool const success = (xfer->result == XFER_RESULT_SUCCESS); TU_LOG_P_CDC("control complete success = %u", success); @@ -1526,14 +1549,14 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { uint8_t const itf_num = 0; uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); cdch_interface_t* p_cdc = get_itf(idx); + TU_ASSERT_COMPLETE(p_cdc && xfer->result == XFER_RESULT_SUCCESS); uintptr_t const state = xfer->user_data; uint8_t buffer[2]; // TODO remove - TU_ASSERT (p_cdc,); - TU_ASSERT (xfer->result == XFER_RESULT_SUCCESS,); switch (state) { case CONFIG_CH34X_READ_VERSION: - TU_ASSERT (ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, buffer, 2, ch34x_process_config, CONFIG_CH34X_SERIAL_INIT),); + TU_ASSERT_COMPLETE(ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, buffer, 2, + ch34x_process_config, CONFIG_CH34X_SERIAL_INIT)); break; case CONFIG_CH34X_SERIAL_INIT: { @@ -1541,41 +1564,43 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { uint8_t const version = xfer->buffer[0]; TU_LOG_P_CDC("Chip Version = %02x", version); // only versions >= 0x30 are tested, below 0x30 seems having other programming, see drivers from WCH vendor, Linux kernel and FreeBSD - TU_ASSERT (version >= 0x30,); + TU_ASSERT_COMPLETE(version >= 0x30); // init CH34x with line coding p_cdc->requested_line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X; uint16_t const div_ps = ch34x_get_divisor_prescaler(p_cdc); - TU_ASSERT(div_ps, ); + TU_ASSERT_COMPLETE(div_ps); uint8_t const lcr = ch34x_get_lcr(p_cdc); - TU_ASSERT(lcr, ); - TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_SERIAL_INIT, tu_u16(lcr, 0x9c), div_ps, - ch34x_process_config, CONFIG_CH34X_SPECIAL_REG_WRITE),); + TU_ASSERT_COMPLETE(lcr); + TU_ASSERT_COMPLETE(ch34x_control_out(p_cdc, CH34X_REQ_SERIAL_INIT, tu_u16(lcr, 0x9c), div_ps, + ch34x_process_config, CONFIG_CH34X_SPECIAL_REG_WRITE)); break; } case CONFIG_CH34X_SPECIAL_REG_WRITE: // overtake line coding and do special reg write, purpose unknown, overtaken from WCH driver p_cdc->line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X; - TU_ASSERT (ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x0F, CH341_REG_0x2C), 0x0007, ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL),); + TU_ASSERT_COMPLETE(ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x0F, CH341_REG_0x2C), 0x0007, + ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL)); break; case CONFIG_CH34X_FLOW_CONTROL: // no hardware flow control - TU_ASSERT (ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x27, CH341_REG_0x27), 0x0000, ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL),); + TU_ASSERT_COMPLETE(ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x27, CH341_REG_0x27), 0x0000, + ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL)); break; case CONFIG_CH34X_MODEM_CONTROL: // !always! set modem controls RTS/DTR (CH34x has no reset state after CH34X_REQ_SERIAL_INIT) p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; - TU_ASSERT (ch34x_set_modem_ctrl(p_cdc, ch34x_process_config, CONFIG_CH34X_COMPLETE),); + TU_ASSERT_COMPLETE(ch34x_set_modem_ctrl(p_cdc, ch34x_process_config, CONFIG_CH34X_COMPLETE)); break; case CONFIG_CH34X_COMPLETE: - set_config_complete(p_cdc, idx, itf_num); + set_config_complete(idx, 0, true); break; default: - TU_ASSERT (false,); + set_config_complete(idx, 0, false); break; } } -- cgit v1.3.1 From 22a12c76689399bd1a67e52531171876e5d88a28 Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 22 Feb 2024 09:28:06 +0100 Subject: improved ACM checks --- src/class/cdc/cdc_host.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 226f389e3..ae8245b0e 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -773,6 +773,11 @@ static bool acm_set_control_line_state(cdch_interface_t* p_cdc, tuh_xfer_cb_t co } static bool acm_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + TU_VERIFY(p_cdc->acm_capability.support_line_request); + TU_VERIFY(p_cdc->requested_line_coding.data_bits && p_cdc->requested_line_coding.bit_rate); + TU_VERIFY((p_cdc->requested_line_coding.data_bits >= 5 && p_cdc->requested_line_coding.data_bits <= 8) || + p_cdc->requested_line_coding.data_bits == 16); + tusb_control_request_t const request = { .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, @@ -813,7 +818,6 @@ static bool acm_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, p_cdc->requested_line_coding.stop_bits = p_cdc->line_coding.stop_bits; p_cdc->requested_line_coding.parity = p_cdc->line_coding.parity; p_cdc->requested_line_coding.data_bits = p_cdc->line_coding.data_bits; - TU_VERIFY(p_cdc->acm_capability.support_line_request); return acm_set_line_coding(p_cdc, complete_cb, user_data); } -- cgit v1.3.1 From 138567af3e39e08742b3d1edf5b83d27d8526a4a Mon Sep 17 00:00:00 2001 From: IngHK Date: Sun, 18 Feb 2024 20:33:54 +0100 Subject: fixed #2448 CH34x ch34x_set_line_coding() callback bug --- src/class/cdc/cdc_host.c | 127 ++++++++++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index ae8245b0e..f6804ca2b 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -80,6 +80,9 @@ typedef struct { uint8_t requested_line_state; tuh_xfer_cb_t user_control_cb; + #if CFG_TUH_CDC_CH34X + tuh_xfer_cb_t requested_complete_cb; + #endif struct { tu_edpt_stream_t tx; @@ -1376,12 +1379,33 @@ static inline bool ch34x_write_reg(cdch_interface_t* p_cdc, uint16_t reg, uint16 // return ch34x_control_in ( p_cdc, CH34X_REQ_READ_REG, reg, 0, buffer, buffersize, complete_cb, user_data ); //} -static bool ch34x_write_reg_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool ch34x_write_reg_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + uint8_t const lcr = ch34x_get_lcr(p_cdc); + TU_VERIFY(lcr); + + return ch34x_write_reg(p_cdc, CH32X_REG16_LCR2_LCR, lcr, complete_cb, user_data); +} + +static bool ch34x_write_reg_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { uint16_t const div_ps = ch34x_get_divisor_prescaler(p_cdc); TU_VERIFY(div_ps); - TU_ASSERT(ch34x_write_reg(p_cdc, CH34X_REG16_DIVISOR_PRESCALER, div_ps, - complete_cb, user_data)); - return true; + + return ch34x_write_reg(p_cdc, CH34X_REG16_DIVISOR_PRESCALER, div_ps, complete_cb, user_data); +} + +static bool ch34x_modem_ctrl_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + uint8_t control = 0; + if (p_cdc->requested_line_state & CDC_CONTROL_LINE_STATE_RTS) { + control |= CH34X_BIT_RTS; + } + if (p_cdc->requested_line_state & CDC_CONTROL_LINE_STATE_DTR) { + control |= CH34X_BIT_DTR; + } + + // CH34x signals are inverted + control = ~control; + + return ch34x_control_out(p_cdc, CH34X_REQ_MODEM_CTRL, control, 0, complete_cb, user_data); } //------------- Driver API -------------// @@ -1431,34 +1455,34 @@ static void ch34x_internal_control_complete(tuh_xfer_t * xfer) { } } -static bool ch34x_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - uint8_t const lcr = ch34x_get_lcr(p_cdc); - TU_VERIFY(lcr); - TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, CH32X_REG16_LCR2_LCR, lcr, - complete_cb ? ch34x_internal_control_complete : NULL, user_data)); +static bool ch34x_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + p_cdc->user_control_cb = complete_cb; + TU_ASSERT(ch34x_write_reg_data_format(p_cdc, complete_cb ? ch34x_internal_control_complete : NULL, user_data)); + return true; } -static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool ch34x_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->user_control_cb = complete_cb; TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, complete_cb ? ch34x_internal_control_complete : NULL, user_data)); + return true; } -static void ch34x_set_line_coding_stage1_complete(tuh_xfer_t* xfer) { - // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber +static void ch34x_set_line_coding_stage1_complete(tuh_xfer_t * xfer) { + // CH34x only has 1 interface and wIndex used as payload and not for bInterfaceNumber uint8_t const itf_num = 0; uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); cdch_interface_t* p_cdc = get_itf(idx); TU_ASSERT(p_cdc, ); if (xfer->result == XFER_RESULT_SUCCESS) { - // stage 1 success, continue to stage 2 - p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; - TU_ASSERT(ch34x_set_data_format(p_cdc, ch34x_internal_control_complete, xfer->user_data), ); + // stage 1 success, continue with stage 2 + p_cdc->user_control_cb = p_cdc->requested_complete_cb; + ch34x_write_reg_data_format(p_cdc, ch34x_internal_control_complete, xfer->user_data); } else { // stage 1 failed, notify user - xfer->complete_cb = p_cdc->user_control_cb; + xfer->complete_cb = p_cdc->requested_complete_cb; if (xfer->complete_cb) { xfer->complete_cb(xfer); } @@ -1466,49 +1490,46 @@ static void ch34x_set_line_coding_stage1_complete(tuh_xfer_t* xfer) { } // 2 stages: set baudrate (stage1) + set data format (stage2) -static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - p_cdc->user_control_cb = complete_cb; - +static bool ch34x_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { if (complete_cb) { // stage 1 set baudrate - TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, ch34x_set_line_coding_stage1_complete, user_data)); + p_cdc->requested_complete_cb = complete_cb; + p_cdc->user_control_cb = ch34x_set_line_coding_stage1_complete; + return ch34x_write_reg_baudrate(p_cdc, ch34x_internal_control_complete, user_data); } else { - // sync call - xfer_result_t result; - + // blocking sequence // stage 1 set baudrate - TU_ASSERT(ch34x_write_reg_baudrate(p_cdc, NULL, (uintptr_t) &result)); + xfer_result_t result = XFER_RESULT_INVALID; // use local result, because user_data ptr may be NULL + bool ret = ch34x_write_reg_baudrate(p_cdc, NULL, (uintptr_t) &result); + + // store/check results + if (user_data) { + *((xfer_result_t*) user_data) = result; + } + TU_ASSERT(ret); TU_VERIFY(result == XFER_RESULT_SUCCESS); + + // overtake baudrate p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; // stage 2 set data format - TU_ASSERT(ch34x_set_data_format(p_cdc, NULL, (uintptr_t) &result)); - TU_VERIFY(result == XFER_RESULT_SUCCESS); + result = XFER_RESULT_INVALID; + ret = ch34x_write_reg_data_format(p_cdc, NULL, (uintptr_t) &result); - // update transfer result, user_data is expected to point to xfer_result_t + // store/check results if (user_data) { *((xfer_result_t*) user_data) = result; } + TU_ASSERT(ret); + return (result == XFER_RESULT_SUCCESS); + // the overtaking of remaining requested_line_coding will be done in tuh_cdc_set_line_coding() } - - return true; } -static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - uint8_t control = 0; - if (p_cdc->requested_line_state & CDC_CONTROL_LINE_STATE_RTS) { - control |= CH34X_BIT_RTS; - } - if (p_cdc->requested_line_state & CDC_CONTROL_LINE_STATE_DTR) { - control |= CH34X_BIT_DTR; - } - - // CH34x signals are inverted - control = ~control; - +static bool ch34x_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->user_control_cb = complete_cb; - TU_ASSERT (ch34x_control_out(p_cdc, CH34X_REQ_MODEM_CTRL, control, 0, - complete_cb ? ch34x_internal_control_complete : NULL, user_data)); + TU_ASSERT(ch34x_modem_ctrl_request(p_cdc, complete_cb ? ch34x_internal_control_complete : NULL, user_data)); + return true; } @@ -1549,25 +1570,27 @@ static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uin } static void ch34x_process_config(tuh_xfer_t* xfer) { - // CH34x only has 1 interface and use wIndex as payload and not for bInterfaceNumber + // CH34x only has 1 interface and wIndex used as payload and not for bInterfaceNumber + uintptr_t const state = xfer->user_data; uint8_t const itf_num = 0; uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); cdch_interface_t* p_cdc = get_itf(idx); TU_ASSERT_COMPLETE(p_cdc && xfer->result == XFER_RESULT_SUCCESS); - uintptr_t const state = xfer->user_data; uint8_t buffer[2]; // TODO remove switch (state) { case CONFIG_CH34X_READ_VERSION: + p_cdc->user_control_cb = ch34x_process_config; // set once for whole process config TU_ASSERT_COMPLETE(ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, buffer, 2, - ch34x_process_config, CONFIG_CH34X_SERIAL_INIT)); + ch34x_process_config, CONFIG_CH34X_SERIAL_INIT)); break; case CONFIG_CH34X_SERIAL_INIT: { // handle version read data, set CH34x line coding (incl. baudrate) uint8_t const version = xfer->buffer[0]; TU_LOG_P_CDC("Chip Version = %02x", version); - // only versions >= 0x30 are tested, below 0x30 seems having other programming, see drivers from WCH vendor, Linux kernel and FreeBSD + // only versions >= 0x30 are tested, below 0x30 seems having other programming + // see drivers from WCH vendor, Linux kernel and FreeBSD TU_ASSERT_COMPLETE(version >= 0x30); // init CH34x with line coding p_cdc->requested_line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X; @@ -1584,19 +1607,19 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { // overtake line coding and do special reg write, purpose unknown, overtaken from WCH driver p_cdc->line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X; TU_ASSERT_COMPLETE(ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x0F, CH341_REG_0x2C), 0x0007, - ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL)); + ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL)); break; case CONFIG_CH34X_FLOW_CONTROL: // no hardware flow control TU_ASSERT_COMPLETE(ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x27, CH341_REG_0x27), 0x0000, - ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL)); + ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL)); break; case CONFIG_CH34X_MODEM_CONTROL: // !always! set modem controls RTS/DTR (CH34x has no reset state after CH34X_REQ_SERIAL_INIT) p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; - TU_ASSERT_COMPLETE(ch34x_set_modem_ctrl(p_cdc, ch34x_process_config, CONFIG_CH34X_COMPLETE)); + TU_ASSERT_COMPLETE(ch34x_modem_ctrl_request(p_cdc, ch34x_internal_control_complete, CONFIG_CH34X_COMPLETE)); break; case CONFIG_CH34X_COMPLETE: @@ -1668,7 +1691,7 @@ static uint8_t ch34x_get_lcr(cdch_interface_t * p_cdc) { uint8_t const data_bits = p_cdc->requested_line_coding.data_bits; uint8_t lcr = CH34X_LCR_ENABLE_RX | CH34X_LCR_ENABLE_TX; - TU_VERIFY(data_bits >= 5 && data_bits <= 8, 0); + TU_VERIFY(data_bits >= 5 && data_bits <= 8); lcr |= (uint8_t) (data_bits - 5); switch(parity) { @@ -1695,7 +1718,7 @@ static uint8_t ch34x_get_lcr(cdch_interface_t * p_cdc) { } // 1.5 stop bits not supported - TU_VERIFY(stop_bits != CDC_LINE_CODING_STOP_BITS_1_5, 0); + TU_VERIFY(stop_bits != CDC_LINE_CODING_STOP_BITS_1_5); if (stop_bits == CDC_LINE_CODING_STOP_BITS_2) { lcr |= CH34X_LCR_STOP_BITS_2; } -- cgit v1.3.1 From db511fb2f3df190f538d84d08b6be83746adae0e Mon Sep 17 00:00:00 2001 From: IngHK Date: Mon, 19 Feb 2024 08:05:16 +0100 Subject: fixed CFG_TUH_CDC_LINE_CONTROL_ON_ENUM handling. only set if defined. value 0 is also valid --- src/class/cdc/cdc_host.c | 17 ++++++++++------- src/class/cdc/cdc_host.h | 10 ---------- 2 files changed, 10 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index f6804ca2b..850c1fceb 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -886,7 +886,7 @@ static void acm_process_config(tuh_xfer_t* xfer) { switch (state) { case CONFIG_ACM_SET_CONTROL_LINE_STATE: - #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM if (p_cdc->acm_capability.support_line_request) { p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; TU_ASSERT_COMPLETE(acm_set_control_line_state(p_cdc, acm_process_config, CONFIG_ACM_SET_LINE_CODING), 1); @@ -1060,7 +1060,7 @@ static void ftdi_process_config(tuh_xfer_t* xfer) { break; case CONFIG_FTDI_MODEM_CTRL: - #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; TU_ASSERT_COMPLETE(ftdi_sio_set_modem_ctrl(p_cdc, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE)); break; @@ -1291,7 +1291,7 @@ static void cp210x_process_config(tuh_xfer_t* xfer) { } case CONFIG_CP210X_SET_DTR_RTS: - #if CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; TU_ASSERT_COMPLETE(cp210x_set_modem_ctrl(p_cdc, cp210x_process_config, CONFIG_CP210X_COMPLETE)); break; @@ -1617,10 +1617,13 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { break; case CONFIG_CH34X_MODEM_CONTROL: - // !always! set modem controls RTS/DTR (CH34x has no reset state after CH34X_REQ_SERIAL_INIT) - p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; - TU_ASSERT_COMPLETE(ch34x_modem_ctrl_request(p_cdc, ch34x_internal_control_complete, CONFIG_CH34X_COMPLETE)); - break; + #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + TU_ASSERT_COMPLETE(ch34x_modem_ctrl_request(p_cdc, ch34x_internal_control_complete, CONFIG_CH34X_COMPLETE)); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif case CONFIG_CH34X_COMPLETE: set_config_complete(idx, 0, true); diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h index d512a23a5..ca6567453 100644 --- a/src/class/cdc/cdc_host.h +++ b/src/class/cdc/cdc_host.h @@ -37,16 +37,6 @@ // Class Driver Configuration //--------------------------------------------------------------------+ -// Set Line Control state on enumeration/mounted: DTR ( bit 0), RTS (bit 1) -#ifndef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM -#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM 0 -#endif - -// Set Line Coding on enumeration/mounted, value for cdc_line_coding_t -//#ifndef CFG_TUH_CDC_LINE_CODING_ON_ENUM -//#define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 } -//#endif - // RX FIFO size #ifndef CFG_TUH_CDC_RX_BUFSIZE #define CFG_TUH_CDC_RX_BUFSIZE USBH_EPSIZE_BULK_MAX -- cgit v1.3.1 From 0b5f85eee0f34a80dd8debe2f764607070293119 Mon Sep 17 00:00:00 2001 From: IngHK Date: Wed, 21 Feb 2024 17:25:13 +0100 Subject: created set_line_coding_sequence() and void set_line_coding_stage1_complete() to be reused by FTDI & CP210x --- src/class/cdc/cdc_host.c | 131 +++++++++++++++++++++++++++++------------------ 1 file changed, 81 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 850c1fceb..8deeffc37 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -433,6 +433,77 @@ bool tuh_cdc_read_clear (uint8_t idx) { // Control Endpoint API //--------------------------------------------------------------------+ +// set line coding using sequence with 2 stages: set baudrate (stage1) + set data format (stage2) +static bool set_line_coding_sequence( + cdch_interface_t * p_cdc, + // control request function to set baudrate + bool (*set_baudrate_request)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data), + // control request function to set data format + bool (*set_data_format_request)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data), + // function to be called after stage 1 completed + void (*set_line_coding_stage1_complete)(tuh_xfer_t * xfer), + // control complete function to be called after request + void (*internal_control_complete)(tuh_xfer_t * xfer), + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + if (complete_cb) { + // non-blocking + // stage 1 set baudrate + p_cdc->requested_complete_cb = complete_cb; // store complete_cb to be used in set_line_coding_stage1_complete() + p_cdc->user_control_cb = set_line_coding_stage1_complete; + return set_baudrate_request(p_cdc, internal_control_complete, user_data); + } else { + // blocking sequence + // stage 1 set baudrate + xfer_result_t result = XFER_RESULT_INVALID; // use local result, because user_data ptr may be NULL + bool ret = set_baudrate_request(p_cdc, NULL, (uintptr_t) &result); + + if (user_data) { + *((xfer_result_t *) user_data) = result; + } + + TU_ASSERT(ret); + TU_VERIFY(result == XFER_RESULT_SUCCESS); + + // overtake baudrate after successful request + p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; + + // stage 2 set data format + result = XFER_RESULT_INVALID; + ret = set_data_format_request(p_cdc, NULL, (uintptr_t) &result); + + if (user_data) { + *((xfer_result_t *) user_data) = result; + } + + TU_ASSERT(ret); + return (result == XFER_RESULT_SUCCESS); + // the overtaking of remaining requested_line_coding will be done in tuh_cdc_set_line_coding() + } +} + +static void set_line_coding_stage1_complete( + tuh_xfer_t * xfer, uint8_t const itf_num, + // control request function to set data format + bool (*set_data_format_request)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data), + // control complete function to be called after request + void (*internal_control_complete)(tuh_xfer_t * xfer)) { + uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc,); + + if (xfer->result == XFER_RESULT_SUCCESS) { + // stage 1 success, continue with stage 2 + p_cdc->user_control_cb = p_cdc->requested_complete_cb; + set_data_format_request(p_cdc, internal_control_complete, xfer->user_data); + } else { + // stage 1 failed, notify user + xfer->complete_cb = p_cdc->requested_complete_cb; + if (xfer->complete_cb) { + xfer->complete_cb(xfer); + } + } +} + // call of (non-)blocking set-functions (to set line state, baudrate, ...) static bool set_function_call ( cdch_interface_t * p_cdc, @@ -1470,60 +1541,20 @@ static bool ch34x_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_ } static void ch34x_set_line_coding_stage1_complete(tuh_xfer_t * xfer) { - // CH34x only has 1 interface and wIndex used as payload and not for bInterfaceNumber - uint8_t const itf_num = 0; - uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); - cdch_interface_t* p_cdc = get_itf(idx); - TU_ASSERT(p_cdc, ); - - if (xfer->result == XFER_RESULT_SUCCESS) { - // stage 1 success, continue with stage 2 - p_cdc->user_control_cb = p_cdc->requested_complete_cb; - ch34x_write_reg_data_format(p_cdc, ch34x_internal_control_complete, xfer->user_data); - } else { - // stage 1 failed, notify user - xfer->complete_cb = p_cdc->requested_complete_cb; - if (xfer->complete_cb) { - xfer->complete_cb(xfer); - } - } + uint8_t const itf_num = 0; // CH34x has only interface 0, because wIndex is used as payload and not for bInterfaceNumber + set_line_coding_stage1_complete(xfer, itf_num, + ch34x_write_reg_data_format, // control request function to set data format + ch34x_internal_control_complete); // control complete function to be called after request } // 2 stages: set baudrate (stage1) + set data format (stage2) static bool ch34x_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - if (complete_cb) { - // stage 1 set baudrate - p_cdc->requested_complete_cb = complete_cb; - p_cdc->user_control_cb = ch34x_set_line_coding_stage1_complete; - return ch34x_write_reg_baudrate(p_cdc, ch34x_internal_control_complete, user_data); - } else { - // blocking sequence - // stage 1 set baudrate - xfer_result_t result = XFER_RESULT_INVALID; // use local result, because user_data ptr may be NULL - bool ret = ch34x_write_reg_baudrate(p_cdc, NULL, (uintptr_t) &result); - - // store/check results - if (user_data) { - *((xfer_result_t*) user_data) = result; - } - TU_ASSERT(ret); - TU_VERIFY(result == XFER_RESULT_SUCCESS); - - // overtake baudrate - p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; - - // stage 2 set data format - result = XFER_RESULT_INVALID; - ret = ch34x_write_reg_data_format(p_cdc, NULL, (uintptr_t) &result); - - // store/check results - if (user_data) { - *((xfer_result_t*) user_data) = result; - } - TU_ASSERT(ret); - return (result == XFER_RESULT_SUCCESS); - // the overtaking of remaining requested_line_coding will be done in tuh_cdc_set_line_coding() - } + return set_line_coding_sequence(p_cdc, + ch34x_write_reg_baudrate, // control request function to set baudrate + ch34x_write_reg_data_format, // control request function to set data format + ch34x_set_line_coding_stage1_complete, // function to be called after stage 1 completed + ch34x_internal_control_complete, // control complete function to be called after request + complete_cb, user_data); } static bool ch34x_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { -- cgit v1.3.1 From 7fef5943eff0d7f1b7122e878cf2caa78bea06aa Mon Sep 17 00:00:00 2001 From: IngHK Date: Sat, 24 Feb 2024 12:58:45 +0100 Subject: improved FTDI support --- src/class/cdc/cdc_host.c | 503 +++++++++++++++++++++++++++++++++------- src/class/cdc/serial/ftdi_sio.h | 385 +++++++++++++++--------------- src/tusb_option.h | 19 +- 3 files changed, 616 insertions(+), 291 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 8deeffc37..f9540efbe 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -35,6 +35,9 @@ #include "host/usbh_pvt.h" #include "cdc_host.h" +#include "serial/ftdi_sio.h" +#include "serial/cp210x.h" +#include "serial/ch34x.h" // Level where CFG_TUSB_DEBUG must be at least for this driver is logged #ifndef CFG_TUH_CDC_LOG_LEVEL @@ -80,10 +83,14 @@ typedef struct { uint8_t requested_line_state; tuh_xfer_cb_t user_control_cb; - #if CFG_TUH_CDC_CH34X + #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CH34X tuh_xfer_cb_t requested_complete_cb; #endif + #if CFG_TUH_CDC_FTDI + ftdi_private_t ftdi; + #endif + struct { tu_edpt_stream_t tx; tu_edpt_stream_t rx; @@ -98,6 +105,9 @@ typedef struct { CFG_TUH_MEM_SECTION static cdch_interface_t cdch_data[CFG_TUH_CDC]; +#if CFG_TUH_CDC_FTDI + static tusb_desc_device_t desc_dev[CFG_TUH_CDC][CFG_TUH_ENUMERATION_BUFSIZE]; +#endif //--------------------------------------------------------------------+ // Serial Driver @@ -114,23 +124,22 @@ static bool acm_set_control_line_state(cdch_interface_t* p_cdc, tuh_xfer_cb_t co //------------- FTDI prototypes -------------// #if CFG_TUH_CDC_FTDI -#include "serial/ftdi_sio.h" - static uint16_t const ftdi_vid_pid_list[][2] = {CFG_TUH_CDC_FTDI_VID_PID_LIST}; +#if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL +static uint8_t const * ftdi_chip_name[] = { FTDI_CHIP_NAMES }; +#endif static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len); static void ftdi_process_config(tuh_xfer_t* xfer); -static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ftdi_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool ftdi_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ftdi_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #endif //------------- CP210X prototypes -------------// #if CFG_TUH_CDC_CP210X -#include "serial/cp210x.h" - static uint16_t const cp210x_vid_pid_list[][2] = {CFG_TUH_CDC_CP210X_VID_PID_LIST}; static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); @@ -144,8 +153,6 @@ static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complet //------------- CH34x prototypes -------------// #if CFG_TUH_CDC_CH34X -#include "serial/ch34x.h" - static uint16_t const ch34x_vid_pid_list[][2] = {CFG_TUH_CDC_CH34X_VID_PID_LIST}; static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len); @@ -212,8 +219,8 @@ static const cdch_serial_driver_t serial_drivers[] = { .vid_pid_count = TU_ARRAY_SIZE(ftdi_vid_pid_list), .open = ftdi_open, .process_set_config = ftdi_process_config, - .set_control_line_state = ftdi_sio_set_modem_ctrl, - .set_baudrate = ftdi_sio_set_baudrate, + .set_control_line_state = ftdi_set_modem_ctrl, + .set_baudrate = ftdi_set_baudrate, .set_data_format = ftdi_set_data_format, .set_line_coding = ftdi_set_line_coding, #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL @@ -992,28 +999,27 @@ static void acm_process_config(tuh_xfer_t* xfer) { //--------------------------------------------------------------------+ #if CFG_TUH_CDC_FTDI -static uint32_t ftdi_232bm_baud_to_divisor(cdch_interface_t* p_cdc); +static bool ftdi_determine_type(cdch_interface_t * p_cdc, uint8_t const idx); +static uint32_t ftdi_get_divisor(cdch_interface_t * p_cdc); +static uint8_t ftdi_get_idx(tuh_xfer_t * xfer); //------------- Control Request -------------// // set request without data -static bool ftdi_sio_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - tusb_control_request_t const request = { - .bmRequestType_bit = { - .recipient = TUSB_REQ_RCPT_DEVICE, - .type = TUSB_REQ_TYPE_VENDOR, - .direction = TUSB_DIR_OUT - }, - .bRequest = command, - .wValue = tu_htole16(value), - .wIndex = 0, - .wLength = 0 +static bool ftdi_set_request(cdch_interface_t * p_cdc, uint8_t request, uint8_t requesttype, + uint16_t value, uint16_t index, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + tusb_control_request_t const request_setup = { + .bmRequestType = requesttype, + .bRequest = request, + .wValue = tu_htole16(value), + .wIndex = tu_htole16(index), + .wLength = 0 }; tuh_xfer_t xfer = { .daddr = p_cdc->daddr, .ep_addr = 0, - .setup = &request, + .setup = &request_setup, .buffer = NULL, .complete_cb = complete_cb, .user_data = user_data @@ -1022,16 +1028,57 @@ static bool ftdi_sio_set_request(cdch_interface_t* p_cdc, uint8_t command, uint1 return tuh_control_xfer(&xfer); } -static bool ftdi_sio_reset(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - return ftdi_sio_set_request(p_cdc, FTDI_SIO_RESET, FTDI_SIO_RESET_SIO, complete_cb, user_data); +#ifdef CFG_TUH_CDC_FTDI_LATENCY +static int8_t ftdi_write_latency_timer(cdch_interface_t * p_cdc, uint16_t latency, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + if (p_cdc->ftdi.chip_type == SIO /* || p_cdc->ftdi.chip_type == FT232A */ ) + return FTDI_NOT_POSSIBLE; + return ftdi_set_request(p_cdc, FTDI_SIO_SET_LATENCY_TIMER_REQUEST, FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE, + latency, p_cdc->ftdi.channel, complete_cb, user_data) ? FTDI_REQUESTED : FTDI_FAIL; +} +#endif + +static inline bool ftdi_sio_reset(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + return ftdi_set_request(p_cdc, FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE, FTDI_SIO_RESET_SIO, + p_cdc->ftdi.channel, complete_cb, user_data); +} + +static bool ftdi_change_speed(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + uint32_t index_value = ftdi_get_divisor(p_cdc); + TU_VERIFY(index_value); + uint16_t value = (uint16_t) index_value; + uint16_t index = (uint16_t) (index_value >> 16); + if (p_cdc->ftdi.channel) { + index = (uint16_t)((index << 8) | p_cdc->ftdi.channel); + } + + return ftdi_set_request(p_cdc, FTDI_SIO_SET_BAUDRATE_REQUEST, FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE, + value, index, complete_cb, user_data); +} + +static bool ftdi_set_data_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + TU_VERIFY(p_cdc->requested_line_coding.data_bits >= 7 && p_cdc->requested_line_coding.data_bits <= 8, 0); + uint16_t value = (uint16_t) ( + ((uint32_t) p_cdc->requested_line_coding.data_bits & 0xf) | // data bit quantity is stored in bits 0-3 + ((uint32_t) p_cdc->requested_line_coding.parity & 0x7) << 8 | // parity is stored in bits 8-10, same coding + ((uint32_t) p_cdc->requested_line_coding.stop_bits & 0x3) << 11 ); // stop bits quantity is stored in bits 11-12, same coding + // not each FTDI supports 1.5 stop bits + + return ftdi_set_request(p_cdc, FTDI_SIO_SET_DATA_REQUEST, FTDI_SIO_SET_DATA_REQUEST_TYPE, + value, p_cdc->ftdi.channel, complete_cb, user_data); +} + +static inline bool ftdi_update_mctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + // FTDI has the same bit coding + return ftdi_set_request(p_cdc, FTDI_SIO_SET_MODEM_CTRL_REQUEST, FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, + p_cdc->requested_line_state, p_cdc->ftdi.channel, complete_cb, user_data); } //------------- Driver API -------------// // internal control complete to update state such as line state, line_coding static void ftdi_internal_control_complete(tuh_xfer_t * xfer) { - uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); - uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + uint8_t const idx = ftdi_get_idx(xfer); cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT(p_cdc,); bool const success = (xfer->result == XFER_RESULT_SUCCESS); @@ -1039,11 +1086,17 @@ static void ftdi_internal_control_complete(tuh_xfer_t * xfer) { if (success) { switch (xfer->setup->bRequest) { - case FTDI_SIO_MODEM_CTRL: + case FTDI_SIO_SET_MODEM_CTRL_REQUEST: p_cdc->line_state = p_cdc->requested_line_state; break; - case FTDI_SIO_SET_BAUD_RATE: + case FTDI_SIO_SET_DATA_REQUEST: + p_cdc->line_coding.stop_bits = p_cdc->requested_line_coding.stop_bits; + p_cdc->line_coding.parity = p_cdc->requested_line_coding.parity; + p_cdc->line_coding.data_bits = p_cdc->requested_line_coding.data_bits; + break; + + case FTDI_SIO_SET_BAUDRATE_REQUEST: p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; break; @@ -1057,52 +1110,62 @@ static void ftdi_internal_control_complete(tuh_xfer_t * xfer) { } } -static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(p_cdc); +static bool ftdi_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + p_cdc->user_control_cb = complete_cb; + TU_ASSERT(ftdi_set_data_request(p_cdc, complete_cb ? ftdi_internal_control_complete : NULL, user_data)); + + return true; +} +static bool ftdi_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->user_control_cb = complete_cb; - TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_SET_BAUD_RATE, divisor, - complete_cb ? ftdi_internal_control_complete : NULL, user_data)); + TU_ASSERT(ftdi_change_speed(p_cdc, complete_cb ? ftdi_internal_control_complete : NULL, user_data)); return true; } -static bool ftdi_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - (void) p_cdc; - (void) complete_cb; - (void) user_data; - // TODO not implemented yet - return false; +static void ftdi_set_line_coding_stage1_complete(tuh_xfer_t * xfer) { + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + set_line_coding_stage1_complete(xfer, itf_num, + ftdi_set_data_request, // control request function to set data format + ftdi_internal_control_complete); // control complete function to be called after request } -static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - (void) p_cdc; - (void) complete_cb; - (void) user_data; - // TODO not implemented yet - return false; +// 2 stages: set baudrate (stage1) + set data format (stage2) +static bool ftdi_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + return set_line_coding_sequence(p_cdc, + ftdi_change_speed, // control request function to set baudrate + ftdi_set_data_request, // control request function to set data format + ftdi_set_line_coding_stage1_complete, // function to be called after stage 1 completed + ftdi_internal_control_complete, // control complete function to be called after request + complete_cb, user_data); } -static bool ftdi_sio_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool ftdi_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->user_control_cb = complete_cb; - TU_ASSERT(ftdi_sio_set_request(p_cdc, FTDI_SIO_MODEM_CTRL, 0x0300 | p_cdc->requested_line_state, - complete_cb ? ftdi_internal_control_complete : NULL, user_data)); + TU_ASSERT(ftdi_update_mctrl(p_cdc, complete_cb ? ftdi_internal_control_complete : NULL, user_data)); + return true; } //------------- Enumeration -------------// enum { - CONFIG_FTDI_RESET = 0, - CONFIG_FTDI_MODEM_CTRL, - CONFIG_FTDI_SET_BAUDRATE, + CONFIG_FTDI_GET_DESC = 0, + CONFIG_FTDI_DETERMINE_TYPE, + CONFIG_FTDI_WRITE_LATENCY, + CONFIG_FTDI_SIO_RESET, CONFIG_FTDI_SET_DATA, + CONFIG_FTDI_SET_BAUDRATE, + CONFIG_FTDI_FLOW_CONTROL, + CONFIG_FTDI_MODEM_CTRL, CONFIG_FTDI_COMPLETE }; -static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len) { +static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t * itf_desc, uint16_t max_len) { // FTDI Interface includes 1 vendor interface + 2 bulk endpoints - TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff && itf_desc->bNumEndpoints == 2); + TU_VERIFY(itf_desc->bInterfaceSubClass == 0xff && itf_desc->bInterfaceProtocol == 0xff && + itf_desc->bNumEndpoints == 2); TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len); cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); @@ -1113,53 +1176,96 @@ static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint // endpoint pair tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); + /* + * NOTE: Some customers have programmed FT232R/FT245R devices + * with an endpoint size of 0 - not good. + */ + TU_ASSERT(desc_ep->wMaxPacketSize != 0); + // data endpoints expected to be in pairs return open_ep_stream_pair(p_cdc, desc_ep); } -static void ftdi_process_config(tuh_xfer_t* xfer) { +static void ftdi_process_config(tuh_xfer_t * xfer) { uintptr_t const state = xfer->user_data; - uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); - uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + uint8_t const idx = ftdi_get_idx(xfer); cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT_COMPLETE(p_cdc && xfer->result == XFER_RESULT_SUCCESS); + uint8_t const itf_num = p_cdc->bInterfaceNumber; switch(state) { - // Note may need to read FTDI eeprom - case CONFIG_FTDI_RESET: - TU_ASSERT_COMPLETE(ftdi_sio_reset(p_cdc, ftdi_process_config, CONFIG_FTDI_MODEM_CTRL)); + + // from here sequence overtaken from Linux Kernel function ftdi_port_probe() + case CONFIG_FTDI_GET_DESC: + // get device descriptor + p_cdc->user_control_cb = ftdi_process_config; // set once for whole process config + if (itf_num == 0) { // only necessary for 1st interface. other interface overtake type from interface 0 + TU_ASSERT_COMPLETE(tuh_descriptor_get_device(xfer->daddr, desc_dev[idx], sizeof(tusb_desc_device_t), + ftdi_process_config, CONFIG_FTDI_DETERMINE_TYPE)); + break; + } + TU_ATTR_FALLTHROUGH; + + case CONFIG_FTDI_DETERMINE_TYPE: + // determine type + if (itf_num == 0) { + TU_ASSERT_COMPLETE(ftdi_determine_type(p_cdc, idx)); + } else { + // other interfaces have same type as interface 0 + uint8_t const idx_itf0 = tuh_cdc_itf_get_index(xfer->daddr, 0); + cdch_interface_t const * p_cdc_itf0 = get_itf(idx_itf0); + p_cdc->ftdi.chip_type = p_cdc_itf0->ftdi.chip_type; + } + TU_ATTR_FALLTHROUGH; + + case CONFIG_FTDI_WRITE_LATENCY: + #ifdef CFG_TUH_CDC_FTDI_LATENCY + int8_t result = ftdi_write_latency_timer(p_cdc, CFG_TUH_CDC_FTDI_LATENCY, ftdi_process_config, + CONFIG_FTDI_SIO_RESET); + TU_ASSERT_COMPLETE(result != FTDI_FAIL); + if(result == FTDI_REQUESTED) { + break; + } // else FTDI_NOT_POSSIBLE => continue directly with next state + #endif + TU_ATTR_FALLTHROUGH; + + // from here sequence overtaken from Linux Kernel function ftdi_open() + case CONFIG_FTDI_SIO_RESET: + TU_ASSERT_COMPLETE(ftdi_sio_reset(p_cdc, ftdi_process_config, CONFIG_FTDI_SET_DATA)); break; - case CONFIG_FTDI_MODEM_CTRL: - #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; - TU_ASSERT_COMPLETE(ftdi_sio_set_modem_ctrl(p_cdc, ftdi_process_config, CONFIG_FTDI_SET_BAUDRATE)); + // from here sequence overtaken from Linux Kernel function ftdi_set_termios() + case CONFIG_FTDI_SET_DATA: + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + p_cdc->requested_line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT_COMPLETE(ftdi_set_data_request(p_cdc, ftdi_internal_control_complete, CONFIG_FTDI_SET_BAUDRATE)); break; #else TU_ATTR_FALLTHROUGH; #endif - case CONFIG_FTDI_SET_BAUDRATE: { + case CONFIG_FTDI_SET_BAUDRATE: #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM - p_cdc->requested_line_coding.bit_rate = ((cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM).bit_rate; - TU_ASSERT_COMPLETE(ftdi_sio_set_baudrate(p_cdc, ftdi_process_config, CONFIG_FTDI_SET_DATA)); + TU_ASSERT_COMPLETE(ftdi_change_speed(p_cdc, ftdi_internal_control_complete, CONFIG_FTDI_FLOW_CONTROL)); break; #else TU_ATTR_FALLTHROUGH; #endif - } - case CONFIG_FTDI_SET_DATA: { - #if 0 // TODO set data format - #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM - cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; - TU_ASSERT_COMPLETE(ftdi_sio_set_data(p_cdc, process_ftdi_config, CONFIG_FTDI_COMPLETE)); + case CONFIG_FTDI_FLOW_CONTROL: + // disable flow control + TU_ASSERT_COMPLETE(ftdi_set_request(p_cdc, FTDI_SIO_SET_FLOW_CTRL_REQUEST, FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, + 0, FTDI_SIO_DISABLE_FLOW_CTRL, ftdi_process_config, CONFIG_FTDI_MODEM_CTRL)); break; - #endif - #endif - TU_ATTR_FALLTHROUGH; - } + case CONFIG_FTDI_MODEM_CTRL: + #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + TU_ASSERT_COMPLETE(ftdi_update_mctrl(p_cdc, ftdi_internal_control_complete, CONFIG_FTDI_COMPLETE)); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif case CONFIG_FTDI_COMPLETE: set_config_complete(idx, 0, true); @@ -1173,28 +1279,249 @@ static void ftdi_process_config(tuh_xfer_t* xfer) { //------------- Helper -------------// +static bool ftdi_determine_type(cdch_interface_t * p_cdc, uint8_t const idx) +{ + uint16_t const version = desc_dev[idx]->bcdDevice; + uint8_t const itf_num = p_cdc->bInterfaceNumber; + + p_cdc->ftdi.chip_type = UNKNOWN; + + /* Assume Hi-Speed type */ + p_cdc->ftdi.channel = CHANNEL_A + itf_num; + + switch (version) { + case 0x200: + // FT232A not supported to keep it simple (no extra _read_latency_timer()) + // not testable + // p_cdc->ftdi.chip_type = FT232A; + // p_cdc->ftdi.baud_base = 48000000 / 2; + // p_cdc->ftdi.channel = 0; + // /* + // * FT232B devices have a bug where bcdDevice gets set to 0x200 + // * when iSerialNumber is 0. Assume it is an FT232B in case the + // * latency timer is readable. + // */ + // if (desc->iSerialNumber == 0 && + // _read_latency_timer(port) >= 0) { + // p_cdc->ftdi.chip_type = FT232B; + // } + break; + case 0x400: + p_cdc->ftdi.chip_type = FT232B; + p_cdc->ftdi.channel = 0; + break; + case 0x500: + p_cdc->ftdi.chip_type = FT2232C; + break; + case 0x600: + p_cdc->ftdi.chip_type = FT232R; + p_cdc->ftdi.channel = 0; + break; + case 0x700: + p_cdc->ftdi.chip_type = FT2232H; + break; + case 0x800: + p_cdc->ftdi.chip_type = FT4232H; + break; + case 0x900: + p_cdc->ftdi.chip_type = FT232H; + break; + case 0x1000: + p_cdc->ftdi.chip_type = FTX; + break; + case 0x2800: + p_cdc->ftdi.chip_type = FT2233HP; + break; + case 0x2900: + p_cdc->ftdi.chip_type = FT4233HP; + break; + case 0x3000: + p_cdc->ftdi.chip_type = FT2232HP; + break; + case 0x3100: + p_cdc->ftdi.chip_type = FT4232HP; + break; + case 0x3200: + p_cdc->ftdi.chip_type = FT233HP; + break; + case 0x3300: + p_cdc->ftdi.chip_type = FT232HP; + break; + case 0x3600: + p_cdc->ftdi.chip_type = FT4232HA; + break; + default: + if (version < 0x200) { + p_cdc->ftdi.chip_type = SIO; + p_cdc->ftdi.channel = 0; + } + break; + } + + TU_LOG_P_CDC("%s detected", ftdi_chip_name[p_cdc->ftdi.chip_type]); + + return (p_cdc->ftdi.chip_type != UNKNOWN); +} + +// FT232A not supported +//static uint32_t ftdi_232am_baud_base_to_divisor(uint32_t baud, uint32_t base) +//{ +// uint32_t divisor; +// /* divisor shifted 3 bits to the left */ +// uint32_t divisor3 = DIV_ROUND_CLOSEST(base, 2 * baud); +// if ((divisor3 & 0x7) == 7) +// divisor3++; /* round x.7/8 up to x+1 */ +// divisor = divisor3 >> 3; +// divisor3 &= 0x7; +// if (divisor3 == 1) +// divisor |= 0xc000; /* +0.125 */ +// else if (divisor3 >= 4) +// divisor |= 0x4000; /* +0.5 */ +// else if (divisor3 != 0) +// divisor |= 0x8000; /* +0.25 */ +// else if (divisor == 1) +// divisor = 0; /* special case for maximum baud rate */ +// return divisor; +//} + +// FT232A not supported +//static inline uint32_t ftdi_232am_baud_to_divisor(uint32_t baud) +//{ +// return ftdi_232am_baud_base_to_divisor(baud, (uint32_t) 48000000); +//} + static uint32_t ftdi_232bm_baud_base_to_divisor(uint32_t baud, uint32_t base) { - const uint8_t divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 }; + uint8_t divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 }; uint32_t divisor; - /* divisor shifted 3 bits to the left */ - uint32_t divisor3 = base / (2 * baud); - divisor = (divisor3 >> 3); - divisor |= (uint32_t) divfrac[divisor3 & 0x7] << 14; + uint32_t divisor3 = DIV_ROUND_CLOSEST(base, 2 * baud); + divisor = divisor3 >> 3; + divisor |= (uint32_t)divfrac[divisor3 & 0x7] << 14; + /* Deal with special cases for highest baud rates. */ + if (divisor == 1) /* 1.0 */ + divisor = 0; + else if (divisor == 0x4001) /* 1.5 */ + divisor = 1; + return divisor; +} + +static inline uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud) +{ + return ftdi_232bm_baud_base_to_divisor(baud, 48000000); +} + +static uint32_t ftdi_2232h_baud_base_to_divisor(uint32_t baud, uint32_t base) +{ + static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 }; + uint32_t divisor; + uint32_t divisor3; + + /* hi-speed baud rate is 10-bit sampling instead of 16-bit */ + divisor3 = DIV_ROUND_CLOSEST(8 * base, 10 * baud); + divisor = divisor3 >> 3; + divisor |= (uint32_t)divfrac[divisor3 & 0x7] << 14; /* Deal with special cases for highest baud rates. */ - if (divisor == 1) { /* 1.0 */ + if (divisor == 1) /* 1.0 */ divisor = 0; - } - else if (divisor == 0x4001) { /* 1.5 */ + else if (divisor == 0x4001) /* 1.5 */ divisor = 1; + /* + * Set this bit to turn off a divide by 2.5 on baud rate generator + * This enables baud rates up to 12Mbaud but cannot reach below 1200 + * baud with this bit set + */ + divisor |= 0x00020000; + return divisor; +} + +static inline uint32_t ftdi_2232h_baud_to_divisor(uint32_t baud) +{ + return ftdi_2232h_baud_base_to_divisor(baud, (uint32_t) 120000000); +} + +static inline uint32_t ftdi_get_divisor(cdch_interface_t * p_cdc) +{ + uint32_t baud = p_cdc->requested_line_coding.bit_rate; + uint32_t div_value = 0; + TU_VERIFY(baud); + + switch (p_cdc->ftdi.chip_type) { + case UNKNOWN: + return 0; + case SIO: + switch (baud) { + case 300: div_value = ftdi_sio_b300; break; + case 600: div_value = ftdi_sio_b600; break; + case 1200: div_value = ftdi_sio_b1200; break; + case 2400: div_value = ftdi_sio_b2400; break; + case 4800: div_value = ftdi_sio_b4800; break; + case 9600: div_value = ftdi_sio_b9600; break; + case 19200: div_value = ftdi_sio_b19200; break; + case 38400: div_value = ftdi_sio_b38400; break; + case 57600: div_value = ftdi_sio_b57600; break; + case 115200: div_value = ftdi_sio_b115200; break; + default: + // Baudrate not supported + return 0; + break; + } + break; + // FT232A not supported + // case FT232A: + // if (baud <= 3000000) { + // div_value = ftdi_232am_baud_to_divisor(baud); + // } else { + // // Baud rate too high! + // baud = 9600; + // div_value = ftdi_232am_baud_to_divisor(9600); + // div_okay = false; + // } + // break; + case FT232B: + case FT2232C: + case FT232R: + case FTX: + TU_VERIFY(baud <= 3000000); // else Baud rate too high! + div_value = ftdi_232bm_baud_to_divisor(baud); + break; + case FT232H: + case FT2232H: + case FT4232H: + case FT4232HA: + case FT232HP: + case FT233HP: + case FT2232HP: + case FT2233HP: + case FT4232HP: + case FT4233HP: + default: + TU_VERIFY(baud <= 12000000); // else Baud rate too high! + if (baud >= 1200) { + div_value = ftdi_2232h_baud_to_divisor(baud); + } else { + div_value = ftdi_232bm_baud_to_divisor(baud); + } + break; } - return divisor; + TU_LOG_P_CDC("Baudrate divisor 0x%lu", div_value); + + return div_value; } -static uint32_t ftdi_232bm_baud_to_divisor(cdch_interface_t* p_cdc) { - return ftdi_232bm_baud_base_to_divisor(p_cdc->requested_line_coding.bit_rate, 48000000u); +static uint8_t ftdi_get_idx(tuh_xfer_t * xfer) { + uint8_t const channel = (uint8_t) tu_le16toh(xfer->setup->wIndex); // channel index, or 0 for legacy types + for (uint8_t i = 0; i < CFG_TUH_CDC; i++) { + const cdch_interface_t * p_cdc = &cdch_data[i]; + if (p_cdc->daddr == xfer->daddr && + (!p_cdc->ftdi.channel || // 0 for legacy types (only interface 0) + channel == p_cdc->ftdi.channel)) { // or multi-channel types (interfaces 0..n) + return i; + } + } + + return TUSB_INDEX_INVALID_8; } #endif diff --git a/src/class/cdc/serial/ftdi_sio.h b/src/class/cdc/serial/ftdi_sio.h index 0825f0719..42716f73e 100644 --- a/src/class/cdc/serial/ftdi_sio.h +++ b/src/class/cdc/serial/ftdi_sio.h @@ -25,222 +25,207 @@ #ifndef TUSB_FTDI_SIO_H #define TUSB_FTDI_SIO_H -// VID for matching FTDI devices -#define TU_FTDI_VID 0x0403 +#include // Commands -#define FTDI_SIO_RESET 0 /* Reset the port */ -#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ -#define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */ -#define FTDI_SIO_SET_BAUD_RATE 3 /* Set baud rate */ -#define FTDI_SIO_SET_DATA 4 /* Set the data characteristics of the port */ -#define FTDI_SIO_GET_MODEM_STATUS 5 /* Retrieve current value of modem status register */ -#define FTDI_SIO_SET_EVENT_CHAR 6 /* Set the event character */ -#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */ -#define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */ -#define FTDI_SIO_GET_LATENCY_TIMER 0x0a /* Get the latency timer */ -#define FTDI_SIO_SET_BITMODE 0x0b /* Set bitbang mode */ -#define FTDI_SIO_READ_PINS 0x0c /* Read immediate value of pins */ -#define FTDI_SIO_READ_EEPROM 0x90 /* Read EEPROM */ - -/* FTDI_SIO_RESET */ +#define FTDI_SIO_RESET 0 // Reset the port +#define FTDI_SIO_MODEM_CTRL 1 // Set the modem control register +#define FTDI_SIO_SET_FLOW_CTRL 2 // Set flow control register +#define FTDI_SIO_SET_BAUD_RATE 3 // Set baud rate +#define FTDI_SIO_SET_DATA 4 // Set the data characteristics of the port +#define FTDI_SIO_GET_MODEM_STATUS 5 // Retrieve current value of modem status register +#define FTDI_SIO_SET_EVENT_CHAR 6 // Set the event character +#define FTDI_SIO_SET_ERROR_CHAR 7 // Set the error character +#define FTDI_SIO_SET_LATENCY_TIMER 9 // Set the latency timer +#define FTDI_SIO_GET_LATENCY_TIMER 10 // Get the latency timer +#define FTDI_SIO_SET_BITMODE 11 // Set bitbang mode +#define FTDI_SIO_READ_PINS 12 // Read immediate value of pins +#define FTDI_SIO_READ_EEPROM 0x90 // Read EEPROM + +// Channel indices for FT2232, FT2232H and FT4232H devices +#define CHANNEL_A 1 +#define CHANNEL_B 2 +#define CHANNEL_C 3 +#define CHANNEL_D 4 + +// Port Identifier Table +#define PIT_DEFAULT 0 // SIOA +#define PIT_SIOA 1 // SIOA +// The device this driver is tested with one has only one port +#define PIT_SIOB 2 // SIOB +#define PIT_PARALLEL 3 // Parallel + +// FTDI_SIO_RESET +#define FTDI_SIO_RESET_REQUEST FTDI_SIO_RESET +#define FTDI_SIO_RESET_REQUEST_TYPE 0x40 #define FTDI_SIO_RESET_SIO 0 #define FTDI_SIO_RESET_PURGE_RX 1 #define FTDI_SIO_RESET_PURGE_TX 2 -/* - * BmRequestType: 0100 0000B - * bRequest: FTDI_SIO_RESET - * wValue: Control Value - * 0 = Reset SIO - * 1 = Purge RX buffer - * 2 = Purge TX buffer - * wIndex: Port - * wLength: 0 - * Data: None - * - * The Reset SIO command has this effect: - * - * Sets flow control set to 'none' - * Event char = $0D - * Event trigger = disabled - * Purge RX buffer - * Purge TX buffer - * Clear DTR - * Clear RTS - * baud and data format not reset - * - * The Purge RX and TX buffer commands affect nothing except the buffers - * - */ - -/* FTDI_SIO_MODEM_CTRL */ -/* - * BmRequestType: 0100 0000B - * bRequest: FTDI_SIO_MODEM_CTRL - * wValue: ControlValue (see below) - * wIndex: Port - * wLength: 0 - * Data: None - * - * NOTE: If the device is in RTS/CTS flow control, the RTS set by this - * command will be IGNORED without an error being returned - * Also - you can not set DTR and RTS with one control message - */ +// FTDI_SIO_SET_BAUDRATE +#define FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_BAUDRATE_REQUEST 3 + +enum ftdi_sio_baudrate { + ftdi_sio_b300 = 0, + ftdi_sio_b600 = 1, + ftdi_sio_b1200 = 2, + ftdi_sio_b2400 = 3, + ftdi_sio_b4800 = 4, + ftdi_sio_b9600 = 5, + ftdi_sio_b19200 = 6, + ftdi_sio_b38400 = 7, + ftdi_sio_b57600 = 8, + ftdi_sio_b115200 = 9 +}; + +// FTDI_SIO_SET_DATA +#define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA +#define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8) +#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8) +#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8) +#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8) +#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8) +#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11) +#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11) +#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11) +#define FTDI_SIO_SET_BREAK (0x1 << 14) + +// FTDI_SIO_MODEM_CTRL +#define FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_MODEM_CTRL_REQUEST FTDI_SIO_MODEM_CTRL #define FTDI_SIO_SET_DTR_MASK 0x1 -#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1) -#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0) +#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1) +#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0) #define FTDI_SIO_SET_RTS_MASK 0x2 #define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2) #define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0) -/* - * ControlValue - * B0 DTR state - * 0 = reset - * 1 = set - * B1 RTS state - * 0 = reset - * 1 = set - * B2..7 Reserved - * B8 DTR state enable - * 0 = ignore - * 1 = use DTR state - * B9 RTS state enable - * 0 = ignore - * 1 = use RTS state - * B10..15 Reserved - */ - -/* FTDI_SIO_SET_FLOW_CTRL */ +// FTDI_SIO_SET_FLOW_CTRL +#define FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_FLOW_CTRL_REQUEST FTDI_SIO_SET_FLOW_CTRL #define FTDI_SIO_DISABLE_FLOW_CTRL 0x0 #define FTDI_SIO_RTS_CTS_HS (0x1 << 8) #define FTDI_SIO_DTR_DSR_HS (0x2 << 8) #define FTDI_SIO_XON_XOFF_HS (0x4 << 8) -/* - * BmRequestType: 0100 0000b - * bRequest: FTDI_SIO_SET_FLOW_CTRL - * wValue: Xoff/Xon - * wIndex: Protocol/Port - hIndex is protocol / lIndex is port - * wLength: 0 - * Data: None - * - * hIndex protocol is: - * B0 Output handshaking using RTS/CTS - * 0 = disabled - * 1 = enabled - * B1 Output handshaking using DTR/DSR - * 0 = disabled - * 1 = enabled - * B2 Xon/Xoff handshaking - * 0 = disabled - * 1 = enabled - * - * A value of zero in the hIndex field disables handshaking - * - * If Xon/Xoff handshaking is specified, the hValue field should contain the - * XOFF character and the lValue field contains the XON character. - */ - -/* FTDI_SIO_SET_BAUD_RATE */ -/* - * BmRequestType: 0100 0000B - * bRequest: FTDI_SIO_SET_BAUDRATE - * wValue: BaudDivisor value - see below - * wIndex: Port - * wLength: 0 - * Data: None - * The BaudDivisor values are calculated as follows (too complicated): - */ - -/* FTDI_SIO_SET_DATA */ -#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8) -#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8) -#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8) -#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8) -#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8) -#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11) -#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11) -#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11) -#define FTDI_SIO_SET_BREAK (0x1 << 14) - -/* - * BmRequestType: 0100 0000B - * bRequest: FTDI_SIO_SET_DATA - * wValue: Data characteristics (see below) - * wIndex: Port - * wLength: 0 - * Data: No - * - * Data characteristics - * - * B0..7 Number of data bits - * B8..10 Parity - * 0 = None - * 1 = Odd - * 2 = Even - * 3 = Mark - * 4 = Space - * B11..13 Stop Bits - * 0 = 1 - * 1 = 1.5 - * 2 = 2 - * B14 - * 1 = TX ON (break) - * 0 = TX OFF (normal state) - * B15 Reserved - * - */ - -/* -* DATA FORMAT -* -* IN Endpoint -* -* The device reserves the first two bytes of data on this endpoint to contain -* the current values of the modem and line status registers. In the absence of -* data, the device generates a message consisting of these two status bytes - * every 40 ms - * - * Byte 0: Modem Status -* -* Offset Description -* B0 Reserved - must be 1 -* B1 Reserved - must be 0 -* B2 Reserved - must be 0 -* B3 Reserved - must be 0 -* B4 Clear to Send (CTS) -* B5 Data Set Ready (DSR) -* B6 Ring Indicator (RI) -* B7 Receive Line Signal Detect (RLSD) -* -* Byte 1: Line Status -* -* Offset Description -* B0 Data Ready (DR) -* B1 Overrun Error (OE) -* B2 Parity Error (PE) -* B3 Framing Error (FE) -* B4 Break Interrupt (BI) -* B5 Transmitter Holding Register (THRE) -* B6 Transmitter Empty (TEMT) -* B7 Error in RCVR FIFO -* -*/ -#define FTDI_RS0_CTS (1 << 4) -#define FTDI_RS0_DSR (1 << 5) -#define FTDI_RS0_RI (1 << 6) -#define FTDI_RS0_RLSD (1 << 7) - -#define FTDI_RS_DR 1 -#define FTDI_RS_OE (1<<1) -#define FTDI_RS_PE (1<<2) -#define FTDI_RS_FE (1<<3) -#define FTDI_RS_BI (1<<4) -#define FTDI_RS_THRE (1<<5) -#define FTDI_RS_TEMT (1<<6) -#define FTDI_RS_FIFO (1<<7) +// FTDI_SIO_GET_LATENCY_TIMER +#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST FTDI_SIO_GET_LATENCY_TIMER +#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE 0xC0 + +// FTDI_SIO_SET_LATENCY_TIMER +#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER +#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40 + +// FTDI_SIO_SET_EVENT_CHAR +#define FTDI_SIO_SET_EVENT_CHAR_REQUEST FTDI_SIO_SET_EVENT_CHAR +#define FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE 0x40 + +// FTDI_SIO_GET_MODEM_STATUS +#define FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE 0xc0 +#define FTDI_SIO_GET_MODEM_STATUS_REQUEST FTDI_SIO_GET_MODEM_STATUS +#define FTDI_SIO_CTS_MASK 0x10 +#define FTDI_SIO_DSR_MASK 0x20 +#define FTDI_SIO_RI_MASK 0x40 +#define FTDI_SIO_RLSD_MASK 0x80 + +// FTDI_SIO_SET_BITMODE +#define FTDI_SIO_SET_BITMODE_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_BITMODE_REQUEST FTDI_SIO_SET_BITMODE + +// Possible bitmodes for FTDI_SIO_SET_BITMODE_REQUEST +#define FTDI_SIO_BITMODE_RESET 0x00 +#define FTDI_SIO_BITMODE_CBUS 0x20 + +// FTDI_SIO_READ_PINS +#define FTDI_SIO_READ_PINS_REQUEST_TYPE 0xc0 +#define FTDI_SIO_READ_PINS_REQUEST FTDI_SIO_READ_PINS + +// FTDI_SIO_READ_EEPROM +#define FTDI_SIO_READ_EEPROM_REQUEST_TYPE 0xc0 +#define FTDI_SIO_READ_EEPROM_REQUEST FTDI_SIO_READ_EEPROM + +#define FTDI_FTX_CBUS_MUX_GPIO 0x8 +#define FTDI_FT232R_CBUS_MUX_GPIO 0xa + +#define FTDI_RS0_CTS (1 << 4) +#define FTDI_RS0_DSR (1 << 5) +#define FTDI_RS0_RI (1 << 6) +#define FTDI_RS0_RLSD (1 << 7) + +#define FTDI_RS_DR 1 +#define FTDI_RS_OE (1<<1) +#define FTDI_RS_PE (1<<2) +#define FTDI_RS_FE (1<<3) +#define FTDI_RS_BI (1<<4) +#define FTDI_RS_THRE (1<<5) +#define FTDI_RS_TEMT (1<<6) +#define FTDI_RS_FIFO (1<<7) + +// chip types and names +enum ftdi_chip_type { + SIO = 0, +// FT232A, + FT232B, + FT2232C, + FT232R, + FT232H, + FT2232H, + FT4232H, + FT4232HA, + FT232HP, + FT233HP, + FT2232HP, + FT2233HP, + FT4232HP, + FT4233HP, + FTX, + UNKNOWN +}; + +#define FTDI_CHIP_NAMES \ + [SIO] = (uint8_t const*) "SIO", /* the serial part of FT8U100AX */ \ +/* [FT232A] = (uint8_t const*) "FT232A", */ \ + [FT232B] = (uint8_t const*) "FT232B", \ + [FT2232C] = (uint8_t const*) "FT2232C/D", \ + [FT232R] = (uint8_t const*) "FT232R", \ + [FT232H] = (uint8_t const*) "FT232H", \ + [FT2232H] = (uint8_t const*) "FT2232H", \ + [FT4232H] = (uint8_t const*) "FT4232H", \ + [FT4232HA] = (uint8_t const*) "FT4232HA", \ + [FT232HP] = (uint8_t const*) "FT232HP", \ + [FT233HP] = (uint8_t const*) "FT233HP", \ + [FT2232HP] = (uint8_t const*) "FT2232HP", \ + [FT2233HP] = (uint8_t const*) "FT2233HP", \ + [FT4232HP] = (uint8_t const*) "FT4232HP", \ + [FT4233HP] = (uint8_t const*) "FT4233HP", \ + [FTX] = (uint8_t const*) "FT-X", \ + [UNKNOWN] = (uint8_t const*) "UNKNOWN" + +// private interface data +typedef struct ftdi_private { + enum ftdi_chip_type chip_type; + uint8_t channel; // channel index, or 0 for legacy types +} ftdi_private_t; + +#define FTDI_OK true +#define FTDI_FAIL false +#define FTDI_NOT_POSSIBLE -1 +#define FTDI_REQUESTED -2 + +// division and round function overtaken from math.h +#define DIV_ROUND_CLOSEST(x, divisor)( \ +{ \ + typeof(x) __x = x; \ + typeof(divisor) __d = divisor; \ + (((typeof(x))-1) > 0 || \ + ((typeof(divisor))-1) > 0 || \ + (((__x) > 0) == ((__d) > 0))) ? \ + (((__x) + ((__d) / 2)) / (__d)) : \ + (((__x) - ((__d) / 2)) / (__d)); \ +} \ +) #endif //TUSB_FTDI_SIO_H diff --git a/src/tusb_option.h b/src/tusb_option.h index 767323bdd..ebf9a4d4d 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -456,9 +456,22 @@ #ifndef CFG_TUH_CDC_FTDI_VID_PID_LIST // List of product IDs that can use the FTDI CDC driver. 0x0403 is FTDI's VID #define CFG_TUH_CDC_FTDI_VID_PID_LIST \ - {0x0403, 0x6001}, {0x0403, 0x6006}, {0x0403, 0x6010}, {0x0403, 0x6011}, \ - {0x0403, 0x6014}, {0x0403, 0x6015}, {0x0403, 0x8372}, {0x0403, 0xFBFA}, \ - {0x0403, 0xCD18} + {0x0403, 0x6001}, /* Similar device to SIO above */ \ + {0x0403, 0x6006}, /* FTDI's alternate PID for above */ \ + {0x0403, 0x6010}, /* Dual channel device */ \ + {0x0403, 0x6011}, /* Quad channel hi-speed device */ \ + {0x0403, 0x6014}, /* Single channel hi-speed device */ \ + {0x0403, 0x6015}, /* FT-X series (FT201X, FT230X, FT231X, etc) */ \ + {0x0403, 0x6040}, /* Dual channel hi-speed device with PD */ \ + {0x0403, 0x6041}, /* Quad channel hi-speed device with PD */ \ + {0x0403, 0x6042}, /* Dual channel hi-speed device with PD */ \ + {0x0403, 0x6043}, /* Quad channel hi-speed device with PD */ \ + {0x0403, 0x6044}, /* Dual channel hi-speed device with PD */ \ + {0x0403, 0x6045}, /* Dual channel hi-speed device with PD */ \ + {0x0403, 0x6048}, /* Quad channel automotive grade hi-speed device */ \ + {0x0403, 0x8372}, /* Product Id SIO application of 8U100AX */ \ + {0x0403, 0xFBFA}, /* Product ID for FT232RL */ \ + {0x0403, 0xCD18}, /* ??? */ #endif #ifndef CFG_TUH_CDC_CP210X -- cgit v1.3.1 From 4547737833de0368354cb4823d908c6b250f92df Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 22 Feb 2024 15:42:33 +0100 Subject: improved CP210x support --- src/class/cdc/cdc_host.c | 135 +++++++++++++++++++++++++++++------------- src/class/cdc/serial/cp210x.h | 63 +++++++++++++++++++- src/tusb_option.h | 4 +- 3 files changed, 159 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index f9540efbe..86c010fa7 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -83,7 +83,7 @@ typedef struct { uint8_t requested_line_state; tuh_xfer_cb_t user_control_cb; - #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CH34X + #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X tuh_xfer_cb_t requested_complete_cb; #endif @@ -1533,7 +1533,8 @@ static uint8_t ftdi_get_idx(tuh_xfer_t * xfer) { //------------- Control Request -------------// -static bool cp210x_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_t value, uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool cp210x_set_request(cdch_interface_t * p_cdc, uint8_t command, uint16_t value, + uint8_t * buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { tusb_control_request_t const request = { .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, @@ -1542,12 +1543,12 @@ static bool cp210x_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_ }, .bRequest = command, .wValue = tu_htole16(value), - .wIndex = p_cdc->bInterfaceNumber, + .wIndex = tu_htole16(p_cdc->bInterfaceNumber), .wLength = tu_htole16(length) }; // use usbh enum buf since application variable does not live long enough - uint8_t* enum_buf = NULL; + uint8_t * enum_buf = NULL; if (buffer && length > 0) { enum_buf = usbh_get_enum_buf(); @@ -1566,18 +1567,52 @@ static bool cp210x_set_request(cdch_interface_t* p_cdc, uint8_t command, uint16_ return tuh_control_xfer(&xfer); } -static bool cp210x_ifc_enable(cdch_interface_t* p_cdc, uint16_t enabled, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static inline bool cp210x_ifc_enable(cdch_interface_t * p_cdc, uint16_t enabled, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { return cp210x_set_request(p_cdc, CP210X_IFC_ENABLE, enabled, NULL, 0, complete_cb, user_data); } +static bool cp210x_set_baudrate_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + // Check baudrate is supported. It's only a specific list. reference: datasheets and AN205 "CP210x Baud Rate Support" + uint32_t const supported_baudrates_list[] = CP210X_SUPPORTED_BAUDRATES_LIST; + uint8_t i; + for ( i=0; supported_baudrates_list[i]; i++ ){ + if (p_cdc->requested_line_coding.bit_rate == supported_baudrates_list[i]) { + break; + } + } + TU_VERIFY(supported_baudrates_list[i]); + uint32_t baud_le = tu_htole32(p_cdc->requested_line_coding.bit_rate); + + return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4, complete_cb, user_data); +} + +static bool cp210x_set_line_ctl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + TU_VERIFY(p_cdc->requested_line_coding.data_bits >= 5 && p_cdc->requested_line_coding.data_bits <= 9, 0); + uint16_t lcr = (uint16_t) ( + ((uint32_t) p_cdc->requested_line_coding.data_bits & 0xf) << 8 | // data bit quantity is stored in bits 8-11 + ((uint32_t) p_cdc->requested_line_coding.parity & 0xf) << 4 | // parity is stored in bits 4-7, same coding + ((uint32_t) p_cdc->requested_line_coding.stop_bits & 0xf)); // parity is stored in bits 0-3, same coding + + return cp210x_set_request(p_cdc, CP210X_SET_LINE_CTL, lcr, NULL, 0, complete_cb, user_data); +} + +static inline bool cp210x_set_mhs(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + // CP210x has the same bit coding + return cp210x_set_request(p_cdc, CP210X_SET_MHS, + (uint16_t) ((uint32_t) CP210X_CONTROL_WRITE_DTR | + (uint32_t) CP210X_CONTROL_WRITE_RTS | p_cdc->requested_line_state), + NULL, 0, complete_cb, user_data); +} + //------------- Driver API -------------// // internal control complete to update state such as line state, encoding static void cp210x_internal_control_complete(tuh_xfer_t * xfer) { uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); - cdch_interface_t* p_cdc = get_itf(idx); - TU_ASSERT(p_cdc, ); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc,); bool const success = (xfer->result == XFER_RESULT_SUCCESS); TU_LOG_P_CDC("control complete success = %u", success); @@ -1587,6 +1622,12 @@ static void cp210x_internal_control_complete(tuh_xfer_t * xfer) { p_cdc->line_state = p_cdc->requested_line_state; break; + case CP210X_SET_LINE_CTL: + p_cdc->line_coding.stop_bits = p_cdc->requested_line_coding.stop_bits; + p_cdc->line_coding.parity = p_cdc->requested_line_coding.parity; + p_cdc->line_coding.data_bits = p_cdc->requested_line_coding.data_bits; + break; + case CP210X_SET_BAUDRATE: p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; break; @@ -1601,46 +1642,55 @@ static void cp210x_internal_control_complete(tuh_xfer_t * xfer) { } } -static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - uint32_t baud_le = tu_htole32(p_cdc->requested_line_coding.bit_rate); +static bool cp210x_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->user_control_cb = complete_cb; - return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4, - complete_cb ? cp210x_internal_control_complete : NULL, user_data); + TU_ASSERT(cp210x_set_baudrate_request(p_cdc, complete_cb ? cp210x_internal_control_complete : NULL, user_data)); + + return true; } -static bool cp210x_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - (void) p_cdc; - (void) complete_cb; - (void) user_data; - // TODO not implemented yet - return false; +static bool cp210x_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + p_cdc->user_control_cb = complete_cb; + TU_ASSERT(cp210x_set_line_ctl(p_cdc, complete_cb ? cp210x_internal_control_complete : NULL, user_data)); + + return true; } -static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - // TODO implement later - (void) p_cdc; - (void) complete_cb; - (void) user_data; - return false; +static void cp210x_set_line_coding_stage1_complete(tuh_xfer_t * xfer) { + uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + set_line_coding_stage1_complete(xfer, itf_num, + cp210x_set_line_ctl, // control request function to set data format + cp210x_internal_control_complete); // control complete function to be called after request +} + +// 2 stages: set baudrate (stage1) + set data format (stage2) +static bool cp210x_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + return set_line_coding_sequence(p_cdc, + cp210x_set_baudrate_request, // control request function to set baudrate + cp210x_set_line_ctl, // control request function to set data format + cp210x_set_line_coding_stage1_complete, // function to be called after stage 1 completed + cp210x_internal_control_complete, // control complete function to be called after request + complete_cb, user_data); } -static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool cp210x_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->user_control_cb = complete_cb; - return cp210x_set_request(p_cdc, CP210X_SET_MHS, 0x0300 | p_cdc->requested_line_state, NULL, 0, - complete_cb ? cp210x_internal_control_complete : NULL, user_data); + TU_ASSERT(cp210x_set_mhs(p_cdc, complete_cb ? cp210x_internal_control_complete : NULL, user_data)); + + return true; } //------------- Enumeration -------------// enum { CONFIG_CP210X_IFC_ENABLE = 0, - CONFIG_CP210X_SET_BAUDRATE, + CONFIG_CP210X_SET_BAUDRATE_REQUEST, CONFIG_CP210X_SET_LINE_CTL, CONFIG_CP210X_SET_DTR_RTS, CONFIG_CP210X_COMPLETE }; -static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { +static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len) { // CP210x Interface includes 1 vendor interface + 2 bulk endpoints TU_VERIFY(itf_desc->bInterfaceSubClass == 0 && itf_desc->bInterfaceProtocol == 0 && itf_desc->bNumEndpoints == 2); TU_VERIFY(sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t) <= max_len); @@ -1657,7 +1707,7 @@ static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, ui return open_ep_stream_pair(p_cdc, desc_ep); } -static void cp210x_process_config(tuh_xfer_t* xfer) { +static void cp210x_process_config(tuh_xfer_t * xfer) { uintptr_t const state = xfer->user_data; uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); @@ -1666,32 +1716,35 @@ static void cp210x_process_config(tuh_xfer_t* xfer) { switch (state) { case CONFIG_CP210X_IFC_ENABLE: - TU_ASSERT_COMPLETE(cp210x_ifc_enable(p_cdc, 1, cp210x_process_config, CONFIG_CP210X_SET_BAUDRATE)); + p_cdc->user_control_cb = cp210x_process_config; // set once for whole process config + TU_ASSERT_COMPLETE(cp210x_ifc_enable(p_cdc, CP210X_UART_ENABLE, cp210x_process_config, + CONFIG_CP210X_SET_BAUDRATE_REQUEST)); break; - case CONFIG_CP210X_SET_BAUDRATE: { + case CONFIG_CP210X_SET_BAUDRATE_REQUEST: #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM - p_cdc->requested_line_coding.bit_rate = ((cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM).bit_rate; - TU_ASSERT_COMPLETE(cp210x_set_baudrate(p_cdc, cp210x_process_config, CONFIG_CP210X_SET_LINE_CTL)); + p_cdc->requested_line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT_COMPLETE(cp210x_set_baudrate_request(p_cdc, cp210x_internal_control_complete, + CONFIG_CP210X_SET_LINE_CTL)); break; #else TU_ATTR_FALLTHROUGH; #endif - } - case CONFIG_CP210X_SET_LINE_CTL: { - #if defined(CFG_TUH_CDC_LINE_CODING_ON_ENUM) && 0 // skip for now - cdc_line_coding_t line_coding = CFG_TUH_CDC_LINE_CODING_ON_ENUM; - break; + case CONFIG_CP210X_SET_LINE_CTL: + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + TU_ASSERT_COMPLETE(cp210x_set_line_ctl(p_cdc, cp210x_internal_control_complete, + CONFIG_CP210X_SET_DTR_RTS)); + break; #else - TU_ATTR_FALLTHROUGH; + TU_ATTR_FALLTHROUGH; #endif - } case CONFIG_CP210X_SET_DTR_RTS: #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; - TU_ASSERT_COMPLETE(cp210x_set_modem_ctrl(p_cdc, cp210x_process_config, CONFIG_CP210X_COMPLETE)); + TU_ASSERT_COMPLETE(cp210x_set_mhs(p_cdc, cp210x_internal_control_complete, + CONFIG_CP210X_COMPLETE)); break; #else TU_ATTR_FALLTHROUGH; diff --git a/src/class/cdc/serial/cp210x.h b/src/class/cdc/serial/cp210x.h index 2c749f522..e18da7d51 100644 --- a/src/class/cdc/serial/cp210x.h +++ b/src/class/cdc/serial/cp210x.h @@ -28,7 +28,8 @@ // Protocol details can be found at AN571: CP210x Virtual COM Port Interface // https://www.silabs.com/documents/public/application-notes/AN571.pdf -#define TU_CP210X_VID 0x10C4 +// parts are overtaken from vendors driver +// https://www.silabs.com/documents/public/software/cp210x-3.1.0.tar.gz /* Config request codes */ #define CP210X_IFC_ENABLE 0x00 @@ -59,4 +60,64 @@ #define CP210X_SET_BAUDRATE 0x1E #define CP210X_VENDOR_SPECIFIC 0xFF // GPIO, Recipient must be Device +/* SILABSER_IFC_ENABLE_REQUEST_CODE */ +#define CP210X_UART_ENABLE 0x0001 +#define CP210X_UART_DISABLE 0x0000 + +/* SILABSER_SET_BAUDDIV_REQUEST_CODE */ +#define CP210X_BAUD_RATE_GEN_FREQ 0x384000 + +/*SILABSER_SET_LINE_CTL_REQUEST_CODE */ +#define CP210X_BITS_DATA_MASK 0x0f00 +#define CP210X_BITS_DATA_5 0x0500 +#define CP210X_BITS_DATA_6 0x0600 +#define CP210X_BITS_DATA_7 0x0700 +#define CP210X_BITS_DATA_8 0x0800 +#define CP210X_BITS_DATA_9 0x0900 + +#define CP210X_BITS_PARITY_MASK 0x00f0 +#define CP210X_BITS_PARITY_NONE 0x0000 +#define CP210X_BITS_PARITY_ODD 0x0010 +#define CP210X_BITS_PARITY_EVEN 0x0020 +#define CP210X_BITS_PARITY_MARK 0x0030 +#define CP210X_BITS_PARITY_SPACE 0x0040 + +#define CP210X_BITS_STOP_MASK 0x000f +#define CP210X_BITS_STOP_1 0x0000 +#define CP210X_BITS_STOP_1_5 0x0001 +#define CP210X_BITS_STOP_2 0x0002 + +/* SILABSER_SET_BREAK_REQUEST_CODE */ +#define CP210X_BREAK_ON 0x0001 +#define CP210X_BREAK_OFF 0x0000 + +/* SILABSER_SET_MHS_REQUEST_CODE */ +#define CP210X_MCR_DTR 0x0001 +#define CP210X_MCR_RTS 0x0002 +#define CP210X_MCR_ALL 0x0003 +#define CP210X_MSR_CTS 0x0010 +#define CP210X_MSR_DSR 0x0020 +#define CP210X_MSR_RING 0x0040 +#define CP210X_MSR_DCD 0x0080 +#define CP210X_MSR_ALL 0x00F0 + +#define CP210X_CONTROL_WRITE_DTR 0x0100 +#define CP210X_CONTROL_WRITE_RTS 0x0200 + +#define CP210X_LSR_BREAK 0x0001 +#define CP210X_LSR_FRAMING_ERROR 0x0002 +#define CP210X_LSR_HW_OVERRUN 0x0004 +#define CP210X_LSR_QUEUE_OVERRUN 0x0008 +#define CP210X_LSR_PARITY_ERROR 0x0010 +#define CP210X_LSR_ALL 0x001F + +// supported baudrates +// reference: datasheets and AN205 "CP210x Baud Rate Support" +#define CP210X_SUPPORTED_BAUDRATES_LIST { \ + 300, 600, \ + 1200, 1800, 2400, 4000, 4800, 7200, 9600, \ + 14400, 16000, 19200, 28800, 38400, 51200, 56000, 57600, 64000, 76800, \ + 115200, 128000, 153600, 230400, 250000, 256000, 460800, 500000, 576000, 921600, \ + 0 } + #endif //TUSB_CP210X_H diff --git a/src/tusb_option.h b/src/tusb_option.h index ebf9a4d4d..281341685 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -482,7 +482,9 @@ #ifndef CFG_TUH_CDC_CP210X_VID_PID_LIST // List of product IDs that can use the CP210X CDC driver. 0x10C4 is Silicon Labs' VID #define CFG_TUH_CDC_CP210X_VID_PID_LIST \ - {0x10C4, 0xEA60}, {0x10C4, 0xEA70} + { 0x10C4, 0xEA60 }, /* Silicon Labs factory default */ \ + { 0x10C4, 0xEA61 }, /* Silicon Labs factory default */ \ + { 0x10C4, 0xEA70 } /* Silicon Labs Dual Port factory default */ #endif #ifndef CFG_TUH_CDC_CH34X -- cgit v1.3.1 From aabee25e189d2a05cb52e5c54329ab1ec875f084 Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 22 Feb 2024 11:45:38 +0100 Subject: added PL2303 support --- examples/host/cdc_msc_hid/src/tusb_config.h | 1 + .../host/cdc_msc_hid_freertos/src/tusb_config.h | 1 + src/class/cdc/cdc_host.c | 754 ++++++++++++++++++++- src/class/cdc/serial/pl2303.h | 172 +++++ src/tusb_option.h | 18 + 5 files changed, 942 insertions(+), 4 deletions(-) create mode 100644 src/class/cdc/serial/pl2303.h (limited to 'src') diff --git a/examples/host/cdc_msc_hid/src/tusb_config.h b/examples/host/cdc_msc_hid/src/tusb_config.h index e4d74077f..fc956c6d3 100644 --- a/examples/host/cdc_msc_hid/src/tusb_config.h +++ b/examples/host/cdc_msc_hid/src/tusb_config.h @@ -106,6 +106,7 @@ #define CFG_TUH_CDC_FTDI 1 // FTDI Serial. FTDI is not part of CDC class, only to re-use CDC driver API #define CFG_TUH_CDC_CP210X 1 // CP210x Serial. CP210X is not part of CDC class, only to re-use CDC driver API #define CFG_TUH_CDC_CH34X 1 // CH340 or CH341 Serial. CH34X is not part of CDC class, only to re-use CDC driver API +#define CFG_TUH_CDC_PL2303 1 // PL2303 Serial. PL2303 is not part of CDC class, only to re-use CDC driver API #define CFG_TUH_HID (3*CFG_TUH_DEVICE_MAX) // typical keyboard + mouse device can have 3-4 HID interfaces #define CFG_TUH_MSC 1 #define CFG_TUH_VENDOR 0 diff --git a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h index 9dc89dc55..dd732c700 100644 --- a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h +++ b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h @@ -111,6 +111,7 @@ #define CFG_TUH_CDC_FTDI 1 // FTDI Serial. FTDI is not part of CDC class, only to re-use CDC driver API #define CFG_TUH_CDC_CP210X 1 // CP210x Serial. CP210X is not part of CDC class, only to re-use CDC driver API #define CFG_TUH_CDC_CH34X 1 // CH340 or CH341 Serial. CH34X is not part of CDC class, only to re-use CDC driver API +#define CFG_TUH_CDC_PL2303 1 // PL2303 Serial. PL2303 is not part of CDC class, only to re-use CDC driver API #define CFG_TUH_HID (3*CFG_TUH_DEVICE_MAX) // typical keyboard + mouse device can have 3-4 HID interfaces #define CFG_TUH_MSC 1 #define CFG_TUH_VENDOR 0 diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 86c010fa7..429d0a113 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -38,6 +38,7 @@ #include "serial/ftdi_sio.h" #include "serial/cp210x.h" #include "serial/ch34x.h" +#include "serial/pl2303.h" // Level where CFG_TUSB_DEBUG must be at least for this driver is logged #ifndef CFG_TUH_CDC_LOG_LEVEL @@ -91,6 +92,10 @@ typedef struct { ftdi_private_t ftdi; #endif + #if CFG_TUH_CDC_PL2303 + pl2303_private_t pl2303; + #endif + struct { tu_edpt_stream_t tx; tu_edpt_stream_t rx; @@ -105,7 +110,7 @@ typedef struct { CFG_TUH_MEM_SECTION static cdch_interface_t cdch_data[CFG_TUH_CDC]; -#if CFG_TUH_CDC_FTDI +#if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_PL2303 static tusb_desc_device_t desc_dev[CFG_TUH_CDC][CFG_TUH_ENUMERATION_BUFSIZE]; #endif @@ -164,6 +169,22 @@ static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complet static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #endif +//------------- PL2303 prototypes -------------// +#if CFG_TUH_CDC_PL2303 +static uint16_t const pl2303_vid_pid_list[][2] = {CFG_TUH_CDC_PL2303_VID_PID_LIST}; +static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {PL2303_TYPE_DATA}; + +CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN + +static bool pl2303_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len); +static void pl2303_process_config(tuh_xfer_t * xfer); + +static bool pl2303_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool pl2303_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool pl2303_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool pl2303_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +#endif + //------------- Common -------------// enum { SERIAL_DRIVER_ACM = 0, @@ -180,6 +201,10 @@ enum { SERIAL_DRIVER_CH34X, #endif +#if CFG_TUH_CDC_PL2303 + SERIAL_DRIVER_PL2303, +#endif + SERIAL_DRIVER_COUNT }; @@ -260,6 +285,22 @@ static const cdch_serial_driver_t serial_drivers[] = { #endif }, #endif + + #if CFG_TUH_CDC_PL2303 + { + .vid_pid_list = pl2303_vid_pid_list, + .vid_pid_count = TU_ARRAY_SIZE(pl2303_vid_pid_list), + .open = pl2303_open, + .process_set_config = pl2303_process_config, + .set_control_line_state = pl2303_set_modem_ctrl, + .set_baudrate = pl2303_set_baudrate, + .set_data_format = pl2303_set_data_format, + .set_line_coding = pl2303_set_line_coding, + #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL + .name = (uint8_t const *) "PL2303" + #endif + } + #endif }; TU_VERIFY_STATIC(TU_ARRAY_SIZE(serial_drivers) == SERIAL_DRIVER_COUNT, "Serial driver count mismatch"); @@ -1863,7 +1904,7 @@ static bool ch34x_modem_ctrl_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t com // internal control complete to update state such as line state, encoding static void ch34x_internal_control_complete(tuh_xfer_t * xfer) { - // CH34x only has 1 interface and wIndex used as payload and not for bInterfaceNumber + // CH34x has only interface 0, because wIndex is used as payload and not for bInterfaceNumber uint8_t const itf_num = 0; uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); cdch_interface_t* p_cdc = get_itf(idx); @@ -1921,7 +1962,8 @@ static bool ch34x_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_ } static void ch34x_set_line_coding_stage1_complete(tuh_xfer_t * xfer) { - uint8_t const itf_num = 0; // CH34x has only interface 0, because wIndex is used as payload and not for bInterfaceNumber + // CH34x has only interface 0, because wIndex is used as payload and not for bInterfaceNumber + uint8_t const itf_num = 0; set_line_coding_stage1_complete(xfer, itf_num, ch34x_write_reg_data_format, // control request function to set data format ch34x_internal_control_complete); // control complete function to be called after request @@ -1981,7 +2023,7 @@ static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uin } static void ch34x_process_config(tuh_xfer_t* xfer) { - // CH34x only has 1 interface and wIndex used as payload and not for bInterfaceNumber + // CH34x has only interface 0, because wIndex is used as payload and not for bInterfaceNumber uintptr_t const state = xfer->user_data; uint8_t const itf_num = 0; uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); @@ -2142,4 +2184,708 @@ static uint8_t ch34x_get_lcr(cdch_interface_t * p_cdc) { #endif // CFG_TUH_CDC_CH34X +//--------------------------------------------------------------------+ +// PL2303 +//--------------------------------------------------------------------+ +#if CFG_TUH_CDC_PL2303 + +static int8_t pl2303_detect_type(cdch_interface_t * p_cdc, uint8_t const idx, uint8_t step, + tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool pl2303_encode_baud_rate(cdch_interface_t * p_cdc, uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE]); + +//------------- Control Request -------------// + +static bool pl2303_set_request(cdch_interface_t * p_cdc, uint8_t request, uint8_t requesttype, + uint16_t value, uint16_t index, uint8_t * buffer, uint16_t length, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + tusb_control_request_t const request_setup = { + .bmRequestType = requesttype, + .bRequest = request, + .wValue = tu_htole16 (value), + .wIndex = tu_htole16 (index), + .wLength = tu_htole16 (length) + }; + + // use usbh enum buf since application variable does not live long enough + uint8_t * enum_buf = NULL; + + if (buffer && length > 0) { + enum_buf = usbh_get_enum_buf(); + if (request_setup.bmRequestType_bit.direction == TUSB_DIR_OUT) { + tu_memcpy_s(enum_buf, CFG_TUH_ENUMERATION_BUFSIZE, buffer, length); + } + } + + tuh_xfer_t xfer = { + .daddr = p_cdc->daddr, + .ep_addr = 0, + .setup = &request_setup, + .buffer = enum_buf, + .complete_cb = complete_cb, + .user_data = user_data + }; + + return tuh_control_xfer(&xfer); +} + +static bool pl2303_vendor_read(cdch_interface_t * p_cdc, uint16_t value, uint8_t * buf, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + uint8_t request = p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN] ? + PL2303_VENDOR_READ_NREQUEST : PL2303_VENDOR_READ_REQUEST; + + return pl2303_set_request(p_cdc, request, PL2303_VENDOR_READ_REQUEST_TYPE, value, 0, buf, 1, complete_cb, user_data); +} + +static bool pl2303_vendor_write(cdch_interface_t * p_cdc, uint16_t value, uint16_t index, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + uint8_t request = p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN] ? + PL2303_VENDOR_WRITE_NREQUEST : PL2303_VENDOR_WRITE_REQUEST; + + return pl2303_set_request(p_cdc, request, PL2303_VENDOR_WRITE_REQUEST_TYPE, value, index, NULL, 0, complete_cb, user_data); +} + +static inline bool pl2303_supports_hx_status(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + uint8_t buf; + + return pl2303_set_request(p_cdc, PL2303_VENDOR_READ_REQUEST, PL2303_VENDOR_READ_REQUEST_TYPE, PL2303_READ_TYPE_HX_STATUS, 0, + &buf, 1, complete_cb, user_data); +} + +static inline bool pl2303_set_control_lines(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + return pl2303_set_request(p_cdc, PL2303_SET_CONTROL_REQUEST, PL2303_SET_CONTROL_REQUEST_TYPE, + p_cdc->requested_line_state, 0, NULL, 0, complete_cb, user_data); +} + +//static bool pl2303_get_line_request(cdch_interface_t * p_cdc, uint8_t buf[PL2303_LINE_CODING_BUFSIZE]) +//{ +// return pl2303_set_request(p_cdc, PL2303_GET_LINE_REQUEST, PL2303_GET_LINE_REQUEST_TYPE, 0, 0, buf, PL2303_LINE_CODING_BUFSIZE); +//} + +static bool pl2303_set_line_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + // the caller has to precheck, that the new line coding different than the current, else false returned + uint8_t buf[PL2303_LINE_CODING_BUFSIZE]; + /* + * Some PL2303 are known to lose bytes if you change serial settings + * even to the same values as before. Thus we actually need to filter + * in this specific case. + */ + // TODO really necessary to check? what to do in this case when no transfer will happen? + // callback is not called... + TU_VERIFY(memcmp(&p_cdc->requested_line_coding, &p_cdc->line_coding, sizeof(cdc_line_coding_t) ) != 0); + + /* For reference buf[6] data bits value */ + TU_VERIFY(p_cdc->requested_line_coding.data_bits >= 5 && p_cdc->requested_line_coding.data_bits <= 8, 0); + buf[6] = p_cdc->requested_line_coding.data_bits; + + /* For reference buf[0]:buf[3] baud rate value */ + TU_VERIFY(pl2303_encode_baud_rate(p_cdc, &buf[0])); + + /* For reference buf[4]=0 is 1 stop bits */ + /* For reference buf[4]=1 is 1.5 stop bits */ + /* For reference buf[4]=2 is 2 stop bits */ + buf[4] = p_cdc->requested_line_coding.stop_bits; // PL2303 has the same coding + + /* For reference buf[5]=0 is none parity */ + /* For reference buf[5]=1 is odd parity */ + /* For reference buf[5]=2 is even parity */ + /* For reference buf[5]=3 is mark parity */ + /* For reference buf[5]=4 is space parity */ + buf[5] = p_cdc->requested_line_coding.parity; // PL2303 has the same coding + + return pl2303_set_request(p_cdc, PL2303_SET_LINE_REQUEST, PL2303_SET_LINE_REQUEST_TYPE, 0, 0, + buf, PL2303_LINE_CODING_BUFSIZE, complete_cb, user_data); +} + +//static bool pl2303_set_break(cdch_interface_t * p_cdc, bool enable) +//{ +// uint16_t state = enable ? PL2303_BREAK_ON : PL2303_BREAK_OFF; +// return pl2303_set_request(p_cdc, PL2303_BREAK_REQUEST, PL2303_BREAK_REQUEST_TYPE, state, 0, NULL, 0); +//} + +static inline int pl2303_clear_halt(cdch_interface_t * p_cdc, uint8_t endp, tuh_xfer_cb_t complete_cb, uintptr_t user_data) +{ + /* we don't care if it wasn't halted first. in fact some devices + * (like some ibmcam model 1 units) seem to expect hosts to make + * this request for iso endpoints, which can't halt! + */ + return pl2303_set_request(p_cdc, TUSB_REQ_CLEAR_FEATURE, PL2303_CLEAR_HALT_REQUEST_TYPE, TUSB_REQ_FEATURE_EDPT_HALT, endp, + NULL, 0, complete_cb, user_data); +} + +//------------- Driver API -------------// + +// internal control complete to update state such as line state, encoding +static void pl2303_internal_control_complete(tuh_xfer_t * xfer) { + // PL2303 has only interface 0, because wIndex is used as payload and not for bInterfaceNumber + uint8_t const itf_num = 0; + uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc,); + bool const success = (xfer->result == XFER_RESULT_SUCCESS); + TU_LOG_P_CDC("control complete success = %u", success); + + if (success) { + if (xfer->setup->bRequest == PL2303_SET_LINE_REQUEST && + xfer->setup->bmRequestType == PL2303_SET_LINE_REQUEST_TYPE) { + p_cdc->line_coding = p_cdc->requested_line_coding; + } + if (xfer->setup->bRequest == PL2303_SET_CONTROL_REQUEST && + xfer->setup->bmRequestType == PL2303_SET_CONTROL_REQUEST_TYPE) { + p_cdc->line_state = p_cdc->requested_line_state; + } + } + + xfer->complete_cb = p_cdc->user_control_cb; + if (xfer->complete_cb) { + xfer->complete_cb(xfer); + } +} + +static bool pl2303_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + p_cdc->user_control_cb = complete_cb; + TU_ASSERT(pl2303_set_line_request(p_cdc, complete_cb ? pl2303_internal_control_complete : NULL, user_data)); + + return true; +} + +static bool pl2303_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + p_cdc->requested_line_coding.bit_rate = p_cdc->line_coding.bit_rate; + p_cdc->user_control_cb = complete_cb; + TU_ASSERT(pl2303_set_line_request(p_cdc, complete_cb ? pl2303_internal_control_complete : NULL, user_data)); + + return true; +} + +static bool pl2303_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + p_cdc->requested_line_coding.stop_bits = p_cdc->line_coding.stop_bits; + p_cdc->requested_line_coding.parity = p_cdc->line_coding.parity; + p_cdc->requested_line_coding.data_bits = p_cdc->line_coding.data_bits; + p_cdc->user_control_cb = complete_cb; + TU_ASSERT(pl2303_set_line_request(p_cdc, complete_cb ? pl2303_internal_control_complete : NULL, user_data)); + + return true; +} + +static bool pl2303_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + // PL2303 has the same bit coding + p_cdc->user_control_cb = complete_cb; + TU_ASSERT(pl2303_set_control_lines(p_cdc, complete_cb ? pl2303_internal_control_complete : NULL, user_data)); + + return true; +} + +//------------- Enumeration -------------// + +enum { + CONFIG_PL2303_GET_DESC = 0, + CONFIG_PL2303_DETECT_TYPE, + CONFIG_PL2303_READ1, + CONFIG_PL2303_WRITE1, + CONFIG_PL2303_READ2, + CONFIG_PL2303_READ3, + CONFIG_PL2303_READ4, + CONFIG_PL2303_WRITE2, + CONFIG_PL2303_READ5, + CONFIG_PL2303_READ6, + CONFIG_PL2303_WRITE3, + CONFIG_PL2303_WRITE4, + CONFIG_PL2303_WRITE5, + CONFIG_PL2303_RESET_ENDP1, + CONFIG_PL2303_RESET_ENDP2, + CONFIG_PL2303_LINE_CODING, + CONFIG_PL2303_MODEM_CONTROL, + CONFIG_PL2303_FLOW_CTRL_READ, + CONFIG_PL2303_FLOW_CTRL_WRITE, + CONFIG_PL2303_COMPLETE +}; + +static bool pl2303_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len) { + // PL2303 Interface includes 1 vendor interface + 1 interrupt endpoints + 2 bulk + TU_VERIFY(itf_desc->bNumEndpoints == 3); + TU_VERIFY(sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t) <= max_len); + + cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); + TU_VERIFY(p_cdc); + + p_cdc->serial_drid = SERIAL_DRIVER_PL2303; + p_cdc->pl2303.serial_private.quirks = 0; + p_cdc->pl2303.supports_hx_status = false; + + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const * ) tu_desc_next(itf_desc); + + // Interrupt endpoint: not used for now + TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(desc_ep) && + TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer); + TU_ASSERT(tuh_edpt_open(daddr, desc_ep)); + p_cdc->ep_notif = desc_ep->bEndpointAddress; + desc_ep += 1; + + // data endpoints expected to be in pairs + TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep)); + + return true; +} + +static void pl2303_process_config(tuh_xfer_t * xfer) { + // PL2303 has only interface 0, because wIndex is used as payload and not for bInterfaceNumber + uint8_t const itf_num = 0; + uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); + cdch_interface_t * p_cdc = get_itf(idx); + // state CONFIG_PL2303_READ1 may have no success due to expected stall by pl2303_supports_hx_status() + TU_ASSERT_COMPLETE(p_cdc && (xfer->result == XFER_RESULT_SUCCESS || xfer->user_data == CONFIG_PL2303_READ1)); + uint8_t buf; + int8_t type; + + switch (xfer->user_data) { + + // from here sequence overtaken from Linux Kernel function pl2303_startup() + case CONFIG_PL2303_GET_DESC: + p_cdc->user_control_cb = pl2303_process_config; // set once for whole process config + // get device descriptor + TU_ASSERT_COMPLETE(tuh_descriptor_get_device(xfer->daddr, desc_dev[idx], sizeof(tusb_desc_device_t), + pl2303_process_config, CONFIG_PL2303_DETECT_TYPE)); + break; + + case CONFIG_PL2303_DETECT_TYPE: + // get type and quirks (step 1) + type = pl2303_detect_type (p_cdc, idx, 1, pl2303_process_config, CONFIG_PL2303_READ1); // step 1 + TU_ASSERT_COMPLETE(type!=PL2303_DETECT_TYPE_FAILED); + if (type == PL2303_SUPPORTS_HX_STATUS_TRIGGERED) { + break; + } // else: no transfer triggered and continue with CONFIG_PL2303_READ1 + TU_ATTR_FALLTHROUGH; + + case CONFIG_PL2303_READ1: + // get supports_hx_status, type and quirks (step 2), do special read + p_cdc->pl2303.supports_hx_status = ( // will not be true, if coming directly from previous case + xfer->user_data == CONFIG_PL2303_READ1 && xfer->result == XFER_RESULT_SUCCESS ); + type = pl2303_detect_type (p_cdc, idx, 2, NULL, 0); // step 2 now with supports_hx_status + TU_ASSERT_COMPLETE(type!=PL2303_DETECT_TYPE_FAILED); + p_cdc->pl2303.serial_private.type = &pl2303_type_data[type]; + p_cdc->pl2303.serial_private.quirks |= p_cdc->pl2303.serial_private.type->quirks; + #if CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL && 0 // can be activated if necessary + TU_LOG(CFG_TUH_CDC_LOG_LEVEL, "PL2303 bDeviceClass = 0x%02x bMaxPacketSize0 = %u bcdUSB = 0x%04x bcdDevice = 0x%04x", + desc_dev[idx]->bDeviceClass, desc_dev[idx]->bMaxPacketSize0, desc_dev[idx]->bcdUSB, desc_dev[idx]->bcdDevice ); + uint16_t vid, pid; + TU_ASSERT_COMPLETE(tuh_vid_pid_get(p_cdc->daddr, &vid, &pid)); + TU_LOG(CFG_TUH_CDC_LOG_LEVEL, " vid = 0x%04x pid = 0x%04x supports_hx_status = %u type = %s quirks = %u", + vid, pid, p_cdc->pl2303.supports_hx_status, + p_cdc->pl2303.serial_private.type->name, p_cdc->pl2303.serial_private.quirks); + #endif + // purpose unknown, overtaken from Linux Kernel driver + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) { + TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0x8484, &buf, pl2303_process_config, CONFIG_PL2303_WRITE1)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + case CONFIG_PL2303_WRITE1: + // purpose unknown, overtaken from Linux Kernel driver + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) { + TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 0x0404, 0, pl2303_process_config, CONFIG_PL2303_READ2)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + case CONFIG_PL2303_READ2: + // purpose unknown, overtaken from Linux Kernel driver + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) { + TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0x8484, &buf, pl2303_process_config, CONFIG_PL2303_READ3)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + case CONFIG_PL2303_READ3: + // purpose unknown, overtaken from Linux Kernel driver + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) { + TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0x8383, &buf, pl2303_process_config, CONFIG_PL2303_READ4)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + case CONFIG_PL2303_READ4: + // purpose unknown, overtaken from Linux Kernel driver + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) { + TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0x8484, &buf, pl2303_process_config, CONFIG_PL2303_WRITE2)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + case CONFIG_PL2303_WRITE2: + // purpose unknown, overtaken from Linux Kernel driver + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) { + TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 0x0404, 1, pl2303_process_config, CONFIG_PL2303_READ5)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + case CONFIG_PL2303_READ5: + // purpose unknown, overtaken from Linux Kernel driver + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) { + TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0x8484, &buf, pl2303_process_config, CONFIG_PL2303_READ6)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + case CONFIG_PL2303_READ6: + // purpose unknown, overtaken from Linux Kernel driver + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) { + TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0x8383, &buf, pl2303_process_config, CONFIG_PL2303_WRITE3)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + case CONFIG_PL2303_WRITE3: + // purpose unknown, overtaken from Linux Kernel driver + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) { + TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 0, 1, pl2303_process_config, CONFIG_PL2303_WRITE4)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + case CONFIG_PL2303_WRITE4: + // purpose unknown, overtaken from Linux Kernel driver + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) { + TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 1, 0, pl2303_process_config, CONFIG_PL2303_WRITE5)); + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + case CONFIG_PL2303_WRITE5: + // purpose unknown, overtaken from Linux Kernel driver + if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) { + if (p_cdc->pl2303.serial_private.quirks & PL2303_QUIRK_LEGACY) { + TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 2, 0x24, pl2303_process_config, CONFIG_PL2303_RESET_ENDP1)); + } else { + TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 2, 0x44, pl2303_process_config, CONFIG_PL2303_RESET_ENDP1)); + } + break; + } // else: continue with next step + TU_ATTR_FALLTHROUGH; + + // from here sequence overtaken from Linux Kernel function pl2303_open() + case CONFIG_PL2303_RESET_ENDP1: + // step 1 + if (p_cdc->pl2303.serial_private.quirks & PL2303_QUIRK_LEGACY) { + TU_ASSERT_COMPLETE(pl2303_clear_halt(p_cdc, PL2303_OUT_EP, pl2303_process_config, CONFIG_PL2303_RESET_ENDP2)); + } else { + /* reset upstream data pipes */ + if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) { + TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, PL2303_HXN_RESET_REG, // skip CONFIG_PL2303_RESET_ENDP2, no 2nd step + PL2303_HXN_RESET_UPSTREAM_PIPE | PL2303_HXN_RESET_DOWNSTREAM_PIPE, + pl2303_process_config, CONFIG_PL2303_LINE_CODING)); + } else { + pl2303_vendor_write(p_cdc, 8, 0, pl2303_process_config, CONFIG_PL2303_RESET_ENDP2); + } + } + break; + + case CONFIG_PL2303_RESET_ENDP2: + // step 2 + if (p_cdc->pl2303.serial_private.quirks & PL2303_QUIRK_LEGACY) { + TU_ASSERT_COMPLETE(pl2303_clear_halt(p_cdc, PL2303_IN_EP, pl2303_process_config, CONFIG_PL2303_LINE_CODING)); + } else { + /* reset upstream data pipes */ + if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) { + // here nothing to do, only structure of previous step overtaken for better reading and comparison + } else { + TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 9, 0, pl2303_process_config, CONFIG_PL2303_LINE_CODING)); + } + } + break; + + // from here sequence overtaken from Linux Kernel function pl2303_set_termios() + // unnecessary pl2303_get_line_request() is skipped due to a stall + case CONFIG_PL2303_LINE_CODING: + #ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM + p_cdc->requested_line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM; + TU_ASSERT_COMPLETE( pl2303_set_line_request(p_cdc, pl2303_internal_control_complete, CONFIG_PL2303_MODEM_CONTROL)); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + + case CONFIG_PL2303_MODEM_CONTROL: + #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + TU_ASSERT_COMPLETE(pl2303_set_control_lines(p_cdc, pl2303_internal_control_complete, CONFIG_PL2303_FLOW_CTRL_READ)); + break; + #else + TU_ATTR_FALLTHROUGH; + #endif + + case CONFIG_PL2303_FLOW_CTRL_READ: + // read flow control register for modify & write back in next step + if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) { + TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, PL2303_HXN_FLOWCTRL_REG, &buf, pl2303_process_config, + CONFIG_PL2303_FLOW_CTRL_WRITE)); + } else { + TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0, &buf, pl2303_process_config, CONFIG_PL2303_FLOW_CTRL_WRITE)); + } + break; + + case CONFIG_PL2303_FLOW_CTRL_WRITE: + // no flow control + buf = xfer->buffer[0]; + if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) { + buf &= (uint8_t) ~PL2303_HXN_FLOWCTRL_MASK; + buf |= PL2303_HXN_FLOWCTRL_NONE; + TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, PL2303_HXN_FLOWCTRL_REG, buf, pl2303_process_config, + CONFIG_PL2303_COMPLETE)); + } else { + buf &= (uint8_t) ~PL2303_FLOWCTRL_MASK; + TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 0, buf, pl2303_process_config, CONFIG_PL2303_COMPLETE)); + } + break; + + case CONFIG_PL2303_COMPLETE: + set_config_complete(idx, 0, true); + break; + + default: + set_config_complete(idx, 0, false); + break; + } +} + +//------------- Helper -------------// + +static int8_t pl2303_detect_type(cdch_interface_t * p_cdc, uint8_t const idx, uint8_t step, + tuh_xfer_cb_t complete_cb, uintptr_t user_data ) +{ + /* + * Legacy PL2303H, variants 0 and 1 (difference unknown). + */ + if (desc_dev[idx]->bDeviceClass == 0x02) { + return TYPE_H; /* variant 0 */ + } + + if (desc_dev[idx]->bMaxPacketSize0 != 0x40) { + if (desc_dev[idx]->bDeviceClass == 0x00 || desc_dev[idx]->bDeviceClass == 0xff) { + return TYPE_H; /* variant 1 */ + } + return TYPE_H; /* variant 0 */ + } + + switch (desc_dev[idx]->bcdUSB) { + case 0x101: + /* USB 1.0.1? Let's assume they meant 1.1... */ + TU_ATTR_FALLTHROUGH; + case 0x110: + switch (desc_dev[idx]->bcdDevice) { + case 0x300: + return TYPE_HX; + case 0x400: + return TYPE_HXD; + default: + return TYPE_HX; + } + break; + case 0x200: + switch (desc_dev[idx]->bcdDevice) { + case 0x100: /* GC */ + case 0x105: + return TYPE_HXN; + case 0x300: /* GT / TA */ + if (step == 1) { + // step 1 trigger pl2303_supports_hx_status() request + TU_ASSERT(pl2303_supports_hx_status (p_cdc, complete_cb, user_data), PL2303_DETECT_TYPE_FAILED); + return PL2303_SUPPORTS_HX_STATUS_TRIGGERED; + } else { + // step 2 use supports_hx_status + if (p_cdc->pl2303.supports_hx_status) { + return TYPE_TA; + } + } + TU_ATTR_FALLTHROUGH; + case 0x305: + case 0x400: /* GL */ + case 0x405: + return TYPE_HXN; + case 0x500: /* GE / TB */ + if (step == 1) { + // step 1 trigger pl2303_supports_hx_status() request + TU_ASSERT(pl2303_supports_hx_status (p_cdc, complete_cb, user_data), PL2303_DETECT_TYPE_FAILED); + return PL2303_SUPPORTS_HX_STATUS_TRIGGERED; + } else { + // step 2 use supports_hx_status + if (p_cdc->pl2303.supports_hx_status) { + return TYPE_TB; + } + } + TU_ATTR_FALLTHROUGH; + case 0x505: + case 0x600: /* GS */ + case 0x605: + case 0x700: /* GR */ + case 0x705: + return TYPE_HXN; + default: + break; + } + break; + default: break; + } + + TU_LOG_P_CDC("unknown device type bcdUSB = 0x%04x", desc_dev[idx]->bcdUSB); + + return PL2303_DETECT_TYPE_FAILED; +} + +/* + * Returns the nearest supported baud rate that can be set directly without + * using divisors. + */ +static uint32_t pl2303_get_supported_baud_rate(uint32_t baud) +{ + static const uint32_t baud_sup[] = { + 75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, + 14400, 19200, 28800, 38400, 57600, 115200, 230400, 460800, + 614400, 921600, 1228800, 2457600, 3000000, 6000000 + }; + + uint8_t i; + for (i = 0; i < TU_ARRAY_SIZE(baud_sup); ++i) { + if (baud_sup[i] > baud) { + break; + } + } + + if (i == TU_ARRAY_SIZE(baud_sup)) { + baud = baud_sup[i - 1]; + } else if (i > 0 && (baud_sup[i] - baud) > (baud - baud_sup[i - 1])) { + baud = baud_sup[i - 1]; + } else { + baud = baud_sup[i]; + } + + return baud; +} + +/* + * NOTE: If unsupported baud rates are set directly, the PL2303 seems to + * use 9600 baud. + */ +static uint32_t pl2303_encode_baud_rate_direct(uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE], uint32_t baud) +{ + uint32_t baud_le = tu_htole32(baud); + buf[0] = (uint8_t) ( baud_le & 0xff); + buf[1] = (uint8_t) ((baud_le >> 8) & 0xff); + buf[2] = (uint8_t) ((baud_le >> 16) & 0xff); + buf[3] = (uint8_t) ((baud_le >> 24) & 0xff); + + return baud; +} + +static uint32_t pl2303_encode_baud_rate_divisor(uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE], uint32_t baud) +{ + uint32_t baseline, mantissa, exponent; + + /* + * Apparently the formula is: + * baudrate = 12M * 32 / (mantissa * 4^exponent) + * where + * mantissa = buf[8:0] + * exponent = buf[11:9] + */ + baseline = 12000000 * 32; + mantissa = baseline / baud; + if (mantissa == 0) + mantissa = 1; /* Avoid dividing by zero if baud > 32 * 12M. */ + exponent = 0; + while (mantissa >= 512) { + if (exponent < 7) { + mantissa >>= 2; /* divide by 4 */ + exponent++; + } else { + /* Exponent is maxed. Trim mantissa and leave. */ + mantissa = 511; + break; + } + } + + buf[3] = 0x80; + buf[2] = 0; + buf[1] = (uint8_t) ((exponent << 1 | mantissa >> 8) & 0xff); + buf[0] = (uint8_t) (mantissa & 0xff); + + /* Calculate and return the exact baud rate. */ + baud = (baseline / mantissa) >> (exponent << 1); + + return baud; +} + +static uint32_t pl2303_encode_baud_rate_divisor_alt(uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE], uint32_t baud) +{ + uint32_t baseline, mantissa, exponent; + + /* + * Apparently, for the TA version the formula is: + * baudrate = 12M * 32 / (mantissa * 2^exponent) + * where + * mantissa = buf[10:0] + * exponent = buf[15:13 16] + */ + baseline = 12000000 * 32; + mantissa = baseline / baud; + if (mantissa == 0) { + mantissa = 1; /* Avoid dividing by zero if baud > 32 * 12M. */ + } + exponent = 0; + while (mantissa >= 2048) { + if (exponent < 15) { + mantissa >>= 1; /* divide by 2 */ + exponent++; + } else { + /* Exponent is maxed. Trim mantissa and leave. */ + mantissa = 2047; + break; + } + } + + buf[3] = 0x80; + buf[2] = (uint8_t) (exponent & 0x01); + buf[1] = (uint8_t) (((exponent & (uint32_t) ~0x01) << 4 | mantissa >> 8 ) & 0xff); + buf[0] = (uint8_t) (mantissa & 0xff); + + /* Calculate and return the exact baud rate. */ + baud = (baseline / mantissa) >> exponent; + + return baud; +} + +static bool pl2303_encode_baud_rate(cdch_interface_t * p_cdc, uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE]) +{ + uint32_t baud = p_cdc->requested_line_coding.bit_rate; + uint32_t baud_sup; + + TU_VERIFY(baud && baud <= p_cdc->pl2303.serial_private.type->max_baud_rate); + /* + * Use direct method for supported baud rates, otherwise use divisors. + * Newer chip types do not support divisor encoding. + */ + if (p_cdc->pl2303.serial_private.type->no_divisors) { + baud_sup = baud; + } else { + baud_sup = pl2303_get_supported_baud_rate(baud); + } + + if (baud == baud_sup) { + baud = pl2303_encode_baud_rate_direct(buf, baud); + } else if (p_cdc->pl2303.serial_private.type->alt_divisors) { + baud = pl2303_encode_baud_rate_divisor_alt(buf, baud); + } else { + baud = pl2303_encode_baud_rate_divisor(buf, baud); + } + TU_LOG_P_CDC("real baudrate = %lu", baud); + + return true; +} + +#endif // CFG_TUH_CDC_PL2303 + #endif diff --git a/src/class/cdc/serial/pl2303.h b/src/class/cdc/serial/pl2303.h new file mode 100644 index 000000000..bf264191b --- /dev/null +++ b/src/class/cdc/serial/pl2303.h @@ -0,0 +1,172 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 Heiko Kuester + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef _PL2303_H_ +#define _PL2303_H_ + +#include +#include + +// There is no official documentation for the PL2303 chips. +// Reference can be found +// - https://github.com/torvalds/linux/blob/master/drivers/usb/serial/pl2303.h and +// https://github.com/torvalds/linux/blob/master/drivers/usb/serial/pl2303.c +// - https://github.com/freebsd/freebsd-src/blob/main/sys/dev/usb/serial/uplcom.c + +/* quirks */ +#define PL2303_QUIRK_UART_STATE_IDX0 1 +#define PL2303_QUIRK_LEGACY 2 +#define PL2303_QUIRK_ENDPOINT_HACK 4 + +/* requests and bits */ +#define PL2303_SET_LINE_REQUEST_TYPE 0x21 // class request host to device interface +#define PL2303_SET_LINE_REQUEST 0x20 // dec 32 + +#define PL2303_SET_CONTROL_REQUEST_TYPE 0x21 // class request host to device interface +#define PL2303_SET_CONTROL_REQUEST 0x22 // dec 34 +#define PL2303_CONTROL_DTR 0x01 // dec 1 +#define PL2303_CONTROL_RTS 0x02 // dec 2 + +#define PL2303_BREAK_REQUEST_TYPE 0x21 // class request host to device interface +#define PL2303_BREAK_REQUEST 0x23 // dec 35 +#define PL2303_BREAK_ON 0xffff +#define PL2303_BREAK_OFF 0x0000 + +#define PL2303_GET_LINE_REQUEST_TYPE 0xa1 // class request device to host interface +#define PL2303_GET_LINE_REQUEST 0x21 // dec 33 + +#define PL2303_VENDOR_WRITE_REQUEST_TYPE 0x40 // vendor request host to device interface +#define PL2303_VENDOR_WRITE_REQUEST 0x01 // dec 1 +#define PL2303_VENDOR_WRITE_NREQUEST 0x80 // dec 128 + +#define PL2303_VENDOR_READ_REQUEST_TYPE 0xc0 // vendor request device to host interface +#define PL2303_VENDOR_READ_REQUEST 0x01 // dec 1 +#define PL2303_VENDOR_READ_NREQUEST 0x81 // dec 129 + +#define PL2303_UART_STATE_INDEX 8 +#define PL2303_UART_STATE_MSR_MASK 0x8b +#define PL2303_UART_STATE_TRANSIENT_MASK 0x74 +#define PL2303_UART_DCD 0x01 +#define PL2303_UART_DSR 0x02 +#define PL2303_UART_BREAK_ERROR 0x04 +#define PL2303_UART_RING 0x08 +#define PL2303_UART_FRAME_ERROR 0x10 +#define PL2303_UART_PARITY_ERROR 0x20 +#define PL2303_UART_OVERRUN_ERROR 0x40 +#define PL2303_UART_CTS 0x80 + +#define PL2303_FLOWCTRL_MASK 0xf0 + +#define PL2303_CLEAR_HALT_REQUEST_TYPE 0x02 // standard request host to device endpoint + +/* registers via vendor read/write requests */ +#define PL2303_READ_TYPE_HX_STATUS 0x8080 + +#define PL2303_HXN_RESET_REG 0x07 +#define PL2303_HXN_RESET_UPSTREAM_PIPE 0x02 +#define PL2303_HXN_RESET_DOWNSTREAM_PIPE 0x01 + +#define PL2303_HXN_FLOWCTRL_REG 0x0a +#define PL2303_HXN_FLOWCTRL_MASK 0x1c +#define PL2303_HXN_FLOWCTRL_NONE 0x1c +#define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18 +#define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c + +/* type data */ +enum pl2303_type { + TYPE_H, + TYPE_HX, + TYPE_TA, + TYPE_TB, + TYPE_HXD, + TYPE_HXN, + TYPE_COUNT +}; + +struct pl2303_type_data { + uint8_t const *name; + uint32_t const max_baud_rate; + uint8_t const quirks; + uint16_t const no_autoxonxoff:1; + uint16_t const no_divisors:1; + uint16_t const alt_divisors:1; +}; + +#define PL2303_TYPE_DATA \ + [TYPE_H] = { \ + .name = (uint8_t const*)"H", \ + .max_baud_rate = 1228800, \ + .quirks = PL2303_QUIRK_LEGACY, \ + .no_autoxonxoff = true, \ + }, \ + [TYPE_HX] = { \ + .name = (uint8_t const*)"HX", \ + .max_baud_rate = 6000000, \ + }, \ + [TYPE_TA] = { \ + .name = (uint8_t const*)"TA", \ + .max_baud_rate = 6000000, \ + .alt_divisors = true, \ + }, \ + [TYPE_TB] = { \ + .name = (uint8_t const*)"TB", \ + .max_baud_rate = 12000000, \ + .alt_divisors = true, \ + }, \ + [TYPE_HXD] = { \ + .name = (uint8_t const*)"HXD", \ + .max_baud_rate = 12000000, \ + }, \ + [TYPE_HXN] = { \ + .name = (uint8_t const*)"G (HXN)", \ + .max_baud_rate = 12000000, \ + .no_divisors = true, \ + } + +/* private data types */ +struct pl2303_serial_private { + const struct pl2303_type_data* type; + uint8_t quirks; +}; + +typedef struct TU_ATTR_PACKED { + struct pl2303_serial_private serial_private; + bool supports_hx_status; +} pl2303_private_t; + +/* buffer sizes for line coding data */ +#define PL2303_LINE_CODING_BUFSIZE 7 +#define PL2303_LINE_CODING_BAUDRATE_BUFSIZE 4 + +/* bulk endpoints */ +#define PL2303_OUT_EP 0x02 +#define PL2303_IN_EP 0x83 + +/* return values of pl2303_detect_type() */ +#define PL2303_SUPPORTS_HX_STATUS_TRIGGERED -1 +#define PL2303_DETECT_TYPE_FAILED -2 + +#endif /* _PL2303_H_ */ diff --git a/src/tusb_option.h b/src/tusb_option.h index 281341685..f023b490a 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -504,6 +504,24 @@ { 0x9986, 0x7523 } /* overtaken from Linux Kernel driver /drivers/usb/serial/ch341.c */ #endif +#ifndef CFG_TUH_CDC_PL2303 + // PL2303 is not part of CDC class, only to re-use CDC driver API + #define CFG_TUH_CDC_PL2303 0 +#endif + +#ifndef CFG_TUH_CDC_PL2303_VID_PID_QUIRKS_LIST + // List of product IDs that can use the PL2303 CDC driver + #define CFG_TUH_CDC_PL2303_VID_PID_LIST \ + { 0x067b, 0x2303 }, /* initial 2303 */ \ + { 0x067b, 0x2304 }, /* TB */ \ + { 0x067b, 0x23a3 }, /* GC */ \ + { 0x067b, 0x23b3 }, /* GB */ \ + { 0x067b, 0x23c3 }, /* GT */ \ + { 0x067b, 0x23d3 }, /* GL */ \ + { 0x067b, 0x23e3 }, /* GE */ \ + { 0x067b, 0x23f3 } /* GS */ +#endif + #ifndef CFG_TUH_HID #define CFG_TUH_HID 0 #endif -- cgit v1.3.1 From ea175a78aad5bd258954aa61550aae0162bc884b Mon Sep 17 00:00:00 2001 From: IngHK Date: Sat, 24 Feb 2024 13:00:46 +0100 Subject: updated contribution, readme and some comments --- CONTRIBUTORS.rst | 7 +++++++ README.rst | 2 +- src/class/cdc/cdc_host.c | 6 +++--- 3 files changed, 11 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index 085f8082a..451ac0783 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -31,6 +31,13 @@ Notable contributors - Most features development +`Heiko Kuester `__ +-------------------------------------------- + +- Add CH34x and PL2303 support (CDC host) +- Improve FTDI and CP210x support (CDC host) + + `Hristo Gochkov `__ ------------------------------------------------- diff --git a/README.rst b/README.rst index fe2417451..6922c231e 100644 --- a/README.rst +++ b/README.rst @@ -77,7 +77,7 @@ Host Stack - Human Interface Device (HID): Keyboard, Mouse, Generic - Mass Storage Class (MSC) - Communication Device Class: CDC-ACM -- Vendor serial over USB: FTDI, CP210x +- Vendor serial over USB: FTDI, CP210x, CH34x, PL2303 - Hub with multiple-level support Similar to the Device Stack, if you have a special requirement, `usbh_app_driver_get_cb()` can be used to write your own class driver without modifying the stack. diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 429d0a113..b35a8fd93 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -24,7 +24,7 @@ * This file is part of the TinyUSB stack. * * Contribution - * - Heiko Kuester: CH34x support + * - Heiko Kuester: add support of CH34x & PL2303, improve support of FTDI & CP210x */ #include "tusb_option.h" @@ -818,12 +818,12 @@ bool cdch_set_config(uint8_t daddr, uint8_t itf_num) { TU_ASSERT(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); TU_LOG_P_CDC("set config"); - // fake transfer to kick-off process + // fake transfer to kick-off process_set_config() tuh_xfer_t xfer; xfer.daddr = daddr; xfer.result = XFER_RESULT_SUCCESS; xfer.setup = &request; - xfer.user_data = 0; // initial state + xfer.user_data = 0; // initial state 0 serial_drivers[p_cdc->serial_drid].process_set_config(&xfer); return true; -- cgit v1.3.1 From 2b507dba4d9901d1208c4166bbc6026bb6062c9b Mon Sep 17 00:00:00 2001 From: IngHK Date: Sat, 24 Feb 2024 13:01:38 +0100 Subject: small changes & code style --- src/class/cdc/cdc_host.c | 236 +++++++++++++++++++++++++---------------------- 1 file changed, 124 insertions(+), 112 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index b35a8fd93..50aa4b6f0 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -85,7 +85,7 @@ typedef struct { tuh_xfer_cb_t user_control_cb; #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X - tuh_xfer_cb_t requested_complete_cb; + tuh_xfer_cb_t requested_complete_cb; #endif #if CFG_TUH_CDC_FTDI @@ -119,54 +119,54 @@ static cdch_interface_t cdch_data[CFG_TUH_CDC]; //--------------------------------------------------------------------+ //------------- ACM prototypes -------------// -static bool acm_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); -static void acm_process_config(tuh_xfer_t* xfer); +static bool acm_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len); +static void acm_process_config(tuh_xfer_t * xfer); -static bool acm_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool acm_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool acm_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool acm_set_control_line_state(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool acm_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool acm_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool acm_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool acm_set_control_line_state(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); //------------- FTDI prototypes -------------// #if CFG_TUH_CDC_FTDI static uint16_t const ftdi_vid_pid_list[][2] = {CFG_TUH_CDC_FTDI_VID_PID_LIST}; #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL -static uint8_t const * ftdi_chip_name[] = { FTDI_CHIP_NAMES }; + static uint8_t const * ftdi_chip_name[] = { FTDI_CHIP_NAMES }; #endif -static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len); -static void ftdi_process_config(tuh_xfer_t* xfer); +static bool ftdi_open(uint8_t daddr, const tusb_desc_interface_t * itf_desc, uint16_t max_len); +static void ftdi_process_config(tuh_xfer_t * xfer); -static bool ftdi_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool ftdi_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool ftdi_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool ftdi_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ftdi_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ftdi_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ftdi_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ftdi_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #endif //------------- CP210X prototypes -------------// #if CFG_TUH_CDC_CP210X static uint16_t const cp210x_vid_pid_list[][2] = {CFG_TUH_CDC_CP210X_VID_PID_LIST}; -static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len); -static void cp210x_process_config(tuh_xfer_t* xfer); +static bool cp210x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len); +static void cp210x_process_config(tuh_xfer_t * xfer); -static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool cp210x_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool cp210x_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool cp210x_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool cp210x_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool cp210x_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool cp210x_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #endif //------------- CH34x prototypes -------------// #if CFG_TUH_CDC_CH34X static uint16_t const ch34x_vid_pid_list[][2] = {CFG_TUH_CDC_CH34X_VID_PID_LIST}; -static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len); -static void ch34x_process_config(tuh_xfer_t* xfer); +static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len); +static void ch34x_process_config(tuh_xfer_t * xfer); -static bool ch34x_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool ch34x_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool ch34x_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -static bool ch34x_set_modem_ctrl(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ch34x_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ch34x_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ch34x_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +static bool ch34x_set_modem_ctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #endif //------------- PL2303 prototypes -------------// @@ -211,14 +211,14 @@ enum { typedef struct { uint16_t const (*vid_pid_list)[2]; uint16_t const vid_pid_count; - bool (*const open)(uint8_t daddr, const tusb_desc_interface_t *itf_desc, uint16_t max_len); - void (*const process_set_config)(tuh_xfer_t* xfer); - bool (*const set_control_line_state)(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); - bool (*const set_baudrate)(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); - bool (*const set_data_format)(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); - bool (*const set_line_coding)(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + bool (*const open)(uint8_t daddr, const tusb_desc_interface_t * itf_desc, uint16_t max_len); + void (*const process_set_config)(tuh_xfer_t * xfer); + bool (*const set_control_line_state)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + bool (*const set_baudrate)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + bool (*const set_data_format)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); + bool (*const set_line_coding)(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data); #if CFG_TUSB_DEBUG && CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL - uint8_t const * name; + uint8_t const * name; #endif } cdch_serial_driver_t; @@ -309,17 +309,17 @@ TU_VERIFY_STATIC(TU_ARRAY_SIZE(serial_drivers) == SERIAL_DRIVER_COUNT, "Serial d // INTERNAL OBJECT & FUNCTION DECLARATION //--------------------------------------------------------------------+ -static inline cdch_interface_t* get_itf(uint8_t idx) { +static inline cdch_interface_t * get_itf(uint8_t idx) { TU_ASSERT(idx < CFG_TUH_CDC, NULL); - cdch_interface_t* p_cdc = &cdch_data[idx]; + cdch_interface_t * p_cdc = &cdch_data[idx]; return (p_cdc->daddr != 0) ? p_cdc : NULL; } static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr) { for(uint8_t i=0; idaddr == daddr) && + cdch_interface_t * p_cdc = &cdch_data[i]; + if ((p_cdc->daddr == daddr) && (ep_addr == p_cdc->ep_notif || ep_addr == p_cdc->stream.rx.ep_addr || ep_addr == p_cdc->stream.tx.ep_addr)) { return i; } @@ -328,10 +328,10 @@ static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr) { return TUSB_INDEX_INVALID_8; } -static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const *itf_desc) { +static cdch_interface_t * make_new_itf(uint8_t daddr, tusb_desc_interface_t const * itf_desc) { for(uint8_t i=0; idaddr = daddr; p_cdc->bInterfaceNumber = itf_desc->bInterfaceNumber; p_cdc->bInterfaceSubClass = itf_desc->bInterfaceSubClass; @@ -345,7 +345,7 @@ static cdch_interface_t* make_new_itf(uint8_t daddr, tusb_desc_interface_t const return NULL; } -static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t const *desc_ep); +static bool open_ep_stream_pair(cdch_interface_t * p_cdc , tusb_desc_endpoint_t const *desc_ep); //--------------------------------------------------------------------+ // APPLICATION API @@ -353,21 +353,21 @@ static bool open_ep_stream_pair(cdch_interface_t* p_cdc , tusb_desc_endpoint_t c uint8_t tuh_cdc_itf_get_index(uint8_t daddr, uint8_t itf_num) { for (uint8_t i = 0; i < CFG_TUH_CDC; i++) { - const cdch_interface_t* p_cdc = &cdch_data[i]; + const cdch_interface_t * p_cdc = &cdch_data[i]; if (p_cdc->daddr == daddr && p_cdc->bInterfaceNumber == itf_num) return i; } return TUSB_INDEX_INVALID_8; } -bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info) { - cdch_interface_t* p_cdc = get_itf(idx); +bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t * info) { + cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc && info); info->daddr = p_cdc->daddr; // re-construct descriptor - tusb_desc_interface_t* desc = &info->desc; + tusb_desc_interface_t * desc = &info->desc; desc->bLength = sizeof(tusb_desc_interface_t); desc->bDescriptorType = TUSB_DESC_INTERFACE; @@ -383,27 +383,27 @@ bool tuh_cdc_itf_get_info(uint8_t idx, tuh_itf_info_t* info) { } bool tuh_cdc_mounted(uint8_t idx) { - cdch_interface_t* p_cdc = get_itf(idx); + cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); return p_cdc->mounted; } bool tuh_cdc_get_dtr(uint8_t idx) { - cdch_interface_t* p_cdc = get_itf(idx); + cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_DTR) ? true : false; } bool tuh_cdc_get_rts(uint8_t idx) { - cdch_interface_t* p_cdc = get_itf(idx); + cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_RTS) ? true : false; } -bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding) { - cdch_interface_t* p_cdc = get_itf(idx); +bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t * line_coding) { + cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); *line_coding = p_cdc->line_coding; @@ -415,29 +415,29 @@ bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t* line_coding) // Write //--------------------------------------------------------------------+ -uint32_t tuh_cdc_write(uint8_t idx, void const* buffer, uint32_t bufsize) { - cdch_interface_t* p_cdc = get_itf(idx); +uint32_t tuh_cdc_write(uint8_t idx, void const * buffer, uint32_t bufsize) { + cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); return tu_edpt_stream_write(&p_cdc->stream.tx, buffer, bufsize); } uint32_t tuh_cdc_write_flush(uint8_t idx) { - cdch_interface_t* p_cdc = get_itf(idx); + cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); return tu_edpt_stream_write_xfer(&p_cdc->stream.tx); } bool tuh_cdc_write_clear(uint8_t idx) { - cdch_interface_t* p_cdc = get_itf(idx); + cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); return tu_edpt_stream_clear(&p_cdc->stream.tx); } uint32_t tuh_cdc_write_available(uint8_t idx) { - cdch_interface_t* p_cdc = get_itf(idx); + cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); return tu_edpt_stream_write_available(&p_cdc->stream.tx); @@ -447,33 +447,34 @@ uint32_t tuh_cdc_write_available(uint8_t idx) { // Read //--------------------------------------------------------------------+ -uint32_t tuh_cdc_read (uint8_t idx, void* buffer, uint32_t bufsize) { - cdch_interface_t* p_cdc = get_itf(idx); +uint32_t tuh_cdc_read (uint8_t idx, void * buffer, uint32_t bufsize) { + cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); return tu_edpt_stream_read(&p_cdc->stream.rx, buffer, bufsize); } uint32_t tuh_cdc_read_available(uint8_t idx) { - cdch_interface_t* p_cdc = get_itf(idx); + cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); return tu_edpt_stream_read_available(&p_cdc->stream.rx); } -bool tuh_cdc_peek(uint8_t idx, uint8_t* ch) { - cdch_interface_t* p_cdc = get_itf(idx); +bool tuh_cdc_peek(uint8_t idx, uint8_t * ch) { + cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); return tu_edpt_stream_peek(&p_cdc->stream.rx, ch); } bool tuh_cdc_read_clear (uint8_t idx) { - cdch_interface_t* p_cdc = get_itf(idx); + cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); bool ret = tu_edpt_stream_clear(&p_cdc->stream.rx); tu_edpt_stream_read_xfer(&p_cdc->stream.rx); + return ret; } @@ -657,7 +658,7 @@ void cdch_init(void) { tu_memclr(cdch_data, sizeof(cdch_data)); for (size_t i = 0; i < CFG_TUH_CDC; i++) { - cdch_interface_t* p_cdc = &cdch_data[i]; + cdch_interface_t * p_cdc = &cdch_data[i]; tu_edpt_stream_init(&p_cdc->stream.tx, true, true, false, p_cdc->stream.tx_ff_buf, CFG_TUH_CDC_TX_BUFSIZE, @@ -671,7 +672,7 @@ void cdch_init(void) { void cdch_close(uint8_t daddr) { for (uint8_t idx = 0; idx < CFG_TUH_CDC; idx++) { - cdch_interface_t* p_cdc = &cdch_data[idx]; + cdch_interface_t * p_cdc = &cdch_data[idx]; if (p_cdc->daddr == daddr) { TU_LOG_P_CDC("close"); @@ -695,35 +696,37 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT(p_cdc); - if ( ep_addr == p_cdc->stream.tx.ep_addr ) { + if (ep_addr == p_cdc->stream.tx.ep_addr) { // invoke tx complete callback to possibly refill tx fifo if (tuh_cdc_tx_complete_cb) tuh_cdc_tx_complete_cb(idx); - if ( 0 == tu_edpt_stream_write_xfer(&p_cdc->stream.tx) ) { + if (0 == tu_edpt_stream_write_xfer(&p_cdc->stream.tx)) { // If there is no data left, a ZLP should be sent if: // - xferred_bytes is multiple of EP Packet size and not zero tu_edpt_stream_write_zlp_if_needed(&p_cdc->stream.tx, xferred_bytes); } - } else if ( ep_addr == p_cdc->stream.rx.ep_addr ) { + } else if (ep_addr == p_cdc->stream.rx.ep_addr) { #if CFG_TUH_CDC_FTDI if (p_cdc->serial_drid == SERIAL_DRIVER_FTDI) { // FTDI reserve 2 bytes for status // uint8_t status[2] = {p_cdc->stream.rx.ep_buf[0], p_cdc->stream.rx.ep_buf[1]}; tu_edpt_stream_read_xfer_complete_offset(&p_cdc->stream.rx, xferred_bytes, 2); - }else + } else #endif { tu_edpt_stream_read_xfer_complete(&p_cdc->stream.rx, xferred_bytes); } // invoke receive callback - if (tuh_cdc_rx_cb) tuh_cdc_rx_cb(idx); + if (tuh_cdc_rx_cb) { + tuh_cdc_rx_cb(idx); + } // prepare for next transfer if needed tu_edpt_stream_read_xfer(&p_cdc->stream.rx); - }else if ( ep_addr == p_cdc->ep_notif ) { + } else if (ep_addr == p_cdc->ep_notif) { // TODO handle notification endpoint - }else { + } else { TU_ASSERT(false); } @@ -734,7 +737,7 @@ bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t // Enumeration //--------------------------------------------------------------------+ -static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t const* desc_ep) { +static bool open_ep_stream_pair(cdch_interface_t * p_cdc, tusb_desc_endpoint_t const * desc_ep) { for (size_t i = 0; i < 2; i++) { TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer); @@ -746,13 +749,13 @@ static bool open_ep_stream_pair(cdch_interface_t* p_cdc, tusb_desc_endpoint_t co tu_edpt_stream_open(&p_cdc->stream.tx, p_cdc->daddr, desc_ep); } - desc_ep = (tusb_desc_endpoint_t const*) tu_desc_next(desc_ep); + desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(desc_ep); } return true; } -bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const *itf_desc, uint16_t max_len) { +bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len) { (void) rhport; cdch_serial_driver_t const * driver_detected = NULL; @@ -826,6 +829,7 @@ bool cdch_set_config(uint8_t daddr, uint8_t itf_num) { xfer.user_data = 0; // initial state 0 serial_drivers[p_cdc->serial_drid].process_set_config(&xfer); + return true; } @@ -864,7 +868,7 @@ static void acm_internal_control_complete(tuh_xfer_t * xfer) { } } -static bool acm_set_control_line_state(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool acm_set_control_line_state(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { TU_VERIFY(p_cdc->acm_capability.support_line_request); tusb_control_request_t const request = { @@ -886,15 +890,16 @@ static bool acm_set_control_line_state(cdch_interface_t* p_cdc, tuh_xfer_cb_t co .ep_addr = 0, .setup = &request, .buffer = NULL, - .complete_cb = complete_cb ? acm_internal_control_complete : NULL, // complete_cb is NULL for sync call + .complete_cb = complete_cb ? acm_internal_control_complete : NULL, .user_data = user_data }; TU_ASSERT(tuh_control_xfer(&xfer)); + return true; } -static bool acm_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool acm_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { TU_VERIFY(p_cdc->acm_capability.support_line_request); TU_VERIFY(p_cdc->requested_line_coding.data_bits && p_cdc->requested_line_coding.bit_rate); TU_VERIFY((p_cdc->requested_line_coding.data_bits >= 5 && p_cdc->requested_line_coding.data_bits <= 8) || @@ -913,30 +918,32 @@ static bool acm_set_line_coding(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_ }; // use usbh enum buf to hold line coding since user line_coding variable does not live long enough - uint8_t* enum_buf = usbh_get_enum_buf(); + uint8_t * enum_buf = usbh_get_enum_buf(); memcpy(enum_buf, &p_cdc->requested_line_coding, sizeof(cdc_line_coding_t)); p_cdc->user_control_cb = complete_cb; + tuh_xfer_t xfer = { .daddr = p_cdc->daddr, .ep_addr = 0, .setup = &request, .buffer = enum_buf, - .complete_cb = complete_cb ? acm_internal_control_complete : NULL, // complete_cb is NULL for sync call + .complete_cb = complete_cb ? acm_internal_control_complete : NULL, .user_data = user_data }; TU_ASSERT(tuh_control_xfer(&xfer)); + return true; } -static bool acm_set_data_format(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool acm_set_data_format(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->requested_line_coding.bit_rate = p_cdc->line_coding.bit_rate; return acm_set_line_coding(p_cdc, complete_cb, user_data); } -static bool acm_set_baudrate(cdch_interface_t* p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool acm_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { p_cdc->requested_line_coding.stop_bits = p_cdc->line_coding.stop_bits; p_cdc->requested_line_coding.parity = p_cdc->line_coding.parity; p_cdc->requested_line_coding.data_bits = p_cdc->line_coding.data_bits; @@ -952,21 +959,22 @@ enum { CONFIG_ACM_COMPLETE, }; -static bool acm_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) { - uint8_t const* p_desc_end = ((uint8_t const*) itf_desc) + max_len; +static bool acm_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len) { + uint8_t const * p_desc_end = ((uint8_t const *) itf_desc) + max_len; - cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc); + cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); TU_VERIFY(p_cdc); + p_cdc->serial_drid = SERIAL_DRIVER_ACM; //------------- Control Interface -------------// - uint8_t const* p_desc = tu_desc_next(itf_desc); + uint8_t const * p_desc = tu_desc_next(itf_desc); // Communication Functional Descriptors while ((p_desc < p_desc_end) && (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc))) { if (CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT == cdc_functional_desc_typeof(p_desc)) { // save ACM bmCapabilities - p_cdc->acm_capability = ((cdc_desc_func_acm_t const*) p_desc)->bmCapabilities; + p_cdc->acm_capability = ((cdc_desc_func_acm_t const *) p_desc)->bmCapabilities; } p_desc = tu_desc_next(p_desc); @@ -975,7 +983,7 @@ static bool acm_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint1 // Open notification endpoint of control interface if any if (itf_desc->bNumEndpoints == 1) { TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)); - tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc; + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc; TU_ASSERT(tuh_edpt_open(daddr, desc_ep)); p_cdc->ep_notif = desc_ep->bEndpointAddress; @@ -985,22 +993,22 @@ static bool acm_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint1 //------------- Data Interface (if any) -------------// if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && - (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const*) p_desc)->bInterfaceClass)) { + (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass)) { // next to endpoint descriptor p_desc = tu_desc_next(p_desc); // data endpoints expected to be in pairs - TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const*) p_desc)); + TU_ASSERT(open_ep_stream_pair(p_cdc, (tusb_desc_endpoint_t const *) p_desc)); } return true; } -static void acm_process_config(tuh_xfer_t* xfer) { +static void acm_process_config(tuh_xfer_t * xfer) { uintptr_t const state = xfer->user_data; uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); - cdch_interface_t* p_cdc = get_itf(idx); + cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT_COMPLETE(p_cdc && xfer->result == XFER_RESULT_SUCCESS, 1); switch (state) { @@ -1814,8 +1822,9 @@ static uint16_t ch34x_get_divisor_prescaler(cdch_interface_t * p_cdc); //------------- Control Request -------------// -static bool ch34x_set_request(cdch_interface_t* p_cdc, uint8_t direction, uint8_t request, uint16_t value, - uint16_t index, uint8_t* buffer, uint16_t length, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static bool ch34x_set_request(cdch_interface_t * p_cdc, uint8_t direction, uint8_t request, + uint16_t value, uint16_t index, uint8_t * buffer, uint16_t length, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { tusb_control_request_t const request_setup = { .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_DEVICE, @@ -1829,7 +1838,7 @@ static bool ch34x_set_request(cdch_interface_t* p_cdc, uint8_t direction, uint8_ }; // use usbh enum buf since application variable does not live long enough - uint8_t* enum_buf = NULL; + uint8_t * enum_buf = NULL; if (buffer && length > 0) { enum_buf = usbh_get_enum_buf(); @@ -1850,22 +1859,23 @@ static bool ch34x_set_request(cdch_interface_t* p_cdc, uint8_t direction, uint8_ return tuh_control_xfer(&xfer); } -static inline bool ch34x_control_out(cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index, +static inline bool ch34x_control_out(cdch_interface_t * p_cdc, uint8_t request, uint16_t value, uint16_t index, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { return ch34x_set_request(p_cdc, TUSB_DIR_OUT, request, value, index, NULL, 0, complete_cb, user_data); } -static inline bool ch34x_control_in(cdch_interface_t* p_cdc, uint8_t request, uint16_t value, uint16_t index, - uint8_t* buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static inline bool ch34x_control_in(cdch_interface_t * p_cdc, uint8_t request, uint16_t value, uint16_t index, + uint8_t * buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { return ch34x_set_request(p_cdc, TUSB_DIR_IN, request, value, index, buffer, buffersize, complete_cb, user_data); } -static inline bool ch34x_write_reg(cdch_interface_t* p_cdc, uint16_t reg, uint16_t reg_value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +static inline bool ch34x_write_reg(cdch_interface_t * p_cdc, uint16_t reg, uint16_t reg_value, + tuh_xfer_cb_t complete_cb, uintptr_t user_data) { return ch34x_control_out(p_cdc, CH34X_REQ_WRITE_REG, reg, reg_value, complete_cb, user_data); } -//static bool ch34x_read_reg_request ( cdch_interface_t* p_cdc, uint16_t reg, +//static bool ch34x_read_reg_request ( cdch_interface_t * p_cdc, uint16_t reg, // uint8_t *buffer, uint16_t buffersize, tuh_xfer_cb_t complete_cb, uintptr_t user_data ) //{ // return ch34x_control_in ( p_cdc, CH34X_REQ_READ_REG, reg, 0, buffer, buffersize, complete_cb, user_data ); @@ -1907,8 +1917,8 @@ static void ch34x_internal_control_complete(tuh_xfer_t * xfer) { // CH34x has only interface 0, because wIndex is used as payload and not for bInterfaceNumber uint8_t const itf_num = 0; uint8_t idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); - cdch_interface_t* p_cdc = get_itf(idx); - TU_ASSERT(p_cdc, ); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc,); bool const success = (xfer->result == XFER_RESULT_SUCCESS); TU_LOG_P_CDC("control complete success = %u", success); @@ -1997,17 +2007,17 @@ enum { CONFIG_CH34X_COMPLETE }; -static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uint16_t max_len) { +static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, uint16_t max_len) { // CH34x Interface includes 1 vendor interface + 2 bulk + 1 interrupt endpoints - TU_VERIFY (itf_desc->bNumEndpoints == 3); - TU_VERIFY (sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t) <= max_len); + TU_VERIFY(itf_desc->bNumEndpoints == 3); + TU_VERIFY(sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t) <= max_len); - cdch_interface_t* p_cdc = make_new_itf(daddr, itf_desc); - TU_VERIFY (p_cdc); + cdch_interface_t * p_cdc = make_new_itf(daddr, itf_desc); + TU_VERIFY(p_cdc); p_cdc->serial_drid = SERIAL_DRIVER_CH34X; - tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) tu_desc_next(itf_desc); + tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); // data endpoints expected to be in pairs TU_ASSERT(open_ep_stream_pair(p_cdc, desc_ep)); @@ -2023,11 +2033,11 @@ static bool ch34x_open(uint8_t daddr, tusb_desc_interface_t const* itf_desc, uin } static void ch34x_process_config(tuh_xfer_t* xfer) { - // CH34x has only interface 0, because wIndex is used as payload and not for bInterfaceNumber uintptr_t const state = xfer->user_data; + // CH34x has only interface 0, because wIndex is used as payload and not for bInterfaceNumber uint8_t const itf_num = 0; uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); - cdch_interface_t* p_cdc = get_itf(idx); + cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT_COMPLETE(p_cdc && xfer->result == XFER_RESULT_SUCCESS); uint8_t buffer[2]; // TODO remove @@ -2035,7 +2045,7 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { case CONFIG_CH34X_READ_VERSION: p_cdc->user_control_cb = ch34x_process_config; // set once for whole process config TU_ASSERT_COMPLETE(ch34x_control_in(p_cdc, CH34X_REQ_READ_VERSION, 0, 0, buffer, 2, - ch34x_process_config, CONFIG_CH34X_SERIAL_INIT)); + ch34x_process_config, CONFIG_CH34X_SERIAL_INIT)); break; case CONFIG_CH34X_SERIAL_INIT: { @@ -2060,19 +2070,20 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { // overtake line coding and do special reg write, purpose unknown, overtaken from WCH driver p_cdc->line_coding = (cdc_line_coding_t) CFG_TUH_CDC_LINE_CODING_ON_ENUM_CH34X; TU_ASSERT_COMPLETE(ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x0F, CH341_REG_0x2C), 0x0007, - ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL)); + ch34x_process_config, CONFIG_CH34X_FLOW_CONTROL)); break; case CONFIG_CH34X_FLOW_CONTROL: // no hardware flow control TU_ASSERT_COMPLETE(ch34x_write_reg(p_cdc, TU_U16(CH341_REG_0x27, CH341_REG_0x27), 0x0000, - ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL)); + ch34x_process_config, CONFIG_CH34X_MODEM_CONTROL)); break; case CONFIG_CH34X_MODEM_CONTROL: #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; - TU_ASSERT_COMPLETE(ch34x_modem_ctrl_request(p_cdc, ch34x_internal_control_complete, CONFIG_CH34X_COMPLETE)); + TU_ASSERT_COMPLETE(ch34x_modem_ctrl_request(p_cdc, ch34x_internal_control_complete, + CONFIG_CH34X_COMPLETE)); break; #else TU_ATTR_FALLTHROUGH; @@ -2431,6 +2442,7 @@ static bool pl2303_open(uint8_t daddr, tusb_desc_interface_t const * itf_desc, u } static void pl2303_process_config(tuh_xfer_t * xfer) { + uintptr_t const state = xfer->user_data; // PL2303 has only interface 0, because wIndex is used as payload and not for bInterfaceNumber uint8_t const itf_num = 0; uint8_t const idx = tuh_cdc_itf_get_index(xfer->daddr, itf_num); @@ -2440,7 +2452,7 @@ static void pl2303_process_config(tuh_xfer_t * xfer) { uint8_t buf; int8_t type; - switch (xfer->user_data) { + switch (state) { // from here sequence overtaken from Linux Kernel function pl2303_startup() case CONFIG_PL2303_GET_DESC: -- cgit v1.3.1 From da93fcfc6dbe51ceb73e990aefea9c8d55d3fc94 Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 22 Feb 2024 15:09:25 +0100 Subject: improved TU_LOGs --- src/class/cdc/cdc_host.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 50aa4b6f0..f3fbe88ea 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -50,6 +50,7 @@ DADDR, ITF_NUM, NAME, ##__VA_ARGS__) #define TU_LOG_P_CDC(TXT,...) TU_LOG_CDC(TXT, p_cdc->daddr, p_cdc->bInterfaceNumber, \ serial_drivers[p_cdc->serial_drid].name, ##__VA_ARGS__) +#define TU_LOG_RESULT(TXT,RESULT) TU_LOG_P_CDC(TXT " " #RESULT " = %s", RESULT ? "true" : "FALSE" ) #define TU_ASSERT_COMPLETE_DEFINE(_cond, _itf_offset) \ do { \ @@ -587,6 +588,7 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c if (ret && !complete_cb) { p_cdc->line_state = (uint8_t) line_state; } + TU_LOG_RESULT("set control line state", ret); return ret; } @@ -604,6 +606,7 @@ bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete if (ret && !complete_cb) { p_cdc->line_coding.bit_rate = baudrate; } + TU_LOG_RESULT("set baudrate", ret); return ret; } @@ -627,6 +630,7 @@ bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uin p_cdc->line_coding.parity = parity; p_cdc->line_coding.data_bits = data_bits; } + TU_LOG_RESULT("set data format", ret); return ret; } @@ -646,6 +650,7 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const * line_coding, if (ret && !complete_cb) { p_cdc->line_coding = *line_coding; } + TU_LOG_RESULT("set line coding", ret); return ret; } @@ -785,6 +790,7 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const * itf_ if (driver_detected) { TU_LOG_CDC("open", daddr, itf_desc->bInterfaceNumber, driver_detected->name); bool ret = driver_detected->open(daddr, itf_desc, max_len); + TU_LOG_CDC("opened ret = %s", daddr, itf_desc->bInterfaceNumber, driver_detected->name, ret ? "true" : "FALSE" ); return ret; } @@ -794,7 +800,7 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const * itf_ static void set_config_complete(uint8_t idx, uint8_t itf_offset, bool success) { cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT(p_cdc,); - TU_LOG_P_CDC("set config complete success = %u", success); + TU_LOG_RESULT("set config complete", success); if (success) { p_cdc->mounted = true; @@ -846,7 +852,7 @@ static void acm_internal_control_complete(tuh_xfer_t * xfer) { cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT(p_cdc,); bool const success = (xfer->result == XFER_RESULT_SUCCESS); - TU_LOG_P_CDC("control complete success = %u", success); + TU_LOG_RESULT(" control complete", success); if (success) { switch (xfer->setup->bRequest) { @@ -1131,7 +1137,7 @@ static void ftdi_internal_control_complete(tuh_xfer_t * xfer) { cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT(p_cdc,); bool const success = (xfer->result == XFER_RESULT_SUCCESS); - TU_LOG_P_CDC("control complete success = %u", success); + TU_LOG_RESULT(" control complete", success); if (success) { switch (xfer->setup->bRequest) { @@ -1407,7 +1413,7 @@ static bool ftdi_determine_type(cdch_interface_t * p_cdc, uint8_t const idx) break; } - TU_LOG_P_CDC("%s detected", ftdi_chip_name[p_cdc->ftdi.chip_type]); + TU_LOG_P_CDC(" %s detected", ftdi_chip_name[p_cdc->ftdi.chip_type]); return (p_cdc->ftdi.chip_type != UNKNOWN); } @@ -1554,7 +1560,7 @@ static inline uint32_t ftdi_get_divisor(cdch_interface_t * p_cdc) break; } - TU_LOG_P_CDC("Baudrate divisor 0x%lu", div_value); + TU_LOG_P_CDC(" Baudrate divisor 0x%lu", div_value); return div_value; } @@ -1663,7 +1669,7 @@ static void cp210x_internal_control_complete(tuh_xfer_t * xfer) { cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT(p_cdc,); bool const success = (xfer->result == XFER_RESULT_SUCCESS); - TU_LOG_P_CDC("control complete success = %u", success); + TU_LOG_RESULT(" control complete", success); if (success) { switch(xfer->setup->bRequest) { @@ -1920,7 +1926,7 @@ static void ch34x_internal_control_complete(tuh_xfer_t * xfer) { cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT(p_cdc,); bool const success = (xfer->result == XFER_RESULT_SUCCESS); - TU_LOG_P_CDC("control complete success = %u", success); + TU_LOG_RESULT(" control complete", success); if (success) { switch (xfer->setup->bRequest) { @@ -2051,7 +2057,7 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { case CONFIG_CH34X_SERIAL_INIT: { // handle version read data, set CH34x line coding (incl. baudrate) uint8_t const version = xfer->buffer[0]; - TU_LOG_P_CDC("Chip Version = %02x", version); + TU_LOG_P_CDC(" Chip Version = %02x", version); // only versions >= 0x30 are tested, below 0x30 seems having other programming // see drivers from WCH vendor, Linux kernel and FreeBSD TU_ASSERT_COMPLETE(version >= 0x30); @@ -2337,7 +2343,7 @@ static void pl2303_internal_control_complete(tuh_xfer_t * xfer) { cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT(p_cdc,); bool const success = (xfer->result == XFER_RESULT_SUCCESS); - TU_LOG_P_CDC("control complete success = %u", success); + TU_LOG_RESULT(" control complete", success); if (success) { if (xfer->setup->bRequest == PL2303_SET_LINE_REQUEST && @@ -2743,7 +2749,7 @@ static int8_t pl2303_detect_type(cdch_interface_t * p_cdc, uint8_t const idx, ui default: break; } - TU_LOG_P_CDC("unknown device type bcdUSB = 0x%04x", desc_dev[idx]->bcdUSB); + TU_LOG_P_CDC(" unknown device type bcdUSB = 0x%04x", desc_dev[idx]->bcdUSB); return PL2303_DETECT_TYPE_FAILED; } @@ -2893,7 +2899,7 @@ static bool pl2303_encode_baud_rate(cdch_interface_t * p_cdc, uint8_t buf[PL2303 } else { baud = pl2303_encode_baud_rate_divisor(buf, baud); } - TU_LOG_P_CDC("real baudrate = %lu", baud); + TU_LOG_P_CDC(" real baudrate = %lu", baud); return true; } -- cgit v1.3.1 From 46a861b0e31a7382d620986079abd0c373dfe6da Mon Sep 17 00:00:00 2001 From: IngHK Date: Fri, 23 Feb 2024 08:30:50 +0100 Subject: improved PL2303 TU_LOGs --- src/class/cdc/cdc_host.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index f3fbe88ea..9d27b1e79 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -2486,13 +2486,14 @@ static void pl2303_process_config(tuh_xfer_t * xfer) { p_cdc->pl2303.serial_private.type = &pl2303_type_data[type]; p_cdc->pl2303.serial_private.quirks |= p_cdc->pl2303.serial_private.type->quirks; #if CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL && 0 // can be activated if necessary - TU_LOG(CFG_TUH_CDC_LOG_LEVEL, "PL2303 bDeviceClass = 0x%02x bMaxPacketSize0 = %u bcdUSB = 0x%04x bcdDevice = 0x%04x", - desc_dev[idx]->bDeviceClass, desc_dev[idx]->bMaxPacketSize0, desc_dev[idx]->bcdUSB, desc_dev[idx]->bcdDevice ); + TU_LOG_P_CDC(" bDeviceClass = 0x%02x bMaxPacketSize0 = %u bcdUSB = 0x%04x bcdDevice = 0x%04x", + desc_dev[idx]->bDeviceClass, desc_dev[idx]->bMaxPacketSize0, + desc_dev[idx]->bcdUSB, desc_dev[idx]->bcdDevice ); uint16_t vid, pid; TU_ASSERT_COMPLETE(tuh_vid_pid_get(p_cdc->daddr, &vid, &pid)); - TU_LOG(CFG_TUH_CDC_LOG_LEVEL, " vid = 0x%04x pid = 0x%04x supports_hx_status = %u type = %s quirks = %u", - vid, pid, p_cdc->pl2303.supports_hx_status, - p_cdc->pl2303.serial_private.type->name, p_cdc->pl2303.serial_private.quirks); + TU_LOG_P_CDC(" vid = 0x%04x pid = 0x%04x supports_hx_status = %u type = %s quirks = %u", + vid, pid, p_cdc->pl2303.supports_hx_status, + p_cdc->pl2303.serial_private.type->name, p_cdc->pl2303.serial_private.quirks); #endif // purpose unknown, overtaken from Linux Kernel driver if (p_cdc->pl2303.serial_private.type != &pl2303_type_data[TYPE_HXN]) { -- cgit v1.3.1 From f97e31226a959944785122d4c0c10d4b3304704c Mon Sep 17 00:00:00 2001 From: IngHK Date: Wed, 28 Feb 2024 13:07:40 +0100 Subject: FTDI fixed itf_num and some improvement --- src/class/cdc/cdc_host.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 9d27b1e79..33d201c07 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -1140,22 +1140,19 @@ static void ftdi_internal_control_complete(tuh_xfer_t * xfer) { TU_LOG_RESULT(" control complete", success); if (success) { - switch (xfer->setup->bRequest) { - case FTDI_SIO_SET_MODEM_CTRL_REQUEST: - p_cdc->line_state = p_cdc->requested_line_state; - break; - - case FTDI_SIO_SET_DATA_REQUEST: - p_cdc->line_coding.stop_bits = p_cdc->requested_line_coding.stop_bits; - p_cdc->line_coding.parity = p_cdc->requested_line_coding.parity; - p_cdc->line_coding.data_bits = p_cdc->requested_line_coding.data_bits; - break; - - case FTDI_SIO_SET_BAUDRATE_REQUEST: - p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; - break; - - default: break; + if (xfer->setup->bRequest == FTDI_SIO_SET_MODEM_CTRL_REQUEST && + xfer->setup->bmRequestType == FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE ) { + p_cdc->line_state = p_cdc->requested_line_state; + } + if (xfer->setup->bRequest == FTDI_SIO_SET_DATA_REQUEST && + xfer->setup->bmRequestType == FTDI_SIO_SET_DATA_REQUEST_TYPE ) { + p_cdc->line_coding.stop_bits = p_cdc->requested_line_coding.stop_bits; + p_cdc->line_coding.parity = p_cdc->requested_line_coding.parity; + p_cdc->line_coding.data_bits = p_cdc->requested_line_coding.data_bits; + } + if (xfer->setup->bRequest == FTDI_SIO_SET_BAUDRATE_REQUEST && + xfer->setup->bmRequestType == FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE ) { + p_cdc->line_coding.bit_rate = p_cdc->requested_line_coding.bit_rate; } } @@ -1180,7 +1177,10 @@ static bool ftdi_set_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_c } static void ftdi_set_line_coding_stage1_complete(tuh_xfer_t * xfer) { - uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex); + uint8_t const idx = ftdi_get_idx(xfer); + cdch_interface_t * p_cdc = get_itf(idx); + TU_ASSERT(p_cdc,); + uint8_t const itf_num = p_cdc->bInterfaceNumber; set_line_coding_stage1_complete(xfer, itf_num, ftdi_set_data_request, // control request function to set data format ftdi_internal_control_complete); // control complete function to be called after request @@ -1413,7 +1413,8 @@ static bool ftdi_determine_type(cdch_interface_t * p_cdc, uint8_t const idx) break; } - TU_LOG_P_CDC(" %s detected", ftdi_chip_name[p_cdc->ftdi.chip_type]); + TU_LOG_P_CDC(" %s detected (bcdDevice = 0x%04x)", + ftdi_chip_name[p_cdc->ftdi.chip_type], desc_dev[idx]->bcdDevice); return (p_cdc->ftdi.chip_type != UNKNOWN); } -- cgit v1.3.1 From d3d61da0384d066b9930e8e5b658717983f02efb Mon Sep 17 00:00:00 2001 From: IngHK Date: Sun, 25 Feb 2024 17:20:02 +0100 Subject: improved & fixed compiler warnings device descriptor handling --- src/class/cdc/cdc_host.c | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 33d201c07..c222e11db 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -111,8 +111,10 @@ typedef struct { CFG_TUH_MEM_SECTION static cdch_interface_t cdch_data[CFG_TUH_CDC]; + #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_PL2303 - static tusb_desc_device_t desc_dev[CFG_TUH_CDC][CFG_TUH_ENUMERATION_BUFSIZE]; + CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN + static tusb_desc_device_t desc_dev[CFG_TUH_ENUMERATION_BUFSIZE]; #endif //--------------------------------------------------------------------+ @@ -1054,7 +1056,7 @@ static void acm_process_config(tuh_xfer_t * xfer) { //--------------------------------------------------------------------+ #if CFG_TUH_CDC_FTDI -static bool ftdi_determine_type(cdch_interface_t * p_cdc, uint8_t const idx); +static bool ftdi_determine_type(cdch_interface_t * p_cdc); static uint32_t ftdi_get_divisor(cdch_interface_t * p_cdc); static uint8_t ftdi_get_idx(tuh_xfer_t * xfer); @@ -1255,7 +1257,7 @@ static void ftdi_process_config(tuh_xfer_t * xfer) { // get device descriptor p_cdc->user_control_cb = ftdi_process_config; // set once for whole process config if (itf_num == 0) { // only necessary for 1st interface. other interface overtake type from interface 0 - TU_ASSERT_COMPLETE(tuh_descriptor_get_device(xfer->daddr, desc_dev[idx], sizeof(tusb_desc_device_t), + TU_ASSERT_COMPLETE(tuh_descriptor_get_device(xfer->daddr, &desc_dev, sizeof(tusb_desc_device_t), ftdi_process_config, CONFIG_FTDI_DETERMINE_TYPE)); break; } @@ -1264,7 +1266,7 @@ static void ftdi_process_config(tuh_xfer_t * xfer) { case CONFIG_FTDI_DETERMINE_TYPE: // determine type if (itf_num == 0) { - TU_ASSERT_COMPLETE(ftdi_determine_type(p_cdc, idx)); + TU_ASSERT_COMPLETE(ftdi_determine_type(p_cdc)); } else { // other interfaces have same type as interface 0 uint8_t const idx_itf0 = tuh_cdc_itf_get_index(xfer->daddr, 0); @@ -1334,9 +1336,9 @@ static void ftdi_process_config(tuh_xfer_t * xfer) { //------------- Helper -------------// -static bool ftdi_determine_type(cdch_interface_t * p_cdc, uint8_t const idx) +static bool ftdi_determine_type(cdch_interface_t * p_cdc) { - uint16_t const version = desc_dev[idx]->bcdDevice; + uint16_t const version = desc_dev->bcdDevice; uint8_t const itf_num = p_cdc->bInterfaceNumber; p_cdc->ftdi.chip_type = UNKNOWN; @@ -1414,7 +1416,7 @@ static bool ftdi_determine_type(cdch_interface_t * p_cdc, uint8_t const idx) } TU_LOG_P_CDC(" %s detected (bcdDevice = 0x%04x)", - ftdi_chip_name[p_cdc->ftdi.chip_type], desc_dev[idx]->bcdDevice); + ftdi_chip_name[p_cdc->ftdi.chip_type], desc_dev->bcdDevice); return (p_cdc->ftdi.chip_type != UNKNOWN); } @@ -2207,7 +2209,7 @@ static uint8_t ch34x_get_lcr(cdch_interface_t * p_cdc) { //--------------------------------------------------------------------+ #if CFG_TUH_CDC_PL2303 -static int8_t pl2303_detect_type(cdch_interface_t * p_cdc, uint8_t const idx, uint8_t step, +static int8_t pl2303_detect_type(cdch_interface_t * p_cdc, uint8_t step, tuh_xfer_cb_t complete_cb, uintptr_t user_data); static bool pl2303_encode_baud_rate(cdch_interface_t * p_cdc, uint8_t buf[PL2303_LINE_CODING_BAUDRATE_BUFSIZE]); @@ -2465,13 +2467,13 @@ static void pl2303_process_config(tuh_xfer_t * xfer) { case CONFIG_PL2303_GET_DESC: p_cdc->user_control_cb = pl2303_process_config; // set once for whole process config // get device descriptor - TU_ASSERT_COMPLETE(tuh_descriptor_get_device(xfer->daddr, desc_dev[idx], sizeof(tusb_desc_device_t), + TU_ASSERT_COMPLETE(tuh_descriptor_get_device(xfer->daddr, &desc_dev, sizeof(tusb_desc_device_t), pl2303_process_config, CONFIG_PL2303_DETECT_TYPE)); break; case CONFIG_PL2303_DETECT_TYPE: // get type and quirks (step 1) - type = pl2303_detect_type (p_cdc, idx, 1, pl2303_process_config, CONFIG_PL2303_READ1); // step 1 + type = pl2303_detect_type (p_cdc, 1, pl2303_process_config, CONFIG_PL2303_READ1); // step 1 TU_ASSERT_COMPLETE(type!=PL2303_DETECT_TYPE_FAILED); if (type == PL2303_SUPPORTS_HX_STATUS_TRIGGERED) { break; @@ -2482,14 +2484,14 @@ static void pl2303_process_config(tuh_xfer_t * xfer) { // get supports_hx_status, type and quirks (step 2), do special read p_cdc->pl2303.supports_hx_status = ( // will not be true, if coming directly from previous case xfer->user_data == CONFIG_PL2303_READ1 && xfer->result == XFER_RESULT_SUCCESS ); - type = pl2303_detect_type (p_cdc, idx, 2, NULL, 0); // step 2 now with supports_hx_status + type = pl2303_detect_type (p_cdc, 2, NULL, 0); // step 2 now with supports_hx_status TU_ASSERT_COMPLETE(type!=PL2303_DETECT_TYPE_FAILED); p_cdc->pl2303.serial_private.type = &pl2303_type_data[type]; p_cdc->pl2303.serial_private.quirks |= p_cdc->pl2303.serial_private.type->quirks; #if CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL && 0 // can be activated if necessary TU_LOG_P_CDC(" bDeviceClass = 0x%02x bMaxPacketSize0 = %u bcdUSB = 0x%04x bcdDevice = 0x%04x", - desc_dev[idx]->bDeviceClass, desc_dev[idx]->bMaxPacketSize0, - desc_dev[idx]->bcdUSB, desc_dev[idx]->bcdDevice ); + desc_dev->bDeviceClass, desc_dev->bMaxPacketSize0, + desc_dev->bcdUSB, desc_dev->bcdDevice ); uint16_t vid, pid; TU_ASSERT_COMPLETE(tuh_vid_pid_get(p_cdc->daddr, &vid, &pid)); TU_LOG_P_CDC(" vid = 0x%04x pid = 0x%04x supports_hx_status = %u type = %s quirks = %u", @@ -2674,29 +2676,29 @@ static void pl2303_process_config(tuh_xfer_t * xfer) { //------------- Helper -------------// -static int8_t pl2303_detect_type(cdch_interface_t * p_cdc, uint8_t const idx, uint8_t step, +static int8_t pl2303_detect_type(cdch_interface_t * p_cdc, uint8_t step, tuh_xfer_cb_t complete_cb, uintptr_t user_data ) { /* * Legacy PL2303H, variants 0 and 1 (difference unknown). */ - if (desc_dev[idx]->bDeviceClass == 0x02) { + if (desc_dev->bDeviceClass == 0x02) { return TYPE_H; /* variant 0 */ } - if (desc_dev[idx]->bMaxPacketSize0 != 0x40) { - if (desc_dev[idx]->bDeviceClass == 0x00 || desc_dev[idx]->bDeviceClass == 0xff) { + if (desc_dev->bMaxPacketSize0 != 0x40) { + if (desc_dev->bDeviceClass == 0x00 || desc_dev->bDeviceClass == 0xff) { return TYPE_H; /* variant 1 */ } return TYPE_H; /* variant 0 */ } - switch (desc_dev[idx]->bcdUSB) { + switch (desc_dev->bcdUSB) { case 0x101: /* USB 1.0.1? Let's assume they meant 1.1... */ TU_ATTR_FALLTHROUGH; case 0x110: - switch (desc_dev[idx]->bcdDevice) { + switch (desc_dev->bcdDevice) { case 0x300: return TYPE_HX; case 0x400: @@ -2706,7 +2708,7 @@ static int8_t pl2303_detect_type(cdch_interface_t * p_cdc, uint8_t const idx, ui } break; case 0x200: - switch (desc_dev[idx]->bcdDevice) { + switch (desc_dev->bcdDevice) { case 0x100: /* GC */ case 0x105: return TYPE_HXN; @@ -2751,7 +2753,7 @@ static int8_t pl2303_detect_type(cdch_interface_t * p_cdc, uint8_t const idx, ui default: break; } - TU_LOG_P_CDC(" unknown device type bcdUSB = 0x%04x", desc_dev[idx]->bcdUSB); + TU_LOG_P_CDC(" unknown device type bcdUSB = 0x%04x", desc_dev->bcdUSB); return PL2303_DETECT_TYPE_FAILED; } -- cgit v1.3.1 From edf13203916931962a53af50abc548dd3eded278 Mon Sep 17 00:00:00 2001 From: IngHK Date: Wed, 28 Feb 2024 13:13:18 +0100 Subject: removed expendable ACM check --- src/class/cdc/cdc_host.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index c222e11db..4d2134f9d 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -909,7 +909,6 @@ static bool acm_set_control_line_state(cdch_interface_t * p_cdc, tuh_xfer_cb_t c static bool acm_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { TU_VERIFY(p_cdc->acm_capability.support_line_request); - TU_VERIFY(p_cdc->requested_line_coding.data_bits && p_cdc->requested_line_coding.bit_rate); TU_VERIFY((p_cdc->requested_line_coding.data_bits >= 5 && p_cdc->requested_line_coding.data_bits <= 8) || p_cdc->requested_line_coding.data_bits == 16); -- cgit v1.3.1 From 3cf9cb98e630a0cb26bc28a2e4c67fb6df19a884 Mon Sep 17 00:00:00 2001 From: IngHK Date: Wed, 28 Feb 2024 13:15:37 +0100 Subject: small PL2303 improvements --- src/class/cdc/cdc_host.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 4d2134f9d..130993547 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -2292,9 +2292,10 @@ static bool pl2303_set_line_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t comp * even to the same values as before. Thus we actually need to filter * in this specific case. */ - // TODO really necessary to check? what to do in this case when no transfer will happen? - // callback is not called... - TU_VERIFY(memcmp(&p_cdc->requested_line_coding, &p_cdc->line_coding, sizeof(cdc_line_coding_t) ) != 0); + TU_VERIFY(p_cdc->requested_line_coding.data_bits != p_cdc->line_coding.data_bits || + p_cdc->requested_line_coding.stop_bits != p_cdc->line_coding.stop_bits || + p_cdc->requested_line_coding.parity != p_cdc->line_coding.parity || + p_cdc->requested_line_coding.bit_rate != p_cdc->line_coding.bit_rate ); /* For reference buf[6] data bits value */ TU_VERIFY(p_cdc->requested_line_coding.data_bits >= 5 && p_cdc->requested_line_coding.data_bits <= 8, 0); -- cgit v1.3.1 From e7308e313a1fb5482dbbb34cdfc6b7b11a54fd6b Mon Sep 17 00:00:00 2001 From: IngHK Date: Wed, 28 Feb 2024 13:28:38 +0100 Subject: improved TU_LOGs --- src/class/cdc/cdc.h | 12 +++++++++ src/class/cdc/cdc_host.c | 65 +++++++++++++++++++++++++++++------------------- 2 files changed, 51 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc.h b/src/class/cdc/cdc.h index 5cbd658fe..b1dca1ad8 100644 --- a/src/class/cdc/cdc.h +++ b/src/class/cdc/cdc.h @@ -192,6 +192,11 @@ typedef enum { CDC_LINE_CODING_STOP_BITS_2 = 2, // 2 bits } cdc_line_coding_stopbits_t; +#define CDC_LINE_CODING_STOP_BITS_TEXT(STOP_BITS) ( \ + STOP_BITS == CDC_LINE_CODING_STOP_BITS_1 ? "1" : \ + STOP_BITS == CDC_LINE_CODING_STOP_BITS_1_5 ? "1.5" : \ + STOP_BITS == CDC_LINE_CODING_STOP_BITS_2 ? "2" : "?" ) + // TODO Backward compatible for typos. Maybe removed in the future release #define CDC_LINE_CONDING_STOP_BITS_1 CDC_LINE_CODING_STOP_BITS_1 #define CDC_LINE_CONDING_STOP_BITS_1_5 CDC_LINE_CODING_STOP_BITS_1_5 @@ -205,6 +210,13 @@ typedef enum { CDC_LINE_CODING_PARITY_SPACE = 4, } cdc_line_coding_parity_t; +#define CDC_LINE_CODING_PARITY_CHAR(PARITY) ( \ + PARITY == CDC_LINE_CODING_PARITY_NONE ? 'N' : \ + PARITY == CDC_LINE_CODING_PARITY_ODD ? 'O' : \ + PARITY == CDC_LINE_CODING_PARITY_EVEN ? 'E' : \ + PARITY == CDC_LINE_CODING_PARITY_MARK ? 'M' : \ + PARITY == CDC_LINE_CODING_PARITY_SPACE ? 'S' : '?' ) + //--------------------------------------------------------------------+ // Management Element Notification (Notification Endpoint) //--------------------------------------------------------------------+ diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 130993547..d2bc63d64 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -50,7 +50,7 @@ DADDR, ITF_NUM, NAME, ##__VA_ARGS__) #define TU_LOG_P_CDC(TXT,...) TU_LOG_CDC(TXT, p_cdc->daddr, p_cdc->bInterfaceNumber, \ serial_drivers[p_cdc->serial_drid].name, ##__VA_ARGS__) -#define TU_LOG_RESULT(TXT,RESULT) TU_LOG_P_CDC(TXT " " #RESULT " = %s", RESULT ? "true" : "FALSE" ) +#define TU_LOG_P_CDC_BOOL(TXT,VAL) TU_LOG_P_CDC(TXT " " #VAL " = %s", VAL ? "true" : "false" ) #define TU_ASSERT_COMPLETE_DEFINE(_cond, _itf_offset) \ do { \ @@ -395,14 +395,20 @@ bool tuh_cdc_get_dtr(uint8_t idx) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); - return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_DTR) ? true : false; + bool ret = (p_cdc->line_state & CDC_CONTROL_LINE_STATE_DTR); +// TU_LOG_P_CDC_BOOL("get DTR", ret); + + return ret; } bool tuh_cdc_get_rts(uint8_t idx) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); - return (p_cdc->line_state & CDC_CONTROL_LINE_STATE_RTS) ? true : false; + bool ret = (p_cdc->line_state & CDC_CONTROL_LINE_STATE_RTS); +// TU_LOG_P_CDC_BOOL("get RTS", ret); + + return ret; } bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t * line_coding) { @@ -410,6 +416,10 @@ bool tuh_cdc_get_local_line_coding(uint8_t idx, cdc_line_coding_t * line_coding) TU_VERIFY(p_cdc); *line_coding = p_cdc->line_coding; + TU_LOG_P_CDC("get line coding %lu %u%c%s", + p_cdc->line_coding.bit_rate, p_cdc->line_coding.data_bits, + CDC_LINE_CODING_PARITY_CHAR(p_cdc->line_coding.parity), + CDC_LINE_CODING_STOP_BITS_TEXT(line_coding->stop_bits)); return true; } @@ -590,7 +600,7 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c if (ret && !complete_cb) { p_cdc->line_state = (uint8_t) line_state; } - TU_LOG_RESULT("set control line state", ret); +// TU_LOG_P_CDC_BOOL("set control line state", ret); return ret; } @@ -598,7 +608,7 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); - TU_LOG_P_CDC("set baudrate = %lu", baudrate); + TU_LOG_P_CDC("set baudrate %lu", baudrate); cdch_serial_driver_t const * driver = &serial_drivers[p_cdc->serial_drid]; p_cdc->requested_line_coding.bit_rate = baudrate; @@ -608,7 +618,7 @@ bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete if (ret && !complete_cb) { p_cdc->line_coding.bit_rate = baudrate; } - TU_LOG_RESULT("set baudrate", ret); +// TU_LOG_P_CDC_BOOL("set baudrate", ret); return ret; } @@ -617,8 +627,9 @@ bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uin tuh_xfer_cb_t complete_cb, uintptr_t user_data) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); - TU_LOG_P_CDC("set data format data_bits = %u parity = %u stop_bits = %u (indexes!)", - data_bits, parity, stop_bits); + TU_LOG_P_CDC("set data format %u%c%s", + data_bits, CDC_LINE_CODING_PARITY_CHAR(parity), + CDC_LINE_CODING_STOP_BITS_TEXT(stop_bits)); cdch_serial_driver_t const * driver = &serial_drivers[p_cdc->serial_drid]; p_cdc->requested_line_coding.stop_bits = stop_bits; @@ -632,7 +643,7 @@ bool tuh_cdc_set_data_format(uint8_t idx, uint8_t stop_bits, uint8_t parity, uin p_cdc->line_coding.parity = parity; p_cdc->line_coding.data_bits = data_bits; } - TU_LOG_RESULT("set data format", ret); +// TU_LOG_P_CDC_BOOL("set data format", ret); return ret; } @@ -641,8 +652,10 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const * line_coding, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); - TU_LOG_P_CDC("set line coding baudrate = %lu data_bits = %u parity = %u stop_bits = %u (indexes!)", - line_coding->bit_rate, line_coding->data_bits, line_coding->parity, line_coding->stop_bits); + TU_LOG_P_CDC("set line coding %lu %u%c%s", + line_coding->bit_rate, line_coding->data_bits, + CDC_LINE_CODING_PARITY_CHAR(line_coding->parity), + CDC_LINE_CODING_STOP_BITS_TEXT(line_coding->stop_bits)); cdch_serial_driver_t const * driver = &serial_drivers[p_cdc->serial_drid]; p_cdc->requested_line_coding = *line_coding; @@ -652,7 +665,7 @@ bool tuh_cdc_set_line_coding(uint8_t idx, cdc_line_coding_t const * line_coding, if (ret && !complete_cb) { p_cdc->line_coding = *line_coding; } - TU_LOG_RESULT("set line coding", ret); +// TU_LOG_P_CDC_BOOL("set line coding", ret); return ret; } @@ -792,7 +805,7 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const * itf_ if (driver_detected) { TU_LOG_CDC("open", daddr, itf_desc->bInterfaceNumber, driver_detected->name); bool ret = driver_detected->open(daddr, itf_desc, max_len); - TU_LOG_CDC("opened ret = %s", daddr, itf_desc->bInterfaceNumber, driver_detected->name, ret ? "true" : "FALSE" ); +// TU_LOG_CDC("opened ret = %s", daddr, itf_desc->bInterfaceNumber, driver_detected->name, ret ? "true" : "FALSE" ); return ret; } @@ -802,7 +815,7 @@ bool cdch_open(uint8_t rhport, uint8_t daddr, tusb_desc_interface_t const * itf_ static void set_config_complete(uint8_t idx, uint8_t itf_offset, bool success) { cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT(p_cdc,); - TU_LOG_RESULT("set config complete", success); + TU_LOG_P_CDC_BOOL("set config complete", success); if (success) { p_cdc->mounted = true; @@ -854,7 +867,7 @@ static void acm_internal_control_complete(tuh_xfer_t * xfer) { cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT(p_cdc,); bool const success = (xfer->result == XFER_RESULT_SUCCESS); - TU_LOG_RESULT(" control complete", success); + TU_LOG_P_CDC_BOOL("control complete", success); if (success) { switch (xfer->setup->bRequest) { @@ -1138,7 +1151,7 @@ static void ftdi_internal_control_complete(tuh_xfer_t * xfer) { cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT(p_cdc,); bool const success = (xfer->result == XFER_RESULT_SUCCESS); - TU_LOG_RESULT(" control complete", success); + TU_LOG_P_CDC_BOOL("control complete", success); if (success) { if (xfer->setup->bRequest == FTDI_SIO_SET_MODEM_CTRL_REQUEST && @@ -1414,7 +1427,7 @@ static bool ftdi_determine_type(cdch_interface_t * p_cdc) break; } - TU_LOG_P_CDC(" %s detected (bcdDevice = 0x%04x)", + TU_LOG_P_CDC("%s detected (bcdDevice = 0x%04x)", ftdi_chip_name[p_cdc->ftdi.chip_type], desc_dev->bcdDevice); return (p_cdc->ftdi.chip_type != UNKNOWN); @@ -1562,7 +1575,7 @@ static inline uint32_t ftdi_get_divisor(cdch_interface_t * p_cdc) break; } - TU_LOG_P_CDC(" Baudrate divisor 0x%lu", div_value); + TU_LOG_P_CDC("Baudrate divisor = 0x%lu", div_value); return div_value; } @@ -1671,7 +1684,7 @@ static void cp210x_internal_control_complete(tuh_xfer_t * xfer) { cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT(p_cdc,); bool const success = (xfer->result == XFER_RESULT_SUCCESS); - TU_LOG_RESULT(" control complete", success); + TU_LOG_P_CDC_BOOL("control complete", success); if (success) { switch(xfer->setup->bRequest) { @@ -1928,7 +1941,7 @@ static void ch34x_internal_control_complete(tuh_xfer_t * xfer) { cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT(p_cdc,); bool const success = (xfer->result == XFER_RESULT_SUCCESS); - TU_LOG_RESULT(" control complete", success); + TU_LOG_P_CDC_BOOL("control complete", success); if (success) { switch (xfer->setup->bRequest) { @@ -2059,7 +2072,7 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { case CONFIG_CH34X_SERIAL_INIT: { // handle version read data, set CH34x line coding (incl. baudrate) uint8_t const version = xfer->buffer[0]; - TU_LOG_P_CDC(" Chip Version = %02x", version); + TU_LOG_P_CDC("Chip Version = 0x%02x", version); // only versions >= 0x30 are tested, below 0x30 seems having other programming // see drivers from WCH vendor, Linux kernel and FreeBSD TU_ASSERT_COMPLETE(version >= 0x30); @@ -2346,7 +2359,7 @@ static void pl2303_internal_control_complete(tuh_xfer_t * xfer) { cdch_interface_t * p_cdc = get_itf(idx); TU_ASSERT(p_cdc,); bool const success = (xfer->result == XFER_RESULT_SUCCESS); - TU_LOG_RESULT(" control complete", success); + TU_LOG_P_CDC_BOOL("control complete", success); if (success) { if (xfer->setup->bRequest == PL2303_SET_LINE_REQUEST && @@ -2489,12 +2502,12 @@ static void pl2303_process_config(tuh_xfer_t * xfer) { p_cdc->pl2303.serial_private.type = &pl2303_type_data[type]; p_cdc->pl2303.serial_private.quirks |= p_cdc->pl2303.serial_private.type->quirks; #if CFG_TUSB_DEBUG >= CFG_TUH_CDC_LOG_LEVEL && 0 // can be activated if necessary - TU_LOG_P_CDC(" bDeviceClass = 0x%02x bMaxPacketSize0 = %u bcdUSB = 0x%04x bcdDevice = 0x%04x", + TU_LOG_P_CDC("bDeviceClass = 0x%02x bMaxPacketSize0 = %u bcdUSB = 0x%04x bcdDevice = 0x%04x", desc_dev->bDeviceClass, desc_dev->bMaxPacketSize0, desc_dev->bcdUSB, desc_dev->bcdDevice ); uint16_t vid, pid; TU_ASSERT_COMPLETE(tuh_vid_pid_get(p_cdc->daddr, &vid, &pid)); - TU_LOG_P_CDC(" vid = 0x%04x pid = 0x%04x supports_hx_status = %u type = %s quirks = %u", + TU_LOG_P_CDC("vid = 0x%04x pid = 0x%04x supports_hx_status = %u type = %s quirks = %u", vid, pid, p_cdc->pl2303.supports_hx_status, p_cdc->pl2303.serial_private.type->name, p_cdc->pl2303.serial_private.quirks); #endif @@ -2753,7 +2766,7 @@ static int8_t pl2303_detect_type(cdch_interface_t * p_cdc, uint8_t step, default: break; } - TU_LOG_P_CDC(" unknown device type bcdUSB = 0x%04x", desc_dev->bcdUSB); + TU_LOG_P_CDC("unknown device type bcdUSB = 0x%04x", desc_dev->bcdUSB); return PL2303_DETECT_TYPE_FAILED; } @@ -2903,7 +2916,7 @@ static bool pl2303_encode_baud_rate(cdch_interface_t * p_cdc, uint8_t buf[PL2303 } else { baud = pl2303_encode_baud_rate_divisor(buf, baud); } - TU_LOG_P_CDC(" real baudrate = %lu", baud); + TU_LOG_P_CDC("real baudrate %lu", baud); return true; } -- cgit v1.3.1 From e6d27b6d3e8fc928cc00ab08898243c7e037ba35 Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 29 Feb 2024 20:19:35 +0100 Subject: fixed IAR compile error --- src/class/cdc/cdc_host.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index d2bc63d64..dfe078de4 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -113,7 +113,6 @@ CFG_TUH_MEM_SECTION static cdch_interface_t cdch_data[CFG_TUH_CDC]; #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_PL2303 - CFG_TUH_MEM_SECTION CFG_TUH_MEM_ALIGN static tusb_desc_device_t desc_dev[CFG_TUH_ENUMERATION_BUFSIZE]; #endif -- cgit v1.3.1 From dea27d28bceeaaa23d3daa6468373cb536390108 Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 29 Feb 2024 20:24:13 +0100 Subject: added explicite (uint16_t) casts inside tu_htole16() --- src/class/cdc/cdc_host.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index dfe078de4..d71cda124 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -932,8 +932,8 @@ static bool acm_set_line_coding(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete }, .bRequest = CDC_REQUEST_SET_LINE_CODING, .wValue = 0, - .wIndex = tu_htole16(p_cdc->bInterfaceNumber), - .wLength = tu_htole16(sizeof(cdc_line_coding_t)) + .wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber), + .wLength = tu_htole16((uint16_t) sizeof(cdc_line_coding_t)) }; // use usbh enum buf to hold line coding since user line_coding variable does not live long enough @@ -1612,7 +1612,7 @@ static bool cp210x_set_request(cdch_interface_t * p_cdc, uint8_t command, uint16 }, .bRequest = command, .wValue = tu_htole16(value), - .wIndex = tu_htole16(p_cdc->bInterfaceNumber), + .wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber), .wLength = tu_htole16(length) }; @@ -1852,9 +1852,9 @@ static bool ch34x_set_request(cdch_interface_t * p_cdc, uint8_t direction, uint8 .direction = direction & 0x01u }, .bRequest = request, - .wValue = tu_htole16 (value), - .wIndex = tu_htole16 (index), - .wLength = tu_htole16 (length) + .wValue = tu_htole16(value), + .wIndex = tu_htole16(index), + .wLength = tu_htole16(length) }; // use usbh enum buf since application variable does not live long enough @@ -2232,9 +2232,9 @@ static bool pl2303_set_request(cdch_interface_t * p_cdc, uint8_t request, uint8_ tusb_control_request_t const request_setup = { .bmRequestType = requesttype, .bRequest = request, - .wValue = tu_htole16 (value), - .wIndex = tu_htole16 (index), - .wLength = tu_htole16 (length) + .wValue = tu_htole16(value), + .wIndex = tu_htole16(index), + .wLength = tu_htole16(length) }; // use usbh enum buf since application variable does not live long enough -- cgit v1.3.1 From e0551043ca8a6b3be92fed57f5e34d6d8014406b Mon Sep 17 00:00:00 2001 From: IngHK Date: Sun, 3 Mar 2024 13:02:58 +0100 Subject: added use of cdc_line_control_state_t type in CDCh --- src/class/cdc/cdc.h | 14 ++++++++------ src/class/cdc/cdc_device.c | 2 ++ src/class/cdc/cdc_host.c | 45 +++++++++++++++++++-------------------------- 3 files changed, 29 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc.h b/src/class/cdc/cdc.h index b1dca1ad8..10aed79ab 100644 --- a/src/class/cdc/cdc.h +++ b/src/class/cdc/cdc.h @@ -414,15 +414,17 @@ typedef struct TU_ATTR_PACKED TU_VERIFY_STATIC(sizeof(cdc_line_coding_t) == 7, "size is not correct"); -typedef struct TU_ATTR_PACKED +typedef union TU_ATTR_PACKED { - uint16_t dtr : 1; - uint16_t rts : 1; - uint16_t : 6; - uint16_t : 8; + struct { + uint8_t dtr : 1; + uint8_t rts : 1; + uint8_t : 6; + }; + uint8_t all; } cdc_line_control_state_t; -TU_VERIFY_STATIC(sizeof(cdc_line_control_state_t) == 2, "size is not correct"); +TU_VERIFY_STATIC(sizeof(cdc_line_control_state_t) == 1, "size is not correct"); TU_ATTR_PACKED_END // End of all packed definitions TU_ATTR_BIT_FIELD_ORDER_END diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index c26264e60..9f51a6d4b 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -56,6 +56,7 @@ typedef struct uint8_t ep_out; // Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send) + // TODO use cdc_line_control_state_t instead of uint8_t uint8_t line_state; /*------------- From this point, data is not cleared by bus reset -------------*/ @@ -124,6 +125,7 @@ bool tud_cdc_n_connected(uint8_t itf) } uint8_t tud_cdc_n_get_line_state (uint8_t itf) +// TODO use cdc_line_control_state_t instead of uint8_t { return _cdcd_itf[itf].line_state; } diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index d71cda124..a3407a5f9 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -81,8 +81,8 @@ typedef struct { TU_ATTR_ALIGNED(4) cdc_line_coding_t requested_line_coding; // 1 byte padding - uint8_t line_state; // DTR (bit0), RTS (bit1) - uint8_t requested_line_state; + cdc_line_control_state_t line_state; + cdc_line_control_state_t requested_line_state; tuh_xfer_cb_t user_control_cb; #if CFG_TUH_CDC_FTDI || CFG_TUH_CDC_CP210X || CFG_TUH_CDC_CH34X @@ -339,7 +339,7 @@ static cdch_interface_t * make_new_itf(uint8_t daddr, tusb_desc_interface_t cons p_cdc->bInterfaceSubClass = itf_desc->bInterfaceSubClass; p_cdc->bInterfaceProtocol = itf_desc->bInterfaceProtocol; p_cdc->line_coding = (cdc_line_coding_t) { 0, 0, 0, 0 }; - p_cdc->line_state = 0; + p_cdc->line_state.all = 0; return p_cdc; } } @@ -394,7 +394,7 @@ bool tuh_cdc_get_dtr(uint8_t idx) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); - bool ret = (p_cdc->line_state & CDC_CONTROL_LINE_STATE_DTR); + bool ret = p_cdc->line_state.dtr; // TU_LOG_P_CDC_BOOL("get DTR", ret); return ret; @@ -404,7 +404,7 @@ bool tuh_cdc_get_rts(uint8_t idx) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc); - bool ret = (p_cdc->line_state & CDC_CONTROL_LINE_STATE_RTS); + bool ret = p_cdc->line_state.rts; // TU_LOG_P_CDC_BOOL("get RTS", ret); return ret; @@ -592,12 +592,12 @@ bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_c TU_LOG_P_CDC("set control line state line_state = %u", line_state); cdch_serial_driver_t const * driver = &serial_drivers[p_cdc->serial_drid]; - p_cdc->requested_line_state = (uint8_t) line_state; + p_cdc->requested_line_state.all = (uint8_t) line_state; bool ret = set_function_call(p_cdc, driver->set_control_line_state, complete_cb, user_data); if (ret && !complete_cb) { - p_cdc->line_state = (uint8_t) line_state; + p_cdc->line_state.all = (uint8_t) line_state; } // TU_LOG_P_CDC_BOOL("set control line state", ret); @@ -898,7 +898,7 @@ static bool acm_set_control_line_state(cdch_interface_t * p_cdc, tuh_xfer_cb_t c .direction = TUSB_DIR_OUT }, .bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE, - .wValue = tu_htole16(p_cdc->requested_line_state), + .wValue = tu_htole16((uint16_t) p_cdc->requested_line_state.all), .wIndex = tu_htole16((uint16_t) p_cdc->bInterfaceNumber), .wLength = 0 }; @@ -1034,7 +1034,7 @@ static void acm_process_config(tuh_xfer_t * xfer) { case CONFIG_ACM_SET_CONTROL_LINE_STATE: #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM if (p_cdc->acm_capability.support_line_request) { - p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + p_cdc->requested_line_state.all = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; TU_ASSERT_COMPLETE(acm_set_control_line_state(p_cdc, acm_process_config, CONFIG_ACM_SET_LINE_CODING), 1); break; } @@ -1139,7 +1139,7 @@ static bool ftdi_set_data_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t comple static inline bool ftdi_update_mctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { // FTDI has the same bit coding return ftdi_set_request(p_cdc, FTDI_SIO_SET_MODEM_CTRL_REQUEST, FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, - p_cdc->requested_line_state, p_cdc->ftdi.channel, complete_cb, user_data); + p_cdc->requested_line_state.all, p_cdc->ftdi.channel, complete_cb, user_data); } //------------- Driver API -------------// @@ -1328,7 +1328,7 @@ static void ftdi_process_config(tuh_xfer_t * xfer) { case CONFIG_FTDI_MODEM_CTRL: #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + p_cdc->requested_line_state.all = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; TU_ASSERT_COMPLETE(ftdi_update_mctrl(p_cdc, ftdi_internal_control_complete, CONFIG_FTDI_COMPLETE)); break; #else @@ -1670,7 +1670,7 @@ static inline bool cp210x_set_mhs(cdch_interface_t * p_cdc, tuh_xfer_cb_t comple // CP210x has the same bit coding return cp210x_set_request(p_cdc, CP210X_SET_MHS, (uint16_t) ((uint32_t) CP210X_CONTROL_WRITE_DTR | - (uint32_t) CP210X_CONTROL_WRITE_RTS | p_cdc->requested_line_state), + (uint32_t) CP210X_CONTROL_WRITE_RTS | p_cdc->requested_line_state.all), NULL, 0, complete_cb, user_data); } @@ -1811,7 +1811,7 @@ static void cp210x_process_config(tuh_xfer_t * xfer) { case CONFIG_CP210X_SET_DTR_RTS: #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + p_cdc->requested_line_state.all = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; TU_ASSERT_COMPLETE(cp210x_set_mhs(p_cdc, cp210x_internal_control_complete, CONFIG_CP210X_COMPLETE)); break; @@ -1916,16 +1916,8 @@ static bool ch34x_write_reg_baudrate(cdch_interface_t * p_cdc, tuh_xfer_cb_t com } static bool ch34x_modem_ctrl_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - uint8_t control = 0; - if (p_cdc->requested_line_state & CDC_CONTROL_LINE_STATE_RTS) { - control |= CH34X_BIT_RTS; - } - if (p_cdc->requested_line_state & CDC_CONTROL_LINE_STATE_DTR) { - control |= CH34X_BIT_DTR; - } - - // CH34x signals are inverted - control = ~control; + uint8_t control = ~((p_cdc->requested_line_state.rts ? CH34X_BIT_RTS : 0) | // CH34x signals are inverted + (p_cdc->requested_line_state.dtr ? CH34X_BIT_DTR : 0)); return ch34x_control_out(p_cdc, CH34X_REQ_MODEM_CTRL, control, 0, complete_cb, user_data); } @@ -2101,7 +2093,7 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { case CONFIG_CH34X_MODEM_CONTROL: #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + p_cdc->requested_line_state.all = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; TU_ASSERT_COMPLETE(ch34x_modem_ctrl_request(p_cdc, ch34x_internal_control_complete, CONFIG_CH34X_COMPLETE)); break; @@ -2287,8 +2279,9 @@ static inline bool pl2303_supports_hx_status(cdch_interface_t * p_cdc, tuh_xfer_ static inline bool pl2303_set_control_lines(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + // PL2303 has the same bit coding return pl2303_set_request(p_cdc, PL2303_SET_CONTROL_REQUEST, PL2303_SET_CONTROL_REQUEST_TYPE, - p_cdc->requested_line_state, 0, NULL, 0, complete_cb, user_data); + p_cdc->requested_line_state.all, 0, NULL, 0, complete_cb, user_data); } //static bool pl2303_get_line_request(cdch_interface_t * p_cdc, uint8_t buf[PL2303_LINE_CODING_BUFSIZE]) @@ -2645,7 +2638,7 @@ static void pl2303_process_config(tuh_xfer_t * xfer) { case CONFIG_PL2303_MODEM_CONTROL: #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - p_cdc->requested_line_state = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + p_cdc->requested_line_state.all = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; TU_ASSERT_COMPLETE(pl2303_set_control_lines(p_cdc, pl2303_internal_control_complete, CONFIG_PL2303_FLOW_CTRL_READ)); break; #else -- cgit v1.3.1 From a9cc07fc83599fab4d9576e6f5b793bc1bf2bf35 Mon Sep 17 00:00:00 2001 From: IngHK Date: Sun, 10 Mar 2024 08:20:12 +0100 Subject: added line control function using cdc_line_control_state_t --- src/class/cdc/cdc_host.c | 35 ++++++++++++++++++++++++++++++----- src/class/cdc/cdc_host.h | 10 ++++++++-- 2 files changed, 38 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index a3407a5f9..86b874b1e 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -50,8 +50,9 @@ DADDR, ITF_NUM, NAME, ##__VA_ARGS__) #define TU_LOG_P_CDC(TXT,...) TU_LOG_CDC(TXT, p_cdc->daddr, p_cdc->bInterfaceNumber, \ serial_drivers[p_cdc->serial_drid].name, ##__VA_ARGS__) -#define TU_LOG_P_CDC_BOOL(TXT,VAL) TU_LOG_P_CDC(TXT " " #VAL " = %s", VAL ? "true" : "false" ) +#define TU_LOG_P_CDC_BOOL(TXT,VAL) TU_LOG_P_CDC(TXT " " #VAL " = %d", VAL) +// assert and set config complete #define TU_ASSERT_COMPLETE_DEFINE(_cond, _itf_offset) \ do { \ if (!(_cond)) { _MESS_FAILED(); TU_BREAKPOINT(); set_config_complete(idx, _itf_offset, false); } \ @@ -586,24 +587,48 @@ static bool set_function_call ( } } -bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { +bool tuh_cdc_set_control_line_state_u(uint8_t idx, cdc_line_control_state_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + // uses cdc_line_control_state_t union for line_state cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); - TU_LOG_P_CDC("set control line state line_state = %u", line_state); + TU_LOG_P_CDC("set control line state dtr = %u rts = %u", line_state.dtr, line_state.rts ); cdch_serial_driver_t const * driver = &serial_drivers[p_cdc->serial_drid]; - p_cdc->requested_line_state.all = (uint8_t) line_state; + p_cdc->requested_line_state = line_state; bool ret = set_function_call(p_cdc, driver->set_control_line_state, complete_cb, user_data); if (ret && !complete_cb) { - p_cdc->line_state.all = (uint8_t) line_state; + p_cdc->line_state = line_state; } // TU_LOG_P_CDC_BOOL("set control line state", ret); return ret; } +bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + // uses uint16_t for line_state => DTR (bit 0), RTS (bit 1) + + return tuh_cdc_set_control_line_state_u(idx, (cdc_line_control_state_t) { .all = (uint8_t) line_state }, + complete_cb, user_data); +} + +bool tuh_cdc_set_dtr(uint8_t idx, bool dtr_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + cdch_interface_t * p_cdc = get_itf(idx); + TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + cdc_line_control_state_t const line_state = { .dtr = dtr_state, .rts = p_cdc->line_state.rts }; + + return tuh_cdc_set_control_line_state_u(idx, line_state, complete_cb, user_data); +} + +bool tuh_cdc_set_rts(uint8_t idx, bool rts_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { + cdch_interface_t * p_cdc = get_itf(idx); + TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); + cdc_line_control_state_t const line_state = { .rts = rts_state, .dtr = p_cdc->line_state.dtr }; + + return tuh_cdc_set_control_line_state_u(idx, line_state, complete_cb, user_data); +} + bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { cdch_interface_t * p_cdc = get_itf(idx); TU_VERIFY(p_cdc && p_cdc->serial_drid < SERIAL_DRIVER_COUNT); diff --git a/src/class/cdc/cdc_host.h b/src/class/cdc/cdc_host.h index ca6567453..77081d3b0 100644 --- a/src/class/cdc/cdc_host.h +++ b/src/class/cdc/cdc_host.h @@ -132,8 +132,14 @@ bool tuh_cdc_read_clear (uint8_t idx); // - The function will return true if transfer is successful, false otherwise. //--------------------------------------------------------------------+ -// Request to Set Control Line State: DTR (bit 0), RTS (bit 1) -bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); +// Request to Set Control Line State +bool tuh_cdc_set_control_line_state_u(uint8_t idx, cdc_line_control_state_t line_state, // uses cdc_line_control_state_t union for line_state + tuh_xfer_cb_t complete_cb, uintptr_t user_data); +bool tuh_cdc_set_control_line_state(uint8_t idx, uint16_t line_state, // uses uint16_t for line_state (legacy function) + tuh_xfer_cb_t complete_cb, uintptr_t user_data); // DTR (bit 0), RTS (bit 1) + +bool tuh_cdc_set_dtr(uint8_t idx, bool dtr_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); // Request to Set DTR +bool tuh_cdc_set_rts(uint8_t idx, bool rts_state, tuh_xfer_cb_t complete_cb, uintptr_t user_data); // Request to Set RTS // Request to set baudrate bool tuh_cdc_set_baudrate(uint8_t idx, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data); -- cgit v1.3.1 From ee92e582b38e36df53381f8c98a0be54c2caef3e Mon Sep 17 00:00:00 2001 From: IngHK Date: Sun, 3 Mar 2024 13:12:10 +0100 Subject: added defines CFG_TUH_CDC_DTR_CONTROL_ON_ENUM & CFG_TUH_CDC_RTS_CONTROL_ON_ENUM --- examples/host/cdc_msc_hid/src/tusb_config.h | 4 +-- .../host/cdc_msc_hid_freertos/src/tusb_config.h | 4 +-- src/class/cdc/cdc_host.c | 40 ++++++++++++++++------ 3 files changed, 34 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/examples/host/cdc_msc_hid/src/tusb_config.h b/examples/host/cdc_msc_hid/src/tusb_config.h index fc956c6d3..32a29bd4f 100644 --- a/examples/host/cdc_msc_hid/src/tusb_config.h +++ b/examples/host/cdc_msc_hid/src/tusb_config.h @@ -121,8 +121,8 @@ //------------- CDC -------------// // Set Line Control state on enumeration/mounted: -// DTR ( bit 0), RTS (bit 1) -#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM 0x03 +#define CFG_TUH_CDC_DTR_CONTROL_ON_ENUM true +#define CFG_TUH_CDC_RTS_CONTROL_ON_ENUM true // Set Line Coding on enumeration/mounted, value for cdc_line_coding_t // bit rate = 115200, 1 stop bit, no parity, 8 bit data width diff --git a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h index dd732c700..88b341e95 100644 --- a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h +++ b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h @@ -126,8 +126,8 @@ //------------- CDC -------------// // Set Line Control state on enumeration/mounted: -// DTR ( bit 0), RTS (bit 1) -#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM 0x03 +#define CFG_TUH_CDC_DTR_CONTROL_ON_ENUM true +#define CFG_TUH_CDC_RTS_CONTROL_ON_ENUM true // Set Line Coding on enumeration/mounted, value for cdc_line_coding_t // bit rate = 115200, 1 stop bit, no parity, 8 bit data width diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 86b874b1e..5b74d031d 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -63,6 +63,26 @@ #define TU_ASSERT_COMPLETE(...) _GET_3RD_ARG(__VA_ARGS__, TU_ASSERT_COMPLETE_2ARGS, TU_ASSERT_COMPLETE_1ARGS, _dummy)(__VA_ARGS__) +// handle line control defines +#if defined(CFG_TUH_CDC_LINE_CONTROL_ON_ENUM) && \ + (defined(CFG_TUH_CDC_DTR_CONTROL_ON_ENUM) || defined(CFG_TUH_CDC_RTS_CONTROL_ON_ENUM)) + TU_VERIFY_STATIC(false, "Contradictory line control defines"); +#endif + +#ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + #define LINE_CONTROL_ON_ENUM CFG_TUH_CDC_LINE_CONTROL_ON_ENUM +#elif defined(CFG_TUH_CDC_DTR_CONTROL_ON_ENUM) || defined(CFG_TUH_CDC_RTS_CONTROL_ON_ENUM) + #ifndef CFG_TUH_CDC_DTR_CONTROL_ON_ENUM + #define CFG_TUH_CDC_DTR_CONTROL_ON_ENUM 0 + #endif + #ifndef CFG_TUH_CDC_RTS_CONTROL_ON_ENUM + #define CFG_TUH_CDC_RTS_CONTROL_ON_ENUM 0 + #endif + #define LINE_CONTROL_ON_ENUM ( ( CFG_TUH_CDC_DTR_CONTROL_ON_ENUM ? CDC_CONTROL_LINE_STATE_DTR : 0 ) | \ + ( CFG_TUH_CDC_RTS_CONTROL_ON_ENUM ? CDC_CONTROL_LINE_STATE_RTS : 0 ) ) +#endif + + //--------------------------------------------------------------------+ // Host CDC Interface //--------------------------------------------------------------------+ @@ -1057,9 +1077,9 @@ static void acm_process_config(tuh_xfer_t * xfer) { switch (state) { case CONFIG_ACM_SET_CONTROL_LINE_STATE: - #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM + #ifdef LINE_CONTROL_ON_ENUM if (p_cdc->acm_capability.support_line_request) { - p_cdc->requested_line_state.all = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + p_cdc->requested_line_state.all = LINE_CONTROL_ON_ENUM; TU_ASSERT_COMPLETE(acm_set_control_line_state(p_cdc, acm_process_config, CONFIG_ACM_SET_LINE_CODING), 1); break; } @@ -1352,8 +1372,8 @@ static void ftdi_process_config(tuh_xfer_t * xfer) { break; case CONFIG_FTDI_MODEM_CTRL: - #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - p_cdc->requested_line_state.all = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + #ifdef LINE_CONTROL_ON_ENUM + p_cdc->requested_line_state.all = LINE_CONTROL_ON_ENUM; TU_ASSERT_COMPLETE(ftdi_update_mctrl(p_cdc, ftdi_internal_control_complete, CONFIG_FTDI_COMPLETE)); break; #else @@ -1835,8 +1855,8 @@ static void cp210x_process_config(tuh_xfer_t * xfer) { #endif case CONFIG_CP210X_SET_DTR_RTS: - #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - p_cdc->requested_line_state.all = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + #ifdef LINE_CONTROL_ON_ENUM + p_cdc->requested_line_state.all = LINE_CONTROL_ON_ENUM; TU_ASSERT_COMPLETE(cp210x_set_mhs(p_cdc, cp210x_internal_control_complete, CONFIG_CP210X_COMPLETE)); break; @@ -2117,8 +2137,8 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { break; case CONFIG_CH34X_MODEM_CONTROL: - #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - p_cdc->requested_line_state.all = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + #ifdef LINE_CONTROL_ON_ENUM + p_cdc->requested_line_state.all = LINE_CONTROL_ON_ENUM; TU_ASSERT_COMPLETE(ch34x_modem_ctrl_request(p_cdc, ch34x_internal_control_complete, CONFIG_CH34X_COMPLETE)); break; @@ -2662,8 +2682,8 @@ static void pl2303_process_config(tuh_xfer_t * xfer) { #endif case CONFIG_PL2303_MODEM_CONTROL: - #ifdef CFG_TUH_CDC_LINE_CONTROL_ON_ENUM - p_cdc->requested_line_state.all = CFG_TUH_CDC_LINE_CONTROL_ON_ENUM; + #ifdef LINE_CONTROL_ON_ENUM + p_cdc->requested_line_state.all = LINE_CONTROL_ON_ENUM; TU_ASSERT_COMPLETE(pl2303_set_control_lines(p_cdc, pl2303_internal_control_complete, CONFIG_PL2303_FLOW_CTRL_READ)); break; #else -- cgit v1.3.1 From 2786a61e8b9811b1065cf1f36502eb4744e9d772 Mon Sep 17 00:00:00 2001 From: IngHK Date: Sun, 3 Mar 2024 13:25:04 +0100 Subject: fixed FTDI set control line --- src/class/cdc/cdc_host.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 5b74d031d..6121e59f0 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -82,7 +82,6 @@ ( CFG_TUH_CDC_RTS_CONTROL_ON_ENUM ? CDC_CONTROL_LINE_STATE_RTS : 0 ) ) #endif - //--------------------------------------------------------------------+ // Host CDC Interface //--------------------------------------------------------------------+ @@ -1172,19 +1171,21 @@ static bool ftdi_change_speed(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_c static bool ftdi_set_data_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { TU_VERIFY(p_cdc->requested_line_coding.data_bits >= 7 && p_cdc->requested_line_coding.data_bits <= 8, 0); uint16_t value = (uint16_t) ( - ((uint32_t) p_cdc->requested_line_coding.data_bits & 0xf) | // data bit quantity is stored in bits 0-3 - ((uint32_t) p_cdc->requested_line_coding.parity & 0x7) << 8 | // parity is stored in bits 8-10, same coding - ((uint32_t) p_cdc->requested_line_coding.stop_bits & 0x3) << 11 ); // stop bits quantity is stored in bits 11-12, same coding - // not each FTDI supports 1.5 stop bits + ((uint32_t) p_cdc->requested_line_coding.data_bits & 0xfu) | // data bit quantity is stored in bits 0-3 + ((uint32_t) p_cdc->requested_line_coding.parity & 0x7u) << 8 | // parity is stored in bits 8-10, same coding + ((uint32_t) p_cdc->requested_line_coding.stop_bits & 0x3u) << 11 ); // stop bits quantity is stored in bits 11-12, same coding + // not each FTDI supports 1.5 stop bits return ftdi_set_request(p_cdc, FTDI_SIO_SET_DATA_REQUEST, FTDI_SIO_SET_DATA_REQUEST_TYPE, value, p_cdc->ftdi.channel, complete_cb, user_data); } static inline bool ftdi_update_mctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - // FTDI has the same bit coding + uint16_t value = (uint16_t) ((p_cdc->requested_line_state.dtr ? (uint32_t) FTDI_SIO_SET_DTR_HIGH : (uint32_t) FTDI_SIO_SET_DTR_LOW) | + (p_cdc->requested_line_state.rts ? (uint32_t) FTDI_SIO_SET_RTS_HIGH : (uint32_t) FTDI_SIO_SET_RTS_LOW)); + return ftdi_set_request(p_cdc, FTDI_SIO_SET_MODEM_CTRL_REQUEST, FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, - p_cdc->requested_line_state.all, p_cdc->ftdi.channel, complete_cb, user_data); + value, p_cdc->ftdi.channel, complete_cb, user_data); } //------------- Driver API -------------// @@ -1704,9 +1705,9 @@ static bool cp210x_set_baudrate_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t static bool cp210x_set_line_ctl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { TU_VERIFY(p_cdc->requested_line_coding.data_bits >= 5 && p_cdc->requested_line_coding.data_bits <= 9, 0); uint16_t lcr = (uint16_t) ( - ((uint32_t) p_cdc->requested_line_coding.data_bits & 0xf) << 8 | // data bit quantity is stored in bits 8-11 - ((uint32_t) p_cdc->requested_line_coding.parity & 0xf) << 4 | // parity is stored in bits 4-7, same coding - ((uint32_t) p_cdc->requested_line_coding.stop_bits & 0xf)); // parity is stored in bits 0-3, same coding + ((uint32_t) p_cdc->requested_line_coding.data_bits & 0xfu) << 8 | // data bit quantity is stored in bits 8-11 + ((uint32_t) p_cdc->requested_line_coding.parity & 0xfu) << 4 | // parity is stored in bits 4-7, same coding + ((uint32_t) p_cdc->requested_line_coding.stop_bits & 0xfu)); // parity is stored in bits 0-3, same coding return cp210x_set_request(p_cdc, CP210X_SET_LINE_CTL, lcr, NULL, 0, complete_cb, user_data); } -- cgit v1.3.1 From cb69ed0d0423b8e09ae77307fcc01165ae4c9386 Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 21 Mar 2024 08:29:28 +0100 Subject: code style and clean up CDC serial header files --- src/class/cdc/serial/ch34x.h | 70 ++++++++-------- src/class/cdc/serial/cp210x.h | 86 ++++++++++---------- src/class/cdc/serial/ftdi_sio.h | 172 ++++++++++++++++++++-------------------- src/class/cdc/serial/pl2303.h | 146 +++++++++++++++++----------------- 4 files changed, 237 insertions(+), 237 deletions(-) (limited to 'src') diff --git a/src/class/cdc/serial/ch34x.h b/src/class/cdc/serial/ch34x.h index c18066f57..7d91f01fe 100644 --- a/src/class/cdc/serial/ch34x.h +++ b/src/class/cdc/serial/ch34x.h @@ -24,8 +24,8 @@ * This file is part of the TinyUSB stack. */ -#ifndef _CH34X_H_ -#define _CH34X_H_ +#ifndef TUSB_CH34X_H +#define TUSB_CH34X_H // There is no official documentation for the CH34x (CH340, CH341) chips. Reference can be found // - https://github.com/WCHSoftGroup/ch341ser_linux @@ -40,45 +40,45 @@ #endif // USB requests -#define CH34X_REQ_READ_VERSION 0x5F // dec 95 -#define CH34X_REQ_WRITE_REG 0x9A // dec 154 -#define CH34X_REQ_READ_REG 0x95 // dec 149 -#define CH34X_REQ_SERIAL_INIT 0xA1 // dec 161 -#define CH34X_REQ_MODEM_CTRL 0xA4 // dev 164 +#define CH34X_REQ_READ_VERSION 0x5F // dec 95 +#define CH34X_REQ_WRITE_REG 0x9A // dec 154 +#define CH34X_REQ_READ_REG 0x95 // dec 149 +#define CH34X_REQ_SERIAL_INIT 0xA1 // dec 161 +#define CH34X_REQ_MODEM_CTRL 0xA4 // dev 164 // registers -#define CH34X_REG_BREAK 0x05 -#define CH34X_REG_PRESCALER 0x12 -#define CH34X_REG_DIVISOR 0x13 -#define CH34X_REG_LCR 0x18 -#define CH34X_REG_LCR2 0x25 -#define CH34X_REG_MCR_MSR 0x06 -#define CH34X_REG_MCR_MSR2 0x07 -#define CH34X_NBREAK_BITS 0x01 +#define CH34X_REG_BREAK 0x05 +#define CH34X_REG_PRESCALER 0x12 +#define CH34X_REG_DIVISOR 0x13 +#define CH34X_REG_LCR 0x18 +#define CH34X_REG_LCR2 0x25 +#define CH34X_REG_MCR_MSR 0x06 +#define CH34X_REG_MCR_MSR2 0x07 +#define CH34X_NBREAK_BITS 0x01 -#define CH341_REG_0x0F 0x0F // undocumented register -#define CH341_REG_0x2C 0x2C // undocumented register -#define CH341_REG_0x27 0x27 // hardware flow control (cts/rts) +#define CH341_REG_0x0F 0x0F // undocumented register +#define CH341_REG_0x2C 0x2C // undocumented register +#define CH341_REG_0x27 0x27 // hardware flow control (cts/rts) -#define CH34X_REG16_DIVISOR_PRESCALER TU_U16(CH34X_REG_DIVISOR, CH34X_REG_PRESCALER) -#define CH32X_REG16_LCR2_LCR TU_U16(CH34X_REG_LCR2, CH34X_REG_LCR) +#define CH34X_REG16_DIVISOR_PRESCALER TU_U16(CH34X_REG_DIVISOR, CH34X_REG_PRESCALER) +#define CH32X_REG16_LCR2_LCR TU_U16(CH34X_REG_LCR2, CH34X_REG_LCR) // modem control bits -#define CH34X_BIT_RTS ( 1 << 6 ) -#define CH34X_BIT_DTR ( 1 << 5 ) +#define CH34X_BIT_RTS (1 << 6) +#define CH34X_BIT_DTR (1 << 5) // line control bits -#define CH34X_LCR_ENABLE_RX 0x80 -#define CH34X_LCR_ENABLE_TX 0x40 -#define CH34X_LCR_MARK_SPACE 0x20 -#define CH34X_LCR_PAR_EVEN 0x10 -#define CH34X_LCR_ENABLE_PAR 0x08 -#define CH34X_LCR_PAR_MASK 0x38 // all parity bits -#define CH34X_LCR_STOP_BITS_2 0x04 -#define CH34X_LCR_CS8 0x03 -#define CH34X_LCR_CS7 0x02 -#define CH34X_LCR_CS6 0x01 -#define CH34X_LCR_CS5 0x00 -#define CH34X_LCR_CS_MASK 0x03 // all CSx bits +#define CH34X_LCR_ENABLE_RX 0x80 +#define CH34X_LCR_ENABLE_TX 0x40 +#define CH34X_LCR_MARK_SPACE 0x20 +#define CH34X_LCR_PAR_EVEN 0x10 +#define CH34X_LCR_ENABLE_PAR 0x08 +#define CH34X_LCR_PAR_MASK 0x38 // all parity bits +#define CH34X_LCR_STOP_BITS_2 0x04 +#define CH34X_LCR_CS8 0x03 +#define CH34X_LCR_CS7 0x02 +#define CH34X_LCR_CS6 0x01 +#define CH34X_LCR_CS5 0x00 +#define CH34X_LCR_CS_MASK 0x03 // all CSx bits -#endif /* _CH34X_H_ */ +#endif // TUSB_CH34X_H diff --git a/src/class/cdc/serial/cp210x.h b/src/class/cdc/serial/cp210x.h index e18da7d51..a553a54da 100644 --- a/src/class/cdc/serial/cp210x.h +++ b/src/class/cdc/serial/cp210x.h @@ -31,7 +31,7 @@ // parts are overtaken from vendors driver // https://www.silabs.com/documents/public/software/cp210x-3.1.0.tar.gz -/* Config request codes */ +// Config request codes #define CP210X_IFC_ENABLE 0x00 #define CP210X_SET_BAUDDIV 0x01 #define CP210X_GET_BAUDDIV 0x02 @@ -60,56 +60,56 @@ #define CP210X_SET_BAUDRATE 0x1E #define CP210X_VENDOR_SPECIFIC 0xFF // GPIO, Recipient must be Device -/* SILABSER_IFC_ENABLE_REQUEST_CODE */ -#define CP210X_UART_ENABLE 0x0001 -#define CP210X_UART_DISABLE 0x0000 +// SILABSER_IFC_ENABLE_REQUEST_CODE +#define CP210X_UART_ENABLE 0x0001 +#define CP210X_UART_DISABLE 0x0000 -/* SILABSER_SET_BAUDDIV_REQUEST_CODE */ -#define CP210X_BAUD_RATE_GEN_FREQ 0x384000 +// SILABSER_SET_BAUDDIV_REQUEST_CODE +#define CP210X_BAUD_RATE_GEN_FREQ 0x384000 -/*SILABSER_SET_LINE_CTL_REQUEST_CODE */ -#define CP210X_BITS_DATA_MASK 0x0f00 -#define CP210X_BITS_DATA_5 0x0500 -#define CP210X_BITS_DATA_6 0x0600 -#define CP210X_BITS_DATA_7 0x0700 -#define CP210X_BITS_DATA_8 0x0800 -#define CP210X_BITS_DATA_9 0x0900 +// SILABSER_SET_LINE_CTL_REQUEST_CODE +#define CP210X_BITS_DATA_MASK 0x0f00 +#define CP210X_BITS_DATA_5 0x0500 +#define CP210X_BITS_DATA_6 0x0600 +#define CP210X_BITS_DATA_7 0x0700 +#define CP210X_BITS_DATA_8 0x0800 +#define CP210X_BITS_DATA_9 0x0900 -#define CP210X_BITS_PARITY_MASK 0x00f0 -#define CP210X_BITS_PARITY_NONE 0x0000 -#define CP210X_BITS_PARITY_ODD 0x0010 -#define CP210X_BITS_PARITY_EVEN 0x0020 -#define CP210X_BITS_PARITY_MARK 0x0030 -#define CP210X_BITS_PARITY_SPACE 0x0040 +#define CP210X_BITS_PARITY_MASK 0x00f0 +#define CP210X_BITS_PARITY_NONE 0x0000 +#define CP210X_BITS_PARITY_ODD 0x0010 +#define CP210X_BITS_PARITY_EVEN 0x0020 +#define CP210X_BITS_PARITY_MARK 0x0030 +#define CP210X_BITS_PARITY_SPACE 0x0040 -#define CP210X_BITS_STOP_MASK 0x000f -#define CP210X_BITS_STOP_1 0x0000 -#define CP210X_BITS_STOP_1_5 0x0001 -#define CP210X_BITS_STOP_2 0x0002 +#define CP210X_BITS_STOP_MASK 0x000f +#define CP210X_BITS_STOP_1 0x0000 +#define CP210X_BITS_STOP_1_5 0x0001 +#define CP210X_BITS_STOP_2 0x0002 -/* SILABSER_SET_BREAK_REQUEST_CODE */ -#define CP210X_BREAK_ON 0x0001 -#define CP210X_BREAK_OFF 0x0000 +// SILABSER_SET_BREAK_REQUEST_CODE +#define CP210X_BREAK_ON 0x0001 +#define CP210X_BREAK_OFF 0x0000 -/* SILABSER_SET_MHS_REQUEST_CODE */ -#define CP210X_MCR_DTR 0x0001 -#define CP210X_MCR_RTS 0x0002 -#define CP210X_MCR_ALL 0x0003 -#define CP210X_MSR_CTS 0x0010 -#define CP210X_MSR_DSR 0x0020 -#define CP210X_MSR_RING 0x0040 -#define CP210X_MSR_DCD 0x0080 -#define CP210X_MSR_ALL 0x00F0 +// SILABSER_SET_MHS_REQUEST_CODE +#define CP210X_MCR_DTR 0x0001 +#define CP210X_MCR_RTS 0x0002 +#define CP210X_MCR_ALL 0x0003 +#define CP210X_MSR_CTS 0x0010 +#define CP210X_MSR_DSR 0x0020 +#define CP210X_MSR_RING 0x0040 +#define CP210X_MSR_DCD 0x0080 +#define CP210X_MSR_ALL 0x00F0 -#define CP210X_CONTROL_WRITE_DTR 0x0100 -#define CP210X_CONTROL_WRITE_RTS 0x0200 +#define CP210X_CONTROL_WRITE_DTR 0x0100 +#define CP210X_CONTROL_WRITE_RTS 0x0200 -#define CP210X_LSR_BREAK 0x0001 -#define CP210X_LSR_FRAMING_ERROR 0x0002 -#define CP210X_LSR_HW_OVERRUN 0x0004 -#define CP210X_LSR_QUEUE_OVERRUN 0x0008 -#define CP210X_LSR_PARITY_ERROR 0x0010 -#define CP210X_LSR_ALL 0x001F +#define CP210X_LSR_BREAK 0x0001 +#define CP210X_LSR_FRAMING_ERROR 0x0002 +#define CP210X_LSR_HW_OVERRUN 0x0004 +#define CP210X_LSR_QUEUE_OVERRUN 0x0008 +#define CP210X_LSR_PARITY_ERROR 0x0010 +#define CP210X_LSR_ALL 0x001F // supported baudrates // reference: datasheets and AN205 "CP210x Baud Rate Support" diff --git a/src/class/cdc/serial/ftdi_sio.h b/src/class/cdc/serial/ftdi_sio.h index 42716f73e..4afedec9b 100644 --- a/src/class/cdc/serial/ftdi_sio.h +++ b/src/class/cdc/serial/ftdi_sio.h @@ -28,43 +28,43 @@ #include // Commands -#define FTDI_SIO_RESET 0 // Reset the port -#define FTDI_SIO_MODEM_CTRL 1 // Set the modem control register -#define FTDI_SIO_SET_FLOW_CTRL 2 // Set flow control register -#define FTDI_SIO_SET_BAUD_RATE 3 // Set baud rate -#define FTDI_SIO_SET_DATA 4 // Set the data characteristics of the port -#define FTDI_SIO_GET_MODEM_STATUS 5 // Retrieve current value of modem status register -#define FTDI_SIO_SET_EVENT_CHAR 6 // Set the event character -#define FTDI_SIO_SET_ERROR_CHAR 7 // Set the error character -#define FTDI_SIO_SET_LATENCY_TIMER 9 // Set the latency timer -#define FTDI_SIO_GET_LATENCY_TIMER 10 // Get the latency timer -#define FTDI_SIO_SET_BITMODE 11 // Set bitbang mode -#define FTDI_SIO_READ_PINS 12 // Read immediate value of pins -#define FTDI_SIO_READ_EEPROM 0x90 // Read EEPROM +#define FTDI_SIO_RESET 0 // Reset the port +#define FTDI_SIO_MODEM_CTRL 1 // Set the modem control register +#define FTDI_SIO_SET_FLOW_CTRL 2 // Set flow control register +#define FTDI_SIO_SET_BAUD_RATE 3 // Set baud rate +#define FTDI_SIO_SET_DATA 4 // Set the data characteristics of the port +#define FTDI_SIO_GET_MODEM_STATUS 5 // Retrieve current value of modem status register +#define FTDI_SIO_SET_EVENT_CHAR 6 // Set the event character +#define FTDI_SIO_SET_ERROR_CHAR 7 // Set the error character +#define FTDI_SIO_SET_LATENCY_TIMER 9 // Set the latency timer +#define FTDI_SIO_GET_LATENCY_TIMER 10 // Get the latency timer +#define FTDI_SIO_SET_BITMODE 11 // Set bitbang mode +#define FTDI_SIO_READ_PINS 12 // Read immediate value of pins +#define FTDI_SIO_READ_EEPROM 0x90 // Read EEPROM // Channel indices for FT2232, FT2232H and FT4232H devices -#define CHANNEL_A 1 -#define CHANNEL_B 2 -#define CHANNEL_C 3 -#define CHANNEL_D 4 +#define CHANNEL_A 1 +#define CHANNEL_B 2 +#define CHANNEL_C 3 +#define CHANNEL_D 4 // Port Identifier Table -#define PIT_DEFAULT 0 // SIOA -#define PIT_SIOA 1 // SIOA +#define PIT_DEFAULT 0 // SIOA +#define PIT_SIOA 1 // SIOA // The device this driver is tested with one has only one port -#define PIT_SIOB 2 // SIOB -#define PIT_PARALLEL 3 // Parallel +#define PIT_SIOB 2 // SIOB +#define PIT_PARALLEL 3 // Parallel // FTDI_SIO_RESET -#define FTDI_SIO_RESET_REQUEST FTDI_SIO_RESET -#define FTDI_SIO_RESET_REQUEST_TYPE 0x40 -#define FTDI_SIO_RESET_SIO 0 -#define FTDI_SIO_RESET_PURGE_RX 1 -#define FTDI_SIO_RESET_PURGE_TX 2 +#define FTDI_SIO_RESET_REQUEST FTDI_SIO_RESET +#define FTDI_SIO_RESET_REQUEST_TYPE 0x40 +#define FTDI_SIO_RESET_SIO 0 +#define FTDI_SIO_RESET_PURGE_RX 1 +#define FTDI_SIO_RESET_PURGE_TX 2 // FTDI_SIO_SET_BAUDRATE -#define FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE 0x40 -#define FTDI_SIO_SET_BAUDRATE_REQUEST 3 +#define FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_BAUDRATE_REQUEST 3 enum ftdi_sio_baudrate { ftdi_sio_b300 = 0, @@ -80,89 +80,89 @@ enum ftdi_sio_baudrate { }; // FTDI_SIO_SET_DATA -#define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA -#define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40 -#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8) -#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8) -#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8) -#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8) -#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8) -#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11) -#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11) -#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11) -#define FTDI_SIO_SET_BREAK (0x1 << 14) +#define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA +#define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8) +#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8) +#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8) +#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8) +#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8) +#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11) // same coding as ACM +#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11) // 1.5 not supported, for future use? +#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11) +#define FTDI_SIO_SET_BREAK (0x1 << 14) // FTDI_SIO_MODEM_CTRL -#define FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE 0x40 -#define FTDI_SIO_SET_MODEM_CTRL_REQUEST FTDI_SIO_MODEM_CTRL +#define FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_MODEM_CTRL_REQUEST FTDI_SIO_MODEM_CTRL -#define FTDI_SIO_SET_DTR_MASK 0x1 -#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1) -#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0) -#define FTDI_SIO_SET_RTS_MASK 0x2 -#define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2) -#define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0) +#define FTDI_SIO_SET_DTR_MASK 0x1 +#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1) +#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0) +#define FTDI_SIO_SET_RTS_MASK 0x2 +#define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2) +#define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0) // FTDI_SIO_SET_FLOW_CTRL -#define FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE 0x40 -#define FTDI_SIO_SET_FLOW_CTRL_REQUEST FTDI_SIO_SET_FLOW_CTRL +#define FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_FLOW_CTRL_REQUEST FTDI_SIO_SET_FLOW_CTRL #define FTDI_SIO_DISABLE_FLOW_CTRL 0x0 -#define FTDI_SIO_RTS_CTS_HS (0x1 << 8) -#define FTDI_SIO_DTR_DSR_HS (0x2 << 8) -#define FTDI_SIO_XON_XOFF_HS (0x4 << 8) +#define FTDI_SIO_RTS_CTS_HS (0x1 << 8) +#define FTDI_SIO_DTR_DSR_HS (0x2 << 8) +#define FTDI_SIO_XON_XOFF_HS (0x4 << 8) // FTDI_SIO_GET_LATENCY_TIMER -#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST FTDI_SIO_GET_LATENCY_TIMER -#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE 0xC0 +#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST FTDI_SIO_GET_LATENCY_TIMER +#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE 0xC0 // FTDI_SIO_SET_LATENCY_TIMER -#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER -#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER +#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40 // FTDI_SIO_SET_EVENT_CHAR -#define FTDI_SIO_SET_EVENT_CHAR_REQUEST FTDI_SIO_SET_EVENT_CHAR -#define FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_EVENT_CHAR_REQUEST FTDI_SIO_SET_EVENT_CHAR +#define FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE 0x40 // FTDI_SIO_GET_MODEM_STATUS -#define FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE 0xc0 -#define FTDI_SIO_GET_MODEM_STATUS_REQUEST FTDI_SIO_GET_MODEM_STATUS -#define FTDI_SIO_CTS_MASK 0x10 -#define FTDI_SIO_DSR_MASK 0x20 -#define FTDI_SIO_RI_MASK 0x40 -#define FTDI_SIO_RLSD_MASK 0x80 +#define FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE 0xc0 +#define FTDI_SIO_GET_MODEM_STATUS_REQUEST FTDI_SIO_GET_MODEM_STATUS +#define FTDI_SIO_CTS_MASK 0x10 +#define FTDI_SIO_DSR_MASK 0x20 +#define FTDI_SIO_RI_MASK 0x40 +#define FTDI_SIO_RLSD_MASK 0x80 // FTDI_SIO_SET_BITMODE -#define FTDI_SIO_SET_BITMODE_REQUEST_TYPE 0x40 -#define FTDI_SIO_SET_BITMODE_REQUEST FTDI_SIO_SET_BITMODE +#define FTDI_SIO_SET_BITMODE_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_BITMODE_REQUEST FTDI_SIO_SET_BITMODE // Possible bitmodes for FTDI_SIO_SET_BITMODE_REQUEST -#define FTDI_SIO_BITMODE_RESET 0x00 -#define FTDI_SIO_BITMODE_CBUS 0x20 +#define FTDI_SIO_BITMODE_RESET 0x00 +#define FTDI_SIO_BITMODE_CBUS 0x20 // FTDI_SIO_READ_PINS -#define FTDI_SIO_READ_PINS_REQUEST_TYPE 0xc0 -#define FTDI_SIO_READ_PINS_REQUEST FTDI_SIO_READ_PINS +#define FTDI_SIO_READ_PINS_REQUEST_TYPE 0xc0 +#define FTDI_SIO_READ_PINS_REQUEST FTDI_SIO_READ_PINS // FTDI_SIO_READ_EEPROM -#define FTDI_SIO_READ_EEPROM_REQUEST_TYPE 0xc0 -#define FTDI_SIO_READ_EEPROM_REQUEST FTDI_SIO_READ_EEPROM +#define FTDI_SIO_READ_EEPROM_REQUEST_TYPE 0xc0 +#define FTDI_SIO_READ_EEPROM_REQUEST FTDI_SIO_READ_EEPROM #define FTDI_FTX_CBUS_MUX_GPIO 0x8 #define FTDI_FT232R_CBUS_MUX_GPIO 0xa #define FTDI_RS0_CTS (1 << 4) #define FTDI_RS0_DSR (1 << 5) -#define FTDI_RS0_RI (1 << 6) +#define FTDI_RS0_RI (1 << 6) #define FTDI_RS0_RLSD (1 << 7) -#define FTDI_RS_DR 1 -#define FTDI_RS_OE (1<<1) -#define FTDI_RS_PE (1<<2) -#define FTDI_RS_FE (1<<3) -#define FTDI_RS_BI (1<<4) -#define FTDI_RS_THRE (1<<5) -#define FTDI_RS_TEMT (1<<6) -#define FTDI_RS_FIFO (1<<7) +#define FTDI_RS_DR 1 +#define FTDI_RS_OE (1 << 1) +#define FTDI_RS_PE (1 << 2) +#define FTDI_RS_FE (1 << 3) +#define FTDI_RS_BI (1 << 4) +#define FTDI_RS_THRE (1 << 5) +#define FTDI_RS_TEMT (1 << 6) +#define FTDI_RS_FIFO (1 << 7) // chip types and names enum ftdi_chip_type { @@ -206,14 +206,14 @@ enum ftdi_chip_type { // private interface data typedef struct ftdi_private { - enum ftdi_chip_type chip_type; - uint8_t channel; // channel index, or 0 for legacy types + enum ftdi_chip_type chip_type; + uint8_t channel; // channel index, or 0 for legacy types } ftdi_private_t; -#define FTDI_OK true -#define FTDI_FAIL false +#define FTDI_OK true +#define FTDI_FAIL false #define FTDI_NOT_POSSIBLE -1 -#define FTDI_REQUESTED -2 +#define FTDI_REQUESTED -2 // division and round function overtaken from math.h #define DIV_ROUND_CLOSEST(x, divisor)( \ diff --git a/src/class/cdc/serial/pl2303.h b/src/class/cdc/serial/pl2303.h index bf264191b..d69bdbfae 100644 --- a/src/class/cdc/serial/pl2303.h +++ b/src/class/cdc/serial/pl2303.h @@ -24,8 +24,8 @@ * This file is part of the TinyUSB stack. */ -#ifndef _PL2303_H_ -#define _PL2303_H_ +#ifndef TUSB_PL2303_H +#define TUSB_PL2303_H #include #include @@ -36,66 +36,66 @@ // https://github.com/torvalds/linux/blob/master/drivers/usb/serial/pl2303.c // - https://github.com/freebsd/freebsd-src/blob/main/sys/dev/usb/serial/uplcom.c -/* quirks */ -#define PL2303_QUIRK_UART_STATE_IDX0 1 -#define PL2303_QUIRK_LEGACY 2 -#define PL2303_QUIRK_ENDPOINT_HACK 4 - -/* requests and bits */ -#define PL2303_SET_LINE_REQUEST_TYPE 0x21 // class request host to device interface -#define PL2303_SET_LINE_REQUEST 0x20 // dec 32 - -#define PL2303_SET_CONTROL_REQUEST_TYPE 0x21 // class request host to device interface -#define PL2303_SET_CONTROL_REQUEST 0x22 // dec 34 -#define PL2303_CONTROL_DTR 0x01 // dec 1 -#define PL2303_CONTROL_RTS 0x02 // dec 2 - -#define PL2303_BREAK_REQUEST_TYPE 0x21 // class request host to device interface -#define PL2303_BREAK_REQUEST 0x23 // dec 35 -#define PL2303_BREAK_ON 0xffff -#define PL2303_BREAK_OFF 0x0000 - -#define PL2303_GET_LINE_REQUEST_TYPE 0xa1 // class request device to host interface -#define PL2303_GET_LINE_REQUEST 0x21 // dec 33 - -#define PL2303_VENDOR_WRITE_REQUEST_TYPE 0x40 // vendor request host to device interface -#define PL2303_VENDOR_WRITE_REQUEST 0x01 // dec 1 -#define PL2303_VENDOR_WRITE_NREQUEST 0x80 // dec 128 - -#define PL2303_VENDOR_READ_REQUEST_TYPE 0xc0 // vendor request device to host interface -#define PL2303_VENDOR_READ_REQUEST 0x01 // dec 1 -#define PL2303_VENDOR_READ_NREQUEST 0x81 // dec 129 - -#define PL2303_UART_STATE_INDEX 8 -#define PL2303_UART_STATE_MSR_MASK 0x8b -#define PL2303_UART_STATE_TRANSIENT_MASK 0x74 -#define PL2303_UART_DCD 0x01 -#define PL2303_UART_DSR 0x02 -#define PL2303_UART_BREAK_ERROR 0x04 -#define PL2303_UART_RING 0x08 -#define PL2303_UART_FRAME_ERROR 0x10 -#define PL2303_UART_PARITY_ERROR 0x20 -#define PL2303_UART_OVERRUN_ERROR 0x40 -#define PL2303_UART_CTS 0x80 - -#define PL2303_FLOWCTRL_MASK 0xf0 - -#define PL2303_CLEAR_HALT_REQUEST_TYPE 0x02 // standard request host to device endpoint - -/* registers via vendor read/write requests */ -#define PL2303_READ_TYPE_HX_STATUS 0x8080 - -#define PL2303_HXN_RESET_REG 0x07 -#define PL2303_HXN_RESET_UPSTREAM_PIPE 0x02 -#define PL2303_HXN_RESET_DOWNSTREAM_PIPE 0x01 - -#define PL2303_HXN_FLOWCTRL_REG 0x0a -#define PL2303_HXN_FLOWCTRL_MASK 0x1c -#define PL2303_HXN_FLOWCTRL_NONE 0x1c -#define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18 -#define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c - -/* type data */ +// quirks +#define PL2303_QUIRK_UART_STATE_IDX0 1 +#define PL2303_QUIRK_LEGACY 2 +#define PL2303_QUIRK_ENDPOINT_HACK 4 + +// requests and bits +#define PL2303_SET_LINE_REQUEST_TYPE 0x21 // class request host to device interface +#define PL2303_SET_LINE_REQUEST 0x20 // dec 32 + +#define PL2303_SET_CONTROL_REQUEST_TYPE 0x21 // class request host to device interface +#define PL2303_SET_CONTROL_REQUEST 0x22 // dec 34 +#define PL2303_CONTROL_DTR 0x01 // dec 1 +#define PL2303_CONTROL_RTS 0x02 // dec 2 + +#define PL2303_BREAK_REQUEST_TYPE 0x21 // class request host to device interface +#define PL2303_BREAK_REQUEST 0x23 // dec 35 +#define PL2303_BREAK_ON 0xffff +#define PL2303_BREAK_OFF 0x0000 + +#define PL2303_GET_LINE_REQUEST_TYPE 0xa1 // class request device to host interface +#define PL2303_GET_LINE_REQUEST 0x21 // dec 33 + +#define PL2303_VENDOR_WRITE_REQUEST_TYPE 0x40 // vendor request host to device interface +#define PL2303_VENDOR_WRITE_REQUEST 0x01 // dec 1 +#define PL2303_VENDOR_WRITE_NREQUEST 0x80 // dec 128 + +#define PL2303_VENDOR_READ_REQUEST_TYPE 0xc0 // vendor request device to host interface +#define PL2303_VENDOR_READ_REQUEST 0x01 // dec 1 +#define PL2303_VENDOR_READ_NREQUEST 0x81 // dec 129 + +#define PL2303_UART_STATE_INDEX 8 +#define PL2303_UART_STATE_MSR_MASK 0x8b +#define PL2303_UART_STATE_TRANSIENT_MASK 0x74 +#define PL2303_UART_DCD 0x01 +#define PL2303_UART_DSR 0x02 +#define PL2303_UART_BREAK_ERROR 0x04 +#define PL2303_UART_RING 0x08 +#define PL2303_UART_FRAME_ERROR 0x10 +#define PL2303_UART_PARITY_ERROR 0x20 +#define PL2303_UART_OVERRUN_ERROR 0x40 +#define PL2303_UART_CTS 0x80 + +#define PL2303_FLOWCTRL_MASK 0xf0 + +#define PL2303_CLEAR_HALT_REQUEST_TYPE 0x02 // standard request host to device endpoint + +// registers via vendor read/write requests +#define PL2303_READ_TYPE_HX_STATUS 0x8080 + +#define PL2303_HXN_RESET_REG 0x07 +#define PL2303_HXN_RESET_UPSTREAM_PIPE 0x02 +#define PL2303_HXN_RESET_DOWNSTREAM_PIPE 0x01 + +#define PL2303_HXN_FLOWCTRL_REG 0x0a +#define PL2303_HXN_FLOWCTRL_MASK 0x1c +#define PL2303_HXN_FLOWCTRL_NONE 0x1c +#define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18 +#define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c + +// type data enum pl2303_type { TYPE_H, TYPE_HX, @@ -107,9 +107,9 @@ enum pl2303_type { }; struct pl2303_type_data { - uint8_t const *name; + uint8_t const *name; uint32_t const max_baud_rate; - uint8_t const quirks; + uint8_t const quirks; uint16_t const no_autoxonxoff:1; uint16_t const no_divisors:1; uint16_t const alt_divisors:1; @@ -146,7 +146,7 @@ struct pl2303_type_data { .no_divisors = true, \ } -/* private data types */ +// private data types struct pl2303_serial_private { const struct pl2303_type_data* type; uint8_t quirks; @@ -157,16 +157,16 @@ typedef struct TU_ATTR_PACKED { bool supports_hx_status; } pl2303_private_t; -/* buffer sizes for line coding data */ -#define PL2303_LINE_CODING_BUFSIZE 7 +// buffer sizes for line coding data +#define PL2303_LINE_CODING_BUFSIZE 7 #define PL2303_LINE_CODING_BAUDRATE_BUFSIZE 4 -/* bulk endpoints */ -#define PL2303_OUT_EP 0x02 -#define PL2303_IN_EP 0x83 +// bulk endpoints +#define PL2303_OUT_EP 0x02 +#define PL2303_IN_EP 0x83 -/* return values of pl2303_detect_type() */ +// return values of pl2303_detect_type() #define PL2303_SUPPORTS_HX_STATUS_TRIGGERED -1 -#define PL2303_DETECT_TYPE_FAILED -2 +#define PL2303_DETECT_TYPE_FAILED -2 -#endif /* _PL2303_H_ */ +#endif // TUSB_PL2303_H -- cgit v1.3.1 From 5e67b92b8c398d6fee92010ecd63cf50b51f8f9a Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 4 Apr 2024 14:10:31 +0200 Subject: fixed compile warnings --- src/class/cdc/cdc_host.c | 29 ++++++++++++++++------------- src/class/cdc/serial/cp210x.h | 4 ++-- src/class/cdc/serial/ftdi_sio.h | 14 +++++++------- 3 files changed, 25 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index e6579ad82..2d900a753 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -1181,9 +1181,9 @@ static bool ftdi_change_speed(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_c static bool ftdi_set_data_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { TU_VERIFY(p_cdc->requested_line_coding.data_bits >= 7 && p_cdc->requested_line_coding.data_bits <= 8, 0); uint16_t value = (uint16_t) ( - ((uint32_t) p_cdc->requested_line_coding.data_bits & 0xfu) | // data bit quantity is stored in bits 0-3 - ((uint32_t) p_cdc->requested_line_coding.parity & 0x7u) << 8 | // parity is stored in bits 8-10, same coding - ((uint32_t) p_cdc->requested_line_coding.stop_bits & 0x3u) << 11 ); // stop bits quantity is stored in bits 11-12, same coding + (p_cdc->requested_line_coding.data_bits & 0xfUL) | // data bit quantity is stored in bits 0-3 + (p_cdc->requested_line_coding.parity & 0x7UL) << 8 | // parity is stored in bits 8-10, same coding + (p_cdc->requested_line_coding.stop_bits & 0x3UL) << 11 ); // stop bits quantity is stored in bits 11-12, same coding // not each FTDI supports 1.5 stop bits return ftdi_set_request(p_cdc, FTDI_SIO_SET_DATA_REQUEST, FTDI_SIO_SET_DATA_REQUEST_TYPE, @@ -1191,8 +1191,8 @@ static bool ftdi_set_data_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t comple } static inline bool ftdi_update_mctrl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - uint16_t value = (uint16_t) ((p_cdc->requested_line_state.dtr ? (uint32_t) FTDI_SIO_SET_DTR_HIGH : (uint32_t) FTDI_SIO_SET_DTR_LOW) | - (p_cdc->requested_line_state.rts ? (uint32_t) FTDI_SIO_SET_RTS_HIGH : (uint32_t) FTDI_SIO_SET_RTS_LOW)); + uint16_t value = (uint16_t) ((p_cdc->requested_line_state.dtr ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW) | + (p_cdc->requested_line_state.rts ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW)); return ftdi_set_request(p_cdc, FTDI_SIO_SET_MODEM_CTRL_REQUEST, FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, value, p_cdc->ftdi.channel, complete_cb, user_data); @@ -1338,7 +1338,10 @@ static void ftdi_process_config(tuh_xfer_t * xfer) { // other interfaces have same type as interface 0 uint8_t const idx_itf0 = tuh_cdc_itf_get_index(xfer->daddr, 0); cdch_interface_t const * p_cdc_itf0 = get_itf(idx_itf0); - p_cdc->ftdi.chip_type = p_cdc_itf0->ftdi.chip_type; + TU_ASSERT_COMPLETE(p_cdc_itf0); + if (p_cdc_itf0) { + p_cdc->ftdi.chip_type = p_cdc_itf0->ftdi.chip_type; + } } TU_ATTR_FALLTHROUGH; @@ -1715,9 +1718,9 @@ static bool cp210x_set_baudrate_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t static bool cp210x_set_line_ctl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { TU_VERIFY(p_cdc->requested_line_coding.data_bits >= 5 && p_cdc->requested_line_coding.data_bits <= 9, 0); uint16_t lcr = (uint16_t) ( - ((uint32_t) p_cdc->requested_line_coding.data_bits & 0xfu) << 8 | // data bit quantity is stored in bits 8-11 - ((uint32_t) p_cdc->requested_line_coding.parity & 0xfu) << 4 | // parity is stored in bits 4-7, same coding - ((uint32_t) p_cdc->requested_line_coding.stop_bits & 0xfu)); // parity is stored in bits 0-3, same coding + (p_cdc->requested_line_coding.data_bits & 0xfUL) << 8 | // data bit quantity is stored in bits 8-11 + (p_cdc->requested_line_coding.parity & 0xfUL) << 4 | // parity is stored in bits 4-7, same coding + (p_cdc->requested_line_coding.stop_bits & 0xfUL)); // parity is stored in bits 0-3, same coding return cp210x_set_request(p_cdc, CP210X_SET_LINE_CTL, lcr, NULL, 0, complete_cb, user_data); } @@ -1725,8 +1728,8 @@ static bool cp210x_set_line_ctl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete static inline bool cp210x_set_mhs(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { // CP210x has the same bit coding return cp210x_set_request(p_cdc, CP210X_SET_MHS, - (uint16_t) ((uint32_t) CP210X_CONTROL_WRITE_DTR | - (uint32_t) CP210X_CONTROL_WRITE_RTS | p_cdc->requested_line_state.all), + (uint16_t) (CP210X_CONTROL_WRITE_DTR | CP210X_CONTROL_WRITE_RTS | + p_cdc->requested_line_state.all), NULL, 0, complete_cb, user_data); } @@ -2327,7 +2330,7 @@ static bool pl2303_vendor_write(cdch_interface_t * p_cdc, uint16_t value, uint16 static inline bool pl2303_supports_hx_status(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - uint8_t buf; + uint8_t buf = 0; return pl2303_set_request(p_cdc, PL2303_VENDOR_READ_REQUEST, PL2303_VENDOR_READ_REQUEST_TYPE, PL2303_READ_TYPE_HX_STATUS, 0, &buf, 1, complete_cb, user_data); @@ -2519,7 +2522,7 @@ static void pl2303_process_config(tuh_xfer_t * xfer) { cdch_interface_t * p_cdc = get_itf(idx); // state CONFIG_PL2303_READ1 may have no success due to expected stall by pl2303_supports_hx_status() TU_ASSERT_COMPLETE(p_cdc && (xfer->result == XFER_RESULT_SUCCESS || xfer->user_data == CONFIG_PL2303_READ1)); - uint8_t buf; + uint8_t buf = 0; int8_t type; switch (state) { diff --git a/src/class/cdc/serial/cp210x.h b/src/class/cdc/serial/cp210x.h index a553a54da..ac9c27330 100644 --- a/src/class/cdc/serial/cp210x.h +++ b/src/class/cdc/serial/cp210x.h @@ -101,8 +101,8 @@ #define CP210X_MSR_DCD 0x0080 #define CP210X_MSR_ALL 0x00F0 -#define CP210X_CONTROL_WRITE_DTR 0x0100 -#define CP210X_CONTROL_WRITE_RTS 0x0200 +#define CP210X_CONTROL_WRITE_DTR 0x0100UL +#define CP210X_CONTROL_WRITE_RTS 0x0200UL #define CP210X_LSR_BREAK 0x0001 #define CP210X_LSR_FRAMING_ERROR 0x0002 diff --git a/src/class/cdc/serial/ftdi_sio.h b/src/class/cdc/serial/ftdi_sio.h index 4afedec9b..f621b3912 100644 --- a/src/class/cdc/serial/ftdi_sio.h +++ b/src/class/cdc/serial/ftdi_sio.h @@ -96,17 +96,17 @@ enum ftdi_sio_baudrate { #define FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE 0x40 #define FTDI_SIO_SET_MODEM_CTRL_REQUEST FTDI_SIO_MODEM_CTRL -#define FTDI_SIO_SET_DTR_MASK 0x1 -#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1) -#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0) -#define FTDI_SIO_SET_RTS_MASK 0x2 -#define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2) -#define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0) +#define FTDI_SIO_SET_DTR_MASK 0x1UL +#define FTDI_SIO_SET_DTR_HIGH ((FTDI_SIO_SET_DTR_MASK << 8) | 1UL) +#define FTDI_SIO_SET_DTR_LOW ((FTDI_SIO_SET_DTR_MASK << 8) | 0UL) +#define FTDI_SIO_SET_RTS_MASK 0x2UL +#define FTDI_SIO_SET_RTS_HIGH ((FTDI_SIO_SET_RTS_MASK << 8) | 2UL) +#define FTDI_SIO_SET_RTS_LOW ((FTDI_SIO_SET_RTS_MASK << 8) | 0UL) // FTDI_SIO_SET_FLOW_CTRL #define FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE 0x40 #define FTDI_SIO_SET_FLOW_CTRL_REQUEST FTDI_SIO_SET_FLOW_CTRL -#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0 +#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0 #define FTDI_SIO_RTS_CTS_HS (0x1 << 8) #define FTDI_SIO_DTR_DSR_HS (0x2 << 8) #define FTDI_SIO_XON_XOFF_HS (0x4 << 8) -- cgit v1.3.1 From e07ee4a7b1f7c7d18f385d605dd41e08962ddba4 Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 4 Apr 2024 14:12:14 +0200 Subject: CP210x removed baudrate check, fixed data bits check --- src/class/cdc/cdc_host.c | 12 ++---------- src/class/cdc/serial/cp210x.h | 9 --------- 2 files changed, 2 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 2d900a753..73d800469 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -1701,22 +1701,14 @@ static inline bool cp210x_ifc_enable(cdch_interface_t * p_cdc, uint16_t enabled, } static bool cp210x_set_baudrate_request(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - // Check baudrate is supported. It's only a specific list. reference: datasheets and AN205 "CP210x Baud Rate Support" - uint32_t const supported_baudrates_list[] = CP210X_SUPPORTED_BAUDRATES_LIST; - uint8_t i; - for ( i=0; supported_baudrates_list[i]; i++ ){ - if (p_cdc->requested_line_coding.bit_rate == supported_baudrates_list[i]) { - break; - } - } - TU_VERIFY(supported_baudrates_list[i]); + // Not every baud rate is supported. See datasheets and AN205 "CP210x Baud Rate Support" uint32_t baud_le = tu_htole32(p_cdc->requested_line_coding.bit_rate); return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4, complete_cb, user_data); } static bool cp210x_set_line_ctl(cdch_interface_t * p_cdc, tuh_xfer_cb_t complete_cb, uintptr_t user_data) { - TU_VERIFY(p_cdc->requested_line_coding.data_bits >= 5 && p_cdc->requested_line_coding.data_bits <= 9, 0); + TU_VERIFY(p_cdc->requested_line_coding.data_bits >= 5 && p_cdc->requested_line_coding.data_bits <= 8, 0); uint16_t lcr = (uint16_t) ( (p_cdc->requested_line_coding.data_bits & 0xfUL) << 8 | // data bit quantity is stored in bits 8-11 (p_cdc->requested_line_coding.parity & 0xfUL) << 4 | // parity is stored in bits 4-7, same coding diff --git a/src/class/cdc/serial/cp210x.h b/src/class/cdc/serial/cp210x.h index ac9c27330..a0eff9e40 100644 --- a/src/class/cdc/serial/cp210x.h +++ b/src/class/cdc/serial/cp210x.h @@ -111,13 +111,4 @@ #define CP210X_LSR_PARITY_ERROR 0x0010 #define CP210X_LSR_ALL 0x001F -// supported baudrates -// reference: datasheets and AN205 "CP210x Baud Rate Support" -#define CP210X_SUPPORTED_BAUDRATES_LIST { \ - 300, 600, \ - 1200, 1800, 2400, 4000, 4800, 7200, 9600, \ - 14400, 16000, 19200, 28800, 38400, 51200, 56000, 57600, 64000, 76800, \ - 115200, 128000, 153600, 230400, 250000, 256000, 460800, 500000, 576000, 921600, \ - 0 } - #endif //TUSB_CP210X_H -- cgit v1.3.1 From a1b1c1f552d944da1bf309ce002b7c07f5a270a0 Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 4 Apr 2024 14:13:24 +0200 Subject: foxed FTDI flow control config --- src/class/cdc/cdc_host.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 73d800469..6006730b7 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -1382,7 +1382,8 @@ static void ftdi_process_config(tuh_xfer_t * xfer) { case CONFIG_FTDI_FLOW_CONTROL: // disable flow control TU_ASSERT_COMPLETE(ftdi_set_request(p_cdc, FTDI_SIO_SET_FLOW_CTRL_REQUEST, FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, - 0, FTDI_SIO_DISABLE_FLOW_CTRL, ftdi_process_config, CONFIG_FTDI_MODEM_CTRL)); + FTDI_SIO_DISABLE_FLOW_CTRL, p_cdc->ftdi.channel, + ftdi_process_config, CONFIG_FTDI_MODEM_CTRL)); break; case CONFIG_FTDI_MODEM_CTRL: -- cgit v1.3.1 From e02a309f1dc09377fe5123c21ecfe36eb6a032c7 Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 4 Apr 2024 14:14:41 +0200 Subject: disable PL2303 flow control config --- src/class/cdc/cdc_host.c | 55 +++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index 6006730b7..b9915f583 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -2475,8 +2475,8 @@ enum { CONFIG_PL2303_RESET_ENDP2, CONFIG_PL2303_LINE_CODING, CONFIG_PL2303_MODEM_CONTROL, - CONFIG_PL2303_FLOW_CTRL_READ, - CONFIG_PL2303_FLOW_CTRL_WRITE, +// CONFIG_PL2303_FLOW_CTRL_READ, +// CONFIG_PL2303_FLOW_CTRL_WRITE, CONFIG_PL2303_COMPLETE }; @@ -2691,35 +2691,38 @@ static void pl2303_process_config(tuh_xfer_t * xfer) { case CONFIG_PL2303_MODEM_CONTROL: #ifdef LINE_CONTROL_ON_ENUM p_cdc->requested_line_state.all = LINE_CONTROL_ON_ENUM; - TU_ASSERT_COMPLETE(pl2303_set_control_lines(p_cdc, pl2303_internal_control_complete, CONFIG_PL2303_FLOW_CTRL_READ)); + TU_ASSERT_COMPLETE(pl2303_set_control_lines(p_cdc, pl2303_internal_control_complete, CONFIG_PL2303_COMPLETE)); break; #else TU_ATTR_FALLTHROUGH; #endif - case CONFIG_PL2303_FLOW_CTRL_READ: - // read flow control register for modify & write back in next step - if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) { - TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, PL2303_HXN_FLOWCTRL_REG, &buf, pl2303_process_config, - CONFIG_PL2303_FLOW_CTRL_WRITE)); - } else { - TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0, &buf, pl2303_process_config, CONFIG_PL2303_FLOW_CTRL_WRITE)); - } - break; - - case CONFIG_PL2303_FLOW_CTRL_WRITE: - // no flow control - buf = xfer->buffer[0]; - if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) { - buf &= (uint8_t) ~PL2303_HXN_FLOWCTRL_MASK; - buf |= PL2303_HXN_FLOWCTRL_NONE; - TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, PL2303_HXN_FLOWCTRL_REG, buf, pl2303_process_config, - CONFIG_PL2303_COMPLETE)); - } else { - buf &= (uint8_t) ~PL2303_FLOWCTRL_MASK; - TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 0, buf, pl2303_process_config, CONFIG_PL2303_COMPLETE)); - } - break; +// skipped, because it's not working with each PL230x. flow control can be also set by PL2303 EEPROM Writer Program +// case CONFIG_PL2303_FLOW_CTRL_READ: +// // read flow control register for modify & write back in next step +// if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) { +// TU_LOG_P_CDC ( "1\r\n" ); +// TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, PL2303_HXN_FLOWCTRL_REG, &buf, pl2303_process_config, +// CONFIG_PL2303_FLOW_CTRL_WRITE)); +// } else { +// TU_LOG_P_CDC ( "2\r\n" ); +// TU_ASSERT_COMPLETE(pl2303_vendor_read(p_cdc, 0, &buf, pl2303_process_config, CONFIG_PL2303_FLOW_CTRL_WRITE)); +// } +// break; +// +// case CONFIG_PL2303_FLOW_CTRL_WRITE: +// // no flow control +// buf = xfer->buffer[0]; +// if (p_cdc->pl2303.serial_private.type == &pl2303_type_data[TYPE_HXN]) { +// buf &= (uint8_t) ~PL2303_HXN_FLOWCTRL_MASK; +// buf |= PL2303_HXN_FLOWCTRL_NONE; +// TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, PL2303_HXN_FLOWCTRL_REG, buf, pl2303_process_config, +// CONFIG_PL2303_COMPLETE)); +// } else { +// buf &= (uint8_t) ~PL2303_FLOWCTRL_MASK; +// TU_ASSERT_COMPLETE(pl2303_vendor_write(p_cdc, 0, buf, pl2303_process_config, CONFIG_PL2303_COMPLETE)); +// } +// break; case CONFIG_PL2303_COMPLETE: set_config_complete(idx, 0, true); -- cgit v1.3.1 From 68602e4adda941ab11826afbd7b82e44134a97ae Mon Sep 17 00:00:00 2001 From: IngHK Date: Thu, 4 Apr 2024 14:16:02 +0200 Subject: small change process config complete --- src/class/cdc/cdc_host.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c index b9915f583..fbe0cc9ca 100644 --- a/src/class/cdc/cdc_host.c +++ b/src/class/cdc/cdc_host.c @@ -1400,7 +1400,7 @@ static void ftdi_process_config(tuh_xfer_t * xfer) { break; default: - set_config_complete(idx, 0, false); + TU_ASSERT_COMPLETE(false); break; } } @@ -1876,7 +1876,7 @@ static void cp210x_process_config(tuh_xfer_t * xfer) { break; default: - set_config_complete(idx, 0, false); + TU_ASSERT_COMPLETE(false); break; } } @@ -2158,7 +2158,7 @@ static void ch34x_process_config(tuh_xfer_t* xfer) { break; default: - set_config_complete(idx, 0, false); + TU_ASSERT_COMPLETE(false); break; } } @@ -2729,7 +2729,7 @@ static void pl2303_process_config(tuh_xfer_t * xfer) { break; default: - set_config_complete(idx, 0, false); + TU_ASSERT_COMPLETE(false); break; } } -- cgit v1.3.1 From 8b4ca69e56860e12596e2bef63fc155f8dce338c Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Fri, 12 Apr 2024 01:14:30 +0200 Subject: cdc_device : save rhport. --- src/class/cdc/cdc_device.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index 2e0a0c30d..e856963f5 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -47,6 +47,7 @@ typedef struct { + uint8_t rhport; uint8_t itf_num; uint8_t ep_notif; uint8_t ep_in; @@ -84,7 +85,6 @@ CFG_TUD_MEM_SECTION tu_static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC]; static bool _prep_out_transaction (cdcd_interface_t* p_cdc) { - uint8_t const rhport = 0; uint16_t available = tu_fifo_remaining(&p_cdc->rx_ff); // Prepare for incoming data but only allow what we can store in the ring buffer. @@ -94,18 +94,18 @@ static bool _prep_out_transaction (cdcd_interface_t* p_cdc) TU_VERIFY(available >= sizeof(p_cdc->epout_buf)); // claim endpoint - TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_out)); + TU_VERIFY(usbd_edpt_claim(p_cdc->rhport, p_cdc->ep_out)); // fifo can be changed before endpoint is claimed available = tu_fifo_remaining(&p_cdc->rx_ff); if ( available >= sizeof(p_cdc->epout_buf) ) { - return usbd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf)); + return usbd_edpt_xfer(p_cdc->rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf)); }else { // Release endpoint since we don't make any transfer - usbd_edpt_release(rhport, p_cdc->ep_out); + usbd_edpt_release(p_cdc->rhport, p_cdc->ep_out); return false; } @@ -135,7 +135,6 @@ void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted) _cdcd_itf[itf].wanted_char = wanted; } - //--------------------------------------------------------------------+ // READ API //--------------------------------------------------------------------+ @@ -194,23 +193,21 @@ uint32_t tud_cdc_n_write_flush (uint8_t itf) // No data to send if ( !tu_fifo_count(&p_cdc->tx_ff) ) return 0; - uint8_t const rhport = 0; - // Claim the endpoint - TU_VERIFY( usbd_edpt_claim(rhport, p_cdc->ep_in), 0 ); + TU_VERIFY( usbd_edpt_claim(p_cdc->rhport, p_cdc->ep_in), 0 ); // Pull data from FIFO uint16_t const count = tu_fifo_read_n(&p_cdc->tx_ff, p_cdc->epin_buf, sizeof(p_cdc->epin_buf)); if ( count ) { - TU_ASSERT( usbd_edpt_xfer(rhport, p_cdc->ep_in, p_cdc->epin_buf, count), 0 ); + TU_ASSERT( usbd_edpt_xfer(p_cdc->rhport, p_cdc->ep_in, p_cdc->epin_buf, count), 0 ); return count; }else { // Release endpoint since we don't make any transfer // Note: data is dropped if terminal is not connected - usbd_edpt_release(rhport, p_cdc->ep_in); + usbd_edpt_release(p_cdc->rhport, p_cdc->ep_in); return 0; } } @@ -319,6 +316,7 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1 TU_ASSERT(p_cdc, 0); //------------- Control Interface -------------// + p_cdc->rhport = rhport; p_cdc->itf_num = itf_desc->bInterfaceNumber; uint16_t drv_len = sizeof(tusb_desc_interface_t); -- cgit v1.3.1 From 67f32da1b9a460262e028784ffb0386edd507612 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Fri, 12 Apr 2024 01:15:46 +0200 Subject: cdc: add uart status notification support. --- src/class/cdc/cdc.h | 29 +++++++++++++++++++++++++++++ src/class/cdc/cdc_device.c | 24 +++++++++++++++++++++++- src/class/cdc/cdc_device.h | 9 +++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/class/cdc/cdc.h b/src/class/cdc/cdc.h index 5cbd658fe..f92ab9231 100644 --- a/src/class/cdc/cdc.h +++ b/src/class/cdc/cdc.h @@ -412,6 +412,35 @@ typedef struct TU_ATTR_PACKED TU_VERIFY_STATIC(sizeof(cdc_line_control_state_t) == 2, "size is not correct"); +//--------------------------------------------------------------------+ +// Notifications +//--------------------------------------------------------------------+ +typedef struct TU_ATTR_PACKED +{ + uint16_t bRxCarrier : 1; + uint16_t bTxCarrier : 1; + uint16_t bBreak : 1; + uint16_t bRingSignal : 1; + uint16_t bFraming : 1; + uint16_t bParity : 1; + uint16_t bOverRun : 1; + uint16_t : 9; +} cdc_uart_state_t; + +typedef struct TU_ATTR_PACKED +{ + uint8_t bmRequestType; + uint8_t bNotification; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; + cdc_uart_state_t bmUartState; +} cdc_notif_serial_state_t; + +TU_VERIFY_STATIC(sizeof(cdc_notif_serial_state_t) == 10, "size is not correct"); + +#define CDC_REQ_TYPE_NOTIF 0xA1 ///< Direction IN; Type Class; Recipient Interface + TU_ATTR_PACKED_END // End of all packed definitions TU_ATTR_BIT_FIELD_ORDER_END diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c index e856963f5..5057805e2 100644 --- a/src/class/cdc/cdc_device.c +++ b/src/class/cdc/cdc_device.c @@ -71,6 +71,7 @@ typedef struct OSAL_MUTEX_DEF(tx_ff_mutex); // Endpoint Transfer buffer + CFG_TUSB_MEM_ALIGN cdc_notif_serial_state_t serial_state_buf; CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EP_BUFSIZE]; CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EP_BUFSIZE]; @@ -130,6 +131,27 @@ void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding) (*coding) = _cdcd_itf[itf].line_coding; } +bool tud_cdc_n_send_uart_state (uint8_t itf, cdc_uart_state_t state) +{ + cdcd_interface_t* p_cdc = &_cdcd_itf[itf]; + + // Skip if usb is not ready yet + TU_VERIFY( tud_ready(), 0 ); + + // claim endpoint + TU_VERIFY(usbd_edpt_claim(p_cdc->rhport, p_cdc->ep_notif)); + + p_cdc->serial_state_buf.bmRequestType = CDC_REQ_TYPE_NOTIF; + p_cdc->serial_state_buf.bNotification = CDC_NOTIF_SERIAL_STATE; + p_cdc->serial_state_buf.wValue = 0; + p_cdc->serial_state_buf.wIndex = p_cdc->itf_num; + p_cdc->serial_state_buf.wLength = 2; + p_cdc->serial_state_buf.bmUartState = state; + + // transfer + return usbd_edpt_xfer(p_cdc->rhport, p_cdc->ep_notif, (uint8_t *)&p_cdc->serial_state_buf, sizeof(p_cdc->serial_state_buf)); +} + void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted) { _cdcd_itf[itf].wanted_char = wanted; @@ -457,7 +479,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_ for (itf = 0; itf < CFG_TUD_CDC; itf++) { p_cdc = &_cdcd_itf[itf]; - if ( ( ep_addr == p_cdc->ep_out ) || ( ep_addr == p_cdc->ep_in ) ) break; + if ( ( ep_addr == p_cdc->ep_out ) || ( ep_addr == p_cdc->ep_in ) || ( ep_addr == p_cdc->ep_notif )) break; } TU_ASSERT(itf < CFG_TUD_CDC); diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h index 20e908451..92131f159 100644 --- a/src/class/cdc/cdc_device.h +++ b/src/class/cdc/cdc_device.h @@ -64,6 +64,9 @@ uint8_t tud_cdc_n_get_line_state (uint8_t itf); // Get current line encoding: bit rate, stop bits parity etc .. void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding); +// Send UART status notification: DCD, DSR etc .. +bool tud_cdc_n_send_uart_state (uint8_t itf, cdc_uart_state_t state); + // Set special character that will trigger tud_cdc_rx_wanted_cb() callback on receiving void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted); @@ -109,6 +112,7 @@ bool tud_cdc_n_write_clear (uint8_t itf); static inline bool tud_cdc_connected (void); static inline uint8_t tud_cdc_get_line_state (void); static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding); +static inline bool tud_cdc_send_uart_state (cdc_uart_state_t state); static inline void tud_cdc_set_wanted_char (char wanted); static inline uint32_t tud_cdc_available (void); @@ -180,6 +184,11 @@ static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding) tud_cdc_n_get_line_coding(0, coding); } +static inline bool tud_cdc_send_uart_state (cdc_uart_state_t state) +{ + return tud_cdc_n_send_uart_state(0, state); +} + static inline void tud_cdc_set_wanted_char (char wanted) { tud_cdc_n_set_wanted_char(0, wanted); -- cgit v1.3.1 From 61eee535c16701acd577b253bc17a7716ae60c7c Mon Sep 17 00:00:00 2001 From: Li Junru Date: Wed, 22 May 2024 20:57:23 +0800 Subject: feat(uvc): support format frame based --- src/class/video/video.h | 55 ++++++++++++++++++++++++++++++------------ src/class/video/video_device.c | 17 ++++++++++++- 2 files changed, 55 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/class/video/video.h b/src/class/video/video.h index b8a9b6369..f348e187b 100644 --- a/src/class/video/video.h +++ b/src/class/video/video.h @@ -540,28 +540,32 @@ typedef struct TU_ATTR_PACKED { TU_VERIFY_STATIC( sizeof(video_probe_and_commit_control_t) == 48, "size is not correct"); -#define TUD_VIDEO_DESC_IAD_LEN 8 -#define TUD_VIDEO_DESC_STD_VC_LEN 9 -#define TUD_VIDEO_DESC_CS_VC_LEN 12 -#define TUD_VIDEO_DESC_INPUT_TERM_LEN 8 -#define TUD_VIDEO_DESC_OUTPUT_TERM_LEN 9 -#define TUD_VIDEO_DESC_CAMERA_TERM_LEN 18 -#define TUD_VIDEO_DESC_STD_VS_LEN 9 -#define TUD_VIDEO_DESC_CS_VS_IN_LEN 13 -#define TUD_VIDEO_DESC_CS_VS_OUT_LEN 9 -#define TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN 27 -#define TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN 11 -#define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN 38 -#define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_DISC_LEN 26 -#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN 38 -#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC_LEN 26 -#define TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN 6 +#define TUD_VIDEO_DESC_IAD_LEN 8 +#define TUD_VIDEO_DESC_STD_VC_LEN 9 +#define TUD_VIDEO_DESC_CS_VC_LEN 12 +#define TUD_VIDEO_DESC_INPUT_TERM_LEN 8 +#define TUD_VIDEO_DESC_OUTPUT_TERM_LEN 9 +#define TUD_VIDEO_DESC_CAMERA_TERM_LEN 18 +#define TUD_VIDEO_DESC_STD_VS_LEN 9 +#define TUD_VIDEO_DESC_CS_VS_IN_LEN 13 +#define TUD_VIDEO_DESC_CS_VS_OUT_LEN 9 +#define TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN 27 +#define TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN 11 +#define TUD_VIDEO_DESC_CS_VS_FMT_FRAME_BASED_LEN 28 +#define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN 38 +#define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_DISC_LEN 26 +#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN 38 +#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC_LEN 26 +#define TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_CONT_LEN 38 +#define TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_DISC_LEN 26 +#define TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN 6 /* 2.2 compression formats */ #define TUD_VIDEO_GUID_YUY2 0x59,0x55,0x59,0x32,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71 #define TUD_VIDEO_GUID_NV12 0x4E,0x56,0x31,0x32,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71 #define TUD_VIDEO_GUID_M420 0x4D,0x34,0x32,0x30,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71 #define TUD_VIDEO_GUID_I420 0x49,0x34,0x32,0x30,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71 +#define TUD_VIDEO_GUID_H264 0x48,0x32,0x36,0x34,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71 #define TUD_VIDEO_DESC_IAD(_firstitf, _nitfs, _stridx) \ TUD_VIDEO_DESC_IAD_LEN, TUSB_DESC_INTERFACE_ASSOCIATION, \ @@ -656,6 +660,25 @@ TU_VERIFY_STATIC( sizeof(video_probe_and_commit_control_t) == 48, "size is not c _frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \ U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__ +/* Motion-Frame-Based 3.1.1 Table 3-1 */ +#define TUD_VIDEO_DESC_CS_VS_FMT_FRAME_BASED(_fmtidx, _numfrmdesc, _guid, _bitsperpix, _frmidx, _asrx, _asry, _interlace, _cp, _variablesize) \ + TUD_VIDEO_DESC_CS_VS_FMT_FRAME_BASED_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED, \ + _fmtidx, _numfrmdesc, TUD_VIDEO_GUID(_guid), _bitsperpix, _frmidx, _asrx, _asry, _interlace, _cp, _variablesize + +/* Motion-Frame-Based 3.1.1 Table 3-2 and 3-3 */ +#define TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_CONT(_frmidx, _cap, _width, _height, _minbr, _maxbr, _frminterval, _bytesperline, _minfrminterval, _maxfrminterval, _frmintervalstep) \ + TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_CONT_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_FRAME_BASED, \ + _frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \ + U32_TO_U8S_LE(_frminterval), 0, U32_TO_U8S_LE(_bytesperline), \ + U32_TO_U8S_LE(_minfrminterval), U32_TO_U8S_LE(_maxfrminterval), U32_TO_U8S_LE(_frmintervalstep) + +/* Motion-Frame-Based 3.1.1 Table 3-2 and 3-4 */ +#define TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_DISC(_frmidx, _cap, _width, _height, _minbr, _maxbr, _frminterval, _bytesperline, ...) \ + TUD_VIDEO_DESC_CS_VS_FRM_FRAME_BASED_DISC_LEN + (TU_ARGS_NUM(__VA_ARGS__)) * 4, \ + TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_FRAME_BASED, \ + _frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \ +U32_TO_U8S_LE(_frminterval), U32_TO_U8S_LE(_bytesperline), (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__ + /* 3.9.2.6 */ #define TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING(_color, _trns, _mat) \ TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN, \ diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index 4218835aa..5310a393b 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -398,7 +398,7 @@ static inline void const *_find_desc_format(void const *beg, void const *end, ui if ((fmt == VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED || fmt == VIDEO_CS_ITF_VS_FORMAT_MJPEG || fmt == VIDEO_CS_ITF_VS_FORMAT_DV || - fmt == VIDEO_CS_ITF_VS_FRAME_FRAME_BASED) && + fmt == VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED) && fmtnum == p[3]) { return cur; } @@ -464,6 +464,9 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm case VIDEO_CS_ITF_VS_FORMAT_MJPEG: break; + case VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED: + break; + default: return false; } @@ -489,6 +492,10 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * 16 / 8; /* YUV422 */ break; + case VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED: + frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * 16 / 8; /* YUV422 */ + break; + default: break; } param->dwMaxVideoFrameSize = frame_size; @@ -578,6 +585,10 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const * frmnum = fmt->mjpeg.bDefaultFrameIndex; break; + case VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED: + frmnum = fmt->frame_based.bDefaultFrameIndex; + break; + default: return false; } break; @@ -596,6 +607,10 @@ static bool _negotiate_streaming_parameters(videod_streaming_interface_t const * frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * 16 / 8; /* YUV422 */ break; + case VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED: + frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * 16 / 8; /* YUV422 */ + break; + default: return false; } param->dwMaxVideoFrameSize = frame_size; -- cgit v1.3.1 From 6b0b889f25ddb2bbec8cf48bb00b35417394e799 Mon Sep 17 00:00:00 2001 From: Hardy Griech Date: Mon, 15 Jul 2024 18:15:01 +0200 Subject: avoid recursions in tud_network_recv_renew() --- src/class/net/ncm_device.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c index 90d747185..1516c329a 100644 --- a/src/class/net/ncm_device.c +++ b/src/class/net/ncm_device.c @@ -110,8 +110,12 @@ typedef struct { NOTIFICATION_SPEED, NOTIFICATION_CONNECTED, NOTIFICATION_DONE - } notification_xmit_state; // state of notification transmission - bool notification_xmit_is_running; // notification is currently transmitted + } notification_xmit_state; // state of notification transmission + bool notification_xmit_is_running; // notification is currently transmitted + + // misc + bool tud_network_recv_renew_active; // tud_network_recv_renew() is active (avoid recursive invocations) + bool tud_network_recv_renew_process_again; // tud_network_recv_renew() should process again } ncm_interface_t; CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN tu_static ncm_interface_t ncm_interface; @@ -689,11 +693,29 @@ void tud_network_xmit(void *ref, uint16_t arg) { /** * Keep the receive logic busy and transfer pending packets to the glue logic. + * Avoid recursive calls due to wrong expectations of the net glue logic, + * see https://github.com/hathach/tinyusb/issues/2711 */ void tud_network_recv_renew(void) { TU_LOG_DRV("tud_network_recv_renew()\n"); - recv_transfer_datagram_to_glue_logic(); + ncm_interface.tud_network_recv_renew_process_again = true; + + if (ncm_interface.tud_network_recv_renew_active) { + TU_LOG_DRV("Re-entrant into tud_network_recv_renew, will process later\n"); + return; + } + + while (ncm_interface.tud_network_recv_renew_process_again) { + ncm_interface.tud_network_recv_renew_process_again = false; + + // If the current function is called within recv_transfer_datagram_to_glue_logic, + // tud_network_recv_renew_process_again will become true, and the loop will run again + // Otherwise the loop will not run again + ncm_interface.tud_network_recv_renew_active = true; + recv_transfer_datagram_to_glue_logic(); + ncm_interface.tud_network_recv_renew_active = false; + } recv_try_to_start_new_reception(ncm_interface.rhport); } // tud_network_recv_renew -- cgit v1.3.1 From 10a3aa3cc8f58a783d2109f5a6c9a4012992f982 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 15 Oct 2024 13:03:12 +0700 Subject: adding hcd dwc2 --- examples/dual/host_hid_to_device_cdc/only.txt | 1 + examples/dual/host_info_to_device_cdc/only.txt | 1 + examples/host/device_info/only.txt | 1 + hw/bsp/stm32h7/family.c | 7 +- hw/bsp/stm32h7/family.cmake | 2 + src/portable/synopsys/dwc2/dcd_dwc2.c | 194 +----------------------- src/portable/synopsys/dwc2/dwc2_common.c | 196 +++++++++++++++++++++++++ src/portable/synopsys/dwc2/dwc2_common.h | 70 +++++++++ src/portable/synopsys/dwc2/dwc2_type.h | 4 +- src/portable/synopsys/dwc2/hcd_dwc2.c | 167 +++++++++++++++++++++ 10 files changed, 447 insertions(+), 196 deletions(-) create mode 100644 src/portable/synopsys/dwc2/dwc2_common.c create mode 100644 src/portable/synopsys/dwc2/dwc2_common.h create mode 100644 src/portable/synopsys/dwc2/hcd_dwc2.c (limited to 'src') diff --git a/examples/dual/host_hid_to_device_cdc/only.txt b/examples/dual/host_hid_to_device_cdc/only.txt index cfc87eb4e..149ece856 100644 --- a/examples/dual/host_hid_to_device_cdc/only.txt +++ b/examples/dual/host_hid_to_device_cdc/only.txt @@ -4,3 +4,4 @@ board:mcb1800 mcu:RP2040 mcu:ra6m5 mcu:MAX3421 +mcu:STM32H7 diff --git a/examples/dual/host_info_to_device_cdc/only.txt b/examples/dual/host_info_to_device_cdc/only.txt index cfc87eb4e..149ece856 100644 --- a/examples/dual/host_info_to_device_cdc/only.txt +++ b/examples/dual/host_info_to_device_cdc/only.txt @@ -4,3 +4,4 @@ board:mcb1800 mcu:RP2040 mcu:ra6m5 mcu:MAX3421 +mcu:STM32H7 diff --git a/examples/host/device_info/only.txt b/examples/host/device_info/only.txt index b3f4468bb..a38fc251b 100644 --- a/examples/host/device_info/only.txt +++ b/examples/host/device_info/only.txt @@ -12,3 +12,4 @@ mcu:MSP432E4 mcu:RP2040 mcu:RX65X mcu:RAXXX +mcu:STM32H7 diff --git a/hw/bsp/stm32h7/family.c b/hw/bsp/stm32h7/family.c index 6e4b388d6..3bed1f5ec 100644 --- a/hw/bsp/stm32h7/family.c +++ b/hw/bsp/stm32h7/family.c @@ -151,7 +151,7 @@ void board_init(void) { HAL_UART_Init(&UartHandle); #endif -#if BOARD_TUD_RHPORT == 0 + //------------- USB FS -------------// // Despite being call USB2_OTG // OTG_FS is marked as RHPort0 by TinyUSB to be consistent across stm32 port // PA9 VUSB, PA10 ID, PA11 DM, PA12 DP @@ -195,10 +195,9 @@ void board_init(void) { USB_OTG_FS->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL; #endif // vbus sense -#elif BOARD_TUD_RHPORT == 1 + //------------- USB HS -------------// // Despite being call USB2_OTG // OTG_HS is marked as RHPort1 by TinyUSB to be consistent across stm32 port - struct { GPIO_TypeDef* port; uint32_t pin; @@ -238,8 +237,6 @@ void board_init(void) { // For waveshare openh743 ULPI PHY reset walkaround board_stm32h7_post_init(); -#endif // rhport = 1 - } //--------------------------------------------------------------------+ diff --git a/hw/bsp/stm32h7/family.cmake b/hw/bsp/stm32h7/family.cmake index 026c7c3df..e4161ffe5 100644 --- a/hw/bsp/stm32h7/family.cmake +++ b/hw/bsp/stm32h7/family.cmake @@ -106,6 +106,8 @@ function(family_configure_example TARGET RTOS) family_add_tinyusb(${TARGET} OPT_MCU_STM32H7 ${RTOS}) target_sources(${TARGET}-tinyusb PUBLIC ${TOP}/src/portable/synopsys/dwc2/dcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/hcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/dwc2_common.c ) target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD}) diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index f30ec5ee3..a9b3770ee 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -35,45 +35,7 @@ #define DWC2_DEBUG 2 #include "device/dcd.h" -#include "dwc2_type.h" - -// Following symbols must be defined by port header -// - _dwc2_controller[]: array of controllers -// - DWC2_EP_MAX: largest EP counts of all controllers -// - dwc2_phy_init/dwc2_phy_update: phy init called before and after core reset -// - dwc2_dcd_int_enable/dwc2_dcd_int_disable -// - dwc2_remote_wakeup_delay - -#if defined(TUP_USBIP_DWC2_STM32) - #include "dwc2_stm32.h" -#elif defined(TUP_USBIP_DWC2_ESP32) - #include "dwc2_esp32.h" -#elif TU_CHECK_MCU(OPT_MCU_GD32VF103) - #include "dwc2_gd32.h" -#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837) - #include "dwc2_bcm.h" -#elif TU_CHECK_MCU(OPT_MCU_EFM32GG) - #include "dwc2_efm32.h" -#elif TU_CHECK_MCU(OPT_MCU_XMC4000) - #include "dwc2_xmc.h" -#else - #error "Unsupported MCUs" -#endif - -enum { - DWC2_CONTROLLER_COUNT = TU_ARRAY_SIZE(_dwc2_controller) -}; - -// DWC2 registers -//#define DWC2_REG(_port) ((dwc2_regs_t*) _dwc2_controller[_port].reg_base) - -TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) { - if (rhport >= DWC2_CONTROLLER_COUNT) { - // user mis-configured, ignore and use first controller - rhport = 0; - } - return (dwc2_regs_t*) _dwc2_controller[rhport].reg_base; -} +#include "dwc2_common.h" //--------------------------------------------------------------------+ // MACRO TYPEDEF CONSTANT ENUM @@ -511,162 +473,16 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c } } -/*------------------------------------------------------------------*/ -/* Controller API - *------------------------------------------------------------------*/ - -static void reset_core(dwc2_regs_t* dwc2) { - // reset core - dwc2->grstctl |= GRSTCTL_CSRST; - - // wait for reset bit is cleared - // TODO version 4.20a should wait for RESET DONE mask - while (dwc2->grstctl & GRSTCTL_CSRST) {} - - // wait for AHB master IDLE - while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {} - - // wait for device mode ? -} - -static bool phy_hs_supported(dwc2_regs_t* dwc2) { - (void) dwc2; - -#if !TUD_OPT_HIGH_SPEED - return false; -#else - return dwc2->ghwcfg2_bm.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED; -#endif -} - -static void phy_fs_init(dwc2_regs_t* dwc2) { - TU_LOG(DWC2_DEBUG, "Fullspeed PHY init\r\n"); - - // Select FS PHY - dwc2->gusbcfg |= GUSBCFG_PHYSEL; - - // MCU specific PHY init before reset - dwc2_phy_init(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED); - - // Reset core after selecting PHY - reset_core(dwc2); - - // USB turnaround time is critical for certification where long cables and 5-Hubs are used. - // So if you need the AHB to run at less than 30 MHz, and if USB turnaround time is not critical, - // these bits can be programmed to a larger value. Default is 5 - dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (5u << GUSBCFG_TRDT_Pos); - - // MCU specific PHY update post reset - dwc2_phy_update(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED); - - // set max speed - dwc2->dcfg = (dwc2->dcfg & ~DCFG_DSPD_Msk) | (DCFG_DSPD_FS << DCFG_DSPD_Pos); -} - -static void phy_hs_init(dwc2_regs_t* dwc2) { - uint32_t gusbcfg = dwc2->gusbcfg; - - // De-select FS PHY - gusbcfg &= ~GUSBCFG_PHYSEL; - - if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) { - TU_LOG(DWC2_DEBUG, "Highspeed ULPI PHY init\r\n"); - - // Select ULPI - gusbcfg |= GUSBCFG_ULPI_UTMI_SEL; - - // ULPI 8-bit interface, single data rate - gusbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL); - - // default internal VBUS Indicator and Drive - gusbcfg &= ~(GUSBCFG_ULPIEVBUSD | GUSBCFG_ULPIEVBUSI); - - // Disable FS/LS ULPI - gusbcfg &= ~(GUSBCFG_ULPIFSLS | GUSBCFG_ULPICSM); - } else { - TU_LOG(DWC2_DEBUG, "Highspeed UTMI+ PHY init\r\n"); - - // Select UTMI+ with 8-bit interface - gusbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16); - - // Set 16-bit interface if supported - if (dwc2->ghwcfg4_bm.phy_data_width) { - gusbcfg |= GUSBCFG_PHYIF16; - } - } - - // Apply config - dwc2->gusbcfg = gusbcfg; - - // mcu specific phy init - dwc2_phy_init(dwc2, dwc2->ghwcfg2_bm.hs_phy_type); - - // Reset core after selecting PHY - reset_core(dwc2); - - // Set turn-around, must after core reset otherwise it will be clear - // - 9 if using 8-bit PHY interface - // - 5 if using 16-bit PHY interface - gusbcfg &= ~GUSBCFG_TRDT_Msk; - gusbcfg |= (dwc2->ghwcfg4_bm.phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos; - dwc2->gusbcfg = gusbcfg; - - // MCU specific PHY update post reset - dwc2_phy_update(dwc2, dwc2->ghwcfg2_bm.hs_phy_type); - - // Set max speed - uint32_t dcfg = dwc2->dcfg; - dcfg &= ~DCFG_DSPD_Msk; - dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos; - - // XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required - // when using with some PHYs such as USB334x (USB3341, USB3343, USB3346, USB3347) - if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) { - dcfg |= DCFG_XCVRDLY; - } - - dwc2->dcfg = dcfg; -} - -static bool check_dwc2(dwc2_regs_t* dwc2) { -#if CFG_TUSB_DEBUG >= DWC2_DEBUG - // print guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4 - // Run 'python dwc2_info.py' and check dwc2_info.md for bit-field value and comparison with other ports - volatile uint32_t const* p = (volatile uint32_t const*) &dwc2->guid; - TU_LOG1("guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4\r\n"); - for (size_t i = 0; i < 5; i++) { - TU_LOG1("0x%08" PRIX32 ", ", p[i]); - } - TU_LOG1("0x%08" PRIX32 "\r\n", p[5]); -#endif - - // For some reason: GD32VF103 snpsid and all hwcfg register are always zero (skip it) - (void) dwc2; -#if !TU_CHECK_MCU(OPT_MCU_GD32VF103) - uint32_t const gsnpsid = dwc2->gsnpsid & GSNPSID_ID_MASK; - TU_ASSERT(gsnpsid == DWC2_OTG_ID || gsnpsid == DWC2_FS_IOT_ID || gsnpsid == DWC2_HS_IOT_ID); -#endif - - return true; -} - +//-------------------------------------------------------------------- +// Controller API +//-------------------------------------------------------------------- bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { - (void) rhport; (void) rh_init; - // Programming model begins in the last section of the chapter on the USB - // peripheral in each Reference Manual. dwc2_regs_t* dwc2 = DWC2_REG(rhport); - // Check Synopsys ID register, failed if controller clock/power is not enabled - TU_ASSERT(check_dwc2(dwc2)); + TU_ASSERT(dwc2_controller_init(rhport, rh_init)); dcd_disconnect(rhport); - if (phy_hs_supported(dwc2)) { - phy_hs_init(dwc2); // Highspeed - } else { - phy_fs_init(dwc2); // core does not support highspeed or hs phy is not present - } - // Restart PHY clock dwc2->pcgctl &= ~(PCGCTL_STOPPCLK | PCGCTL_GATEHCLK | PCGCTL_PWRCLMP | PCGCTL_RSTPDWNMODULE); diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c new file mode 100644 index 000000000..7fd2311db --- /dev/null +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -0,0 +1,196 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#define DWC2_COMMON_DEBUG 2 + +#if defined(TUP_USBIP_DWC2) && (CFG_TUH_ENABLED || CFG_TUD_ENABLED) + +#include "common/tusb_common.h" +#include "dwc2_common.h" + +static void reset_core(dwc2_regs_t* dwc2) { + // reset core + dwc2->grstctl |= GRSTCTL_CSRST; + + // wait for reset bit is cleared + // TODO version 4.20a should wait for RESET DONE mask + while (dwc2->grstctl & GRSTCTL_CSRST) {} + + // wait for AHB master IDLE + while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {} + + // wait for device mode ? +} + +static bool phy_hs_supported(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init) { + (void) dwc2; + + +#if !TUD_OPT_HIGH_SPEED + return false; +#else + return dwc2->ghwcfg2_bm.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED; +#endif +} + +static void phy_fs_init(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init) { + TU_LOG(DWC2_COMMON_DEBUG, "Fullspeed PHY init\r\n"); + + // Select FS PHY + dwc2->gusbcfg |= GUSBCFG_PHYSEL; + + // MCU specific PHY init before reset + dwc2_phy_init(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED); + + // Reset core after selecting PHY + reset_core(dwc2); + + // USB turnaround time is critical for certification where long cables and 5-Hubs are used. + // So if you need the AHB to run at less than 30 MHz, and if USB turnaround time is not critical, + // these bits can be programmed to a larger value. Default is 5 + dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (5u << GUSBCFG_TRDT_Pos); + + // MCU specific PHY update post reset + dwc2_phy_update(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED); + + // set max speed + dwc2->dcfg = (dwc2->dcfg & ~DCFG_DSPD_Msk) | (DCFG_DSPD_FS << DCFG_DSPD_Pos); +} + +static void phy_hs_init(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init) { + uint32_t gusbcfg = dwc2->gusbcfg; + + // De-select FS PHY + gusbcfg &= ~GUSBCFG_PHYSEL; + + if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) { + TU_LOG(DWC2_COMMON_DEBUG, "Highspeed ULPI PHY init\r\n"); + + // Select ULPI PHY (external) + gusbcfg |= GUSBCFG_ULPI_UTMI_SEL; + + // ULPI is always 8-bit interface + gusbcfg &= ~GUSBCFG_PHYIF16; + + // ULPI select single data rate + gusbcfg &= ~GUSBCFG_DDRSEL; + + // default internal VBUS Indicator and Drive + gusbcfg &= ~(GUSBCFG_ULPIEVBUSD | GUSBCFG_ULPIEVBUSI); + + // Disable FS/LS ULPI + gusbcfg &= ~(GUSBCFG_ULPIFSLS | GUSBCFG_ULPICSM); + } else { + TU_LOG(DWC2_COMMON_DEBUG, "Highspeed UTMI+ PHY init\r\n"); + + // Select UTMI+ PHY (internal) + gusbcfg &= ~GUSBCFG_ULPI_UTMI_SEL; + + // Set 16-bit interface if supported + if (dwc2->ghwcfg4_bm.phy_data_width) { + gusbcfg |= GUSBCFG_PHYIF16; // 16 bit + }else { + gusbcfg &= ~GUSBCFG_PHYIF16; // 8 bit + } + } + + // Apply config + dwc2->gusbcfg = gusbcfg; + + // mcu specific phy init + dwc2_phy_init(dwc2, dwc2->ghwcfg2_bm.hs_phy_type); + + // Reset core after selecting PHY + reset_core(dwc2); + + // Set turn-around, must after core reset otherwise it will be clear + // - 9 if using 8-bit PHY interface + // - 5 if using 16-bit PHY interface + gusbcfg &= ~GUSBCFG_TRDT_Msk; + gusbcfg |= (dwc2->ghwcfg4_bm.phy_data_width ? 5u : 9u) << GUSBCFG_TRDT_Pos; + dwc2->gusbcfg = gusbcfg; + + // MCU specific PHY update post reset + dwc2_phy_update(dwc2, dwc2->ghwcfg2_bm.hs_phy_type); + + // Set max speed + uint32_t dcfg = dwc2->dcfg; + dcfg &= ~DCFG_DSPD_Msk; + dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos; + + // XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required + // when using with some PHYs such as USB334x (USB3341, USB3343, USB3346, USB3347) + if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) { + dcfg |= DCFG_XCVRDLY; + } + + dwc2->dcfg = dcfg; +} + +static bool check_dwc2(dwc2_regs_t* dwc2) { +#if CFG_TUSB_DEBUG >= DWC2_COMMON_DEBUG + // print guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4 + // Run 'python dwc2_info.py' and check dwc2_info.md for bit-field value and comparison with other ports + volatile uint32_t const* p = (volatile uint32_t const*) &dwc2->guid; + TU_LOG1("guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4\r\n"); + for (size_t i = 0; i < 5; i++) { + TU_LOG1("0x%08" PRIX32 ", ", p[i]); + } + TU_LOG1("0x%08" PRIX32 "\r\n", p[5]); +#endif + + // For some reason: GD32VF103 gsnpsid and all hwcfg register are always zero (skip it) + (void) dwc2; +#if !TU_CHECK_MCU(OPT_MCU_GD32VF103) + uint32_t const gsnpsid = dwc2->gsnpsid & GSNPSID_ID_MASK; + TU_ASSERT(gsnpsid == DWC2_OTG_ID || gsnpsid == DWC2_FS_IOT_ID || gsnpsid == DWC2_HS_IOT_ID); +#endif + + return true; +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- +bool dwc2_controller_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { + (void) rh_init; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + // Check Synopsys ID register, failed if controller clock/power is not enabled + TU_ASSERT(check_dwc2(dwc2)); + + if (phy_hs_supported(dwc2, rh_init)) { + phy_hs_init(dwc2, rh_init); // Highspeed + } else { + phy_fs_init(dwc2, rh_init); // core does not support highspeed or hs phy is not present + } + + return true; +} + +#endif diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h new file mode 100644 index 000000000..2a48654c0 --- /dev/null +++ b/src/portable/synopsys/dwc2/dwc2_common.h @@ -0,0 +1,70 @@ +/* +* The MIT License (MIT) + * + * Copyright (c) 2024 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef TUSB_DWC2_COMMON_H +#define TUSB_DWC2_COMMON_H + +#include "dwc2_type.h" + +// Following symbols must be defined by port header +// - _dwc2_controller[]: array of controllers +// - DWC2_EP_MAX: largest EP counts of all controllers +// - dwc2_phy_init/dwc2_phy_update: phy init called before and after core reset +// - dwc2_dcd_int_enable/dwc2_dcd_int_disable +// - dwc2_remote_wakeup_delay + +#if defined(TUP_USBIP_DWC2_STM32) + #include "dwc2_stm32.h" +#elif defined(TUP_USBIP_DWC2_ESP32) + #include "dwc2_esp32.h" +#elif TU_CHECK_MCU(OPT_MCU_GD32VF103) + #include "dwc2_gd32.h" +#elif TU_CHECK_MCU(OPT_MCU_BCM2711, OPT_MCU_BCM2835, OPT_MCU_BCM2837) + #include "dwc2_bcm.h" +#elif TU_CHECK_MCU(OPT_MCU_EFM32GG) + #include "dwc2_efm32.h" +#elif TU_CHECK_MCU(OPT_MCU_XMC4000) + #include "dwc2_xmc.h" +#else + #error "Unsupported MCUs" +#endif + +enum { + DWC2_CONTROLLER_COUNT = TU_ARRAY_SIZE(_dwc2_controller) +}; + +TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) { + if (rhport >= DWC2_CONTROLLER_COUNT) { + // user mis-configured, ignore and use first controller + rhport = 0; + } + return (dwc2_regs_t*)_dwc2_controller[rhport].reg_base; +} + + +bool dwc2_controller_init(uint8_t rhport, const tusb_rhport_init_t* rh_init); + +#endif diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index cf05bbfec..b9dc413b1 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -31,8 +31,8 @@ * opensource.org/licenses/BSD-3-Clause */ -#ifndef _TUSB_DWC2_TYPES_H_ -#define _TUSB_DWC2_TYPES_H_ +#ifndef TUSB_DWC2_TYPES_H_ +#define TUSB_DWC2_TYPES_H_ #include "stdint.h" diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c new file mode 100644 index 000000000..8798f982c --- /dev/null +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -0,0 +1,167 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if CFG_TUH_ENABLED && defined(TUP_USBIP_DWC2) + +// Debug level for DWC2 +#define DWC2_DEBUG 2 + +#include "host/hcd.h" +#include "dwc2_common.h" + +//--------------------------------------------------------------------+ +// Controller API +//--------------------------------------------------------------------+ + +// optional hcd configuration, called by tuh_configure() +bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) { + (void) rhport; + (void) cfg_id; + (void) cfg_param; + + return false; +} + +// Initialize controller to host mode +bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { + (void) rhport; + (void) rh_init; + return false; +} + +// Interrupt Handler +void hcd_int_handler(uint8_t rhport, bool in_isr) { + (void) rhport; + (void) in_isr; +} + +// Enable USB interrupt +void hcd_int_enable (uint8_t rhport) { + (void) rhport; +} + +// Disable USB interrupt +void hcd_int_disable(uint8_t rhport) { + (void) rhport; +} + +// Get frame number (1ms) +uint32_t hcd_frame_number(uint8_t rhport) { + (void) rhport; + + return 0; +} + +//--------------------------------------------------------------------+ +// Port API +//--------------------------------------------------------------------+ + +// Get the current connect status of roothub port +bool hcd_port_connect_status(uint8_t rhport) { + (void) rhport; + + return false; +} + +// Reset USB bus on the port. Return immediately, bus reset sequence may not be complete. +// Some port would require hcd_port_reset_end() to be invoked after 10ms to complete the reset sequence. +void hcd_port_reset(uint8_t rhport) { + (void) rhport; +} + +// Complete bus reset sequence, may be required by some controllers +void hcd_port_reset_end(uint8_t rhport) { + (void) rhport; +} + +// Get port link speed +tusb_speed_t hcd_port_speed_get(uint8_t rhport) { + (void) rhport; + + return TUSB_SPEED_FULL; +} + +// HCD closes all opened endpoints belong to this device +void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { + (void) rhport; + (void) dev_addr; +} + +//--------------------------------------------------------------------+ +// Endpoints API +//--------------------------------------------------------------------+ + +// Open an endpoint +bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) { + (void) rhport; + (void) dev_addr; + (void) ep_desc; + + return false; +} + +// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked +bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) { + (void) rhport; + (void) dev_addr; + (void) ep_addr; + (void) buffer; + (void) buflen; + + return false; +} + +// Abort a queued transfer. Note: it can only abort transfer that has not been started +// Return true if a queued transfer is aborted, false if there is no transfer to abort +bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { + (void) rhport; + (void) dev_addr; + (void) ep_addr; + + return false; +} + +// Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked +bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) { + (void) rhport; + (void) dev_addr; + (void) setup_packet; + + return false; +} + +// clear stall, data toggle is also reset to DATA0 +bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { + (void) rhport; + (void) dev_addr; + (void) ep_addr; + + return false; +} + +#endif -- cgit v1.3.1 From 4012e150757b517620465d93a22b0787c6400c5d Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 15 Oct 2024 17:55:24 +0700 Subject: move core init code to dwc2 common. update/correct build for esppressif --- .../src/FreeRTOSConfig/FreeRTOSConfig.h | 2 +- .../device/audio_4_channel_mic_freertos/src/main.c | 6 +-- .../audio_4_channel_mic_freertos/src/tusb_config.h | 2 +- .../src/FreeRTOSConfig/FreeRTOSConfig.h | 2 +- examples/device/audio_test_freertos/src/main.c | 9 ++-- .../device/audio_test_freertos/src/tusb_config.h | 2 +- examples/device/board_test/src/main.c | 2 +- examples/device/board_test/src/tusb_config.h | 2 +- .../src/FreeRTOSConfig/FreeRTOSConfig.h | 2 +- examples/device/cdc_msc_freertos/src/main.c | 6 +-- examples/device/cdc_msc_freertos/src/tusb_config.h | 2 +- .../src/FreeRTOSConfig/FreeRTOSConfig.h | 2 +- examples/device/hid_composite_freertos/src/main.c | 6 +-- .../hid_composite_freertos/src/tusb_config.h | 2 +- examples/device/video_capture/src/main.c | 4 +- examples/device/video_capture/src/tusb_config.h | 2 +- examples/device/video_capture_2ch/src/main.c | 4 +- .../device/video_capture_2ch/src/tusb_config.h | 2 +- .../src/FreeRTOSConfig/FreeRTOSConfig.h | 2 +- examples/host/cdc_msc_hid_freertos/src/cdc_app.c | 2 +- examples/host/cdc_msc_hid_freertos/src/main.c | 6 +-- .../host/cdc_msc_hid_freertos/src/tusb_config.h | 2 +- examples/typec/power_delivery/src/main.c | 2 +- hw/bsp/board_api.h | 2 +- hw/bsp/broadcom_32bit/family.cmake | 2 + hw/bsp/broadcom_64bit/family.cmake | 2 + .../components/tinyusb_src/CMakeLists.txt | 2 + hw/bsp/gd32vf103/family.cmake | 2 + hw/bsp/stm32f2/family.cmake | 2 + hw/bsp/stm32f4/family.cmake | 2 + hw/bsp/stm32f7/family.cmake | 2 + hw/bsp/stm32h7/boards/stm32h743eval/board.cmake | 2 +- hw/bsp/stm32u5/family.cmake | 17 +++--- hw/bsp/xmc4000/family.cmake | 2 + src/common/tusb_verify.h | 2 +- src/portable/synopsys/dwc2/dcd_dwc2.c | 34 +++++++----- src/portable/synopsys/dwc2/dwc2_common.c | 63 ++++++++++++---------- src/portable/synopsys/dwc2/dwc2_common.h | 5 +- src/portable/synopsys/dwc2/dwc2_esp32.h | 5 +- src/portable/synopsys/dwc2/dwc2_type.h | 60 ++++++++++----------- src/tusb_option.h | 3 +- tools/build_utils.py | 29 +++++----- 42 files changed, 171 insertions(+), 140 deletions(-) (limited to 'src') diff --git a/examples/device/audio_4_channel_mic_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/audio_4_channel_mic_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h index 869500ad2..902a54f08 100644 --- a/examples/device/audio_4_channel_mic_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h +++ b/examples/device/audio_4_channel_mic_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h @@ -48,7 +48,7 @@ // Include MCU header #include "bsp/board_mcu.h" -#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3 +#if TUSB_MCU_VENDOR_ESPRESSIF #error "ESP32-Sx should use IDF's FreeRTOSConfig.h" #endif diff --git a/examples/device/audio_4_channel_mic_freertos/src/main.c b/examples/device/audio_4_channel_mic_freertos/src/main.c index 2ac7516cd..c9de4029a 100644 --- a/examples/device/audio_4_channel_mic_freertos/src/main.c +++ b/examples/device/audio_4_channel_mic_freertos/src/main.c @@ -39,7 +39,7 @@ #include "bsp/board_api.h" #include "tusb.h" -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF // ESP-IDF need "freertos/" prefix in include path. // CFG_TUSB_OS_INC_PATH should be defined accordingly. #include "freertos/FreeRTOS.h" @@ -186,14 +186,14 @@ int main(void) #endif // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 - #if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) + #if !TUSB_MCU_VENDOR_ESPRESSIF vTaskStartScheduler(); #endif return 0; } -#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) +#if TUSB_MCU_VENDOR_ESPRESSIF void app_main(void) { main(); diff --git a/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h b/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h index 88f20278b..5cd93b0d6 100644 --- a/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h +++ b/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h @@ -59,7 +59,7 @@ extern "C" { #endif // Espressif IDF requires "freertos/" prefix in include path -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF #define CFG_TUSB_OS_INC_PATH freertos/ #endif diff --git a/examples/device/audio_test_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/audio_test_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h index 869500ad2..902a54f08 100644 --- a/examples/device/audio_test_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h +++ b/examples/device/audio_test_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h @@ -48,7 +48,7 @@ // Include MCU header #include "bsp/board_mcu.h" -#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3 +#if TUSB_MCU_VENDOR_ESPRESSIF #error "ESP32-Sx should use IDF's FreeRTOSConfig.h" #endif diff --git a/examples/device/audio_test_freertos/src/main.c b/examples/device/audio_test_freertos/src/main.c index 4e2264b5d..c5143c3fc 100644 --- a/examples/device/audio_test_freertos/src/main.c +++ b/examples/device/audio_test_freertos/src/main.c @@ -38,7 +38,7 @@ #include "bsp/board_api.h" #include "tusb.h" -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF // ESP-IDF need "freertos/" prefix in include path. // CFG_TUSB_OS_INC_PATH should be defined accordingly. #include "freertos/FreeRTOS.h" @@ -133,16 +133,15 @@ int main(void) #endif // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 - #if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) + #if !TUSB_MCU_VENDOR_ESPRESSIF vTaskStartScheduler(); #endif return 0; } -#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) -void app_main(void) -{ +#if TUSB_MCU_VENDOR_ESPRESSIF +void app_main(void) { main(); } #endif diff --git a/examples/device/audio_test_freertos/src/tusb_config.h b/examples/device/audio_test_freertos/src/tusb_config.h index 8b376a4c3..61c5cbb96 100644 --- a/examples/device/audio_test_freertos/src/tusb_config.h +++ b/examples/device/audio_test_freertos/src/tusb_config.h @@ -59,7 +59,7 @@ extern "C" { #endif // Espressif IDF requires "freertos/" prefix in include path -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF #define CFG_TUSB_OS_INC_PATH freertos/ #endif diff --git a/examples/device/board_test/src/main.c b/examples/device/board_test/src/main.c index 91799eb89..2269d45f1 100644 --- a/examples/device/board_test/src/main.c +++ b/examples/device/board_test/src/main.c @@ -67,7 +67,7 @@ int main(void) { } } -#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3 +#if TUSB_MCU_VENDOR_ESPRESSIF void app_main(void) { main(); } diff --git a/examples/device/board_test/src/tusb_config.h b/examples/device/board_test/src/tusb_config.h index 89c27d1c0..8ac3bc8de 100644 --- a/examples/device/board_test/src/tusb_config.h +++ b/examples/device/board_test/src/tusb_config.h @@ -44,7 +44,7 @@ #endif // Espressif IDF requires "freertos/" prefix in include path -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF #define CFG_TUSB_OS_INC_PATH freertos/ #endif diff --git a/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h index 869500ad2..902a54f08 100644 --- a/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h +++ b/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h @@ -48,7 +48,7 @@ // Include MCU header #include "bsp/board_mcu.h" -#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3 +#if TUSB_MCU_VENDOR_ESPRESSIF #error "ESP32-Sx should use IDF's FreeRTOSConfig.h" #endif diff --git a/examples/device/cdc_msc_freertos/src/main.c b/examples/device/cdc_msc_freertos/src/main.c index f70267e33..a05ceff58 100644 --- a/examples/device/cdc_msc_freertos/src/main.c +++ b/examples/device/cdc_msc_freertos/src/main.c @@ -30,7 +30,7 @@ #include "bsp/board_api.h" #include "tusb.h" -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF // ESP-IDF need "freertos/" prefix in include path. // CFG_TUSB_OS_INC_PATH should be defined accordingly. #include "freertos/FreeRTOS.h" @@ -111,14 +111,14 @@ int main(void) { #endif // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 -#if !TUP_MCU_ESPRESSIF +#if !TUSB_MCU_VENDOR_ESPRESSIF vTaskStartScheduler(); #endif return 0; } -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF void app_main(void) { main(); } diff --git a/examples/device/cdc_msc_freertos/src/tusb_config.h b/examples/device/cdc_msc_freertos/src/tusb_config.h index e743c9148..c3f2f7fb5 100644 --- a/examples/device/cdc_msc_freertos/src/tusb_config.h +++ b/examples/device/cdc_msc_freertos/src/tusb_config.h @@ -59,7 +59,7 @@ #endif // Espressif IDF requires "freertos/" prefix in include path -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF #define CFG_TUSB_OS_INC_PATH freertos/ #endif diff --git a/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h index 869500ad2..902a54f08 100644 --- a/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h +++ b/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h @@ -48,7 +48,7 @@ // Include MCU header #include "bsp/board_mcu.h" -#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3 +#if TUSB_MCU_VENDOR_ESPRESSIF #error "ESP32-Sx should use IDF's FreeRTOSConfig.h" #endif diff --git a/examples/device/hid_composite_freertos/src/main.c b/examples/device/hid_composite_freertos/src/main.c index ae091571e..30c0331ef 100644 --- a/examples/device/hid_composite_freertos/src/main.c +++ b/examples/device/hid_composite_freertos/src/main.c @@ -31,7 +31,7 @@ #include "tusb.h" #include "usb_descriptors.h" -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF // ESP-IDF need "freertos/" prefix in include path. // CFG_TUSB_OS_INC_PATH should be defined accordingly. #include "freertos/FreeRTOS.h" @@ -113,14 +113,14 @@ int main(void) xTimerStart(blinky_tm, 0); // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 -#if !TUP_MCU_ESPRESSIF +#if !TUSB_MCU_VENDOR_ESPRESSIF vTaskStartScheduler(); #endif return 0; } -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF void app_main(void) { main(); diff --git a/examples/device/hid_composite_freertos/src/tusb_config.h b/examples/device/hid_composite_freertos/src/tusb_config.h index 0689e0f23..6ec38b95c 100644 --- a/examples/device/hid_composite_freertos/src/tusb_config.h +++ b/examples/device/hid_composite_freertos/src/tusb_config.h @@ -59,7 +59,7 @@ #endif // Espressif IDF requires "freertos/" prefix in include path -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF #define CFG_TUSB_OS_INC_PATH freertos/ #endif diff --git a/examples/device/video_capture/src/main.c b/examples/device/video_capture/src/main.c index aeeeb8930..8bb924c61 100644 --- a/examples/device/video_capture/src/main.c +++ b/examples/device/video_capture/src/main.c @@ -292,7 +292,7 @@ void led_blinking_task(void* param) { #define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE #define VIDEO_STACK_SIZE (configMINIMAL_STACK_SIZE*4) -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF #define USBD_STACK_SIZE 4096 int main(void); void app_main(void) { @@ -352,7 +352,7 @@ void freertos_init_task(void) { #endif // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 - #if !TUP_MCU_ESPRESSIF + #if !TUSB_MCU_VENDOR_ESPRESSIF vTaskStartScheduler(); #endif } diff --git a/examples/device/video_capture/src/tusb_config.h b/examples/device/video_capture/src/tusb_config.h index 3a6daa3d3..6dbd6f2a5 100644 --- a/examples/device/video_capture/src/tusb_config.h +++ b/examples/device/video_capture/src/tusb_config.h @@ -58,7 +58,7 @@ #endif // Espressif IDF requires "freertos/" prefix in include path -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF #define CFG_TUSB_OS_INC_PATH freertos/ #endif diff --git a/examples/device/video_capture_2ch/src/main.c b/examples/device/video_capture_2ch/src/main.c index dd6983766..245e7abb8 100644 --- a/examples/device/video_capture_2ch/src/main.c +++ b/examples/device/video_capture_2ch/src/main.c @@ -300,7 +300,7 @@ void led_blinking_task(void* param) { #define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE #define VIDEO_STACK_SIZE (configMINIMAL_STACK_SIZE*4) -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF #define USBD_STACK_SIZE 4096 int main(void); void app_main(void) { @@ -360,7 +360,7 @@ void freertos_init_task(void) { #endif // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 - #if !TUP_MCU_ESPRESSIF + #if !TUSB_MCU_VENDOR_ESPRESSIF vTaskStartScheduler(); #endif } diff --git a/examples/device/video_capture_2ch/src/tusb_config.h b/examples/device/video_capture_2ch/src/tusb_config.h index 43c7dfc90..91775a327 100644 --- a/examples/device/video_capture_2ch/src/tusb_config.h +++ b/examples/device/video_capture_2ch/src/tusb_config.h @@ -58,7 +58,7 @@ #endif // Espressif IDF requires "freertos/" prefix in include path -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF #define CFG_TUSB_OS_INC_PATH freertos/ #endif diff --git a/examples/host/cdc_msc_hid_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/host/cdc_msc_hid_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h index bd754518d..6a886adec 100644 --- a/examples/host/cdc_msc_hid_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h +++ b/examples/host/cdc_msc_hid_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h @@ -48,7 +48,7 @@ // Include MCU header #include "bsp/board_mcu.h" -#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3 +#if TUSB_MCU_VENDOR_ESPRESSIF #error "ESP32-Sx should use IDF's FreeRTOSConfig.h" #endif diff --git a/examples/host/cdc_msc_hid_freertos/src/cdc_app.c b/examples/host/cdc_msc_hid_freertos/src/cdc_app.c index f3495ab28..dfe1418f6 100644 --- a/examples/host/cdc_msc_hid_freertos/src/cdc_app.c +++ b/examples/host/cdc_msc_hid_freertos/src/cdc_app.c @@ -27,7 +27,7 @@ #include "tusb.h" #include "bsp/board_api.h" -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF // ESP-IDF need "freertos/" prefix in include path. // CFG_TUSB_OS_INC_PATH should be defined accordingly. #include "freertos/FreeRTOS.h" diff --git a/examples/host/cdc_msc_hid_freertos/src/main.c b/examples/host/cdc_msc_hid_freertos/src/main.c index 7fb84a40f..fe050d334 100644 --- a/examples/host/cdc_msc_hid_freertos/src/main.c +++ b/examples/host/cdc_msc_hid_freertos/src/main.c @@ -30,7 +30,7 @@ #include "bsp/board_api.h" #include "tusb.h" -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF // ESP-IDF need "freertos/" prefix in include path. // CFG_TUSB_OS_INC_PATH should be defined accordingly. #include "freertos/FreeRTOS.h" @@ -107,14 +107,14 @@ int main(void) { xTimerStart(blinky_tm, 0); // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 -#if !TUP_MCU_ESPRESSIF +#if !TUSB_MCU_VENDOR_ESPRESSIF vTaskStartScheduler(); #endif return 0; } -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF void app_main(void) { main(); } diff --git a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h index 0aab2cd2f..02de4197b 100644 --- a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h +++ b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h @@ -44,7 +44,7 @@ #endif // Espressif IDF requires "freertos/" prefix in include path -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF #define CFG_TUSB_OS_INC_PATH freertos/ #endif diff --git a/examples/typec/power_delivery/src/main.c b/examples/typec/power_delivery/src/main.c index a8214f34e..068dbbeb1 100644 --- a/examples/typec/power_delivery/src/main.c +++ b/examples/typec/power_delivery/src/main.c @@ -71,7 +71,7 @@ int main(void) } } -#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3 +#if TUSB_MCU_VENDOR_ESPRESSIF void app_main(void) { main(); diff --git a/hw/bsp/board_api.h b/hw/bsp/board_api.h index a458a3fdc..eaee1ee97 100644 --- a/hw/bsp/board_api.h +++ b/hw/bsp/board_api.h @@ -39,7 +39,7 @@ extern "C" { #include "tusb.h" #if CFG_TUSB_OS == OPT_OS_FREERTOS -#if TUP_MCU_ESPRESSIF +#if TUSB_MCU_VENDOR_ESPRESSIF // ESP-IDF need "freertos/" prefix in include path. // CFG_TUSB_OS_INC_PATH should be defined accordingly. #include "freertos/FreeRTOS.h" diff --git a/hw/bsp/broadcom_32bit/family.cmake b/hw/bsp/broadcom_32bit/family.cmake index 6205d4e1b..93e7d3545 100644 --- a/hw/bsp/broadcom_32bit/family.cmake +++ b/hw/bsp/broadcom_32bit/family.cmake @@ -97,6 +97,8 @@ function(family_configure_example TARGET RTOS) family_add_tinyusb(${TARGET} OPT_MCU_BCM2835 ${RTOS}) target_sources(${TARGET}-tinyusb PUBLIC ${TOP}/src/portable/synopsys/dwc2/dcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/hcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/dwc2_common.c ) target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD}) diff --git a/hw/bsp/broadcom_64bit/family.cmake b/hw/bsp/broadcom_64bit/family.cmake index f373dc633..d790944bc 100644 --- a/hw/bsp/broadcom_64bit/family.cmake +++ b/hw/bsp/broadcom_64bit/family.cmake @@ -104,6 +104,8 @@ function(family_configure_example TARGET RTOS) family_add_tinyusb(${TARGET} OPT_MCU_BCM${BCM_VERSION} ${RTOS}) target_sources(${TARGET}-tinyusb PUBLIC ${TOP}/src/portable/synopsys/dwc2/dcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/hcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/dwc2_common.c ) target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD}) diff --git a/hw/bsp/espressif/components/tinyusb_src/CMakeLists.txt b/hw/bsp/espressif/components/tinyusb_src/CMakeLists.txt index 26c7e4030..ae94a0ea4 100644 --- a/hw/bsp/espressif/components/tinyusb_src/CMakeLists.txt +++ b/hw/bsp/espressif/components/tinyusb_src/CMakeLists.txt @@ -34,6 +34,8 @@ list(APPEND srcs ${tusb_src}/class/vendor/vendor_device.c ${tusb_src}/class/video/video_device.c ${tusb_src}/portable/synopsys/dwc2/dcd_dwc2.c + ${tusb_src}/portable/synopsys/dwc2/hcd_dwc2.c + ${tusb_src}/portable/synopsys/dwc2/dwc2_common.c # host ${tusb_src}/host/usbh.c ${tusb_src}/host/hub.c diff --git a/hw/bsp/gd32vf103/family.cmake b/hw/bsp/gd32vf103/family.cmake index 5f4a3da8d..1441e41de 100644 --- a/hw/bsp/gd32vf103/family.cmake +++ b/hw/bsp/gd32vf103/family.cmake @@ -108,6 +108,8 @@ function(family_configure_example TARGET RTOS) family_add_tinyusb(${TARGET} OPT_MCU_GD32VF103 ${RTOS}) target_sources(${TARGET}-tinyusb PUBLIC ${TOP}/src/portable/synopsys/dwc2/dcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/hcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/dwc2_common.c ) target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD}) diff --git a/hw/bsp/stm32f2/family.cmake b/hw/bsp/stm32f2/family.cmake index a01ebef5c..538a6cd66 100644 --- a/hw/bsp/stm32f2/family.cmake +++ b/hw/bsp/stm32f2/family.cmake @@ -102,6 +102,8 @@ function(family_configure_example TARGET RTOS) family_add_tinyusb(${TARGET} OPT_MCU_STM32F2 ${RTOS}) target_sources(${TARGET}-tinyusb PUBLIC ${TOP}/src/portable/synopsys/dwc2/dcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/hcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/dwc2_common.c ) target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD}) diff --git a/hw/bsp/stm32f4/family.cmake b/hw/bsp/stm32f4/family.cmake index f24ef366e..a2832763a 100644 --- a/hw/bsp/stm32f4/family.cmake +++ b/hw/bsp/stm32f4/family.cmake @@ -102,6 +102,8 @@ function(family_configure_example TARGET RTOS) family_add_tinyusb(${TARGET} OPT_MCU_STM32F4 ${RTOS}) target_sources(${TARGET}-tinyusb PUBLIC ${TOP}/src/portable/synopsys/dwc2/dcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/hcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/dwc2_common.c ) target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD}) diff --git a/hw/bsp/stm32f7/family.cmake b/hw/bsp/stm32f7/family.cmake index 938c2697a..798dc4dbb 100644 --- a/hw/bsp/stm32f7/family.cmake +++ b/hw/bsp/stm32f7/family.cmake @@ -104,6 +104,8 @@ function(family_configure_example TARGET RTOS) family_add_tinyusb(${TARGET} OPT_MCU_STM32F7 ${RTOS}) target_sources(${TARGET}-tinyusb PUBLIC ${TOP}/src/portable/synopsys/dwc2/dcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/hcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/dwc2_common.c ) target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD}) diff --git a/hw/bsp/stm32h7/boards/stm32h743eval/board.cmake b/hw/bsp/stm32h7/boards/stm32h743eval/board.cmake index 6d7a97741..2ff874690 100644 --- a/hw/bsp/stm32h7/boards/stm32h743eval/board.cmake +++ b/hw/bsp/stm32h7/boards/stm32h743eval/board.cmake @@ -1,6 +1,6 @@ set(MCU_VARIANT stm32h743xx) set(JLINK_DEVICE stm32h743xi) -# set(JLINK_OPTION "-USB jtrace") +set(JLINK_OPTION "-USB jtrace") set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/../../linker/${MCU_VARIANT}_flash.ld) diff --git a/hw/bsp/stm32u5/family.cmake b/hw/bsp/stm32u5/family.cmake index f7a7aeb33..3ab7cdf8b 100644 --- a/hw/bsp/stm32u5/family.cmake +++ b/hw/bsp/stm32u5/family.cmake @@ -102,16 +102,13 @@ function(family_configure_example TARGET RTOS) # Add TinyUSB target and port source family_add_tinyusb(${TARGET} OPT_MCU_STM32U5 ${RTOS}) - if ((${MCU_VARIANT} STREQUAL "stm32u535xx") OR (${MCU_VARIANT} STREQUAL "stm32u545xx")) - target_sources(${TARGET}-tinyusb PUBLIC - ${TOP}/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c - ) - else () - target_sources(${TARGET}-tinyusb PUBLIC - ${TOP}/src/portable/synopsys/dwc2/dcd_dwc2.c - #${TOP}/src/portable/st/typec/typec_stm32.c - ) - endif () + target_sources(${TARGET}-tinyusb PUBLIC + ${TOP}/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c + ${TOP}/src/portable/synopsys/dwc2/dcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/hcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/dwc2_common.c + #${TOP}/src/portable/st/typec/typec_stm32.c + ) target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD}) # Link dependencies diff --git a/hw/bsp/xmc4000/family.cmake b/hw/bsp/xmc4000/family.cmake index d91e6f0b6..85444db28 100644 --- a/hw/bsp/xmc4000/family.cmake +++ b/hw/bsp/xmc4000/family.cmake @@ -86,6 +86,8 @@ function(family_configure_example TARGET RTOS) family_add_tinyusb(${TARGET} OPT_MCU_XMC4000 ${RTOS}) target_sources(${TARGET}-tinyusb PUBLIC ${TOP}/src/portable/synopsys/dwc2/dcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/hcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/dwc2_common.c ) target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD}) diff --git a/src/common/tusb_verify.h b/src/common/tusb_verify.h index 3e0f1f106..6d02d3572 100644 --- a/src/common/tusb_verify.h +++ b/src/common/tusb_verify.h @@ -83,7 +83,7 @@ if ( (*ARM_CM_DHCSR) & 1UL ) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ \ } while(0) -#elif defined(__riscv) && !TUP_MCU_ESPRESSIF +#elif defined(__riscv) && !TUSB_MCU_VENDOR_ESPRESSIF #define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0) #elif defined(_mips) diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index a9b3770ee..c67c34404 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -480,21 +480,28 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { (void) rh_init; dwc2_regs_t* dwc2 = DWC2_REG(rhport); - TU_ASSERT(dwc2_controller_init(rhport, rh_init)); + TU_ASSERT(dwc2_core_init(rhport, rh_init)); + + // Device Initialization dcd_disconnect(rhport); - // Restart PHY clock - dwc2->pcgctl &= ~(PCGCTL_STOPPCLK | PCGCTL_GATEHCLK | PCGCTL_PWRCLMP | PCGCTL_RSTPDWNMODULE); + // Set device max speed + uint32_t dcfg = dwc2->dcfg & ~DCFG_DSPD_Msk; + if (dwc2_core_is_highspeed(dwc2, rh_init)) { + dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos; + + // XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required + // when using with some PHYs such as USB334x (USB3341, USB3343, USB3346, USB3347) + if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) { + dcfg |= DCFG_XCVRDLY; + } + }else { + dcfg |= DCFG_DSPD_FS << DCFG_DSPD_Pos; + } + dwc2->dcfg = dcfg; - /* Set HS/FS Timeout Calibration to 7 (max available value). - * The number of PHY clocks that the application programs in - * this field is added to the high/full speed interpacket timeout - * duration in the core to account for any additional delays - * introduced by the PHY. This can be required, because the delay - * introduced by the PHY in generating the linestate condition - * can vary from one PHY to another. - */ - dwc2->gusbcfg |= (7ul << GUSBCFG_TOCAL_Pos); + // Enable PHY clock TODO stop/gate clock when suspended mode + dwc2->pcgcctl &= ~(PCGCCTL_STOPPCLK | PCGCCTL_GATEHCLK | PCGCCTL_PWRCLMP | PCGCCTL_RSTPDWNMODULE); // Force device mode dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FHMOD) | GUSBCFG_FDMOD; @@ -502,8 +509,7 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Clear A override, force B Valid dwc2->gotgctl = (dwc2->gotgctl & ~GOTGCTL_AVALOEN) | GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL; - // If USB host misbehaves during status portion of control xfer - // (non zero-length packet), send STALL back and discard. + // If USB host misbehaves during status portion of control xfer (non zero-length packet), send STALL back and discard dwc2->dcfg |= DCFG_NZLSOHSK; dfifo_flush_tx(dwc2, 0x10); // all tx fifo diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index 7fd2311db..190dc0734 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -30,7 +30,14 @@ #if defined(TUP_USBIP_DWC2) && (CFG_TUH_ENABLED || CFG_TUD_ENABLED) -#include "common/tusb_common.h" +#if CFG_TUD_ENABLED +#include "device/dcd.h" +#endif + +#if CFG_TUH_ENABLED +#include "host/hcd.h" +#endif + #include "dwc2_common.h" static void reset_core(dwc2_regs_t* dwc2) { @@ -47,18 +54,24 @@ static void reset_core(dwc2_regs_t* dwc2) { // wait for device mode ? } -static bool phy_hs_supported(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init) { +bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init) { (void) dwc2; +#if CFG_TUD_ENABLED + if (rh_init->role == TUSB_ROLE_DEVICE && !TUD_OPT_HIGH_SPEED) { + return false; + } +#endif +#if CFG_TUH_ENABLED + if (rh_init->role == TUSB_ROLE_DEVICE && !TUH_OPT_HIGH_SPEED) { + return false; + } +#endif -#if !TUD_OPT_HIGH_SPEED - return false; -#else return dwc2->ghwcfg2_bm.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED; -#endif } -static void phy_fs_init(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init) { +static void phy_fs_init(dwc2_regs_t* dwc2) { TU_LOG(DWC2_COMMON_DEBUG, "Fullspeed PHY init\r\n"); // Select FS PHY @@ -77,12 +90,9 @@ static void phy_fs_init(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init) { // MCU specific PHY update post reset dwc2_phy_update(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED); - - // set max speed - dwc2->dcfg = (dwc2->dcfg & ~DCFG_DSPD_Msk) | (DCFG_DSPD_FS << DCFG_DSPD_Pos); } -static void phy_hs_init(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init) { +static void phy_hs_init(dwc2_regs_t* dwc2) { uint32_t gusbcfg = dwc2->gusbcfg; // De-select FS PHY @@ -137,19 +147,6 @@ static void phy_hs_init(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init) { // MCU specific PHY update post reset dwc2_phy_update(dwc2, dwc2->ghwcfg2_bm.hs_phy_type); - - // Set max speed - uint32_t dcfg = dwc2->dcfg; - dcfg &= ~DCFG_DSPD_Msk; - dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos; - - // XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required - // when using with some PHYs such as USB334x (USB3341, USB3343, USB3346, USB3347) - if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) { - dcfg |= DCFG_XCVRDLY; - } - - dwc2->dcfg = dcfg; } static bool check_dwc2(dwc2_regs_t* dwc2) { @@ -177,19 +174,29 @@ static bool check_dwc2(dwc2_regs_t* dwc2) { //-------------------------------------------------------------------- // //-------------------------------------------------------------------- -bool dwc2_controller_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { +bool dwc2_core_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { (void) rh_init; dwc2_regs_t* dwc2 = DWC2_REG(rhport); // Check Synopsys ID register, failed if controller clock/power is not enabled TU_ASSERT(check_dwc2(dwc2)); - if (phy_hs_supported(dwc2, rh_init)) { - phy_hs_init(dwc2, rh_init); // Highspeed + if (dwc2_core_is_highspeed(dwc2, rh_init)) { + phy_hs_init(dwc2); // Highspeed } else { - phy_fs_init(dwc2, rh_init); // core does not support highspeed or hs phy is not present + phy_fs_init(dwc2); // core does not support highspeed or hs phy is not present } + /* Set HS/FS Timeout Calibration to 7 (max available value). + * The number of PHY clocks that the application programs in + * this field is added to the high/full speed interpacket timeout + * duration in the core to account for any additional delays + * introduced by the PHY. This can be required, because the delay + * introduced by the PHY in generating the linestate condition + * can vary from one PHY to another. + */ + dwc2->gusbcfg |= (7ul << GUSBCFG_TOCAL_Pos); + return true; } diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h index 2a48654c0..4039ebaa3 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.h +++ b/src/portable/synopsys/dwc2/dwc2_common.h @@ -27,6 +27,7 @@ #ifndef TUSB_DWC2_COMMON_H #define TUSB_DWC2_COMMON_H +#include "common/tusb_common.h" #include "dwc2_type.h" // Following symbols must be defined by port header @@ -64,7 +65,7 @@ TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) { return (dwc2_regs_t*)_dwc2_controller[rhport].reg_base; } - -bool dwc2_controller_init(uint8_t rhport, const tusb_rhport_init_t* rh_init); +bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init); +bool dwc2_core_init(uint8_t rhport, const tusb_rhport_init_t* rh_init); #endif diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h index 4cdbcdb7a..c8c5bffd0 100644 --- a/src/portable/synopsys/dwc2/dwc2_esp32.h +++ b/src/portable/synopsys/dwc2/dwc2_esp32.h @@ -25,13 +25,14 @@ */ -#ifndef _DWC2_ESP32_H_ -#define _DWC2_ESP32_H_ +#ifndef TUSB_DWC2_ESP32_H_ +#define TUSB_DWC2_ESP32_H_ #ifdef __cplusplus extern "C" { #endif +#include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_intr_alloc.h" diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index b9dc413b1..81e3ab6b8 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -464,8 +464,8 @@ typedef struct { uint32_t reservedd00[64]; // D00..DFF //------------- Power Clock -------------// - volatile uint32_t pcgctl; // E00 Power and Clock Gating Control - volatile uint32_t pcgctl1; // E04 + volatile uint32_t pcgcctl; // E00 Power and Clock Gating Characteristic Control + volatile uint32_t pcgcctl1; // E04 Power and Clock Gating Characteristic Control 1 uint32_t reservede08[126]; // E08..FFF //------------- FIFOs -------------// @@ -478,7 +478,7 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, channel) == 0x0500, "incorrect size"); TU_VERIFY_STATIC(offsetof(dwc2_regs_t, dcfg ) == 0x0800, "incorrect size"); TU_VERIFY_STATIC(offsetof(dwc2_regs_t, epin ) == 0x0900, "incorrect size"); TU_VERIFY_STATIC(offsetof(dwc2_regs_t, epout ) == 0x0B00, "incorrect size"); -TU_VERIFY_STATIC(offsetof(dwc2_regs_t, pcgctl ) == 0x0E00, "incorrect size"); +TU_VERIFY_STATIC(offsetof(dwc2_regs_t, pcgcctl) == 0x0E00, "incorrect size"); TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); //--------------------------------------------------------------------+ @@ -720,7 +720,7 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); /******************** Bit definition for GUSBCFG register ********************/ #define GUSBCFG_TOCAL_Pos (0U) #define GUSBCFG_TOCAL_Msk (0x7UL << GUSBCFG_TOCAL_Pos) // 0x00000007 -#define GUSBCFG_TOCAL GUSBCFG_TOCAL_Msk // FS timeout calibration +#define GUSBCFG_TOCAL GUSBCFG_TOCAL_Msk // HS/FS timeout calibration #define GUSBCFG_PHYIF16_Pos (3U) #define GUSBCFG_PHYIF16_Msk (0x1UL << GUSBCFG_PHYIF16_Pos) // 0x00000008 #define GUSBCFG_PHYIF16 GUSBCFG_PHYIF16_Msk // PHY Interface (PHYIf) @@ -1973,32 +1973,32 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define DOEPTSIZ_STUPCNT_1 (0x2UL << DOEPTSIZ_STUPCNT_Pos) // 0x40000000 /******************** Bit definition for PCGCTL register ********************/ -#define PCGCTL_IF_DEV_MODE TU_BIT(31) -#define PCGCTL_P2HD_PRT_SPD_MASK (0x3ul << 29) -#define PCGCTL_P2HD_PRT_SPD_SHIFT 29 -#define PCGCTL_P2HD_DEV_ENUM_SPD_MASK (0x3ul << 27) -#define PCGCTL_P2HD_DEV_ENUM_SPD_SHIFT 27 -#define PCGCTL_MAC_DEV_ADDR_MASK (0x7ful << 20) -#define PCGCTL_MAC_DEV_ADDR_SHIFT 20 -#define PCGCTL_MAX_TERMSEL TU_BIT(19) -#define PCGCTL_MAX_XCVRSELECT_MASK (0x3ul << 17) -#define PCGCTL_MAX_XCVRSELECT_SHIFT 17 -#define PCGCTL_PORT_POWER TU_BIT(16) -#define PCGCTL_PRT_CLK_SEL_MASK (0x3ul << 14) -#define PCGCTL_PRT_CLK_SEL_SHIFT 14 -#define PCGCTL_ESS_REG_RESTORED TU_BIT(13) -#define PCGCTL_EXTND_HIBER_SWITCH TU_BIT(12) -#define PCGCTL_EXTND_HIBER_PWRCLMP TU_BIT(11) -#define PCGCTL_ENBL_EXTND_HIBER TU_BIT(10) -#define PCGCTL_RESTOREMODE TU_BIT(9) -#define PCGCTL_RESETAFTSUSP TU_BIT(8) -#define PCGCTL_DEEP_SLEEP TU_BIT(7) -#define PCGCTL_PHY_IN_SLEEP TU_BIT(6) -#define PCGCTL_ENBL_SLEEP_GATING TU_BIT(5) -#define PCGCTL_RSTPDWNMODULE TU_BIT(3) -#define PCGCTL_PWRCLMP TU_BIT(2) -#define PCGCTL_GATEHCLK TU_BIT(1) -#define PCGCTL_STOPPCLK TU_BIT(0) +#define PCGCCTL_IF_DEV_MODE TU_BIT(31) +#define PCGCCTL_P2HD_PRT_SPD_MASK (0x3ul << 29) +#define PCGCCTL_P2HD_PRT_SPD_SHIFT 29 +#define PCGCCTL_P2HD_DEV_ENUM_SPD_MASK (0x3ul << 27) +#define PCGCCTL_P2HD_DEV_ENUM_SPD_SHIFT 27 +#define PCGCCTL_MAC_DEV_ADDR_MASK (0x7ful << 20) +#define PCGCCTL_MAC_DEV_ADDR_SHIFT 20 +#define PCGCCTL_MAX_TERMSEL TU_BIT(19) +#define PCGCCTL_MAX_XCVRSELECT_MASK (0x3ul << 17) +#define PCGCCTL_MAX_XCVRSELECT_SHIFT 17 +#define PCGCCTL_PORT_POWER TU_BIT(16) +#define PCGCCTL_PRT_CLK_SEL_MASK (0x3ul << 14) +#define PCGCCTL_PRT_CLK_SEL_SHIFT 14 +#define PCGCCTL_ESS_REG_RESTORED TU_BIT(13) +#define PCGCCTL_EXTND_HIBER_SWITCH TU_BIT(12) +#define PCGCCTL_EXTND_HIBER_PWRCLMP TU_BIT(11) +#define PCGCCTL_ENBL_EXTND_HIBER TU_BIT(10) +#define PCGCCTL_RESTOREMODE TU_BIT(9) +#define PCGCCTL_RESETAFTSUSP TU_BIT(8) +#define PCGCCTL_DEEP_SLEEP TU_BIT(7) +#define PCGCCTL_PHY_IN_SLEEP TU_BIT(6) +#define PCGCCTL_ENBL_SLEEP_GATING TU_BIT(5) +#define PCGCCTL_RSTPDWNMODULE TU_BIT(3) +#define PCGCCTL_PWRCLMP TU_BIT(2) +#define PCGCCTL_GATEHCLK TU_BIT(1) +#define PCGCCTL_STOPPCLK TU_BIT(0) #define PCGCTL1_TIMER (0x3ul << 1) #define PCGCTL1_GATEEN TU_BIT(0) diff --git a/src/tusb_option.h b/src/tusb_option.h index fdc949747..d36d0bcad 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -125,7 +125,8 @@ #define OPT_MCU_ESP32C2 905 ///< Espressif ESP32-C2 #define OPT_MCU_ESP32H2 906 ///< Espressif ESP32-H2 #define OPT_MCU_ESP32P4 907 ///< Espressif ESP32-P4 -#define TUP_MCU_ESPRESSIF (CFG_TUSB_MCU >= 900 && CFG_TUSB_MCU < 1000) // check if Espressif MCU +#define TUSB_MCU_VENDOR_ESPRESSIF (CFG_TUSB_MCU >= 900 && CFG_TUSB_MCU < 1000) // check if Espressif MCU +#define TUP_MCU_ESPRESSIF TUSB_MCU_VENDOR_ESPRESSIF // for backward compatibility // Dialog #define OPT_MCU_DA1469X 1000 ///< Dialog Semiconductor DA1469x diff --git a/tools/build_utils.py b/tools/build_utils.py index 5462829e2..b4a1dc096 100755 --- a/tools/build_utils.py +++ b/tools/build_utils.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import subprocess import pathlib -import time +import re build_format = '| {:29} | {:30} | {:18} | {:7} | {:6} | {:6} |' @@ -36,18 +36,21 @@ def skip_example(example, board): mk_contents = board_mk.read_text() mcu = "NONE" - for token in mk_contents.split(): - if "CFG_TUSB_MCU=OPT_MCU_" in token: - # Strip " because cmake files has them. - token = token.strip("\"") - _, opt_mcu = token.split("=") - mcu = opt_mcu[len("OPT_MCU_"):] - if "esp32s2" in token: - mcu = "ESP32S2" - if "esp32s3" in token: - mcu = "ESP32S3" - if mcu != "NONE": - break + if family == "espressif": + for line in mk_contents.splitlines(): + match = re.search(r'set\(IDF_TARGET\s+"([^"]+)"\)', line) + if match: + mcu = match.group(1) + break + else: + for token in mk_contents.split(): + if "CFG_TUSB_MCU=OPT_MCU_" in token: + # Strip " because cmake files has them. + token = token.strip("\"") + _, opt_mcu = token.split("=") + mcu = opt_mcu[len("OPT_MCU_"):] + if mcu != "NONE": + break # Skip all OPT_MCU_NONE these are WIP port if mcu == "NONE": -- cgit v1.3.1 From 1b17bc04f964301857abb80e9a9ed46f02803fc1 Mon Sep 17 00:00:00 2001 From: pschatzmann Date: Tue, 15 Oct 2024 22:22:24 +0200 Subject: RP2040 New ISO API --- src/class/video/video_device.c | 2 +- src/common/tusb_mcu.h | 1 + src/portable/raspberrypi/rp2040/dcd_rp2040.c | 99 ++++++++++++++++++++++------ 3 files changed, 81 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index 70dbb5b9e..ec41e0991 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -1307,7 +1307,7 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin #ifdef TUP_DCD_EDPT_ISO_ALLOC /* Allocate ISO endpoints */ uint16_t ep_size = 0; - uint16_t ep_addr = 0; + uint8_t ep_addr = 0; uint8_t const *p_desc = (uint8_t const*)itf_desc + stm->desc.beg; uint8_t const *p_desc_end = (uint8_t const*)itf_desc + stm->desc.end; while (p_desc < p_desc_end) { diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index c77afecad..c64356028 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -366,6 +366,7 @@ // Raspberry Pi //--------------------------------------------------------------------+ #elif TU_CHECK_MCU(OPT_MCU_RP2040) + #define TUP_DCD_EDPT_ISO_ALLOC #define TUP_DCD_ENDPOINT_MAX 16 #define TU_ATTR_FAST_FUNC __attribute__((section(".time_critical.tinyusb"))) diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c index 2e177d5cb..bb0a2f3fb 100644 --- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c +++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c @@ -46,9 +46,11 @@ /*------------------------------------------------------------------*/ /* Low level controller *------------------------------------------------------------------*/ +static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type); +static void hw_set_endpoint_control_reg(struct hw_endpoint* ep, uint dpram_offset); // Init these in dcd_init -static uint8_t* next_buffer_ptr; +static uint8_t* next_buffer_ptr = NULL; // USB_MAX_ENDPOINTS Endpoints, direction TUSB_DIR_OUT for out and TUSB_DIR_IN for in. static struct hw_endpoint hw_endpoints[USB_MAX_ENDPOINTS][2]; @@ -66,31 +68,82 @@ TU_ATTR_ALWAYS_INLINE static inline struct hw_endpoint* hw_endpoint_get_by_addr( return hw_endpoint_get_by_num(num, dir); } -static void _hw_endpoint_alloc(struct hw_endpoint* ep, uint8_t transfer_type) { +// Allocate from the USB buffer space (max 3840 bytes) +static void hw_endpoint_alloc(struct hw_endpoint* ep, size_t size) { + static uint8_t *end; + // determine buffer end + if (end == NULL){ + end = &usb_dpram->epx_data[0] + 0xF00; + } + // determine first next_buffer_ptr if necessary + if (next_buffer_ptr == NULL){ + next_buffer_ptr = &usb_dpram->epx_data[0]; + } + // assign buffer + ep->hw_data_buf = next_buffer_ptr; + next_buffer_ptr += size; + + hard_assert(next_buffer_ptr < end); + + pico_info(" Allocated %d bytes (0x%p)\r\n", size, ep->hw_data_buf); +} + +// allocate endpoint and fill endpoint control registers +static void hw_endpoint_alloc_and_control(struct hw_endpoint* ep, uint8_t transfer_type) { // size must be multiple of 64 uint size = tu_div_ceil(ep->wMaxPacketSize, 64) * 64u; - // double buffered Bulk endpoint if (transfer_type == TUSB_XFER_BULK) { size *= 2u; } + ep->transfer_type = transfer_type; + hw_endpoint_alloc(ep, size); - ep->hw_data_buf = next_buffer_ptr; - next_buffer_ptr += size; - - assert(((uintptr_t) next_buffer_ptr & 0b111111u) == 0); uint dpram_offset = hw_data_offset(ep->hw_data_buf); - hard_assert(hw_data_offset(next_buffer_ptr) <= USB_DPRAM_MAX); - pico_info(" Allocated %d bytes at offset 0x%x (0x%p)\r\n", size, dpram_offset, ep->hw_data_buf); - // Fill in endpoint control register with buffer offset - uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint) transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; + hw_set_endpoint_control_reg(ep, dpram_offset); +} +static void hw_set_endpoint_control_reg(struct hw_endpoint* ep, uint dpram_offset) { + // Fill in endpoint control register with buffer offset + uint32_t const reg = EP_CTRL_ENABLE_BITS | ((uint) ep->transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | dpram_offset; *ep->endpoint_control = reg; } -static void _hw_endpoint_close(struct hw_endpoint* ep) { +// New API: Allocate packet buffer used by ISO endpoints +// Some MCU need manual packet buffer allocation, we allocate the largest size to avoid clustering +bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { + (void) rhport; + assert(rhport == 0); + struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); + // size must be multiple of 64 + uint16_t size = (uint16_t)tu_div_ceil(largest_packet_size, 64) * 64u; + ep->wMaxPacketSize = size; + hw_endpoint_alloc(ep, size); + return true; +} + +// New API: Configure and enable an ISO endpoint according to descriptor +bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) { + (void) rhport; + assert(rhport == 0); + const uint8_t ep_addr = ep_desc->bEndpointAddress; + const uint16_t mps = ep_desc->wMaxPacketSize; + uint16_t size = (uint16_t)tu_div_ceil(mps, 64) * 64u; + + // init w/o allocate + hw_endpoint_init(ep_addr, size, TUSB_XFER_ISOCHRONOUS); + + // Fill in endpoint control register with buffer offset + struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); + uint dpram_offset = hw_data_offset(ep->hw_data_buf); + hw_set_endpoint_control_reg(ep, dpram_offset); + return true; +} + +static void hw_endpoint_close(uint8_t ep_addr) { + struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); // Clear hardware registers and then zero the struct // Clears endpoint enable *ep->endpoint_control = 0; @@ -113,11 +166,20 @@ static void _hw_endpoint_close(struct hw_endpoint* ep) { } } -static void hw_endpoint_close(uint8_t ep_addr) { +// Legacy init called by dcd_init (which does allocation) +static void hw_endpoint_init_and_alloc(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); - _hw_endpoint_close(ep); + uint16_t size = (uint16_t) tu_div_ceil(wMaxPacketSize, 64) * 64u; + // size must be multiple of 64 + hw_endpoint_init(ep_addr, size, transfer_type); + const uint8_t num = tu_edpt_number(ep_addr); + if (num != 0) { + // alloc a buffer and fill in endpoint control register + hw_endpoint_alloc_and_control(ep, transfer_type); + } } +// main processing for dcd_edpt_iso_activate static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t transfer_type) { struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr); @@ -156,9 +218,6 @@ static void hw_endpoint_init(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t } else { ep->endpoint_control = &usb_dpram->ep_ctrl[num - 1].out; } - - // alloc a buffer and fill in endpoint control register - _hw_endpoint_alloc(ep, transfer_type); } } @@ -387,8 +446,8 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Init control endpoints tu_memclr(hw_endpoints[0], 2 * sizeof(hw_endpoint_t)); - hw_endpoint_init(0x0, 64, TUSB_XFER_CONTROL); - hw_endpoint_init(0x80, 64, TUSB_XFER_CONTROL); + hw_endpoint_init_and_alloc(0x0, 64, TUSB_XFER_CONTROL); + hw_endpoint_init_and_alloc(0x80, 64, TUSB_XFER_CONTROL); // Init non-control endpoints reset_non_control_endpoints(); @@ -495,7 +554,7 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* req bool dcd_edpt_open(__unused uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) { assert(rhport == 0); - hw_endpoint_init(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), desc_edpt->bmAttributes.xfer); + hw_endpoint_init_and_alloc(desc_edpt->bEndpointAddress, tu_edpt_packet_size(desc_edpt), desc_edpt->bmAttributes.xfer); return true; } -- cgit v1.3.1 From 8d9d3d9a2ac51378e7cb973628972e93ba356e1d Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 16 Oct 2024 13:19:28 +0700 Subject: move gahbcfg/gintmsk with dma to dwc2 common --- src/portable/synopsys/dwc2/dcd_dwc2.c | 84 +++------------------ src/portable/synopsys/dwc2/dwc2_common.c | 35 ++++++++- src/portable/synopsys/dwc2/dwc2_common.h | 37 ++++++++++ src/portable/synopsys/dwc2/dwc2_type.h | 121 ++++++++++++++++++------------- src/portable/synopsys/dwc2/hcd_dwc2.c | 64 ++++++++++++++-- src/tusb_option.h | 4 + 6 files changed, 212 insertions(+), 133 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index c67c34404..6760af3f3 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -68,22 +68,6 @@ static bool _sof_en; // DMA //-------------------------------------------------------------------- -TU_ATTR_ALWAYS_INLINE static inline bool dma_enabled(const dwc2_regs_t* dwc2) { - #if !CFG_TUD_DWC2_DMA - (void) dwc2; - return false; - #else - // Internal DMA only - return (dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA); - #endif -} - -TU_ATTR_ALWAYS_INLINE static inline uint16_t dma_cal_epfifo_base(uint8_t rhport) { - // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per endpoint direction - const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport]; - return dwc2_controller->ep_fifo_size/4 - 2*dwc2_controller->ep_count; -} - static void dma_setup_prepare(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); @@ -103,16 +87,6 @@ static void dma_setup_prepare(uint8_t rhport) { // Data FIFO //--------------------------------------------------------------------+ -TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_tx(dwc2_regs_t* dwc2, uint8_t epnum) { - // flush TX fifo and wait for it cleared - dwc2->grstctl = GRSTCTL_TXFFLSH | (epnum << GRSTCTL_TXFNUM_Pos); - while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {} -} -TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_rx(dwc2_regs_t* dwc2) { - // flush RX fifo and wait for it cleared - dwc2->grstctl = GRSTCTL_RXFFLSH; - while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {} -} /* USB Data FIFO Layout @@ -215,7 +189,7 @@ static void dfifo_init(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); dwc2->grxfsiz = calc_grxfsiz(CFG_TUD_ENDPOINT0_SIZE, dwc2_controller->ep_count); - if(dma_enabled(dwc2)) { + if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { // DMA use last DFIFO to store metadata _dfifo_top = dma_cal_epfifo_base(rhport); }else { @@ -395,7 +369,7 @@ static void bus_reset(uint8_t rhport) { xfer_status[0][TUSB_DIR_OUT].max_size = 64; xfer_status[0][TUSB_DIR_IN].max_size = 64; - if(dma_enabled(dwc2)) { + if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { dma_setup_prepare(rhport); } else { dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); @@ -427,7 +401,7 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c dep->dieptsiz = (num_packets << DIEPTSIZ_PKTCNT_Pos) | ((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk); - if(dma_enabled(dwc2)) { + if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { dep->diepdma = (uintptr_t)xfer->buffer; // For ISO endpoint set correct odd/even bit for next frame. @@ -465,7 +439,7 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c dep->doepctl |= (odd_frame_now ? DOEPCTL_SD0PID_SEVNFRM_Msk : DOEPCTL_SODDFRM_Msk); } - if(dma_enabled(dwc2)) { + if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { dep->doepdma = (uintptr_t)xfer->buffer; } @@ -477,9 +451,9 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c // Controller API //-------------------------------------------------------------------- bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { - (void) rh_init; dwc2_regs_t* dwc2 = DWC2_REG(rhport); + // Core Initialization TU_ASSERT(dwc2_core_init(rhport, rh_init)); // Device Initialization @@ -500,9 +474,6 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { } dwc2->dcfg = dcfg; - // Enable PHY clock TODO stop/gate clock when suspended mode - dwc2->pcgcctl &= ~(PCGCCTL_STOPPCLK | PCGCCTL_GATEHCLK | PCGCCTL_PWRCLMP | PCGCCTL_RSTPDWNMODULE); - // Force device mode dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FHMOD) | GUSBCFG_FDMOD; @@ -512,44 +483,13 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // If USB host misbehaves during status portion of control xfer (non zero-length packet), send STALL back and discard dwc2->dcfg |= DCFG_NZLSOHSK; - dfifo_flush_tx(dwc2, 0x10); // all tx fifo - dfifo_flush_rx(dwc2); - - // Clear all interrupts - uint32_t int_mask = dwc2->gintsts; - dwc2->gintsts |= int_mask; - int_mask = dwc2->gotgint; - dwc2->gotgint |= int_mask; - - // Required as part of core initialization. - dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM; + // Enable required interrupts + dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM; // Configure TX FIFO empty level for interrupt. Default is complete empty dwc2->gahbcfg |= GAHBCFG_TXFELVL; - if (dma_enabled(dwc2)) { - const uint16_t epinfo_base = dma_cal_epfifo_base(rhport); - dwc2->gdfifocfg = (epinfo_base << GDFIFOCFG_EPINFOBASE_SHIFT) | epinfo_base; - - // DMA seems to be only settable after a core reset - dwc2->gahbcfg |= GAHBCFG_DMAEN | GAHBCFG_HBSTLEN_2; - }else { - dwc2->gintmsk |= GINTMSK_RXFLVLM; - } - - // Enable global interrupt - dwc2->gahbcfg |= GAHBCFG_GINT; - - // make sure we are in device mode -// TU_ASSERT(!(dwc2->gintsts & GINTSTS_CMOD), ); - -// TU_LOG_HEX(DWC2_DEBUG, dwc2->gotgctl); -// TU_LOG_HEX(DWC2_DEBUG, dwc2->gusbcfg); -// TU_LOG_HEX(DWC2_DEBUG, dwc2->dcfg); -// TU_LOG_HEX(DWC2_DEBUG, dwc2->gahbcfg); - dcd_connect(rhport); - return true; } @@ -749,7 +689,7 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { edpt_disable(rhport, ep_addr, true); - if((tu_edpt_number(ep_addr) == 0) && dma_enabled(DWC2_REG(rhport))) { + if((tu_edpt_number(ep_addr) == 0) && dwc2_dma_enabled(DWC2_REG(rhport), TUSB_ROLE_DEVICE)) { dma_setup_prepare(rhport); } } @@ -866,7 +806,7 @@ static void handle_epout_irq(uint8_t rhport) { if (doepint & DOEPINT_SETUP) { epout->doepint = DOEPINT_SETUP; - if(dma_enabled(dwc2)) { + if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { dma_setup_prepare(rhport); } @@ -882,7 +822,7 @@ static void handle_epout_irq(uint8_t rhport) { if (!(doepint & (DOEPINT_SETUP | DOEPINT_STPKTRX | DOEPINT_STSPHSRX))) { xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); - if(dma_enabled(dwc2)) { + if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { if ((epnum == 0) && ep0_pending[TUSB_DIR_OUT]) { // EP0 can only handle one packet Schedule another packet to be received. edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]); @@ -932,7 +872,7 @@ static void handle_epin_irq(uint8_t rhport) { // Schedule another packet to be transmitted. edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]); } else { - if((n == 0) && dma_enabled(dwc2)) { + if((n == 0) && dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { dma_setup_prepare(rhport); } dcd_event_xfer_complete(rhport, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); @@ -982,7 +922,7 @@ static void handle_epin_irq(uint8_t rhport) { /* Interrupt Hierarchy - DxEPMSK.XferComplMsk DxEPINTn.XferCompl + DxEPINTn.XferCompl DxEPMSK.XferComplMsk | | +---------- AND --------+ | diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index 190dc0734..50d0c28b3 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -63,7 +63,7 @@ bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init } #endif #if CFG_TUH_ENABLED - if (rh_init->role == TUSB_ROLE_DEVICE && !TUH_OPT_HIGH_SPEED) { + if (rh_init->role == TUSB_ROLE_HOST && !TUH_OPT_HIGH_SPEED) { return false; } #endif @@ -193,10 +193,39 @@ bool dwc2_core_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { * duration in the core to account for any additional delays * introduced by the PHY. This can be required, because the delay * introduced by the PHY in generating the linestate condition - * can vary from one PHY to another. - */ + * can vary from one PHY to another. */ dwc2->gusbcfg |= (7ul << GUSBCFG_TOCAL_Pos); + // Enable PHY clock TODO stop/gate clock when suspended mode + dwc2->pcgcctl &= ~(PCGCCTL_STOPPCLK | PCGCCTL_GATEHCLK | PCGCCTL_PWRCLMP | PCGCCTL_RSTPDWNMODULE); + + dfifo_flush_tx(dwc2, 0x10); // all tx fifo + dfifo_flush_rx(dwc2); + + // Clear all pending interrupts + uint32_t int_mask; + + int_mask = dwc2->gintsts; + dwc2->gintsts |= int_mask; + + int_mask = dwc2->gotgint; + dwc2->gotgint |= int_mask; + + dwc2->gintmsk = GINTMSK_OTGINT; + + if (dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { + const uint16_t epinfo_base = dma_cal_epfifo_base(rhport); + dwc2->gdfifocfg = (epinfo_base << GDFIFOCFG_EPINFOBASE_SHIFT) | epinfo_base; + + // DMA seems to be only settable after a core reset, and not possible to switch on-the-fly + dwc2->gahbcfg |= GAHBCFG_DMAEN | GAHBCFG_HBSTLEN_2; + }else { + dwc2->gintmsk |= GINTMSK_RXFLVLM; + } + + // Enable global interrupt + dwc2->gahbcfg |= GAHBCFG_GINT; + return true; } diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h index 4039ebaa3..538fcd179 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.h +++ b/src/portable/synopsys/dwc2/dwc2_common.h @@ -57,6 +57,8 @@ enum { DWC2_CONTROLLER_COUNT = TU_ARRAY_SIZE(_dwc2_controller) }; + +//------------- Core -------------// TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) { if (rhport >= DWC2_CONTROLLER_COUNT) { // user mis-configured, ignore and use first controller @@ -68,4 +70,39 @@ TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) { bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init); bool dwc2_core_init(uint8_t rhport, const tusb_rhport_init_t* rh_init); +//------------- DFIFO -------------// +TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_tx(dwc2_regs_t* dwc2, uint8_t fnum) { + // flush TX fifo and wait for it cleared + dwc2->grstctl = GRSTCTL_TXFFLSH | (fnum << GRSTCTL_TXFNUM_Pos); + while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {} +} +TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_rx(dwc2_regs_t* dwc2) { + // flush RX fifo and wait for it cleared + dwc2->grstctl = GRSTCTL_RXFFLSH; + while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {} +} + +//------------- DMA -------------// +TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dma_enabled(const dwc2_regs_t* dwc2, tusb_role_t role) { + (void) dwc2; + + if (CFG_TUD_DWC2_DMA == 0 && role == TUSB_ROLE_DEVICE) { + return false; + } + + if (CFG_TUH_DWC2_DMA == 0 && role == TUSB_ROLE_HOST) { + return false; + } + + // Internal DMA only + return dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA; +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t dma_cal_epfifo_base(uint8_t rhport) { + // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per endpoint direction + const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport]; + return dwc2_controller->ep_fifo_size/4 - 2*dwc2_controller->ep_count; +} + + #endif diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index 81e3ab6b8..c8f6a5d1f 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -122,6 +122,12 @@ enum { GHWCFFG4_PHY_DATA_WIDTH_8_16 = 2, // software selectable }; +enum { + HPRT_SPEED_HIGH = 0, + HPRT_SPEED_FULL = 1, + HPRT_SPEED_LOW = 2 +}; + //-------------------------------------------------------------------- // Register bitfield definitions //-------------------------------------------------------------------- @@ -301,6 +307,24 @@ typedef struct TU_ATTR_PACKED { }dwc2_ghwcfg4_t; TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg4_t) == 4, "incorrect size"); +typedef struct TU_ATTR_PACKED { + uint32_t conn_status : 1; // 0 Port connect status + uint32_t conn_detected : 1; // 1 Port connect detected + uint32_t enable : 1; // 2 Port enable status + uint32_t enable_change : 1; // 3 Port enable change + uint32_t over_current_active : 1; // 4 Port Over-current active + uint32_t over_current_change : 1; // 5 Port Over-current change + uint32_t resume : 1; // 6 Port resume + uint32_t suspend : 1; // 7 Port suspend + uint32_t reset : 1; // 8 Port reset + uint32_t rsv9 : 1; // 9 Reserved + uint32_t line_status : 2; // 10..11 Line status + uint32_t power : 1; // 12 Port power + uint32_t test_control : 4; // 13..16 Port Test control + uint32_t speed : 2; // 17..18 Port speed + uint32_t rsv19_31 :13; // 19..31 Reserved +}dwc2_hprt_t; + // Host Channel typedef struct { volatile uint32_t hcchar; // 500 + 20*ch Host Channel Characteristics @@ -1448,56 +1472,53 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define DIEPEACHMSK1_NAKM DIEPEACHMSK1_NAKM_Msk // NAK interrupt mask /******************** Bit definition for HPRT register ********************/ -#define HPRT_PCSTS_Pos (0U) -#define HPRT_PCSTS_Msk (0x1UL << HPRT_PCSTS_Pos) // 0x00000001 -#define HPRT_PCSTS HPRT_PCSTS_Msk // Port connect status -#define HPRT_PCDET_Pos (1U) -#define HPRT_PCDET_Msk (0x1UL << HPRT_PCDET_Pos) // 0x00000002 -#define HPRT_PCDET HPRT_PCDET_Msk // Port connect detected -#define HPRT_PENA_Pos (2U) -#define HPRT_PENA_Msk (0x1UL << HPRT_PENA_Pos) // 0x00000004 -#define HPRT_PENA HPRT_PENA_Msk // Port enable -#define HPRT_PENCHNG_Pos (3U) -#define HPRT_PENCHNG_Msk (0x1UL << HPRT_PENCHNG_Pos) // 0x00000008 -#define HPRT_PENCHNG HPRT_PENCHNG_Msk // Port enable/disable change -#define HPRT_POCA_Pos (4U) -#define HPRT_POCA_Msk (0x1UL << HPRT_POCA_Pos) // 0x00000010 -#define HPRT_POCA HPRT_POCA_Msk // Port overcurrent active -#define HPRT_POCCHNG_Pos (5U) -#define HPRT_POCCHNG_Msk (0x1UL << HPRT_POCCHNG_Pos) // 0x00000020 -#define HPRT_POCCHNG HPRT_POCCHNG_Msk // Port overcurrent change -#define HPRT_PRES_Pos (6U) -#define HPRT_PRES_Msk (0x1UL << HPRT_PRES_Pos) // 0x00000040 -#define HPRT_PRES HPRT_PRES_Msk // Port resume -#define HPRT_PSUSP_Pos (7U) -#define HPRT_PSUSP_Msk (0x1UL << HPRT_PSUSP_Pos) // 0x00000080 -#define HPRT_PSUSP HPRT_PSUSP_Msk // Port suspend -#define HPRT_PRST_Pos (8U) -#define HPRT_PRST_Msk (0x1UL << HPRT_PRST_Pos) // 0x00000100 -#define HPRT_PRST HPRT_PRST_Msk // Port reset - -#define HPRT_PLSTS_Pos (10U) -#define HPRT_PLSTS_Msk (0x3UL << HPRT_PLSTS_Pos) // 0x00000C00 -#define HPRT_PLSTS HPRT_PLSTS_Msk // Port line status -#define HPRT_PLSTS_0 (0x1UL << HPRT_PLSTS_Pos) // 0x00000400 -#define HPRT_PLSTS_1 (0x2UL << HPRT_PLSTS_Pos) // 0x00000800 -#define HPRT_PPWR_Pos (12U) -#define HPRT_PPWR_Msk (0x1UL << HPRT_PPWR_Pos) // 0x00001000 -#define HPRT_PPWR HPRT_PPWR_Msk // Port power - -#define HPRT_PTCTL_Pos (13U) -#define HPRT_PTCTL_Msk (0xFUL << HPRT_PTCTL_Pos) // 0x0001E000 -#define HPRT_PTCTL HPRT_PTCTL_Msk // Port test control -#define HPRT_PTCTL_0 (0x1UL << HPRT_PTCTL_Pos) // 0x00002000 -#define HPRT_PTCTL_1 (0x2UL << HPRT_PTCTL_Pos) // 0x00004000 -#define HPRT_PTCTL_2 (0x4UL << HPRT_PTCTL_Pos) // 0x00008000 -#define HPRT_PTCTL_3 (0x8UL << HPRT_PTCTL_Pos) // 0x00010000 - -#define HPRT_PSPD_Pos (17U) -#define HPRT_PSPD_Msk (0x3UL << HPRT_PSPD_Pos) // 0x00060000 -#define HPRT_PSPD HPRT_PSPD_Msk // Port speed -#define HPRT_PSPD_0 (0x1UL << HPRT_PSPD_Pos) // 0x00020000 -#define HPRT_PSPD_1 (0x2UL << HPRT_PSPD_Pos) // 0x00040000 +#define HPRT_CONN_STATUS_Pos (0U) +#define HPRT_CONN_STATUS_Msk (0x1UL << HPRT_CONN_STATUS_Pos) // 0x00000001 +#define HPRT_CONN_STATUS HPRT_CONN_STATUS_Msk // Port connect status +#define HPRT_CONN_DETECTEDT_Pos (1U) +#define HPRT_CONN_DETECTEDT_Msk (0x1UL << HPRT_CONN_DETECTEDT_Pos) // 0x00000002 +#define HPRT_CONN_DETECTEDT HPRT_CONN_DETECTEDT_Msk // Port connect detected +#define HPRT_ENABLE_Pos (2U) +#define HPRT_ENABLE_Msk (0x1UL << HPRT_ENABLE_Pos) // 0x00000004 +#define HPRT_ENABLE HPRT_ENABLE_Msk // Port enable +#define HPRT_EN_CHANGE_Pos (3U) +#define HPRT_EN_CHANGE_Msk (0x1UL << HPRT_EN_CHANGE_Pos) // 0x00000008 +#define HPRT_EN_CHANGE HPRT_EN_CHANGE_Msk // Port enable/disable change +#define HPRT_OVER_CURRENT_ACTIVE_Pos (4U) +#define HPRT_OVER_CURRENT_ACTIVE_Msk (0x1UL << HPRT_OVER_CURRENT_ACTIVE_Pos) // 0x00000010 +#define HPRT_OVER_CURRENT_ACTIVE HPRT_OVER_CURRENT_ACTIVE_Msk // Port overcurrent active +#define HPRT_OVER_CURRENT_CHANGE_Pos (5U) +#define HPRT_OVER_CURRENT_CHANGE_Msk (0x1UL << HPRT_OVER_CURRENT_CHANGE_Pos) // 0x00000020 +#define HPRT_OVER_CURRENT_CHANGE HPRT_OVER_CURRENT_CHANGE_Msk // Port overcurrent change +#define HPRT_RESUME_Pos (6U) +#define HPRT_RESUME_Msk (0x1UL << HPRT_RESUME_Pos) // 0x00000040 +#define HPRT_RESUME HPRT_RESUME_Msk // Port resume +#define HPRT_SUSPEND_Pos (7U) +#define HPRT_SUSPEND_Msk (0x1UL << HPRT_SUSPEND_Pos) // 0x00000080 +#define HPRT_SUSPEND HPRT_SUSPEND_Msk // Port suspend +#define HPRT_RESET_Pos (8U) +#define HPRT_RESET_Msk (0x1UL << HPRT_RESET_Pos) // 0x00000100 +#define HPRT_RESET HPRT_RESET_Msk // Port reset +#define HPRT_LINE_STATUS_Pos (10U) +#define HPRT_LINE_STATUS_Msk (0x3UL << HPRT_LINE_STATUS_Pos) // 0x00000C00 +#define HPRT_LINE_STATUS HPRT_LINE_STATUS_Msk // Port line status +#define HPRT_LINE_STATUS_0 (0x1UL << HPRT_LINE_STATUS_Pos) // 0x00000400 +#define HPRT_LINE_STATUS_1 (0x2UL << HPRT_LINE_STATUS_Pos) // 0x00000800 +#define HPRT_POWER_Pos (12U) +#define HPRT_POWER_Msk (0x1UL << HPRT_POWER_Pos) // 0x00001000 +#define HPRT_POWER HPRT_POWER_Msk // Port power +#define HPRT_TEST_CONTROL_Pos (13U) +#define HPRT_TEST_CONTROL_Msk (0xFUL << HPRT_TEST_CONTROL_Pos) // 0x0001E000 +#define HPRT_TEST_CONTROL HPRT_TEST_CONTROL_Msk // Port test control +#define HPRT_TEST_CONTROL_0 (0x1UL << HPRT_TEST_CONTROL_Pos) // 0x00002000 +#define HPRT_TEST_CONTROL_1 (0x2UL << HPRT_TEST_CONTROL_Pos) // 0x00004000 +#define HPRT_TEST_CONTROL_2 (0x4UL << HPRT_TEST_CONTROL_Pos) // 0x00008000 +#define HPRT_TEST_CONTROL_3 (0x8UL << HPRT_TEST_CONTROL_Pos) // 0x00010000 +#define HPRT_SPEED_Pos (17U) +#define HPRT_SPEED_Msk (0x3UL << HPRT_SPEED_Pos) // 0x00060000 +#define HPRT_SPEED HPRT_SPEED_Msk // Port speed +#define HPRT_SPEED_0 (0x1UL << HPRT_SPEED_Pos) // 0x00020000 +#define HPRT_SPEED_1 (0x2UL << HPRT_SPEED_Pos) // 0x00040000 /******************** Bit definition for DOEPEACHMSK1 register ********************/ #define DOEPEACHMSK1_XFRCM_Pos (0U) diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 8798f982c..b383b4069 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -49,15 +49,29 @@ bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) { // Initialize controller to host mode bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { - (void) rhport; - (void) rh_init; - return false; -} + dwc2_regs_t* dwc2 = DWC2_REG(rhport); -// Interrupt Handler -void hcd_int_handler(uint8_t rhport, bool in_isr) { - (void) rhport; - (void) in_isr; + // Core Initialization + TU_ASSERT(dwc2_core_init(rhport, rh_init)); + + // force host mode + dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FDMOD) | GUSBCFG_FHMOD; + + //------------- 3.1 Host Initialization -------------// + // Enable required interrupts + dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_PRTIM | GINTMSK_WUIM; + + // max speed + if (dwc2_core_is_highspeed(dwc2, rh_init)) { + dwc2->hcfg &= ~HCFG_FSLSS; + } else { + dwc2->hcfg |= HCFG_FSLSS; + } + + // port power on -> drive VBUS + dwc2->hprt = HPRT_POWER; + + return true; } // Enable USB interrupt @@ -164,4 +178,38 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { return false; } +//-------------------------------------------------------------------- +// HCD Event Handler +//-------------------------------------------------------------------- + +/* Interrupt Hierarchy + + HCINTn.XferCompl HCINTMSKn.XferComplMsk + | | + +---------- AND --------+ + | + HAINT.CHn HAINTMSK.CHn + | | + +---------- AND --------+ + | + GINTSTS.PrtInt GINTMSK.PrtInt + | | + +---------- AND --------+ + | + GAHBCFG.GblIntrMsk + | + IRQn + */ +void hcd_int_handler(uint8_t rhport, bool in_isr) { + (void) in_isr; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + const uint32_t int_mask = dwc2->gintmsk; + const uint32_t int_status = dwc2->gintsts & int_mask; + + if (int_status & GINTSTS_HPRTINT) { + TU_LOG1_HEX(dwc2->hprt); + } +} + #endif diff --git a/src/tusb_option.h b/src/tusb_option.h index d36d0bcad..3607a8c1a 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -254,6 +254,10 @@ #define CFG_TUD_DWC2_DMA 0 #endif +#ifndef CFG_TUH_DWC2_DMA + #define CFG_TUH_DWC2_DMA 0 +#endif + // Enable PIO-USB software host controller #ifndef CFG_TUH_RPI_PIO_USB #define CFG_TUH_RPI_PIO_USB 0 -- cgit v1.3.1 From b5a4f18879110d9f5f8fe27c473818f2416e40e4 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 17 Oct 2024 15:56:12 +0700 Subject: get hpri triggered --- src/portable/synopsys/dwc2/dcd_dwc2.c | 4 +- src/portable/synopsys/dwc2/dwc2_common.c | 76 ++++++++++----- src/portable/synopsys/dwc2/dwc2_stm32.h | 16 +++- src/portable/synopsys/dwc2/dwc2_type.h | 27 +++--- src/portable/synopsys/dwc2/hcd_dwc2.c | 156 +++++++++++++++++++++++++++---- src/tusb.c | 2 +- 6 files changed, 217 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index 6760af3f3..55d8f5766 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -486,8 +486,8 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Enable required interrupts dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM; - // Configure TX FIFO empty level for interrupt. Default is complete empty - dwc2->gahbcfg |= GAHBCFG_TXFELVL; + // Enable global interrupt + dwc2->gahbcfg |= GAHBCFG_GINT; dcd_connect(rhport); return true; diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index 50d0c28b3..12dcdb7c6 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -44,18 +44,21 @@ static void reset_core(dwc2_regs_t* dwc2) { // reset core dwc2->grstctl |= GRSTCTL_CSRST; - // wait for reset bit is cleared - // TODO version 4.20a should wait for RESET DONE mask - while (dwc2->grstctl & GRSTCTL_CSRST) {} - - // wait for AHB master IDLE - while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {} + if ((dwc2->gsnpsid & DWC2_CORE_REV_MASK) < (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) { + // prior v42.0 CSRST is self-clearing + while (dwc2->grstctl & GRSTCTL_CSRST) {} + } else { + // From v4.20a CSRST bit is write only, CSRT_DONE (w1c) is introduced for checking. + // CSRST must also be explicitly cleared + while (!(dwc2->grstctl & GRSTCTL_CSRST_DONE)) {} + dwc2->grstctl = (dwc2->grstctl & ~GRSTCTL_CSRST) | GRSTCTL_CSRST_DONE; + } - // wait for device mode ? + while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {} // wait for AHB master IDLE } bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init) { - (void) dwc2; + (void)dwc2; #if CFG_TUD_ENABLED if (rh_init->role == TUSB_ROLE_DEVICE && !TUD_OPT_HIGH_SPEED) { @@ -71,11 +74,14 @@ bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init return dwc2->ghwcfg2_bm.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED; } -static void phy_fs_init(dwc2_regs_t* dwc2) { +static void phy_fs_init(dwc2_regs_t* dwc2, tusb_role_t role) { TU_LOG(DWC2_COMMON_DEBUG, "Fullspeed PHY init\r\n"); + uint32_t gusbcfg = dwc2->gusbcfg; + // Select FS PHY - dwc2->gusbcfg |= GUSBCFG_PHYSEL; + gusbcfg |= GUSBCFG_PHYSEL; + dwc2->gusbcfg = gusbcfg; // MCU specific PHY init before reset dwc2_phy_init(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED); @@ -86,13 +92,33 @@ static void phy_fs_init(dwc2_regs_t* dwc2) { // USB turnaround time is critical for certification where long cables and 5-Hubs are used. // So if you need the AHB to run at less than 30 MHz, and if USB turnaround time is not critical, // these bits can be programmed to a larger value. Default is 5 - dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_TRDT_Msk) | (5u << GUSBCFG_TRDT_Pos); + gusbcfg &= ~GUSBCFG_TRDT_Msk; + gusbcfg |= 5u << GUSBCFG_TRDT_Pos; + dwc2->gusbcfg = gusbcfg; + + // FS/LS PHY Clock Select + if (role == TUSB_ROLE_HOST) { + uint32_t hcfg = dwc2->hcfg; + hcfg |= HCFG_FSLS_ONLY; + hcfg &= ~HCFG_FSLS_PHYCLK_SEL; + + if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI && + dwc2->ghwcfg2_bm.fs_phy_type == GHWCFG2_FSPHY_DEDICATED) { + // dedicated FS PHY with 48 mhz + hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ; + } else { + // shared HS PHY running at full speed + hcfg |= HCFG_FSLS_PHYCLK_SEL_30_60MHZ; + } + dwc2->hcfg = hcfg; + } // MCU specific PHY update post reset dwc2_phy_update(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED); } -static void phy_hs_init(dwc2_regs_t* dwc2) { +static void phy_hs_init(dwc2_regs_t* dwc2, tusb_role_t role) { + (void) role; uint32_t gusbcfg = dwc2->gusbcfg; // De-select FS PHY @@ -123,8 +149,8 @@ static void phy_hs_init(dwc2_regs_t* dwc2) { // Set 16-bit interface if supported if (dwc2->ghwcfg4_bm.phy_data_width) { - gusbcfg |= GUSBCFG_PHYIF16; // 16 bit - }else { + gusbcfg |= GUSBCFG_PHYIF16; // 16 bit + } else { gusbcfg &= ~GUSBCFG_PHYIF16; // 8 bit } } @@ -138,6 +164,10 @@ static void phy_hs_init(dwc2_regs_t* dwc2) { // Reset core after selecting PHY reset_core(dwc2); + if (role == TUSB_ROLE_HOST) { + dwc2->hcfg &= ~HCFG_FSLS_ONLY; + } + // Set turn-around, must after core reset otherwise it will be clear // - 9 if using 8-bit PHY interface // - 5 if using 16-bit PHY interface @@ -162,7 +192,7 @@ static bool check_dwc2(dwc2_regs_t* dwc2) { #endif // For some reason: GD32VF103 gsnpsid and all hwcfg register are always zero (skip it) - (void) dwc2; + (void)dwc2; #if !TU_CHECK_MCU(OPT_MCU_GD32VF103) uint32_t const gsnpsid = dwc2->gsnpsid & GSNPSID_ID_MASK; TU_ASSERT(gsnpsid == DWC2_OTG_ID || gsnpsid == DWC2_FS_IOT_ID || gsnpsid == DWC2_HS_IOT_ID); @@ -175,16 +205,16 @@ static bool check_dwc2(dwc2_regs_t* dwc2) { // //-------------------------------------------------------------------- bool dwc2_core_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { - (void) rh_init; + (void)rh_init; dwc2_regs_t* dwc2 = DWC2_REG(rhport); // Check Synopsys ID register, failed if controller clock/power is not enabled TU_ASSERT(check_dwc2(dwc2)); if (dwc2_core_is_highspeed(dwc2, rh_init)) { - phy_hs_init(dwc2); // Highspeed + phy_hs_init(dwc2, rh_init->role); } else { - phy_fs_init(dwc2); // core does not support highspeed or hs phy is not present + phy_fs_init(dwc2, rh_init->role); } /* Set HS/FS Timeout Calibration to 7 (max available value). @@ -211,20 +241,20 @@ bool dwc2_core_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { int_mask = dwc2->gotgint; dwc2->gotgint |= int_mask; - dwc2->gintmsk = GINTMSK_OTGINT; + dwc2->gintmsk = 0; - if (dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { + if (dwc2_dma_enabled(dwc2, rh_init->role)) { const uint16_t epinfo_base = dma_cal_epfifo_base(rhport); dwc2->gdfifocfg = (epinfo_base << GDFIFOCFG_EPINFOBASE_SHIFT) | epinfo_base; // DMA seems to be only settable after a core reset, and not possible to switch on-the-fly dwc2->gahbcfg |= GAHBCFG_DMAEN | GAHBCFG_HBSTLEN_2; - }else { + } else { dwc2->gintmsk |= GINTMSK_RXFLVLM; } - // Enable global interrupt - dwc2->gahbcfg |= GAHBCFG_GINT; + // Configure TX FIFO empty level for interrupt. Default is complete empty + dwc2->gahbcfg |= GAHBCFG_TXFELVL; return true; } diff --git a/src/portable/synopsys/dwc2/dwc2_stm32.h b/src/portable/synopsys/dwc2/dwc2_stm32.h index 395b6d870..8719c4820 100644 --- a/src/portable/synopsys/dwc2/dwc2_stm32.h +++ b/src/portable/synopsys/dwc2/dwc2_stm32.h @@ -124,13 +124,19 @@ static const dwc2_controller_t _dwc2_controller[] = { // SystemCoreClock is already included by family header // extern uint32_t SystemCoreClock; -TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_enable(uint8_t rhport) { - NVIC_EnableIRQ((IRQn_Type) _dwc2_controller[rhport].irqnum); +TU_ATTR_ALWAYS_INLINE static inline void dwc2_int_set(uint8_t rhport, tusb_role_t role, bool enabled) { + (void) role; + const IRQn_Type irqn = (IRQn_Type) _dwc2_controller[rhport].irqnum; + if (enabled) { + NVIC_EnableIRQ(irqn); + } else { + NVIC_DisableIRQ(irqn); + } } -TU_ATTR_ALWAYS_INLINE static inline void dwc2_dcd_int_disable(uint8_t rhport) { - NVIC_DisableIRQ((IRQn_Type) _dwc2_controller[rhport].irqnum); -} +#define dwc2_dcd_int_enable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, true) +#define dwc2_dcd_int_disable(_rhport) dwc2_int_set(_rhport, TUSB_ROLE_DEVICE, false) + TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) { // try to delay for 1 ms diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index c8f6a5d1f..f99bf3e85 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -566,14 +566,15 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define GOTGCTL_OTGVER GOTGCTL_OTGVER_Msk // OTG version /******************** Bit definition for HCFG register ********************/ -#define HCFG_FSLSPCS_Pos (0U) -#define HCFG_FSLSPCS_Msk (0x3UL << HCFG_FSLSPCS_Pos) // 0x00000003 -#define HCFG_FSLSPCS HCFG_FSLSPCS_Msk // FS/LS PHY clock select -#define HCFG_FSLSPCS_0 (0x1UL << HCFG_FSLSPCS_Pos) // 0x00000001 -#define HCFG_FSLSPCS_1 (0x2UL << HCFG_FSLSPCS_Pos) // 0x00000002 -#define HCFG_FSLSS_Pos (2U) -#define HCFG_FSLSS_Msk (0x1UL << HCFG_FSLSS_Pos) // 0x00000004 -#define HCFG_FSLSS HCFG_FSLSS_Msk // FS- and LS-only support +#define HCFG_FSLS_PHYCLK_SEL_Pos (0U) +#define HCFG_FSLS_PHYCLK_SEL_Msk (0x3UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000003 +#define HCFG_FSLS_PHYCLK_SEL HCFG_FSLS_PHYCLK_SEL_Msk // FS/LS PHY clock select +#define HCFG_FSLS_PHYCLK_SEL_30_60MHZ (0x0UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000000 +#define HCFG_FSLS_PHYCLK_SEL_48MHZ (0x1UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000001 + +#define HCFG_FSLS_ONLY_Pos (2U) +#define HCFG_FSLS_ONLY_Msk (0x1UL << HCFG_FSLS_ONLY_Pos) // 0x00000004 +#define HCFG_FSLS_ONLY HCFG_FSLS_ONLY_Msk // FS- and LS-only support /******************** Bit definition for PCGCR register ********************/ #define PCGCR_STPPCLK_Pos (0U) @@ -828,8 +829,8 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define GRSTCTL_TXFNUM_2 (0x04UL << GRSTCTL_TXFNUM_Pos) // 0x00000100 #define GRSTCTL_TXFNUM_3 (0x08UL << GRSTCTL_TXFNUM_Pos) // 0x00000200 #define GRSTCTL_TXFNUM_4 (0x10UL << GRSTCTL_TXFNUM_Pos) // 0x00000400 -#define GRSTCTL_CSFTRST_DONE_Pos (29) -#define GRSTCTL_CSFTRST_DONE (1u << GRSTCTL_CSFTRST_DONE_Pos) // Reset Done, only available from v4.20a +#define GRSTCTL_CSRST_DONE_Pos (29) +#define GRSTCTL_CSRST_DONE (1u << GRSTCTL_CSRST_DONE_Pos) // Reset Done, only available from v4.20a #define GRSTCTL_DMAREQ_Pos (30U) #define GRSTCTL_DMAREQ_Msk (0x1UL << GRSTCTL_DMAREQ_Pos) // 0x40000000 #define GRSTCTL_DMAREQ GRSTCTL_DMAREQ_Msk // DMA request signal @@ -1475,9 +1476,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define HPRT_CONN_STATUS_Pos (0U) #define HPRT_CONN_STATUS_Msk (0x1UL << HPRT_CONN_STATUS_Pos) // 0x00000001 #define HPRT_CONN_STATUS HPRT_CONN_STATUS_Msk // Port connect status -#define HPRT_CONN_DETECTEDT_Pos (1U) -#define HPRT_CONN_DETECTEDT_Msk (0x1UL << HPRT_CONN_DETECTEDT_Pos) // 0x00000002 -#define HPRT_CONN_DETECTEDT HPRT_CONN_DETECTEDT_Msk // Port connect detected +#define HPRT_CONN_DETECT_Pos (1U) +#define HPRT_CONN_DETECT_Msk (0x1UL << HPRT_CONN_DETECT_Pos) // 0x00000002 +#define HPRT_CONN_DETECT HPRT_CONN_DETECT_Msk // Port connect detected #define HPRT_ENABLE_Pos (2U) #define HPRT_ENABLE_Msk (0x1UL << HPRT_ENABLE_Pos) // 0x00000004 #define HPRT_ENABLE HPRT_ENABLE_Msk // Port enable diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index b383b4069..97068d4c5 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -34,6 +34,10 @@ #include "host/hcd.h" #include "dwc2_common.h" +enum { + HPRT_W1C_MASK = HPRT_CONN_DETECT | HPRT_ENABLE | HPRT_EN_CHANGE | HPRT_OVER_CURRENT_CHANGE +}; + //--------------------------------------------------------------------+ // Controller API //--------------------------------------------------------------------+ @@ -54,40 +58,41 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Core Initialization TU_ASSERT(dwc2_core_init(rhport, rh_init)); + //------------- 3.1 Host Initialization -------------// + + // max speed + // if (dwc2_core_is_highspeed(dwc2, rh_init)) { + // dwc2->hcfg &= ~HCFG_FSLS_ONLY; + // } else { + // dwc2->hcfg |= HCFG_FSLS_ONLY; + // } + // force host mode dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FDMOD) | GUSBCFG_FHMOD; - //------------- 3.1 Host Initialization -------------// + dwc2->hprt = HPRT_W1C_MASK; // clear all write-1-clear bits + dwc2->hprt = HPRT_POWER; // port power on -> drive VBUS + // Enable required interrupts dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_PRTIM | GINTMSK_WUIM; - - // max speed - if (dwc2_core_is_highspeed(dwc2, rh_init)) { - dwc2->hcfg &= ~HCFG_FSLSS; - } else { - dwc2->hcfg |= HCFG_FSLSS; - } - - // port power on -> drive VBUS - dwc2->hprt = HPRT_POWER; + dwc2->gahbcfg |= GAHBCFG_GINT; // Enable global interrupt return true; } // Enable USB interrupt void hcd_int_enable (uint8_t rhport) { - (void) rhport; + dwc2_int_set(rhport, TUSB_ROLE_HOST, true); } // Disable USB interrupt void hcd_int_disable(uint8_t rhport) { - (void) rhport; + dwc2_int_set(rhport, TUSB_ROLE_HOST, false); } // Get frame number (1ms) uint32_t hcd_frame_number(uint8_t rhport) { (void) rhport; - return 0; } @@ -97,20 +102,23 @@ uint32_t hcd_frame_number(uint8_t rhport) { // Get the current connect status of roothub port bool hcd_port_connect_status(uint8_t rhport) { - (void) rhport; - - return false; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + return dwc2->hprt & HPRT_CONN_STATUS; } // Reset USB bus on the port. Return immediately, bus reset sequence may not be complete. // Some port would require hcd_port_reset_end() to be invoked after 10ms to complete the reset sequence. void hcd_port_reset(uint8_t rhport) { - (void) rhport; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + dwc2->hprt = HPRT_RESET; } // Complete bus reset sequence, may be required by some controllers void hcd_port_reset_end(uint8_t rhport) { - (void) rhport; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint32_t hprt = dwc2->hprt & ~HPRT_W1C_MASK; // skip w1c bits + hprt &= ~HPRT_RESET; + dwc2->hprt = hprt; } // Get port link speed @@ -182,6 +190,101 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { // HCD Event Handler //-------------------------------------------------------------------- +#if 1 +static void handle_rxflvl_irq(uint8_t rhport) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + volatile uint32_t const* rx_fifo = dwc2->fifo[0]; + + // Pop control word off FIFO + uint32_t const grxstsp = dwc2->grxstsp; + uint8_t const pktsts = (grxstsp & GRXSTSP_PKTSTS_Msk) >> GRXSTSP_PKTSTS_Pos; + uint8_t const epnum = (grxstsp & GRXSTSP_EPNUM_Msk) >> GRXSTSP_EPNUM_Pos; + uint16_t const bcnt = (grxstsp & GRXSTSP_BCNT_Msk) >> GRXSTSP_BCNT_Pos; + // dwc2_epout_t* epout = &dwc2->epout[epnum]; + + (void) epnum; (void) bcnt; (void) rx_fifo; + + TU_LOG1_INT(pktsts); + + // switch (pktsts) { + // // Global OUT NAK: do nothing + // case GRXSTS_PKTSTS_GLOBALOUTNAK: + // break; + // + // case GRXSTS_PKTSTS_SETUPRX: + // // Setup packet received + // // We can receive up to three setup packets in succession, but only the last one is valid. + // _setup_packet[0] = (*rx_fifo); + // _setup_packet[1] = (*rx_fifo); + // break; + // + // case GRXSTS_PKTSTS_SETUPDONE: + // // Setup packet done: + // // After popping this out, dwc2 asserts a DOEPINT_SETUP interrupt which is handled by handle_epout_irq() + // epout->doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); + // break; + // + // case GRXSTS_PKTSTS_OUTRX: { + // // Out packet received + // xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); + // + // // Read packet off RxFIFO + // if (xfer->ff) { + // // Ring buffer + // tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, bcnt); + // } else { + // // Linear buffer + // dfifo_read_packet(rhport, xfer->buffer, bcnt); + // + // // Increment pointer to xfer data + // xfer->buffer += bcnt; + // } + // + // // Truncate transfer length in case of short packet + // if (bcnt < xfer->max_size) { + // xfer->total_len -= (epout->doeptsiz & DOEPTSIZ_XFRSIZ_Msk) >> DOEPTSIZ_XFRSIZ_Pos; + // if (epnum == 0) { + // xfer->total_len -= ep0_pending[TUSB_DIR_OUT]; + // ep0_pending[TUSB_DIR_OUT] = 0; + // } + // } + // break; + // } + // + // case GRXSTS_PKTSTS_OUTDONE: + // /* Out packet done + // After this entry is popped from the receive FIFO, dwc2 asserts a Transfer Completed interrupt on + // the specified OUT endpoint which will be handled by handle_epout_irq() */ + // break; + // + // default: + // TU_BREAKPOINT(); + // break; + // } +} +#endif + +/* Handle Host Port interrupt, possible source are: + - Connection Detection + - Enable Change + - Over Current Change +*/ +TU_ATTR_ALWAYS_INLINE static inline void handle_hprt_irq(uint8_t rhport, bool in_isr) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint32_t hprt = dwc2->hprt; + + if (hprt & HPRT_CONN_DETECT) { + // Port Connect Detect + dwc2->hprt = HPRT_CONN_DETECT; // clear + + if (hprt & HPRT_CONN_STATUS) { + hcd_event_device_attach(rhport, in_isr); + } else { + hcd_event_device_remove(rhport, in_isr); + } + } +} + /* Interrupt Hierarchy HCINTn.XferCompl HCINTMSKn.XferComplMsk @@ -201,7 +304,6 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { IRQn */ void hcd_int_handler(uint8_t rhport, bool in_isr) { - (void) in_isr; dwc2_regs_t* dwc2 = DWC2_REG(rhport); const uint32_t int_mask = dwc2->gintmsk; @@ -209,7 +311,21 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { if (int_status & GINTSTS_HPRTINT) { TU_LOG1_HEX(dwc2->hprt); + handle_hprt_irq(rhport, in_isr); } + + // RxFIFO non-empty interrupt handling. + if (int_status & GINTSTS_RXFLVL) { + // RXFLVL bit is read-only + dwc2->gintmsk &= ~GINTMSK_RXFLVLM; // disable RXFLVL interrupt while reading + + do { + handle_rxflvl_irq(rhport); // read all packets + } while(dwc2->gintsts & GINTSTS_RXFLVL); + + dwc2->gintmsk |= GINTMSK_RXFLVLM; + } + } #endif diff --git a/src/tusb.c b/src/tusb.c index e6f8055b7..6a0cc0805 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -77,6 +77,7 @@ bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // new API with explicit rhport and role TU_ASSERT(rhport < TUP_USBIP_CONTROLLER_NUM && rh_init->role != TUSB_ROLE_INVALID); + _rhport_role[rhport] = rh_init->role; #if CFG_TUD_ENABLED if (rh_init->role == TUSB_ROLE_DEVICE) { @@ -90,7 +91,6 @@ bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { } #endif - _rhport_role[rhport] = rh_init->role; return true; } -- cgit v1.3.1 From 61b33ca926065cd25a2045077ab96dd990ce2560 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 18 Oct 2024 22:16:21 +0700 Subject: testing hcd with f723 due to h743eval issue with mfx vbus drive. --- hw/bsp/stm32f7/boards/stm32f723disco/board.cmake | 2 +- hw/bsp/stm32f7/boards/stm32f723disco/board.h | 41 ++++++++++++++++++++++-- hw/bsp/stm32f7/family.c | 24 ++++++-------- src/common/tusb_types.h | 4 +-- 4 files changed, 50 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/hw/bsp/stm32f7/boards/stm32f723disco/board.cmake b/hw/bsp/stm32f7/boards/stm32f723disco/board.cmake index dd3c35b00..8a7b4cb27 100644 --- a/hw/bsp/stm32f7/boards/stm32f723disco/board.cmake +++ b/hw/bsp/stm32f7/boards/stm32f723disco/board.cmake @@ -1,6 +1,6 @@ set(MCU_VARIANT stm32f723xx) set(JLINK_DEVICE stm32f723ie) - +#set(JLINK_OPTION "-USB 000776606156") set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/STM32F723xE_FLASH.ld) function(update_board TARGET) diff --git a/hw/bsp/stm32f7/boards/stm32f723disco/board.h b/hw/bsp/stm32f7/boards/stm32f723disco/board.h index 258786b7f..178fd6e5e 100644 --- a/hw/bsp/stm32f7/boards/stm32f723disco/board.h +++ b/hw/bsp/stm32f7/boards/stm32f723disco/board.h @@ -56,8 +56,7 @@ //--------------------------------------------------------------------+ // RCC Clock //--------------------------------------------------------------------+ -static inline void board_clock_init(void) -{ +static inline void board_clock_init(void) { RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_OscInitTypeDef RCC_OscInitStruct; @@ -74,7 +73,7 @@ static inline void board_clock_init(void) RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; - RCC_OscInitStruct.PLL.PLLM = HSE_VALUE/1000000; + RCC_OscInitStruct.PLL.PLLM = HSE_VALUE / 1000000; RCC_OscInitStruct.PLL.PLLN = 432; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 9; @@ -98,6 +97,42 @@ static inline void board_clock_init(void) // //} +typedef struct { + GPIO_TypeDef* port; + GPIO_InitTypeDef pin_init; + bool active_state; +} board_pindef_t; + +static board_pindef_t vbus_pindef[] = { +{ + .port = GPIOG, + .pin_init = { + .Pin = GPIO_PIN_8, .Mode = GPIO_MODE_OUTPUT_OD, .Pull = GPIO_NOPULL, + .Speed = GPIO_SPEED_HIGH, .Alternate = 0 + }, + .active_state = false + }, +{ + .port = GPIOH, + .pin_init = { + .Pin = GPIO_PIN_12, .Mode = GPIO_MODE_OUTPUT_PP, .Pull = GPIO_NOPULL, + .Speed = GPIO_SPEED_HIGH, .Alternate = 0 + }, + .active_state = true + }, +}; + +static inline void board_vbus_set(uint8_t rhport, bool state) { + static bool pin_inited[2] = { false, false }; + board_pindef_t* pindef = &vbus_pindef[rhport]; + if (!pin_inited[rhport]) { + HAL_GPIO_Init(pindef->port, &pindef->pin_init); + pin_inited[rhport] = true; + } + + HAL_GPIO_WritePin(pindef->port, pindef->pin_init.Pin, state == pindef->active_state ? GPIO_PIN_SET : GPIO_PIN_RESET); +} + #ifdef __cplusplus } #endif diff --git a/hw/bsp/stm32f7/family.c b/hw/bsp/stm32f7/family.c index 61f1d2a7f..1f49d7693 100644 --- a/hw/bsp/stm32f7/family.c +++ b/hw/bsp/stm32f7/family.c @@ -34,13 +34,13 @@ // Forward USB interrupt events to TinyUSB IRQ Handler //--------------------------------------------------------------------+ void OTG_FS_IRQHandler(void) { - tud_int_handler(0); + tusb_int_handler(0, true); } // Despite being call USB2_OTG // OTG_HS is marked as RHPort1 by TinyUSB to be consistent across stm32 port void OTG_HS_IRQHandler(void) { - tud_int_handler(1); + tusb_int_handler(1, true); } //--------------------------------------------------------------------+ @@ -65,7 +65,7 @@ void board_init(void) { __HAL_RCC_GPIOJ_CLK_ENABLE(); #endif - UART_CLK_EN(); + UART_CLK_EN(); #if CFG_TUSB_OS == OPT_OS_NONE // 1ms tick timer @@ -122,9 +122,7 @@ void board_init(void) { UartHandle.Init.OverSampling = UART_OVERSAMPLING_16; HAL_UART_Init(&UartHandle); -#if BOARD_TUD_RHPORT == 0 - // OTG_FS - + //------------- rhport0: OTG_FS -------------// /* Configure DM DP Pins */ GPIO_InitStruct.Pin = (GPIO_PIN_11 | GPIO_PIN_12); GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; @@ -171,9 +169,7 @@ void board_init(void) { USB_OTG_FS->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL; #endif // vbus sense -#else - // OTG_HS - + //------------- rhport1: OTG_HS -------------// #ifdef USB_HS_PHYC // MCU with built-in HS PHY such as F723, F733, F730 @@ -261,12 +257,10 @@ void board_init(void) { USB_OTG_HS->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL; #endif - // Force device mode - USB_OTG_HS->GUSBCFG &= ~USB_OTG_GUSBCFG_FHMOD; - USB_OTG_HS->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; - -#endif // BOARD_TUD_RHPORT - + // Turn on host vbus +#if CFG_TUH_ENABLED + board_vbus_set(BOARD_TUH_RHPORT, true); +#endif } //--------------------------------------------------------------------+ diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index 53bb367cb..0faa6eee9 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -41,8 +41,8 @@ typedef enum { TUSB_ROLE_INVALID = 0, - TUSB_ROLE_DEVICE, - TUSB_ROLE_HOST, + TUSB_ROLE_DEVICE = 0x1, + TUSB_ROLE_HOST = 0x2, } tusb_role_t; /// defined base on EHCI specs value for Endpoint Speed -- cgit v1.3.1 From ebcd7067c9b86b8df8a3f9cf07ba76edd9e7440c Mon Sep 17 00:00:00 2001 From: GuavTek Date: Fri, 18 Oct 2024 18:05:58 +0200 Subject: Recover host enumeration from zero length descriptors --- src/host/usbh.c | 7 +++++++ src/tusb.c | 4 ++++ 2 files changed, 11 insertions(+) (limited to 'src') diff --git a/src/host/usbh.c b/src/host/usbh.c index b5df29f50..1b45fd1be 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -1645,6 +1645,13 @@ static bool _parse_configuration_descriptor(uint8_t dev_addr, tusb_desc_configur // desc_iad->bFunctionClass == desc_itf->bInterfaceClass); } + if ( 0 == tu_desc_len(p_desc) ) { + // A zero length descriptor indicates that the wTotalLength field is wrong. + // Parsed interfaces should still be usable + TU_LOG_USBH("Encountered a zero-length descriptor after %u bytes\r\n", (uint32_t)p_desc - (uint32_t)desc_cfg); + break; + } + TU_ASSERT( TUSB_DESC_INTERFACE == tu_desc_type(p_desc) ); tusb_desc_interface_t const* desc_itf = (tusb_desc_interface_t const*) p_desc; diff --git a/src/tusb.c b/src/tusb.c index e6f8055b7..799ffdce9 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -255,6 +255,10 @@ uint16_t tu_desc_get_interface_total_len(tusb_desc_interface_t const* desc_itf, ((tusb_desc_interface_t const*) p_desc)->bAlternateSetting == 0) { break; } + if (tu_desc_len(p_desc) == 0) { + // Escape infinite loop + break; + } len += tu_desc_len(p_desc); p_desc = tu_desc_next(p_desc); -- cgit v1.3.1 From f5978876d2a5d8cdc6cfd3bf99f9b820c5a6ac8a Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 21 Oct 2024 11:43:37 +0700 Subject: get hprt interrupt triggered --- examples/dual/host_info_to_device_cdc/only.txt | 1 + examples/host/device_info/only.txt | 1 + src/portable/synopsys/dwc2/dcd_dwc2.c | 4 +- src/portable/synopsys/dwc2/dwc2_common.c | 95 ++++++++++---------- src/portable/synopsys/dwc2/dwc2_common.h | 6 +- src/portable/synopsys/dwc2/dwc2_type.h | 27 ++++-- src/portable/synopsys/dwc2/hcd_dwc2.c | 117 +++++++++---------------- 7 files changed, 120 insertions(+), 131 deletions(-) (limited to 'src') diff --git a/examples/dual/host_info_to_device_cdc/only.txt b/examples/dual/host_info_to_device_cdc/only.txt index 149ece856..f9db1db85 100644 --- a/examples/dual/host_info_to_device_cdc/only.txt +++ b/examples/dual/host_info_to_device_cdc/only.txt @@ -5,3 +5,4 @@ mcu:RP2040 mcu:ra6m5 mcu:MAX3421 mcu:STM32H7 +mcu:STM32F7 diff --git a/examples/host/device_info/only.txt b/examples/host/device_info/only.txt index a38fc251b..aed9583b4 100644 --- a/examples/host/device_info/only.txt +++ b/examples/host/device_info/only.txt @@ -13,3 +13,4 @@ mcu:RP2040 mcu:RX65X mcu:RAXXX mcu:STM32H7 +mcu:STM32F7 diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index 55d8f5766..e7440529a 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -454,7 +454,9 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); // Core Initialization - TU_ASSERT(dwc2_core_init(rhport, rh_init)); + const bool is_highspeed = dwc2_core_is_highspeed(dwc2, rh_init); + const bool is_dma = dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE); + TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma)); // Device Initialization dcd_disconnect(rhport); diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index 12dcdb7c6..a5f1c2b08 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -40,6 +40,9 @@ #include "dwc2_common.h" +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- static void reset_core(dwc2_regs_t* dwc2) { // reset core dwc2->grstctl |= GRSTCTL_CSRST; @@ -57,24 +60,7 @@ static void reset_core(dwc2_regs_t* dwc2) { while (!(dwc2->grstctl & GRSTCTL_AHBIDL)) {} // wait for AHB master IDLE } -bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init) { - (void)dwc2; - -#if CFG_TUD_ENABLED - if (rh_init->role == TUSB_ROLE_DEVICE && !TUD_OPT_HIGH_SPEED) { - return false; - } -#endif -#if CFG_TUH_ENABLED - if (rh_init->role == TUSB_ROLE_HOST && !TUH_OPT_HIGH_SPEED) { - return false; - } -#endif - - return dwc2->ghwcfg2_bm.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED; -} - -static void phy_fs_init(dwc2_regs_t* dwc2, tusb_role_t role) { +static void phy_fs_init(dwc2_regs_t* dwc2) { TU_LOG(DWC2_COMMON_DEBUG, "Fullspeed PHY init\r\n"); uint32_t gusbcfg = dwc2->gusbcfg; @@ -96,29 +82,11 @@ static void phy_fs_init(dwc2_regs_t* dwc2, tusb_role_t role) { gusbcfg |= 5u << GUSBCFG_TRDT_Pos; dwc2->gusbcfg = gusbcfg; - // FS/LS PHY Clock Select - if (role == TUSB_ROLE_HOST) { - uint32_t hcfg = dwc2->hcfg; - hcfg |= HCFG_FSLS_ONLY; - hcfg &= ~HCFG_FSLS_PHYCLK_SEL; - - if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI && - dwc2->ghwcfg2_bm.fs_phy_type == GHWCFG2_FSPHY_DEDICATED) { - // dedicated FS PHY with 48 mhz - hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ; - } else { - // shared HS PHY running at full speed - hcfg |= HCFG_FSLS_PHYCLK_SEL_30_60MHZ; - } - dwc2->hcfg = hcfg; - } - // MCU specific PHY update post reset dwc2_phy_update(dwc2, GHWCFG2_HSPHY_NOT_SUPPORTED); } -static void phy_hs_init(dwc2_regs_t* dwc2, tusb_role_t role) { - (void) role; +static void phy_hs_init(dwc2_regs_t* dwc2) { uint32_t gusbcfg = dwc2->gusbcfg; // De-select FS PHY @@ -164,10 +132,6 @@ static void phy_hs_init(dwc2_regs_t* dwc2, tusb_role_t role) { // Reset core after selecting PHY reset_core(dwc2); - if (role == TUSB_ROLE_HOST) { - dwc2->hcfg &= ~HCFG_FSLS_ONLY; - } - // Set turn-around, must after core reset otherwise it will be clear // - 9 if using 8-bit PHY interface // - 5 if using 16-bit PHY interface @@ -204,17 +168,36 @@ static bool check_dwc2(dwc2_regs_t* dwc2) { //-------------------------------------------------------------------- // //-------------------------------------------------------------------- -bool dwc2_core_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { - (void)rh_init; +bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init) { + (void)dwc2; + +#if CFG_TUD_ENABLED + if (rh_init->role == TUSB_ROLE_DEVICE && !TUD_OPT_HIGH_SPEED) { + return false; + } +#endif +#if CFG_TUH_ENABLED + if (rh_init->role == TUSB_ROLE_HOST && !TUH_OPT_HIGH_SPEED) { + return false; + } +#endif + + return dwc2->ghwcfg2_bm.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED; +} + +bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); // Check Synopsys ID register, failed if controller clock/power is not enabled TU_ASSERT(check_dwc2(dwc2)); - if (dwc2_core_is_highspeed(dwc2, rh_init)) { - phy_hs_init(dwc2, rh_init->role); + // disable global interrupt + // dwc2->gahbcfg &= ~GAHBCFG_GINT; + + if (is_highspeed) { + phy_hs_init(dwc2); } else { - phy_fs_init(dwc2, rh_init->role); + phy_fs_init(dwc2); } /* Set HS/FS Timeout Calibration to 7 (max available value). @@ -243,7 +226,12 @@ bool dwc2_core_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { dwc2->gintmsk = 0; - if (dwc2_dma_enabled(dwc2, rh_init->role)) { + // TODO can be enabled with device as well but tested with host for now + // if (rh_init->role == TUSB_ROLE_HOST) { + // dwc2->gintmsk |= OTG_INT_COMMON; + // } + + if (is_dma) { const uint16_t epinfo_base = dma_cal_epfifo_base(rhport); dwc2->gdfifocfg = (epinfo_base << GDFIFOCFG_EPINFOBASE_SHIFT) | epinfo_base; @@ -259,4 +247,17 @@ bool dwc2_core_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { return true; } +// void dwc2_core_handle_common_irq(uint8_t rhport, bool in_isr) { +// (void) in_isr; +// dwc2_regs_t * const dwc2 = DWC2_REG(rhport); +// const uint32_t int_mask = dwc2->gintmsk; +// const uint32_t int_status = dwc2->gintsts & int_mask; +// +// // Device disconnect +// if (int_status & GINTSTS_DISCINT) { +// dwc2->gintsts = GINTSTS_DISCINT; +// } +// +// } + #endif diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h index 538fcd179..7088ff3c3 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.h +++ b/src/portable/synopsys/dwc2/dwc2_common.h @@ -57,6 +57,9 @@ enum { DWC2_CONTROLLER_COUNT = TU_ARRAY_SIZE(_dwc2_controller) }; +enum { + OTG_INT_COMMON = 0 // GINTSTS_DISCINT | GINTSTS_CONIDSTSCHNG +}; //------------- Core -------------// TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) { @@ -68,7 +71,8 @@ TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) { } bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init); -bool dwc2_core_init(uint8_t rhport, const tusb_rhport_init_t* rh_init); +bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma); +void dwc2_core_handle_common_irq(uint8_t rhport, bool in_isr); //------------- DFIFO -------------// TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_tx(dwc2_regs_t* dwc2, uint8_t fnum) { diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index f99bf3e85..8fb926a21 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -86,6 +86,11 @@ typedef struct } HS_PHYC_GlobalTypeDef; #endif +enum { + GOTGCTL_OTG_VERSION_1_3 = 0, + GOTGCTL_OTG_VERSION_2_0 = 1, +}; + enum { GHWCFG2_OPMODE_HNP_SRP = 0, GHWCFG2_OPMODE_SRP = 1, @@ -128,6 +133,11 @@ enum { HPRT_SPEED_LOW = 2 }; +enum { + GINTSTS_CMODE_DEVICE = 0, + GINTSTS_CMODE_HOST = 1, +}; + //-------------------------------------------------------------------- // Register bitfield definitions //-------------------------------------------------------------------- @@ -152,7 +162,7 @@ typedef struct TU_ATTR_PACKED { uint32_t ases_valid : 1; // 18 A-session valid uint32_t bses_valid : 1; // 19 B-session valid uint32_t otg_ver : 1; // 20 OTG version 0: v1.3, 1: v2.0 - uint32_t current_mode : 1; // 21 Current mode of operation 0: device, 1: host + uint32_t current_mode : 1; // 21 Current mode of operation. Only from v3.00a uint32_t mult_val_id_bc : 5; // 22..26 Multi-valued input pin ID battery charger uint32_t chirp_en : 1; // 27 Chirp detection enable uint32_t rsv28_30 : 3; // 28.30: Reserved @@ -391,7 +401,10 @@ TU_VERIFY_STATIC(sizeof(dwc2_dep_t) == 0x20, "incorrect size"); //-------------------------------------------------------------------- typedef struct { //------------- Core Global -------------// + union { volatile uint32_t gotgctl; // 000 OTG Control and Status + volatile dwc2_gotgctl_t gotgctl_bm; + }; volatile uint32_t gotgint; // 004 OTG Interrupt volatile uint32_t gahbcfg; // 008 AHB Configuration volatile uint32_t gusbcfg; // 00c USB Configuration @@ -1008,9 +1021,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define GINTSTS_LPMINT_Pos (27U) #define GINTSTS_LPMINT_Msk (0x1UL << GINTSTS_LPMINT_Pos) // 0x08000000 #define GINTSTS_LPMINT GINTSTS_LPMINT_Msk // LPM interrupt -#define GINTSTS_CIDSCHG_Pos (28U) -#define GINTSTS_CIDSCHG_Msk (0x1UL << GINTSTS_CIDSCHG_Pos) // 0x10000000 -#define GINTSTS_CIDSCHG GINTSTS_CIDSCHG_Msk // Connector ID status change +#define GINTSTS_CONIDSTSCHNG_Pos (28U) +#define GINTSTS_CONIDSTSCHNG_Msk (0x1UL << GINTSTS_CONIDSTSCHNG_Pos) // 0x10000000 +#define GINTSTS_CONIDSTSCHNG GINTSTS_CONIDSTSCHNG_Msk // Connector ID status change #define GINTSTS_DISCINT_Pos (29U) #define GINTSTS_DISCINT_Msk (0x1UL << GINTSTS_DISCINT_Pos) // 0x20000000 #define GINTSTS_DISCINT GINTSTS_DISCINT_Msk // Disconnect detected interrupt @@ -1094,9 +1107,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define GINTMSK_LPMINTM_Pos (27U) #define GINTMSK_LPMINTM_Msk (0x1UL << GINTMSK_LPMINTM_Pos) // 0x08000000 #define GINTMSK_LPMINTM GINTMSK_LPMINTM_Msk // LPM interrupt Mask -#define GINTMSK_CIDSCHGM_Pos (28U) -#define GINTMSK_CIDSCHGM_Msk (0x1UL << GINTMSK_CIDSCHGM_Pos) // 0x10000000 -#define GINTMSK_CIDSCHGM GINTMSK_CIDSCHGM_Msk // Connector ID status change mask +#define GINTMSK_CONIDSTSCHNGM_Pos (28U) +#define GINTMSK_CONIDSTSCHNGM_Msk (0x1UL << GINTMSK_CONIDSTSCHNGM_Pos) // 0x10000000 +#define GINTMSK_CONIDSTSCHNGM GINTMSK_CONIDSTSCHNGM_Msk // Connector ID status change mask #define GINTMSK_DISCINT_Pos (29U) #define GINTMSK_DISCINT_Msk (0x1UL << GINTMSK_DISCINT_Pos) // 0x20000000 #define GINTMSK_DISCINT GINTMSK_DISCINT_Msk // Disconnect detected interrupt mask diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 97068d4c5..2d4368e94 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -56,25 +56,40 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); // Core Initialization - TU_ASSERT(dwc2_core_init(rhport, rh_init)); + const bool is_highspeed = dwc2_core_is_highspeed(dwc2, rh_init); + const bool is_dma = dwc2_dma_enabled(dwc2, TUSB_ROLE_HOST); + TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma)); //------------- 3.1 Host Initialization -------------// - // max speed - // if (dwc2_core_is_highspeed(dwc2, rh_init)) { - // dwc2->hcfg &= ~HCFG_FSLS_ONLY; - // } else { - // dwc2->hcfg |= HCFG_FSLS_ONLY; - // } + // FS/LS PHY Clock Select + uint32_t hcfg = dwc2->hcfg; + if (is_highspeed) { + hcfg &= ~HCFG_FSLS_ONLY; + } else { + hcfg &= ~HCFG_FSLS_ONLY; // since we are using FS PHY + hcfg &= ~HCFG_FSLS_PHYCLK_SEL; + + if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI && + dwc2->ghwcfg2_bm.fs_phy_type == GHWCFG2_FSPHY_DEDICATED) { + // dedicated FS PHY with 48 mhz + hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ; + } else { + // shared HS PHY running at full speed + hcfg |= HCFG_FSLS_PHYCLK_SEL_30_60MHZ; + } + } + dwc2->hcfg = hcfg; - // force host mode + // force host mode and wait for mode switch dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FDMOD) | GUSBCFG_FHMOD; + while( (dwc2->gintsts & GINTSTS_CMOD) != GINTSTS_CMODE_HOST) {} dwc2->hprt = HPRT_W1C_MASK; // clear all write-1-clear bits - dwc2->hprt = HPRT_POWER; // port power on -> drive VBUS + dwc2->hprt = HPRT_POWER; // turn on VBUS // Enable required interrupts - dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_PRTIM | GINTMSK_WUIM; + dwc2->gintmsk |= GINTMSK_OTGINT | GINTSTS_CONIDSTSCHNG | GINTMSK_PRTIM; // | GINTMSK_WUIM; dwc2->gahbcfg |= GAHBCFG_GINT; // Enable global interrupt return true; @@ -193,74 +208,15 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { #if 1 static void handle_rxflvl_irq(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - volatile uint32_t const* rx_fifo = dwc2->fifo[0]; + // volatile uint32_t const* rx_fifo = dwc2->fifo[0]; // Pop control word off FIFO uint32_t const grxstsp = dwc2->grxstsp; - uint8_t const pktsts = (grxstsp & GRXSTSP_PKTSTS_Msk) >> GRXSTSP_PKTSTS_Pos; - uint8_t const epnum = (grxstsp & GRXSTSP_EPNUM_Msk) >> GRXSTSP_EPNUM_Pos; - uint16_t const bcnt = (grxstsp & GRXSTSP_BCNT_Msk) >> GRXSTSP_BCNT_Pos; + (void) grxstsp; + // uint8_t const pktsts = (grxstsp & GRXSTSP_PKTSTS_Msk) >> GRXSTSP_PKTSTS_Pos; + // uint8_t const epnum = (grxstsp & GRXSTSP_EPNUM_Msk) >> GRXSTSP_EPNUM_Pos; + // uint16_t const bcnt = (grxstsp & GRXSTSP_BCNT_Msk) >> GRXSTSP_BCNT_Pos; // dwc2_epout_t* epout = &dwc2->epout[epnum]; - - (void) epnum; (void) bcnt; (void) rx_fifo; - - TU_LOG1_INT(pktsts); - - // switch (pktsts) { - // // Global OUT NAK: do nothing - // case GRXSTS_PKTSTS_GLOBALOUTNAK: - // break; - // - // case GRXSTS_PKTSTS_SETUPRX: - // // Setup packet received - // // We can receive up to three setup packets in succession, but only the last one is valid. - // _setup_packet[0] = (*rx_fifo); - // _setup_packet[1] = (*rx_fifo); - // break; - // - // case GRXSTS_PKTSTS_SETUPDONE: - // // Setup packet done: - // // After popping this out, dwc2 asserts a DOEPINT_SETUP interrupt which is handled by handle_epout_irq() - // epout->doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); - // break; - // - // case GRXSTS_PKTSTS_OUTRX: { - // // Out packet received - // xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); - // - // // Read packet off RxFIFO - // if (xfer->ff) { - // // Ring buffer - // tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, bcnt); - // } else { - // // Linear buffer - // dfifo_read_packet(rhport, xfer->buffer, bcnt); - // - // // Increment pointer to xfer data - // xfer->buffer += bcnt; - // } - // - // // Truncate transfer length in case of short packet - // if (bcnt < xfer->max_size) { - // xfer->total_len -= (epout->doeptsiz & DOEPTSIZ_XFRSIZ_Msk) >> DOEPTSIZ_XFRSIZ_Pos; - // if (epnum == 0) { - // xfer->total_len -= ep0_pending[TUSB_DIR_OUT]; - // ep0_pending[TUSB_DIR_OUT] = 0; - // } - // } - // break; - // } - // - // case GRXSTS_PKTSTS_OUTDONE: - // /* Out packet done - // After this entry is popped from the receive FIFO, dwc2 asserts a Transfer Completed interrupt on - // the specified OUT endpoint which will be handled by handle_epout_irq() */ - // break; - // - // default: - // TU_BREAKPOINT(); - // break; - // } } #endif @@ -305,10 +261,21 @@ TU_ATTR_ALWAYS_INLINE static inline void handle_hprt_irq(uint8_t rhport, bool in */ void hcd_int_handler(uint8_t rhport, bool in_isr) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - const uint32_t int_mask = dwc2->gintmsk; const uint32_t int_status = dwc2->gintsts & int_mask; + TU_LOG1_HEX(int_status); + + if (int_status & GINTSTS_CONIDSTSCHNG) { + // Connector ID status change + dwc2->gintsts = GINTSTS_CONIDSTSCHNG; + + //if (dwc2->gotgctl) + // dwc2->hprt = HPRT_POWER; // power on port to turn on VBUS + //dwc2->gintmsk |= GINTMSK_PRTIM; + // TODO wait for SRP if OTG + } + if (int_status & GINTSTS_HPRTINT) { TU_LOG1_HEX(dwc2->hprt); handle_hprt_irq(rhport, in_isr); -- cgit v1.3.1 From 8461525d48cb4a97a7b18816f65b548a875ffe50 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 21 Oct 2024 17:45:40 +0700 Subject: add tusb_time_millis(), able to reset and enable dwc2 port and get SOF active --- hw/bsp/board.c | 5 ++ src/host/usbh.c | 21 ++++++--- src/portable/synopsys/dwc2/dwc2_common.c | 8 ++++ src/portable/synopsys/dwc2/dwc2_info.md | 2 +- src/portable/synopsys/dwc2/dwc2_info.py | 6 ++- src/portable/synopsys/dwc2/dwc2_type.h | 20 +++++--- src/portable/synopsys/dwc2/hcd_dwc2.c | 81 +++++++++++++++++++++++++++----- src/tusb.h | 10 ++-- 8 files changed, 123 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/hw/bsp/board.c b/hw/bsp/board.c index bb339f613..d4fdb7586 100644 --- a/hw/bsp/board.c +++ b/hw/bsp/board.c @@ -134,3 +134,8 @@ int board_getchar(void) { char c; return (sys_read(0, &c, 1) > 0) ? (int) c : (-1); } + + +uint32_t tusb_time_millis(void) { + return board_millis(); +} diff --git a/src/host/usbh.c b/src/host/usbh.c index b5df29f50..8bf40a607 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -447,9 +447,9 @@ bool tuh_deinit(uint8_t rhport) { } bool tuh_task_event_ready(void) { - // Skip if stack is not initialized - if ( !tuh_inited() ) return false; - + if (!tuh_inited()) { + return false; // Skip if stack is not initialized + } return !osal_queue_empty(_usbh_q); } @@ -1520,10 +1520,17 @@ static bool enum_new_device(hcd_event_t* event) { _dev0.hub_port = event->connection.hub_port; if (_dev0.hub_addr == 0) { - // connected/disconnected directly with roothub + // connected directly to roothub hcd_port_reset(_dev0.rhport); - osal_task_delay(ENUM_RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since - // sof of controller may not running while resetting +#if CFG_TUSB_OS == OPT_OS_NONE + // Since we are in middle of rhport reset, frame number is not available for time delay + // need to depend on tusb_time_millis() instead + const uint32_t start_reset = tusb_time_millis(); + while ((tusb_time_millis() - start_reset) < ENUM_RESET_DELAY) {} +#else + osal_task_delay(ENUM_RESET_DELAY); +#endif + hcd_port_reset_end(_dev0.rhport); // wait until device connection is stable TODO non blocking @@ -1548,7 +1555,7 @@ static bool enum_new_device(hcd_event_t* event) { } #if CFG_TUH_HUB else { - // connected/disconnected via external hub + // connected via external hub // wait until device connection is stable TODO non blocking osal_task_delay(ENUM_CONTACT_DEBOUNCING_DELAY); diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index a5f1c2b08..d0ad764a5 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -185,6 +185,14 @@ bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init return dwc2->ghwcfg2_bm.hs_phy_type != GHWCFG2_HSPHY_NOT_SUPPORTED; } +/* dwc2 has several PHYs option + * - UTMI+ is internal highspeed PHY, clock can be 30 Mhz (8-bit) or 60 Mhz (16-bit) + * - ULPI is external highspeed PHY, clock is 60Mhz with only 8-bit interface + * - Dedicated FS PHY is internal with clock 48Mhz. + * + * In addition, UTMI+/ULPI can be shared to run at fullspeed mode with 48Mhz + * +*/ bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); diff --git a/src/portable/synopsys/dwc2/dwc2_info.md b/src/portable/synopsys/dwc2/dwc2_info.md index cfd0c81a8..92293a072 100644 --- a/src/portable/synopsys/dwc2/dwc2_info.md +++ b/src/portable/synopsys/dwc2/dwc2_info.md @@ -7,7 +7,7 @@ | GHWCFG2 | 0x228DDD50 | 0x228F5910 | 0x224DD930 | 0x215FFFD0 | 0x229DCD20 | 0x229ED590 | 0x229ED520 | 0x229ED520 | 0x229FE1D0 | 0x229FE190 | 0x229FE190 | 0x229ED520 | 0x228FE052 | 0x00000000 | 0x228F5930 | | - op_mode | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | HNP SRP | noHNP noSRP | HNP SRP | HNP SRP | | - arch | DMA internal | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | Slave only | DMA internal | DMA internal | DMA internal | Slave only | DMA internal | Slave only | DMA internal | -| - p2p (hub support) | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | +| - single_point | hub | hub | n/a | hub | n/a | hub | n/a | n/a | hub | hub | hub | n/a | hub | hub | n/a | | - hs_phy_type | UTMI+ | n/a | n/a | UTMI+/ULPI | n/a | ULPI | n/a | n/a | UTMI+/ULPI | ULPI | ULPI | n/a | UTMI+ | n/a | n/a | | - fs_phy_type | Dedicated | Dedicated | Dedicated | Shared ULPI | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | Dedicated | n/a | n/a | Dedicated | | - num_dev_ep | 7 | 6 | 6 | 15 | 3 | 5 | 5 | 5 | 8 | 8 | 8 | 5 | 8 | 0 | 6 | diff --git a/src/portable/synopsys/dwc2/dwc2_info.py b/src/portable/synopsys/dwc2/dwc2_info.py index d1793a005..d97a1192e 100755 --- a/src/portable/synopsys/dwc2/dwc2_info.py +++ b/src/portable/synopsys/dwc2/dwc2_info.py @@ -44,7 +44,7 @@ class GHWCFG2(ctypes.LittleEndianStructure): _fields_ = [ ("op_mode", ctypes.c_uint32, 3), ("arch", ctypes.c_uint32, 2), - ("p2p (hub support)", ctypes.c_uint32, 1), + ("single_point", ctypes.c_uint32, 1), ("hs_phy_type", ctypes.c_uint32, 2), ("fs_phy_type", ctypes.c_uint32, 2), ("num_dev_ep", ctypes.c_uint32, 4), @@ -119,6 +119,10 @@ GHWCFG2_field = { 1: "DMA external", 2: "DMA internal" }, + 'single_point': { + 0: "hub", + 1: "n/a" + }, 'hs_phy_type': { 0: "n/a", 1: "UTMI+", diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index 8fb926a21..d2993bdd9 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -208,7 +208,7 @@ typedef struct TU_ATTR_PACKED { based on the speed of enumeration. The number of bit times added per PHY clock are as follows: - High-speed: PHY clock One 30-MHz = 16 bit times, One 60-MHz = 8 bit times - Full-speed: PHY clock One 30-MHz = 0.4 bit times, One 60-MHz = 0.2 bit times, One 48-MHz = 0.25 bit times */ - uint32_t phy_if : 1; // 3 PHY interface. 0: 8 bits, 1: 16 bits + uint32_t phy_if16 : 1; // 3 PHY interface. 0: 8 bits, 1: 16 bits uint32_t ulpi_utmi_sel : 1; // 4 ULPI/UTMI select. 0: UTMI+, 1: ULPI uint32_t fs_intf_sel : 1; // 5 Fullspeed serial interface select. 0: 6-pin, 1: 3-pin uint32_t phy_sel : 1; // 6 HS/FS PHY selection. 0: HS UTMI+ or ULPI, 1: FS serial transceiver @@ -259,7 +259,7 @@ TU_VERIFY_STATIC(sizeof(dwc2_grstctl_t) == 4, "incorrect size"); typedef struct TU_ATTR_PACKED { uint32_t op_mode : 3; // 0..2 HNP/SRP Host/Device/OTG mode uint32_t arch : 2; // 3..4 Slave/External/Internal DMA - uint32_t point2point : 1; // 5 0: support hub and split | 1: no hub, no split + uint32_t single_point : 1; // 5 0: support hub and split | 1: no hub, no split uint32_t hs_phy_type : 2; // 6..7 0: not supported | 1: UTMI+ | 2: ULPI | 3: UTMI+ and ULPI uint32_t fs_phy_type : 2; // 8..9 0: not supported | 1: dedicated | 2: UTMI+ | 3: ULPI uint32_t num_dev_ep : 4; // 10..13 Number of device endpoints (excluding EP0) @@ -407,7 +407,10 @@ typedef struct { }; volatile uint32_t gotgint; // 004 OTG Interrupt volatile uint32_t gahbcfg; // 008 AHB Configuration + union { volatile uint32_t gusbcfg; // 00c USB Configuration + volatile dwc2_gusbcfg_t gusbcfg_bm; + }; volatile uint32_t grstctl; // 010 Reset volatile uint32_t gintsts; // 014 Interrupt volatile uint32_t gintmsk; // 018 Interrupt Mask @@ -459,7 +462,10 @@ typedef struct { volatile uint32_t haintmsk; // 418 Host All Channels Interrupt Mask volatile uint32_t hflbaddr; // 41C Host Frame List Base Address uint32_t reserved420[8]; // 420..43F + union { volatile uint32_t hprt; // 440 Host Port Control and Status + volatile dwc2_hprt_t hprt_bm; + }; uint32_t reserved444[47]; // 444..4FF //------------- Host Channel -------------// @@ -1490,14 +1496,14 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define HPRT_CONN_STATUS_Msk (0x1UL << HPRT_CONN_STATUS_Pos) // 0x00000001 #define HPRT_CONN_STATUS HPRT_CONN_STATUS_Msk // Port connect status #define HPRT_CONN_DETECT_Pos (1U) -#define HPRT_CONN_DETECT_Msk (0x1UL << HPRT_CONN_DETECT_Pos) // 0x00000002 -#define HPRT_CONN_DETECT HPRT_CONN_DETECT_Msk // Port connect detected +#define HPRT_CONN_DETECT_Msk (0x1UL << HPRT_CONN_DETECT_Pos) // 0x00000002 +#define HPRT_CONN_DETECT HPRT_CONN_DETECT_Msk // Port connect detected #define HPRT_ENABLE_Pos (2U) #define HPRT_ENABLE_Msk (0x1UL << HPRT_ENABLE_Pos) // 0x00000004 #define HPRT_ENABLE HPRT_ENABLE_Msk // Port enable -#define HPRT_EN_CHANGE_Pos (3U) -#define HPRT_EN_CHANGE_Msk (0x1UL << HPRT_EN_CHANGE_Pos) // 0x00000008 -#define HPRT_EN_CHANGE HPRT_EN_CHANGE_Msk // Port enable/disable change +#define HPRT_ENABLE_CHANGE_Pos (3U) +#define HPRT_ENABLE_CHANGE_Msk (0x1UL << HPRT_ENABLE_CHANGE_Pos) // 0x00000008 +#define HPRT_ENABLE_CHANGE HPRT_ENABLE_CHANGE_Msk // Port enable/disable change #define HPRT_OVER_CURRENT_ACTIVE_Pos (4U) #define HPRT_OVER_CURRENT_ACTIVE_Msk (0x1UL << HPRT_OVER_CURRENT_ACTIVE_Pos) // 0x00000010 #define HPRT_OVER_CURRENT_ACTIVE HPRT_OVER_CURRENT_ACTIVE_Msk // Port overcurrent active diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 2d4368e94..bb5086631 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -35,9 +35,20 @@ #include "dwc2_common.h" enum { - HPRT_W1C_MASK = HPRT_CONN_DETECT | HPRT_ENABLE | HPRT_EN_CHANGE | HPRT_OVER_CURRENT_CHANGE + HPRT_W1C_MASK = HPRT_CONN_DETECT | HPRT_ENABLE | HPRT_ENABLE_CHANGE | HPRT_OVER_CURRENT_CHANGE }; +TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t convert_hprt_speed(uint32_t hprt_speed) { + tusb_speed_t speed; + switch(hprt_speed) { + case HPRT_SPEED_HIGH: speed = TUSB_SPEED_HIGH; break; + case HPRT_SPEED_FULL: speed = TUSB_SPEED_FULL; break; + case HPRT_SPEED_LOW : speed = TUSB_SPEED_LOW ; break; + default: TU_BREAKPOINT(); break; + } + return speed; +} + //--------------------------------------------------------------------+ // Controller API //--------------------------------------------------------------------+ @@ -81,6 +92,8 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { } dwc2->hcfg = hcfg; + // Enable HFIR reload + // force host mode and wait for mode switch dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FDMOD) | GUSBCFG_FHMOD; while( (dwc2->gintsts & GINTSTS_CMOD) != GINTSTS_CMODE_HOST) {} @@ -107,8 +120,8 @@ void hcd_int_disable(uint8_t rhport) { // Get frame number (1ms) uint32_t hcd_frame_number(uint8_t rhport) { - (void) rhport; - return 0; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + return dwc2->hfnum & HFNUM_FRNUM_Msk; } //--------------------------------------------------------------------+ @@ -125,7 +138,9 @@ bool hcd_port_connect_status(uint8_t rhport) { // Some port would require hcd_port_reset_end() to be invoked after 10ms to complete the reset sequence. void hcd_port_reset(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - dwc2->hprt = HPRT_RESET; + uint32_t hprt = dwc2->hprt & ~HPRT_W1C_MASK; + hprt |= HPRT_RESET; + dwc2->hprt = hprt; } // Complete bus reset sequence, may be required by some controllers @@ -138,9 +153,9 @@ void hcd_port_reset_end(uint8_t rhport) { // Get port link speed tusb_speed_t hcd_port_speed_get(uint8_t rhport) { - (void) rhport; - - return TUSB_SPEED_FULL; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + const tusb_speed_t speed = convert_hprt_speed(dwc2->hprt_bm.speed); + return speed; } // HCD closes all opened endpoints belong to this device @@ -227,18 +242,62 @@ static void handle_rxflvl_irq(uint8_t rhport) { */ TU_ATTR_ALWAYS_INLINE static inline void handle_hprt_irq(uint8_t rhport, bool in_isr) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - uint32_t hprt = dwc2->hprt; + uint32_t hprt = dwc2->hprt & ~HPRT_W1C_MASK; + const dwc2_hprt_t hprt_bm = dwc2->hprt_bm; - if (hprt & HPRT_CONN_DETECT) { + if (dwc2->hprt & HPRT_CONN_DETECT) { // Port Connect Detect - dwc2->hprt = HPRT_CONN_DETECT; // clear + hprt |= HPRT_CONN_DETECT; - if (hprt & HPRT_CONN_STATUS) { + if (hprt_bm.conn_status) { hcd_event_device_attach(rhport, in_isr); } else { hcd_event_device_remove(rhport, in_isr); } } + + if (dwc2->hprt & HPRT_ENABLE_CHANGE) { + // Port enable change + hprt |= HPRT_ENABLE_CHANGE; + + if (hprt_bm.enable) { + // Port enable + // Config HCFG FS/LS clock and HFIR for SOF interval according to link speed (value is in PHY clock unit) + const tusb_speed_t speed = convert_hprt_speed(hprt_bm.speed); + uint32_t hcfg = dwc2->hcfg & ~HCFG_FSLS_PHYCLK_SEL; + + const dwc2_gusbcfg_t gusbcfg_bm = dwc2->gusbcfg_bm; + uint32_t clock = 60; + if (gusbcfg_bm.phy_sel) { + // dedicated FS is 48Mhz + clock = 48; + hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ; + } else { + // UTMI+ or ULPI + if (gusbcfg_bm.ulpi_utmi_sel) { + clock = 60; // ULPI 8-bit is 60Mhz + } else if (gusbcfg_bm.phy_if16) { + clock = 30; // UTMI+ 16-bit is 30Mhz + } else { + clock = 60; // UTMI+ 8-bit is 60Mhz + } + hcfg |= HCFG_FSLS_PHYCLK_SEL_30_60MHZ; + } + + dwc2->hcfg = hcfg; + + uint32_t hfir = dwc2->hfir & ~HFIR_FRIVL_Msk; + if (speed == TUSB_SPEED_HIGH) { + hfir |= 125*clock; + } else { + hfir |= 1000*clock; + } + + dwc2->hfir = hfir; + } + } + + dwc2->hprt = hprt; // clear interrupt } /* Interrupt Hierarchy diff --git a/src/tusb.h b/src/tusb.h index 2f30a5739..3ffaafded 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -127,10 +127,8 @@ //--------------------------------------------------------------------+ -// APPLICATION API +// User API //--------------------------------------------------------------------+ - - #if CFG_TUH_ENABLED || CFG_TUD_ENABLED // Internal helper for backward compatible with tusb_init(void) @@ -167,6 +165,12 @@ void tusb_int_handler(uint8_t rhport, bool in_isr); #endif +//--------------------------------------------------------------------+ +// API Implemented by user +//--------------------------------------------------------------------+ + +// Get current milliseconds, maybe required by some port with no RTOS +uint32_t tusb_time_millis(void); #ifdef __cplusplus } -- cgit v1.3.1 From 063661e3a32429e2d5f74c097da72aa3679cc889 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 25 Oct 2024 00:20:34 +0700 Subject: more progress on dwc2 hcd, initial code for edpt xfer --- src/common/tusb_common.h | 8 +- src/common/tusb_types.h | 8 +- src/portable/synopsys/dwc2/dcd_dwc2.c | 61 ++++--- src/portable/synopsys/dwc2/dwc2_common.c | 14 +- src/portable/synopsys/dwc2/dwc2_common.h | 35 +--- src/portable/synopsys/dwc2/dwc2_info.py | 1 - src/portable/synopsys/dwc2/dwc2_type.h | 149 ++++++++------- src/portable/synopsys/dwc2/hcd_dwc2.c | 301 +++++++++++++++++++++++++++---- src/tusb_option.h | 2 +- 9 files changed, 407 insertions(+), 172 deletions(-) (limited to 'src') diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index d3e0cf888..59cc0fb48 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -123,11 +123,15 @@ TU_ATTR_ALWAYS_INLINE static inline int tu_memcpy_s(void *dest, size_t destsz, c //------------- Bytes -------------// TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32(uint8_t b3, uint8_t b2, uint8_t b1, uint8_t b0) { - return ( ((uint32_t) b3) << 24) | ( ((uint32_t) b2) << 16) | ( ((uint32_t) b1) << 8) | b0; + return (((uint32_t)b3) << 24) | (((uint32_t)b2) << 16) | (((uint32_t)b1) << 8) | b0; +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32_from_u16(uint16_t high, uint16_t low) { + return (((uint32_t)high) << 16) | low; } TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u16(uint8_t high, uint8_t low) { - return (uint16_t) ((((uint16_t) high) << 8) | low); + return (uint16_t)((((uint16_t)high) << 8) | low); } TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte3(uint32_t ui32) { return TU_U32_BYTE3(ui32); } diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index 0faa6eee9..3e5aa6818 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -56,10 +56,10 @@ typedef enum { /// defined base on USB Specs Endpoint's bmAttributes typedef enum { - TUSB_XFER_CONTROL = 0 , - TUSB_XFER_ISOCHRONOUS , - TUSB_XFER_BULK , - TUSB_XFER_INTERRUPT + TUSB_XFER_CONTROL = 0, + TUSB_XFER_ISOCHRONOUS = 1, + TUSB_XFER_BULK = 2, + TUSB_XFER_INTERRUPT = 3 } tusb_xfer_type_t; typedef enum { diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index e7440529a..0731a88c1 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -56,7 +56,7 @@ static xfer_ctl_t xfer_status[DWC2_EP_MAX][2]; // EP0 transfers are limited to 1 packet - larger sizes has to be split static uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type -static uint16_t _dfifo_top; // top free location in FIFO RAM +static uint16_t _dfifo_top; // top free location in DFIFO in words // Number of IN endpoints active static uint8_t _allocated_ep_in_count; @@ -68,6 +68,12 @@ static bool _sof_en; // DMA //-------------------------------------------------------------------- +TU_ATTR_ALWAYS_INLINE static inline bool dma_device_enabled(const dwc2_regs_t* dwc2) { + (void) dwc2; + // Internal DMA only + return CFG_TUD_DWC2_DMA && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA; +} + static void dma_setup_prepare(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); @@ -103,11 +109,9 @@ static void dma_setup_prepare(uint8_t rhport) { possible since the free space is located between the RX and TX FIFOs. ---------------- ep_fifo_size - | EPInfo | - | for DMA | + | EPInfo DMA | |-------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth) - | IN FIFO 0 | - | control | + | IN FIFO 0 | control EP |-------------| | IN FIFO 1 | |-------------| @@ -126,13 +130,13 @@ static void dma_setup_prepare(uint8_t rhport) { - All EP OUT shared a unique OUT FIFO which uses (for Slave or Buffer DMA, Scatt/Gather DMA use different formula): - 13 for setup packets + control words (up to 3 setup packets). - 1 for global NAK (not required/used here). - - Largest-EPsize / 4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4) + 1" + - Largest-EPsize/4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4 + 1)" - 2 for each used OUT endpoint Therefore GRXFSIZ = 13 + 1 + 2 x (Largest-EPsize/4 + 1) + 2 x EPOUTnum */ -TU_ATTR_ALWAYS_INLINE static inline uint16_t calc_grxfsiz(uint16_t largest_ep_size, uint8_t ep_count) { +TU_ATTR_ALWAYS_INLINE static inline uint16_t calc_device_grxfsiz(uint16_t largest_ep_size, uint8_t ep_count) { return 13 + 1 + 2 * ((largest_ep_size / 4) + 1) + 2 * ep_count; } @@ -148,7 +152,7 @@ static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) { uint16_t fifo_size = tu_div_ceil(packet_size, 4); if (dir == TUSB_DIR_OUT) { // Calculate required size of RX FIFO - uint16_t const new_sz = calc_grxfsiz(4 * fifo_size, ep_count); + uint16_t const new_sz = calc_device_grxfsiz(4 * fifo_size, ep_count); // If size_rx needs to be extended check if there is enough free space if (dwc2->grxfsiz < new_sz) { @@ -184,17 +188,18 @@ static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) { return true; } -static void dfifo_init(uint8_t rhport) { +static void dfifo_device_init(uint8_t rhport) { const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport]; dwc2_regs_t* dwc2 = DWC2_REG(rhport); - dwc2->grxfsiz = calc_grxfsiz(CFG_TUD_ENDPOINT0_SIZE, dwc2_controller->ep_count); + dwc2->grxfsiz = calc_device_grxfsiz(CFG_TUD_ENDPOINT0_SIZE, dwc2_controller->ep_count); - if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { - // DMA use last DFIFO to store metadata - _dfifo_top = dma_cal_epfifo_base(rhport); - }else { - _dfifo_top = dwc2_controller->ep_fifo_size / 4; + // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per endpoint direction + const bool is_dma = dma_device_enabled(dwc2); + _dfifo_top = dwc2_controller->ep_fifo_size/4; + if (is_dma) { + _dfifo_top -= 2 * dwc2_controller->ep_count; } + dwc2->gdfifocfg = (_dfifo_top << GDFIFOCFG_EPINFOBASE_SHIFT) | _dfifo_top; // Allocate FIFO for EP0 IN dfifo_alloc(rhport, 0x80, CFG_TUD_ENDPOINT0_SIZE); @@ -357,7 +362,7 @@ static void bus_reset(uint8_t rhport) { dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM; // 4. Set up DFIFO - dfifo_init(rhport); + dfifo_device_init(rhport); // 5. Reset device address dwc2->dcfg &= ~DCFG_DAD_Msk; @@ -369,7 +374,7 @@ static void bus_reset(uint8_t rhport) { xfer_status[0][TUSB_DIR_OUT].max_size = 64; xfer_status[0][TUSB_DIR_IN].max_size = 64; - if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { + if(dma_device_enabled(dwc2)) { dma_setup_prepare(rhport); } else { dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); @@ -401,7 +406,7 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c dep->dieptsiz = (num_packets << DIEPTSIZ_PKTCNT_Pos) | ((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk); - if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { + if(dma_device_enabled(dwc2)) { dep->diepdma = (uintptr_t)xfer->buffer; // For ISO endpoint set correct odd/even bit for next frame. @@ -439,7 +444,7 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c dep->doepctl |= (odd_frame_now ? DOEPCTL_SD0PID_SEVNFRM_Msk : DOEPCTL_SODDFRM_Msk); } - if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { + if(dma_device_enabled(dwc2)) { dep->doepdma = (uintptr_t)xfer->buffer; } @@ -451,11 +456,12 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c // Controller API //-------------------------------------------------------------------- bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { + (void) rh_init; dwc2_regs_t* dwc2 = DWC2_REG(rhport); // Core Initialization - const bool is_highspeed = dwc2_core_is_highspeed(dwc2, rh_init); - const bool is_dma = dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE); + const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_DEVICE); + const bool is_dma = dma_device_enabled(dwc2); TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma)); // Device Initialization @@ -463,7 +469,7 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Set device max speed uint32_t dcfg = dwc2->dcfg & ~DCFG_DSPD_Msk; - if (dwc2_core_is_highspeed(dwc2, rh_init)) { + if (is_highspeed) { dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos; // XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required @@ -611,7 +617,7 @@ void dcd_edpt_close_all(uint8_t rhport) { dfifo_flush_tx(dwc2, 0x10); // all tx fifo dfifo_flush_rx(dwc2); - dfifo_init(rhport); // re-init dfifo + dfifo_device_init(rhport); // re-init dfifo } bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { @@ -690,8 +696,9 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { } void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); edpt_disable(rhport, ep_addr, true); - if((tu_edpt_number(ep_addr) == 0) && dwc2_dma_enabled(DWC2_REG(rhport), TUSB_ROLE_DEVICE)) { + if((tu_edpt_number(ep_addr) == 0) && dma_device_enabled(dwc2)) { dma_setup_prepare(rhport); } } @@ -808,7 +815,7 @@ static void handle_epout_irq(uint8_t rhport) { if (doepint & DOEPINT_SETUP) { epout->doepint = DOEPINT_SETUP; - if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { + if(dma_device_enabled(dwc2)) { dma_setup_prepare(rhport); } @@ -824,7 +831,7 @@ static void handle_epout_irq(uint8_t rhport) { if (!(doepint & (DOEPINT_SETUP | DOEPINT_STPKTRX | DOEPINT_STSPHSRX))) { xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); - if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { + if(dma_device_enabled(dwc2)) { if ((epnum == 0) && ep0_pending[TUSB_DIR_OUT]) { // EP0 can only handle one packet Schedule another packet to be received. edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]); @@ -874,7 +881,7 @@ static void handle_epin_irq(uint8_t rhport) { // Schedule another packet to be transmitted. edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]); } else { - if((n == 0) && dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { + if((n == 0) && dma_device_enabled(dwc2)) { dma_setup_prepare(rhport); } dcd_event_xfer_complete(rhport, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index d0ad764a5..c5990c53f 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -168,16 +168,16 @@ static bool check_dwc2(dwc2_regs_t* dwc2) { //-------------------------------------------------------------------- // //-------------------------------------------------------------------- -bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init) { +bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role) { (void)dwc2; #if CFG_TUD_ENABLED - if (rh_init->role == TUSB_ROLE_DEVICE && !TUD_OPT_HIGH_SPEED) { + if (role == TUSB_ROLE_DEVICE && !TUD_OPT_HIGH_SPEED) { return false; } #endif #if CFG_TUH_ENABLED - if (rh_init->role == TUSB_ROLE_HOST && !TUH_OPT_HIGH_SPEED) { + if (role == TUSB_ROLE_HOST && !TUH_OPT_HIGH_SPEED) { return false; } #endif @@ -234,15 +234,7 @@ bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) { dwc2->gintmsk = 0; - // TODO can be enabled with device as well but tested with host for now - // if (rh_init->role == TUSB_ROLE_HOST) { - // dwc2->gintmsk |= OTG_INT_COMMON; - // } - if (is_dma) { - const uint16_t epinfo_base = dma_cal_epfifo_base(rhport); - dwc2->gdfifocfg = (epinfo_base << GDFIFOCFG_EPINFOBASE_SHIFT) | epinfo_base; - // DMA seems to be only settable after a core reset, and not possible to switch on-the-fly dwc2->gahbcfg |= GAHBCFG_DMAEN | GAHBCFG_HBSTLEN_2; } else { diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h index 7088ff3c3..fff21ca5c 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.h +++ b/src/portable/synopsys/dwc2/dwc2_common.h @@ -61,7 +61,9 @@ enum { OTG_INT_COMMON = 0 // GINTSTS_DISCINT | GINTSTS_CONIDSTSCHNG }; -//------------- Core -------------// +//--------------------------------------------------------------------+ +// Core/Controller +//--------------------------------------------------------------------+ TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) { if (rhport >= DWC2_CONTROLLER_COUNT) { // user mis-configured, ignore and use first controller @@ -70,11 +72,13 @@ TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) { return (dwc2_regs_t*)_dwc2_controller[rhport].reg_base; } -bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init); +bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role); bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma); void dwc2_core_handle_common_irq(uint8_t rhport, bool in_isr); -//------------- DFIFO -------------// +//--------------------------------------------------------------------+ +// DFIFO +//--------------------------------------------------------------------+ TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_tx(dwc2_regs_t* dwc2, uint8_t fnum) { // flush TX fifo and wait for it cleared dwc2->grstctl = GRSTCTL_TXFFLSH | (fnum << GRSTCTL_TXFNUM_Pos); @@ -86,27 +90,8 @@ TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_rx(dwc2_regs_t* dwc2) { while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {} } -//------------- DMA -------------// -TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dma_enabled(const dwc2_regs_t* dwc2, tusb_role_t role) { - (void) dwc2; - - if (CFG_TUD_DWC2_DMA == 0 && role == TUSB_ROLE_DEVICE) { - return false; - } - - if (CFG_TUH_DWC2_DMA == 0 && role == TUSB_ROLE_HOST) { - return false; - } - - // Internal DMA only - return dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA; -} - -TU_ATTR_ALWAYS_INLINE static inline uint16_t dma_cal_epfifo_base(uint8_t rhport) { - // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per endpoint direction - const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport]; - return dwc2_controller->ep_fifo_size/4 - 2*dwc2_controller->ep_count; -} - +//--------------------------------------------------------------------+ +// DMA +//--------------------------------------------------------------------+ #endif diff --git a/src/portable/synopsys/dwc2/dwc2_info.py b/src/portable/synopsys/dwc2/dwc2_info.py index d97a1192e..fe08ecce9 100755 --- a/src/portable/synopsys/dwc2/dwc2_info.py +++ b/src/portable/synopsys/dwc2/dwc2_info.py @@ -24,7 +24,6 @@ dwc2_reg_value = { 'ST U5A5 HS': [0x5000, 0x4F54411A, 0, 0x228FE052, 0x03B882E8, 0xE2103E30], 'GD32VF103': [0x1000, 0, 0, 0, 0, 0], 'XMC4500': [0xAEC000, 0x4F54292A, 0, 0x228F5930, 0x027A01E5, 0xDBF08030] - } # Combine dwc2_info with dwc2_reg_list diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index d2993bdd9..ed335f1a3 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -138,6 +138,13 @@ enum { GINTSTS_CMODE_HOST = 1, }; +enum { + HCTSIZ_PID_DATA0 = 0, + HCTSIZ_PID_DATA2 = 1, + HCTSIZ_PID_DATA1 = 2, + HCTSIZ_PID_SETUP = 3, +}; + //-------------------------------------------------------------------- // Register bitfield definitions //-------------------------------------------------------------------- @@ -334,17 +341,49 @@ typedef struct TU_ATTR_PACKED { uint32_t speed : 2; // 17..18 Port speed uint32_t rsv19_31 :13; // 19..31 Reserved }dwc2_hprt_t; +TU_VERIFY_STATIC(sizeof(dwc2_hprt_t) == 4, "incorrect size"); + +typedef struct TU_ATTR_PACKED { + uint32_t ep_size : 11; // 0..10 Maximum packet size + uint32_t ep_num : 4; // 11..14 Endpoint number + uint32_t ep_dir : 1; // 15 Endpoint direction + uint32_t rsv16 : 1; // 16 Reserved + uint32_t low_speed_dev : 1; // 17 Low-speed device + uint32_t ep_type : 2; // 18..19 Endpoint type + uint32_t err_multi_count : 2; // 20..21 Error (splitEn = 1) / Multi (SplitEn = 0) count + uint32_t dev_addr : 7; // 22..28 Device address + uint32_t odd_frame : 1; // 29 Odd frame + uint32_t disable : 1; // 30 Channel disable + uint32_t enable : 1; // 31 Channel enable +} dwc2_channel_char_t; +TU_VERIFY_STATIC(sizeof(dwc2_channel_char_t) == 4, "incorrect size"); + +typedef struct TU_ATTR_PACKED { + uint32_t hub_port : 7; // 0..6 Hub port number + uint32_t hub_addr : 7; // 7..13 Hub address + uint32_t xact_pos : 2; // 14..15 Transaction position + uint32_t split_compl : 1; // 16 Split completion + uint32_t rsv17_30 : 14; // 17..30 Reserved + uint32_t split_en : 1; // 31 Split enable +} dwc2_channel_split_t; +TU_VERIFY_STATIC(sizeof(dwc2_channel_split_t) == 4, "incorrect size"); // Host Channel typedef struct { - volatile uint32_t hcchar; // 500 + 20*ch Host Channel Characteristics - volatile uint32_t hcsplt; // 504 + 20*ch Host Channel Split Control - volatile uint32_t hcint; // 508 + 20*ch Host Channel Interrupt - volatile uint32_t hcintmsk; // 50C + 20*ch Host Channel Interrupt Mask - volatile uint32_t hctsiz; // 510 + 20*ch Host Channel Transfer Size - volatile uint32_t hcdma; // 514 + 20*ch Host Channel DMA Address - uint32_t reserved518; // 518 + 20*ch - volatile uint32_t hcdmab; // 51C + 20*ch Host Channel DMA Address + union { + volatile uint32_t hcchar; // 500 + 20*ch Host Channel Characteristics + volatile dwc2_channel_char_t hcchar_bm; + }; + union { + volatile uint32_t hcsplt; // 504 + 20*ch Host Channel Split Control + volatile dwc2_channel_split_t hcsplt_bm; + }; + volatile uint32_t hcint; // 508 + 20*ch Host Channel Interrupt + volatile uint32_t hcintmsk; // 50C + 20*ch Host Channel Interrupt Mask + volatile uint32_t hctsiz; // 510 + 20*ch Host Channel Transfer Size + volatile uint32_t hcdma; // 514 + 20*ch Host Channel DMA Address + uint32_t reserved518; // 518 + 20*ch + volatile uint32_t hcdmab; // 51C + 20*ch Host Channel DMA Address } dwc2_channel_t; // Endpoint IN @@ -1720,15 +1759,15 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define HCSPLT_SPLITEN HCSPLT_SPLITEN_Msk // Split enable /******************** Bit definition for HCINT register ********************/ -#define HCINT_XFRC_Pos (0U) -#define HCINT_XFRC_Msk (0x1UL << HCINT_XFRC_Pos) // 0x00000001 -#define HCINT_XFRC HCINT_XFRC_Msk // Transfer completed -#define HCINT_CHH_Pos (1U) -#define HCINT_CHH_Msk (0x1UL << HCINT_CHH_Pos) // 0x00000002 -#define HCINT_CHH HCINT_CHH_Msk // Channel halted -#define HCINT_AHBERR_Pos (2U) -#define HCINT_AHBERR_Msk (0x1UL << HCINT_AHBERR_Pos) // 0x00000004 -#define HCINT_AHBERR HCINT_AHBERR_Msk // AHB error +#define HCINT_XFER_COMPLETE_Pos (0U) +#define HCINT_XFER_COMPLETE_Msk (0x1UL << HCINT_XFER_COMPLETE_Pos) // 0x00000001 +#define HCINT_XFER_COMPLETE HCINT_XFER_COMPLETE_Msk // Transfer completed +#define HCINT_CHANNEL_HALTED_Pos (1U) +#define HCINT_CHANNEL_HALTED_Msk (0x1UL << HCINT_CHANNEL_HALTED_Pos) // 0x00000002 +#define HCINT_CHANNEL_HALTED HCINT_CHANNEL_HALTED_Msk // Channel halted +#define HCINT_AHB_ERR_Pos (2U) +#define HCINT_AHB_ERR_Msk (0x1UL << HCINT_AHB_ERR_Pos) // 0x00000004 +#define HCINT_AHB_ERR HCINT_AHB_ERR_Msk // AHB error #define HCINT_STALL_Pos (3U) #define HCINT_STALL_Msk (0x1UL << HCINT_STALL_Pos) // 0x00000008 #define HCINT_STALL HCINT_STALL_Msk // STALL response received interrupt @@ -1741,18 +1780,27 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define HCINT_NYET_Pos (6U) #define HCINT_NYET_Msk (0x1UL << HCINT_NYET_Pos) // 0x00000040 #define HCINT_NYET HCINT_NYET_Msk // Response received interrupt -#define HCINT_TXERR_Pos (7U) -#define HCINT_TXERR_Msk (0x1UL << HCINT_TXERR_Pos) // 0x00000080 -#define HCINT_TXERR HCINT_TXERR_Msk // Transaction error -#define HCINT_BBERR_Pos (8U) -#define HCINT_BBERR_Msk (0x1UL << HCINT_BBERR_Pos) // 0x00000100 -#define HCINT_BBERR HCINT_BBERR_Msk // Babble error -#define HCINT_FRMOR_Pos (9U) -#define HCINT_FRMOR_Msk (0x1UL << HCINT_FRMOR_Pos) // 0x00000200 -#define HCINT_FRMOR HCINT_FRMOR_Msk // Frame overrun -#define HCINT_DTERR_Pos (10U) -#define HCINT_DTERR_Msk (0x1UL << HCINT_DTERR_Pos) // 0x00000400 -#define HCINT_DTERR HCINT_DTERR_Msk // Data toggle error +#define HCINT_XACT_ERR_Pos (7U) +#define HCINT_XACT_ERR_Msk (0x1UL << HCINT_XACT_ERR_Pos) // 0x00000080 +#define HCINT_XACT_ERR HCINT_XACT_ERR_Msk // Transaction error +#define HCINT_BABBLE_ERR_Pos (8U) +#define HCINT_BABBLE_ERR_Msk (0x1UL << HCINT_BABBLE_ERR_Pos) // 0x00000100 +#define HCINT_BABBLE_ERR HCINT_BABBLE_ERR_Msk // Babble error +#define HCINT_FARME_OVERRUN_Pos (9U) +#define HCINT_FARME_OVERRUN_Msk (0x1UL << HCINT_FARME_OVERRUN_Pos) // 0x00000200 +#define HCINT_FARME_OVERRUN HCINT_FARME_OVERRUN_Msk // Frame overrun +#define HCINT_DATATOGGLE_ERR_Pos (10U) +#define HCINT_DATATOGGLE_ERR_Msk (0x1UL << HCINT_DATATOGGLE_ERR_Pos) // 0x00000400 +#define HCINT_DATATOGGLE_ERR HCINT_DATATOGGLE_ERR_Msk // Data toggle error +#define HCINT_BUFFER_NAK_Pos (11U) +#define HCINT_BUFFER_NAK_Msk (0x1UL << HCINT_BUFFER_NAK_Pos) // 0x00000800 +#define HCINT_BUFFER_NAK HCINT_BUFFER_NAK_Msk // Buffer not available interrupt +#define HCINT_XCS_XACT_ERR_Pos (12U) +#define HCINT_XCS_XACT_ERR_Msk (0x1UL << HCINT_XCS_XACT_ERR_Pos) // 0x00001000 +#define HCINT_XCS_XACT_ERR HCINT_XCS_XACT_ERR_Msk // Excessive transaction error +#define HCINT_DESC_ROLLOVER_Pos (13U) +#define HCINT_DESC_ROLLOVER_Msk (0x1UL << HCINT_DESC_ROLLOVER_Pos) // 0x00002000 +#define HCINT_DESC_ROLLOVER HCINT_DESC_ROLLOVER_Msk // Descriptor rollover /******************** Bit definition for DIEPINT register ********************/ #define DIEPINT_XFRC_Pos (0U) @@ -1795,41 +1843,6 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define DIEPINT_NAK_Msk (0x1UL << DIEPINT_NAK_Pos) // 0x00002000 #define DIEPINT_NAK DIEPINT_NAK_Msk // NAK interrupt -/******************** Bit definition for HCINTMSK register ********************/ -#define HCINTMSK_XFRCM_Pos (0U) -#define HCINTMSK_XFRCM_Msk (0x1UL << HCINTMSK_XFRCM_Pos) // 0x00000001 -#define HCINTMSK_XFRCM HCINTMSK_XFRCM_Msk // Transfer completed mask -#define HCINTMSK_CHHM_Pos (1U) -#define HCINTMSK_CHHM_Msk (0x1UL << HCINTMSK_CHHM_Pos) // 0x00000002 -#define HCINTMSK_CHHM HCINTMSK_CHHM_Msk // Channel halted mask -#define HCINTMSK_AHBERR_Pos (2U) -#define HCINTMSK_AHBERR_Msk (0x1UL << HCINTMSK_AHBERR_Pos) // 0x00000004 -#define HCINTMSK_AHBERR HCINTMSK_AHBERR_Msk // AHB error -#define HCINTMSK_STALLM_Pos (3U) -#define HCINTMSK_STALLM_Msk (0x1UL << HCINTMSK_STALLM_Pos) // 0x00000008 -#define HCINTMSK_STALLM HCINTMSK_STALLM_Msk // STALL response received interrupt mask -#define HCINTMSK_NAKM_Pos (4U) -#define HCINTMSK_NAKM_Msk (0x1UL << HCINTMSK_NAKM_Pos) // 0x00000010 -#define HCINTMSK_NAKM HCINTMSK_NAKM_Msk // NAK response received interrupt mask -#define HCINTMSK_ACKM_Pos (5U) -#define HCINTMSK_ACKM_Msk (0x1UL << HCINTMSK_ACKM_Pos) // 0x00000020 -#define HCINTMSK_ACKM HCINTMSK_ACKM_Msk // ACK response received/transmitted interrupt mask -#define HCINTMSK_NYET_Pos (6U) -#define HCINTMSK_NYET_Msk (0x1UL << HCINTMSK_NYET_Pos) // 0x00000040 -#define HCINTMSK_NYET HCINTMSK_NYET_Msk // response received interrupt mask -#define HCINTMSK_TXERRM_Pos (7U) -#define HCINTMSK_TXERRM_Msk (0x1UL << HCINTMSK_TXERRM_Pos) // 0x00000080 -#define HCINTMSK_TXERRM HCINTMSK_TXERRM_Msk // Transaction error mask -#define HCINTMSK_BBERRM_Pos (8U) -#define HCINTMSK_BBERRM_Msk (0x1UL << HCINTMSK_BBERRM_Pos) // 0x00000100 -#define HCINTMSK_BBERRM HCINTMSK_BBERRM_Msk // Babble error mask -#define HCINTMSK_FRMORM_Pos (9U) -#define HCINTMSK_FRMORM_Msk (0x1UL << HCINTMSK_FRMORM_Pos) // 0x00000200 -#define HCINTMSK_FRMORM HCINTMSK_FRMORM_Msk // Frame overrun mask -#define HCINTMSK_DTERRM_Pos (10U) -#define HCINTMSK_DTERRM_Msk (0x1UL << HCINTMSK_DTERRM_Pos) // 0x00000400 -#define HCINTMSK_DTERRM HCINTMSK_DTERRM_Msk // Data toggle error mask - /******************** Bit definition for DIEPTSIZ register ********************/ #define DIEPTSIZ_XFRSIZ_Pos (0U) @@ -1851,11 +1864,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define HCTSIZ_DOPING_Pos (31U) #define HCTSIZ_DOPING_Msk (0x1UL << HCTSIZ_DOPING_Pos) // 0x80000000 #define HCTSIZ_DOPING HCTSIZ_DOPING_Msk // Do PING -#define HCTSIZ_DPID_Pos (29U) -#define HCTSIZ_DPID_Msk (0x3UL << HCTSIZ_DPID_Pos) // 0x60000000 -#define HCTSIZ_DPID HCTSIZ_DPID_Msk // Data PID -#define HCTSIZ_DPID_0 (0x1UL << HCTSIZ_DPID_Pos) // 0x20000000 -#define HCTSIZ_DPID_1 (0x2UL << HCTSIZ_DPID_Pos) // 0x40000000 +#define HCTSIZ_PID_Pos (29U) +#define HCTSIZ_PID_Msk (0x3UL << HCTSIZ_PID_Pos) // 0x60000000 +#define HCTSIZ_PID HCTSIZ_PID_Msk // Data PID /******************** Bit definition for DIEPDMA register ********************/ #define DIEPDMA_DMAADDR_Pos (0U) diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index bb5086631..f37bf717d 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -34,10 +34,37 @@ #include "host/hcd.h" #include "dwc2_common.h" +// DWC2 has limit number of channel, in order to support all endpoints we can store channel char/split to swap later on +#ifndef CFG_TUH_DWC2_CHANNEL_MAX +#define CFG_TUH_DWC2_CHANNEL_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX + CFG_TUH_HUB) +#endif + enum { HPRT_W1C_MASK = HPRT_CONN_DETECT | HPRT_ENABLE | HPRT_ENABLE_CHANGE | HPRT_OVER_CURRENT_CHANGE }; +typedef struct { + union { + uint32_t hcchar; + dwc2_channel_char_t hcchar_bm; + }; + union { + uint32_t hcsplt; + dwc2_channel_split_t hcsplt_bm; + }; + + uint8_t next_data_toggle; +} hcd_pipe_t; + +typedef struct { + hcd_pipe_t pipe[CFG_TUH_DWC2_CHANNEL_MAX]; +} dwc2_hcd_t; + +dwc2_hcd_t _hcd_data; + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t convert_hprt_speed(uint32_t hprt_speed) { tusb_speed_t speed; switch(hprt_speed) { @@ -49,6 +76,85 @@ TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t convert_hprt_speed(uint32_t hpr return speed; } +TU_ATTR_ALWAYS_INLINE static inline bool dma_host_enabled(const dwc2_regs_t* dwc2) { + (void) dwc2; + // Internal DMA only + return CFG_TUH_DWC2_DMA && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA; +} + +/* USB Data FIFO Layout + + The FIFO is split up into + - EPInfo: for storing DMA metadata (check dcd_dwc2.c for more details) + - 1 RX FIFO: for receiving data + - 1 TX FIFO for non-periodic (NPTX) + - 1 TX FIFO for periodic (PTX) + + We allocated TX FIFO from top to bottom (using top pointer), this to allow the RX FIFO to grow dynamically which is + possible since the free space is located between the RX and TX FIFOs. + + ----------------- ep_fifo_size + | EPInfo DMA | + |--------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth) + | Non-Periodic | + | TX FIFO | + |--------------|--- GNPTXFSIZ.addr (fixed size) + | Periodic | + | TX FIFO | + |--------------|--- HPTXFSIZ.addr (expandable downward) + | FREE | + | | + |--------------|-- GRXFSIZ (expandable upward) + | RX FIFO | + ---------------- 0 +*/ + +/* Programming Guide 2.1.2 FIFO RAM allocation + * RX + * - Largest-EPsize/4 + 2 (status info). recommended x2 if high bandwidth or multiple ISO are used. + * - 2 for transfer complete and channel halted status + * - 1 for each Control/Bulk out endpoint to Handle NAK/NYET (i.e max is number of host channel) + * + * TX non-periodic (NPTX) + * - At least largest-EPsize/4, recommended x2 + * + * TX periodic (PTX) + * - At least largest-EPsize*MulCount/4 (MulCount up to 3 for high-bandwidth ISO/interrupt) +*/ +static void dfifo_host_init(uint8_t rhport) { + const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport]; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per channel + const bool is_dma = dma_host_enabled(dwc2); + uint16_t dfifo_top = dwc2_controller->ep_fifo_size/4; + if (is_dma) { + dfifo_top -= dwc2->ghwcfg2_bm.num_host_ch; + } + + // fixed allocation for now, improve later: + // - ptx_largest is limited to 256 for FS since most FS core only has 1024 bytes total + bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_HOST); + uint32_t nptx_largest = is_highspeed ? TUSB_EPSIZE_BULK_HS/4 : TUSB_EPSIZE_BULK_FS/4; + uint32_t ptx_largest = is_highspeed ? TUSB_EPSIZE_ISO_HS_MAX/4 : 256/4; + + uint16_t nptxfsiz = 2 * nptx_largest; + uint16_t rxfsiz = 2 * (ptx_largest + 2) + dwc2->ghwcfg2_bm.num_host_ch; + TU_ASSERT(dfifo_top >= (nptxfsiz + rxfsiz),); + uint16_t ptxfsiz = dfifo_top - (nptxfsiz + rxfsiz); + + dwc2->gdfifocfg = (dfifo_top << GDFIFOCFG_EPINFOBASE_SHIFT) | dfifo_top; + + dfifo_top -= rxfsiz; + dwc2->grxfsiz = rxfsiz; + + dfifo_top -= nptxfsiz; + dwc2->gnptxfsiz = tu_u32_from_u16(nptxfsiz, dfifo_top); + + dfifo_top -= ptxfsiz; + dwc2->hptxfsiz = tu_u32_from_u16(ptxfsiz, dfifo_top); +} + //--------------------------------------------------------------------+ // Controller API //--------------------------------------------------------------------+ @@ -64,11 +170,14 @@ bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) { // Initialize controller to host mode bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { + (void) rh_init; dwc2_regs_t* dwc2 = DWC2_REG(rhport); + tu_memclr(&_hcd_data, sizeof(_hcd_data)); + // Core Initialization - const bool is_highspeed = dwc2_core_is_highspeed(dwc2, rh_init); - const bool is_dma = dwc2_dma_enabled(dwc2, TUSB_ROLE_HOST); + const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_HOST); + const bool is_dma = dma_host_enabled(dwc2); TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma)); //------------- 3.1 Host Initialization -------------// @@ -98,6 +207,9 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FDMOD) | GUSBCFG_FHMOD; while( (dwc2->gintsts & GINTSTS_CMOD) != GINTSTS_CMODE_HOST) {} + // configure fixed-allocated fifo scheme + dfifo_host_init(rhport); + dwc2->hprt = HPRT_W1C_MASK; // clear all write-1-clear bits dwc2->hprt = HPRT_POWER; // turn on VBUS @@ -169,23 +281,108 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { //--------------------------------------------------------------------+ // Open an endpoint -bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) { +// channel0 is reserved for dev0 control endpoint +bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep) { (void) rhport; - (void) dev_addr; - (void) ep_desc; + //dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + hcd_devtree_info_t devtree_info; + hcd_devtree_get_info(dev_addr, &devtree_info); + + // find a free pipe + for (uint32_t i = 0; i < CFG_TUH_DWC2_CHANNEL_MAX; i++) { + hcd_pipe_t* pipe = &_hcd_data.pipe[i]; + dwc2_channel_char_t* hcchar_bm = &pipe->hcchar_bm; + dwc2_channel_split_t* hcsplt_bm = &pipe->hcsplt_bm; + + if (hcchar_bm->enable == 0) { + hcchar_bm->ep_size = tu_edpt_packet_size(desc_ep); + hcchar_bm->ep_num = tu_edpt_number(desc_ep->bEndpointAddress); + hcchar_bm->ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress); + hcchar_bm->low_speed_dev = (devtree_info.speed == TUSB_SPEED_LOW) ? 1 : 0; + hcchar_bm->ep_type = desc_ep->bmAttributes.xfer; + hcchar_bm->err_multi_count = 0; + hcchar_bm->dev_addr = dev_addr; + hcchar_bm->odd_frame = 0; + hcchar_bm->disable = 0; + hcchar_bm->enable = 1; + + hcsplt_bm->hub_port = devtree_info.hub_port; + hcsplt_bm->hub_addr = devtree_info.hub_addr; + // TODO not support split transaction yet + hcsplt_bm->xact_pos = 0; + hcsplt_bm->split_compl = 0; + hcsplt_bm->split_en = 0; + + pipe->next_data_toggle = HCTSIZ_PID_DATA0; + + return true; + } + } return false; } +TU_ATTR_ALWAYS_INLINE static inline uint8_t find_free_channel(dwc2_regs_t* dwc2) { + const uint8_t max_channel = tu_min8(dwc2->ghwcfg2_bm.num_host_ch, 16); + for (uint8_t i=0; ihaintmsk, i)) { + return i; + } + } + return TUSB_INDEX_INVALID_8; +} + +TU_ATTR_ALWAYS_INLINE static inline uint8_t find_opened_pipe(uint8_t dev_addr, uint8_t ep_addr) { + for (uint32_t i = 0; i < CFG_TUH_DWC2_CHANNEL_MAX; i++) { + const dwc2_channel_char_t* hcchar_bm = &_hcd_data.pipe[i].hcchar_bm; + if (hcchar_bm->enable && hcchar_bm->dev_addr == dev_addr && + ep_addr == tu_edpt_addr(hcchar_bm->ep_num, hcchar_bm->ep_dir)) { + return i; + } + } + return TUSB_INDEX_INVALID_8; +} + // Submit a transfer, when complete hcd_event_xfer_complete() must be invoked bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) { - (void) rhport; - (void) dev_addr; - (void) ep_addr; - (void) buffer; - (void) buflen; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); - return false; + uint8_t pipe_id = find_opened_pipe(dev_addr, ep_addr); + TU_ASSERT(pipe_id < CFG_TUH_DWC2_CHANNEL_MAX); // no opened pipe + hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; + const dwc2_channel_char_t* hcchar_bm = &pipe->hcchar_bm; + + uint8_t ch_id = find_free_channel(dwc2); + TU_ASSERT(ch_id < 16); // all channel are in use + dwc2->haintmsk |= TU_BIT(ch_id); + + dwc2_channel_t* channel = &dwc2->channel[ch_id]; + channel->hcintmsk = HCINT_XFER_COMPLETE | HCINT_CHANNEL_HALTED | HCINT_STALL | + HCINT_AHB_ERR | HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR; + + const uint16_t packet_count = tu_div_ceil(buflen, hcchar_bm->ep_size); + channel->hctsiz = (pipe->next_data_toggle << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | buflen; + + // Control transfer always start with DATA1 for data and status stage. May has issue with ZLP + if (pipe->next_data_toggle == HCTSIZ_PID_DATA0 || tu_edpt_number(ep_addr) == 0) { + pipe->next_data_toggle = HCTSIZ_PID_DATA1; + } else { + pipe->next_data_toggle = HCTSIZ_PID_DATA0; + } + + if (dma_host_enabled(dwc2)) { + channel->hcdma = (uint32_t) buffer; + } else { + TU_ASSERT(false); // not yet support + } + + // TODO support split transaction + channel->hcsplt = pipe->hcsplt; + channel->hcchar = pipe->hcchar; // kick-off transfer + + return true; } // Abort a queued transfer. Note: it can only abort transfer that has not been started @@ -199,12 +396,13 @@ bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { } // Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked -bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) { - (void) rhport; - (void) dev_addr; - (void) setup_packet; +bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet[8]) { + uint8_t pipe_id = find_opened_pipe(dev_addr, 0); + TU_ASSERT(pipe_id < CFG_TUH_DWC2_CHANNEL_MAX); // no opened pipe + hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; + pipe->next_data_toggle = HCTSIZ_PID_SETUP; - return false; + return hcd_edpt_xfer(rhport, dev_addr, 0, (uint8_t*)(uintptr_t) setup_packet, 8); } // clear stall, data toggle is also reset to DATA0 @@ -300,23 +498,55 @@ TU_ATTR_ALWAYS_INLINE static inline void handle_hprt_irq(uint8_t rhport, bool in dwc2->hprt = hprt; // clear interrupt } -/* Interrupt Hierarchy +void handle_channel_irq(uint8_t rhport, bool in_isr) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + for(uint8_t ch_id=0; ch_id<32; ch_id++) { + if (tu_bit_test(dwc2->haint, ch_id)) { + dwc2_channel_t* channel = &dwc2->channel[ch_id]; + uint32_t hcint = channel->hcint; + hcint &= channel->hcintmsk; + + xfer_result_t result = XFER_RESULT_FAILED; + if (hcint & HCINT_XFER_COMPLETE) { + result = XFER_RESULT_SUCCESS; + } + if (hcint & HCINT_STALL) { + result = XFER_RESULT_STALLED; + } + if (hcint & (HCINT_CHANNEL_HALTED | HCINT_AHB_ERR | HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR | + HCINT_BUFFER_NAK | HCINT_XCS_XACT_ERR | HCINT_DESC_ROLLOVER)) { + result = XFER_RESULT_FAILED; + } + + const uint8_t ep_addr = tu_edpt_addr(channel->hcchar_bm.ep_num, channel->hcchar_bm.ep_dir); + hcd_event_xfer_complete(channel->hcchar_bm.dev_addr, ep_addr, 0, result, in_isr); - HCINTn.XferCompl HCINTMSKn.XferComplMsk - | | - +---------- AND --------+ - | - HAINT.CHn HAINTMSK.CHn - | | - +---------- AND --------+ - | - GINTSTS.PrtInt GINTMSK.PrtInt - | | - +---------- AND --------+ - | - GAHBCFG.GblIntrMsk - | - IRQn + channel->hcint = hcint; // clear all interrupt flags + + // de-allocate channel by clearing haintmsk + dwc2->haintmsk &= ~TU_BIT(ch_id); + } + } +} + +/* Interrupt Hierarchy + HCINTn.XferCompl HCINTMSKn.XferComplMsk HPRT ConnDetect PrtEnChng OverCurChng + | | | | | + +---------- AND --------+ +------------ OR -----------+ + | | + HAINT.CHn HAINTMSK.CHn | + | | | + +---------- AND --------+ | + | | + GINTSTS.HCInt GINTMSK.HCInt GINTSTS.PrtInt GINTMSK.PrtInt + | | | | + +---------- AND ---------+ +---------- AND ---------+ + | | + +-------------------- OR ---------------------------+ + | + GAHBCFG.GblIntrMsk + | + IRQn */ void hcd_int_handler(uint8_t rhport, bool in_isr) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); @@ -336,10 +566,17 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { } if (int_status & GINTSTS_HPRTINT) { + // Host port interrupt: source is cleared in HPRT register TU_LOG1_HEX(dwc2->hprt); handle_hprt_irq(rhport, in_isr); } + if (int_status & GINTSTS_HCINT) { + // Host Channel interrupt: source is cleared in HCINT register + TU_LOG1_HEX(dwc2->hprt); + handle_channel_irq(rhport, in_isr); + } + // RxFIFO non-empty interrupt handling. if (int_status & GINTSTS_RXFLVL) { // RXFLVL bit is read-only diff --git a/src/tusb_option.h b/src/tusb_option.h index 3607a8c1a..f3a8bb724 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -255,7 +255,7 @@ #endif #ifndef CFG_TUH_DWC2_DMA - #define CFG_TUH_DWC2_DMA 0 + #define CFG_TUH_DWC2_DMA 1 #endif // Enable PIO-USB software host controller -- cgit v1.3.1 From 07abc722b69ea95c6234c3dfc9a1473b43150915 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 25 Oct 2024 19:00:45 +0700 Subject: hcd able to send setup packet --- src/portable/synopsys/dwc2/dcd_dwc2.c | 99 +++++-------------- src/portable/synopsys/dwc2/dwc2_common.c | 63 +++++++++++- src/portable/synopsys/dwc2/dwc2_common.h | 4 + src/portable/synopsys/dwc2/dwc2_type.h | 110 ++++++++++++++------- src/portable/synopsys/dwc2/hcd_dwc2.c | 163 +++++++++++++++++++++++-------- 5 files changed, 287 insertions(+), 152 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index 0731a88c1..b389b0f74 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -94,7 +94,7 @@ static void dma_setup_prepare(uint8_t rhport) { //--------------------------------------------------------------------+ -/* USB Data FIFO Layout +/* Device Data FIFO scheme The FIFO is split up into - EPInfo: for storing DMA metadata, only required when use DMA. Maximum size is called @@ -109,7 +109,7 @@ static void dma_setup_prepare(uint8_t rhport) { possible since the free space is located between the RX and TX FIFOs. ---------------- ep_fifo_size - | EPInfo DMA | + | DxEPIDMAn | |-------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth) | IN FIFO 0 | control EP |-------------| @@ -167,7 +167,7 @@ static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) { } // If The TXFELVL is configured as half empty, the fifo must be twice the max_size. - if ((dwc2->gahbcfg & GAHBCFG_TXFELVL) == 0) { + if ((dwc2->gahbcfg & GAHBCFG_TX_FIFO_EPMTY_LVL) == 0) { fifo_size *= 2; } @@ -205,59 +205,10 @@ static void dfifo_device_init(uint8_t rhport) { dfifo_alloc(rhport, 0x80, CFG_TUD_ENDPOINT0_SIZE); } -// Read a single data packet from receive FIFO -static void dfifo_read_packet(uint8_t rhport, uint8_t* dst, uint16_t len) { - (void) rhport; - - dwc2_regs_t* dwc2 = DWC2_REG(rhport); - volatile const uint32_t* rx_fifo = dwc2->fifo[0]; - - // Reading full available 32 bit words from fifo - uint16_t full_words = len >> 2; - while (full_words--) { - tu_unaligned_write32(dst, *rx_fifo); - dst += 4; - } - - // Read the remaining 1-3 bytes from fifo - uint8_t const bytes_rem = len & 0x03; - if (bytes_rem != 0) { - uint32_t const tmp = *rx_fifo; - dst[0] = tu_u32_byte0(tmp); - if (bytes_rem > 1) dst[1] = tu_u32_byte1(tmp); - if (bytes_rem > 2) dst[2] = tu_u32_byte2(tmp); - } -} - -// Write a single data packet to EPIN FIFO -static void dfifo_write_packet(uint8_t rhport, uint8_t fifo_num, uint8_t const* src, uint16_t len) { - (void) rhport; - - dwc2_regs_t* dwc2 = DWC2_REG(rhport); - volatile uint32_t* tx_fifo = dwc2->fifo[fifo_num]; - - // Pushing full available 32 bit words to fifo - uint16_t full_words = len >> 2; - while (full_words--) { - *tx_fifo = tu_unaligned_read32(src); - src += 4; - } - - // Write the remaining 1-3 bytes into fifo - uint8_t const bytes_rem = len & 0x03; - if (bytes_rem) { - uint32_t tmp_word = src[0]; - if (bytes_rem > 1) tmp_word |= (src[1] << 8); - if (bytes_rem > 2) tmp_word |= (src[2] << 16); - - *tx_fifo = tmp_word; - } -} //-------------------------------------------------------------------- // Endpoint //-------------------------------------------------------------------- - static void edpt_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress); @@ -404,7 +355,7 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c if (dir == TUSB_DIR_IN) { // A full IN transfer (multiple packets, possibly) triggers XFRC. dep->dieptsiz = (num_packets << DIEPTSIZ_PKTCNT_Pos) | - ((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk); + ((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk); if(dma_device_enabled(dwc2)) { dep->diepdma = (uintptr_t)xfer->buffer; @@ -648,11 +599,10 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to // Schedule the first transaction for EP0 transfer edpt_schedule_packets(rhport, epnum, dir, 1, ep0_pending[dir]); } else { - uint16_t num_packets = (total_bytes / xfer->max_size); - uint16_t const short_packet_size = total_bytes % xfer->max_size; - - // Zero-size packet is special case. - if ((short_packet_size > 0) || (total_bytes == 0)) num_packets++; + uint16_t num_packets = tu_div_ceil(total_bytes, xfer->max_size); + if (num_packets == 0) { + num_packets = 1; // zero length packet still count as 1 + } // Schedule packets to be sent within interrupt edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes); @@ -758,7 +708,7 @@ static void handle_rxflvl_irq(uint8_t rhport) { tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, bcnt); } else { // Linear buffer - dfifo_read_packet(rhport, xfer->buffer, bcnt); + dfifo_read_packet(dwc2, xfer->buffer, bcnt); // Increment pointer to xfer data xfer->buffer += bcnt; @@ -863,8 +813,7 @@ static void handle_epout_irq(uint8_t rhport) { static void handle_epin_irq(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - uint8_t const ep_count = _dwc2_controller[rhport].ep_count; - dwc2_epin_t* epin = dwc2->epin; + const uint8_t ep_count = _dwc2_controller[rhport].ep_count; // DAINT for a given EP clears when DIEPINTx is cleared. // IEPINT will be cleared when DAINT's out bits are cleared. @@ -872,9 +821,10 @@ static void handle_epin_irq(uint8_t rhport) { if (dwc2->daint & TU_BIT(DAINT_IEPINT_Pos + n)) { // IN XFER complete (entire xfer). xfer_ctl_t* xfer = XFER_CTL_BASE(n, TUSB_DIR_IN); + dwc2_epin_t* epin = &dwc2->epin[n]; - if (epin[n].diepint & DIEPINT_XFRC) { - epin[n].diepint = DIEPINT_XFRC; + if (epin->diepint & DIEPINT_XFRC) { + epin->diepint = DIEPINT_XFRC; // EP0 can only handle one packet if ((n == 0) && ep0_pending[TUSB_DIR_IN]) { @@ -889,39 +839,38 @@ static void handle_epin_irq(uint8_t rhport) { } // XFER FIFO empty - if ((epin[n].diepint & DIEPINT_TXFE) && (dwc2->diepempmsk & (1 << n))) { + if ((epin->diepint & DIEPINT_TXFE) && (dwc2->diepempmsk & (1 << n))) { // diepint's TXFE bit is read-only, software cannot clear it. // It will only be cleared by hardware when written bytes is more than // - 64 bytes or - // - Half of TX FIFO size (configured by DIEPTXF) - - uint16_t remaining_packets = (epin[n].dieptsiz & DIEPTSIZ_PKTCNT_Msk) >> DIEPTSIZ_PKTCNT_Pos; + // - Half/Empty of TX FIFO size (configured by GAHBCFG.TXFELVL) + const uint16_t remain_packets = epin->dieptsiz_bm.packet_count; // Process every single packet (only whole packets can be written to fifo) - for (uint16_t i = 0; i < remaining_packets; i++) { - uint16_t const remaining_bytes = (epin[n].dieptsiz & DIEPTSIZ_XFRSIZ_Msk) >> DIEPTSIZ_XFRSIZ_Pos; + for (uint16_t i = 0; i < remain_packets; i++) { + const uint16_t remain_bytes = epin->dieptsiz_bm.xfer_size; // Packet can not be larger than ep max size - uint16_t const packet_size = tu_min16(remaining_bytes, xfer->max_size); + const uint16_t packet_size = tu_min16(remain_bytes, xfer->max_size); // It's only possible to write full packets into FIFO. Therefore DTXFSTS register of current // EP has to be checked if the buffer can take another WHOLE packet - if (packet_size > ((epin[n].dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) break; + if (packet_size > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) { + break; + } // Push packet to Tx-FIFO if (xfer->ff) { volatile uint32_t* tx_fifo = dwc2->fifo[n]; tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*) (uintptr_t) tx_fifo, packet_size); } else { - dfifo_write_packet(rhport, n, xfer->buffer, packet_size); - - // Increment pointer to xfer data + dfifo_write_packet(dwc2, n, xfer->buffer, packet_size); xfer->buffer += packet_size; } } // Turn off TXFE if all bytes are written. - if (((epin[n].dieptsiz & DIEPTSIZ_XFRSIZ_Msk) >> DIEPTSIZ_XFRSIZ_Pos) == 0) { + if (epin->dieptsiz_bm.xfer_size == 0) { dwc2->diepempmsk &= ~(1 << n); } } diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index c5990c53f..1527932a5 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -158,7 +158,8 @@ static bool check_dwc2(dwc2_regs_t* dwc2) { // For some reason: GD32VF103 gsnpsid and all hwcfg register are always zero (skip it) (void)dwc2; #if !TU_CHECK_MCU(OPT_MCU_GD32VF103) - uint32_t const gsnpsid = dwc2->gsnpsid & GSNPSID_ID_MASK; + enum { GSNPSID_ID_MASK = TU_GENMASK(31, 16) }; + const uint32_t gsnpsid = dwc2->gsnpsid & GSNPSID_ID_MASK; TU_ASSERT(gsnpsid == DWC2_OTG_ID || gsnpsid == DWC2_FS_IOT_ID || gsnpsid == DWC2_HS_IOT_ID); #endif @@ -200,7 +201,7 @@ bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) { TU_ASSERT(check_dwc2(dwc2)); // disable global interrupt - // dwc2->gahbcfg &= ~GAHBCFG_GINT; + dwc2->gahbcfg &= ~GAHBCFG_GINT; if (is_highspeed) { phy_hs_init(dwc2); @@ -241,8 +242,8 @@ bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) { dwc2->gintmsk |= GINTMSK_RXFLVLM; } - // Configure TX FIFO empty level for interrupt. Default is complete empty - dwc2->gahbcfg |= GAHBCFG_TXFELVL; + // (non-periodic) TX FIFO empty level for interrupt is complete empty + dwc2->gahbcfg |= GAHBCFG_TX_FIFO_EPMTY_LVL; return true; } @@ -260,4 +261,58 @@ bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) { // // } +//-------------------------------------------------------------------- +// DFIFO +//-------------------------------------------------------------------- +// Read a single data packet from receive DFIFO +void dfifo_read_packet(dwc2_regs_t* dwc2, uint8_t* dst, uint16_t len) { + const volatile uint32_t* rx_fifo = dwc2->fifo[0]; + + // Reading full available 32 bit words from fifo + uint16_t word_count = len >> 2; + while (word_count--) { + tu_unaligned_write32(dst, *rx_fifo); + dst += 4; + } + + // Read the remaining 1-3 bytes from fifo + const uint8_t bytes_rem = len & 0x03; + if (bytes_rem != 0) { + const uint32_t tmp = *rx_fifo; + dst[0] = tu_u32_byte0(tmp); + if (bytes_rem > 1) { + dst[1] = tu_u32_byte1(tmp); + } + if (bytes_rem > 2) { + dst[2] = tu_u32_byte2(tmp); + } + } +} + +// Write a single data packet to DFIFO +void dfifo_write_packet(dwc2_regs_t* dwc2, uint8_t fifo_num, const uint8_t* src, uint16_t len) { + volatile uint32_t* tx_fifo = dwc2->fifo[fifo_num]; + + // Pushing full available 32 bit words to fifo + uint16_t word_count = len >> 2; + while (word_count--) { + *tx_fifo = tu_unaligned_read32(src); + src += 4; + } + + // Write the remaining 1-3 bytes into fifo + const uint8_t bytes_rem = len & 0x03; + if (bytes_rem) { + uint32_t tmp_word = src[0]; + if (bytes_rem > 1) { + tmp_word |= (src[1] << 8); + } + if (bytes_rem > 2) { + tmp_word |= (src[2] << 16); + } + + *tx_fifo = tmp_word; + } +} + #endif diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h index fff21ca5c..18b93894f 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.h +++ b/src/portable/synopsys/dwc2/dwc2_common.h @@ -84,12 +84,16 @@ TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_tx(dwc2_regs_t* dwc2, uint8 dwc2->grstctl = GRSTCTL_TXFFLSH | (fnum << GRSTCTL_TXFNUM_Pos); while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {} } + TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_rx(dwc2_regs_t* dwc2) { // flush RX fifo and wait for it cleared dwc2->grstctl = GRSTCTL_RXFFLSH; while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {} } +void dfifo_read_packet(dwc2_regs_t* dwc2, uint8_t* dst, uint16_t len); +void dfifo_write_packet(dwc2_regs_t* dwc2, uint8_t fifo_num, uint8_t const* src, uint16_t len); + //--------------------------------------------------------------------+ // DMA //--------------------------------------------------------------------+ diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index ed335f1a3..36e917d43 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -146,7 +146,7 @@ enum { }; //-------------------------------------------------------------------- -// Register bitfield definitions +// Common Register Bitfield //-------------------------------------------------------------------- typedef struct TU_ATTR_PACKED { uint32_t ses_req_scs : 1; // 0 Session request success @@ -324,6 +324,20 @@ typedef struct TU_ATTR_PACKED { }dwc2_ghwcfg4_t; TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg4_t) == 4, "incorrect size"); +//-------------------------------------------------------------------- +// Host Register Bitfield +//-------------------------------------------------------------------- + +typedef struct TU_ATTR_PACKED { + uint32_t fifo_available : 16; // 0..15 Number of words available in the Tx FIFO + uint32_t queue_available : 8; // 16..23 Number of spaces available in the NPT transmit request queue for both IN and OUT + // 24..31 is top entry in the request queue that is currently being processed by the MAC + uint32_t qtop_terminate : 1; // 24 Last entry for selected channel + uint32_t qtop_token : 2; // 25..26 Token 0: In/Out 1: ZLP, 2: Ping/cspit, 3: Channel halt command + uint32_t qtop_ch_num : 4; // 27..30 Channel number +} dwc2_hnptxsts_t; +TU_VERIFY_STATIC(sizeof(dwc2_hnptxsts_t) == 4, "incorrect size"); + typedef struct TU_ATTR_PACKED { uint32_t conn_status : 1; // 0 Port connect status uint32_t conn_detected : 1; // 1 Port connect detected @@ -368,6 +382,13 @@ typedef struct TU_ATTR_PACKED { } dwc2_channel_split_t; TU_VERIFY_STATIC(sizeof(dwc2_channel_split_t) == 4, "incorrect size"); +typedef struct TU_ATTR_PACKED { + uint32_t xfer_size : 19; // 0..18 Transfer size in bytes + uint32_t packet_count : 10; // 19..28 Number of packets + uint32_t pid : 2; // 29..30 Packet ID +} dwc2_channel_tsize_t; +TU_VERIFY_STATIC(sizeof(dwc2_channel_tsize_t) == 4, "incorrect size"); + // Host Channel typedef struct { union { @@ -380,35 +401,55 @@ typedef struct { }; volatile uint32_t hcint; // 508 + 20*ch Host Channel Interrupt volatile uint32_t hcintmsk; // 50C + 20*ch Host Channel Interrupt Mask + union { volatile uint32_t hctsiz; // 510 + 20*ch Host Channel Transfer Size + volatile dwc2_channel_tsize_t hctsiz_bm; + }; volatile uint32_t hcdma; // 514 + 20*ch Host Channel DMA Address uint32_t reserved518; // 518 + 20*ch volatile uint32_t hcdmab; // 51C + 20*ch Host Channel DMA Address } dwc2_channel_t; +//-------------------------------------------------------------------- +// Device Register Bitfield +//-------------------------------------------------------------------- +typedef struct TU_ATTR_PACKED { + uint32_t xfer_size : 19; // 0..18 Transfer size in bytes + uint32_t packet_count : 10; // 19..28 Number of packets + uint32_t mc_pid : 2; // 29..30 IN: Multi Count, OUT: PID +} dwc2_ep_tsize_t; +TU_VERIFY_STATIC(sizeof(dwc2_ep_tsize_t) == 4, "incorrect size"); + // Endpoint IN typedef struct { - volatile uint32_t diepctl; // 900 + 20*ep Device IN Endpoint Control - uint32_t reserved04; // 904 - volatile uint32_t diepint; // 908 + 20*ep Device IN Endpoint Interrupt - uint32_t reserved0c; // 90C - volatile uint32_t dieptsiz; // 910 + 20*ep Device IN Endpoint Transfer Size - volatile uint32_t diepdma; // 914 + 20*ep Device IN Endpoint DMA Address - volatile uint32_t dtxfsts; // 918 + 20*ep Device IN Endpoint Tx FIFO Status - uint32_t reserved1c; // 91C + volatile uint32_t diepctl; // 900 + 20*ep Device IN Endpoint Control + uint32_t reserved04; // 904 + volatile uint32_t diepint; // 908 + 20*ep Device IN Endpoint Interrupt + uint32_t reserved0c; // 90C + union { + volatile uint32_t dieptsiz; // 910 + 20*ep Device IN Endpoint Transfer Size + volatile dwc2_ep_tsize_t dieptsiz_bm; + }; + volatile uint32_t diepdma; // 914 + 20*ep Device IN Endpoint DMA Address + volatile uint32_t dtxfsts; // 918 + 20*ep Device IN Endpoint Tx FIFO Status + uint32_t reserved1c; // 91C } dwc2_epin_t; // Endpoint OUT typedef struct { - volatile uint32_t doepctl; // B00 + 20*ep Device OUT Endpoint Control - uint32_t reserved04; // B04 - volatile uint32_t doepint; // B08 + 20*ep Device OUT Endpoint Interrupt - uint32_t reserved0c; // B0C - volatile uint32_t doeptsiz; // B10 + 20*ep Device OUT Endpoint Transfer Size - volatile uint32_t doepdma; // B14 + 20*ep Device OUT Endpoint DMA Address - uint32_t reserved18[2]; // B18..B1C + volatile uint32_t doepctl; // B00 + 20*ep Device OUT Endpoint Control + uint32_t reserved04; // B04 + volatile uint32_t doepint; // B08 + 20*ep Device OUT Endpoint Interrupt + uint32_t reserved0c; // B0C + union { + volatile uint32_t doeptsiz; // B10 + 20*ep Device OUT Endpoint Transfer Size + volatile dwc2_ep_tsize_t doeptsiz_bm; + }; + volatile uint32_t doepdma; // B14 + 20*ep Device OUT Endpoint DMA Address + uint32_t reserved18[2]; // B18..B1C } dwc2_epout_t; +// Universal Endpoint typedef struct { union { volatile uint32_t diepctl; @@ -424,6 +465,7 @@ typedef struct { union { volatile uint32_t dieptsiz; volatile uint32_t doeptsiz; + volatile dwc2_ep_tsize_t deptsiz_bm; }; union { volatile uint32_t diepdma; @@ -460,7 +502,11 @@ typedef struct { volatile uint32_t dieptxf0; // 028 EP0 Tx FIFO Size volatile uint32_t gnptxfsiz; // 028 Non-periodic Transmit FIFO Size }; - volatile uint32_t gnptxsts; // 02c Non-periodic Transmit FIFO/Queue Status + union { + volatile uint32_t hnptxsts; // 02c Non-periodic Transmit FIFO/Queue Status + volatile dwc2_hnptxsts_t hnptxsts_bm; + volatile uint32_t gnptxsts; + }; volatile uint32_t gi2cctl; // 030 I2C Address volatile uint32_t gpvndctl; // 034 PHY Vendor Control union { @@ -791,14 +837,12 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define GAHBCFG_DMAEN_Pos (5U) #define GAHBCFG_DMAEN_Msk (0x1UL << GAHBCFG_DMAEN_Pos) // 0x00000020 #define GAHBCFG_DMAEN GAHBCFG_DMAEN_Msk // DMA enable -#define GAHBCFG_TXFELVL_Pos (7U) -#define GAHBCFG_TXFELVL_Msk (0x1UL << GAHBCFG_TXFELVL_Pos) // 0x00000080 -#define GAHBCFG_TXFELVL GAHBCFG_TXFELVL_Msk // TxFIFO empty level -#define GAHBCFG_PTXFELVL_Pos (8U) -#define GAHBCFG_PTXFELVL_Msk (0x1UL << GAHBCFG_PTXFELVL_Pos) // 0x00000100 -#define GAHBCFG_PTXFELVL GAHBCFG_PTXFELVL_Msk // Periodic TxFIFO empty level - -#define GSNPSID_ID_MASK TU_GENMASK(31, 16) +#define GAHBCFG_TX_FIFO_EPMTY_LVL_Pos (7U) +#define GAHBCFG_TX_FIFO_EPMTY_LVL_Msk (0x1UL << GAHBCFG_TX_FIFO_EPMTY_LVL_Pos) // 0x00000080 +#define GAHBCFG_TX_FIFO_EPMTY_LVL GAHBCFG_TX_FIFO_EPMTY_LVL_Msk // TxFIFO empty level +#define GAHBCFG_PTX_FIFO_EPMTY_LVL_Pos (8U) +#define GAHBCFG_PTX_FIFO_EPMTY_LVL_Msk (0x1UL << GAHBCFG_PTX_FIFO_EPMTY_LVL_Pos) // 0x00000100 +#define GAHBCFG_PTX_FIFO_EPMTY_LVL GAHBCFG_PTX_FIFO_EPMTY_LVL_Msk // Periodic TxFIFO empty level /******************** Bit definition for GUSBCFG register ********************/ #define GUSBCFG_TOCAL_Pos (0U) @@ -1009,9 +1053,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define GINTSTS_RXFLVL_Pos (4U) #define GINTSTS_RXFLVL_Msk (0x1UL << GINTSTS_RXFLVL_Pos) // 0x00000010 #define GINTSTS_RXFLVL GINTSTS_RXFLVL_Msk // RxFIFO nonempty -#define GINTSTS_NPTXFE_Pos (5U) -#define GINTSTS_NPTXFE_Msk (0x1UL << GINTSTS_NPTXFE_Pos) // 0x00000020 -#define GINTSTS_NPTXFE GINTSTS_NPTXFE_Msk // Nonperiodic TxFIFO empty +#define GINTSTS_NPTX_FIFO_EMPTY_Pos (5U) +#define GINTSTS_NPTX_FIFO_EMPTY_Msk (0x1UL << GINTSTS_NPTX_FIFO_EMPTY_Pos) // 0x00000020 +#define GINTSTS_NPTX_FIFO_EMPTY GINTSTS_NPTX_FIFO_EMPTY_Msk // Nonperiodic TxFIFO empty #define GINTSTS_GINAKEFF_Pos (6U) #define GINTSTS_GINAKEFF_Msk (0x1UL << GINTSTS_GINAKEFF_Pos) // 0x00000040 #define GINTSTS_GINAKEFF GINTSTS_GINAKEFF_Msk // Global IN nonperiodic NAK effective @@ -1060,15 +1104,15 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define GINTSTS_HCINT_Pos (25U) #define GINTSTS_HCINT_Msk (0x1UL << GINTSTS_HCINT_Pos) // 0x02000000 #define GINTSTS_HCINT GINTSTS_HCINT_Msk // Host channels interrupt -#define GINTSTS_PTXFE_Pos (26U) -#define GINTSTS_PTXFE_Msk (0x1UL << GINTSTS_PTXFE_Pos) // 0x04000000 -#define GINTSTS_PTXFE GINTSTS_PTXFE_Msk // Periodic TxFIFO empty +#define GINTSTS_PTX_FIFO_EMPTY_Pos (26U) +#define GINTSTS_PTX_FIFO_EMPTY_Msk (0x1UL << GINTSTS_PTX_FIFO_EMPTY_Pos) // 0x04000000 +#define GINTSTS_PTX_FIFO_EMPTY GINTSTS_PTX_FIFO_EMPTY_Msk // Periodic TxFIFO empty #define GINTSTS_LPMINT_Pos (27U) #define GINTSTS_LPMINT_Msk (0x1UL << GINTSTS_LPMINT_Pos) // 0x08000000 #define GINTSTS_LPMINT GINTSTS_LPMINT_Msk // LPM interrupt #define GINTSTS_CONIDSTSCHNG_Pos (28U) -#define GINTSTS_CONIDSTSCHNG_Msk (0x1UL << GINTSTS_CONIDSTSCHNG_Pos) // 0x10000000 -#define GINTSTS_CONIDSTSCHNG GINTSTS_CONIDSTSCHNG_Msk // Connector ID status change +#define GINTSTS_CONIDSTSCHNG_Msk (0x1UL << GINTSTS_CONIDSTSCHNG_Pos) // 0x10000000 +#define GINTSTS_CONIDSTSCHNG GINTSTS_CONIDSTSCHNG_Msk // Connector ID status change #define GINTSTS_DISCINT_Pos (29U) #define GINTSTS_DISCINT_Msk (0x1UL << GINTSTS_DISCINT_Pos) // 0x20000000 #define GINTSTS_DISCINT GINTSTS_DISCINT_Msk // Disconnect detected interrupt diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index f37bf717d..f388776c5 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -35,14 +35,15 @@ #include "dwc2_common.h" // DWC2 has limit number of channel, in order to support all endpoints we can store channel char/split to swap later on -#ifndef CFG_TUH_DWC2_CHANNEL_MAX -#define CFG_TUH_DWC2_CHANNEL_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX + CFG_TUH_HUB) +#ifndef CFG_TUH_DWC2_ENDPOINT_MAX +#define CFG_TUH_DWC2_ENDPOINT_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX + CFG_TUH_HUB) #endif enum { HPRT_W1C_MASK = HPRT_CONN_DETECT | HPRT_ENABLE | HPRT_ENABLE_CHANGE | HPRT_OVER_CURRENT_CHANGE }; +// Host driver for each opened endpoint typedef struct { union { uint32_t hcchar; @@ -53,11 +54,14 @@ typedef struct { dwc2_channel_split_t hcsplt_bm; }; + uint8_t* buf; + uint16_t total_len; uint8_t next_data_toggle; + bool pending_tx; } hcd_pipe_t; typedef struct { - hcd_pipe_t pipe[CFG_TUH_DWC2_CHANNEL_MAX]; + hcd_pipe_t pipe[CFG_TUH_DWC2_ENDPOINT_MAX]; } dwc2_hcd_t; dwc2_hcd_t _hcd_data; @@ -94,7 +98,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool dma_host_enabled(const dwc2_regs_t* dwc possible since the free space is located between the RX and TX FIFOs. ----------------- ep_fifo_size - | EPInfo DMA | + | HCDMAn | |--------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth) | Non-Periodic | | TX FIFO | @@ -214,8 +218,12 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { dwc2->hprt = HPRT_POWER; // turn on VBUS // Enable required interrupts - dwc2->gintmsk |= GINTMSK_OTGINT | GINTSTS_CONIDSTSCHNG | GINTMSK_PRTIM; // | GINTMSK_WUIM; - dwc2->gahbcfg |= GAHBCFG_GINT; // Enable global interrupt + dwc2->gintmsk |= GINTMSK_OTGINT | GINTSTS_CONIDSTSCHNG | GINTMSK_PRTIM ; // | GINTMSK_WUIM; + + // NPTX can hold at least 2 packet, change interrupt level to half-empty + uint32_t gahbcfg = dwc2->gahbcfg & ~GAHBCFG_TX_FIFO_EPMTY_LVL; + gahbcfg |= GAHBCFG_GINT; // Enable global interrupt + dwc2->gahbcfg = gahbcfg; return true; } @@ -290,7 +298,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const hcd_devtree_get_info(dev_addr, &devtree_info); // find a free pipe - for (uint32_t i = 0; i < CFG_TUH_DWC2_CHANNEL_MAX; i++) { + for (uint32_t i = 0; i < CFG_TUH_DWC2_ENDPOINT_MAX; i++) { hcd_pipe_t* pipe = &_hcd_data.pipe[i]; dwc2_channel_char_t* hcchar_bm = &pipe->hcchar_bm; dwc2_channel_split_t* hcsplt_bm = &pipe->hcsplt_bm; @@ -334,25 +342,40 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t find_free_channel(dwc2_regs_t* dwc2) return TUSB_INDEX_INVALID_8; } -TU_ATTR_ALWAYS_INLINE static inline uint8_t find_opened_pipe(uint8_t dev_addr, uint8_t ep_addr) { - for (uint32_t i = 0; i < CFG_TUH_DWC2_CHANNEL_MAX; i++) { +TU_ATTR_ALWAYS_INLINE static inline uint8_t find_opened_pipe(uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) { + for (uint8_t i = 0; i < (uint8_t) CFG_TUH_DWC2_ENDPOINT_MAX; i++) { const dwc2_channel_char_t* hcchar_bm = &_hcd_data.pipe[i].hcchar_bm; + // find enabled pipe: note EP0 is bidirectional if (hcchar_bm->enable && hcchar_bm->dev_addr == dev_addr && - ep_addr == tu_edpt_addr(hcchar_bm->ep_num, hcchar_bm->ep_dir)) { + hcchar_bm->ep_num == ep_num && (ep_num == 0 || hcchar_bm->ep_dir == ep_dir)) { return i; } } return TUSB_INDEX_INVALID_8; } +void schedule_out_packet(dwc2_regs_t* dwc2, uint8_t pipe_id, uint8_t ch_id) { + // To prevent conflict with other channel, we will enable periodic/non-periodic FIFO empty interrupt accordingly. + // And write packet in the interrupt handler + hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; + dwc2_channel_t* channel = &dwc2->channel[ch_id]; + (void) channel; + const uint8_t ep_type = pipe->hcchar_bm.ep_type; + const bool is_periodic = ep_type == TUSB_XFER_INTERRUPT || ep_type == TUSB_XFER_ISOCHRONOUS; + pipe->pending_tx = true; + dwc2->gintmsk |= (is_periodic ? GINTSTS_PTX_FIFO_EMPTY : GINTSTS_NPTX_FIFO_EMPTY); +} + // Submit a transfer, when complete hcd_event_xfer_complete() must be invoked bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - uint8_t pipe_id = find_opened_pipe(dev_addr, ep_addr); - TU_ASSERT(pipe_id < CFG_TUH_DWC2_CHANNEL_MAX); // no opened pipe + const uint8_t ep_num = tu_edpt_number(ep_addr); + const uint8_t ep_dir = tu_edpt_dir(ep_addr); + uint8_t pipe_id = find_opened_pipe(dev_addr, ep_num, ep_dir); + TU_ASSERT(pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX); // no opened pipe hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; - const dwc2_channel_char_t* hcchar_bm = &pipe->hcchar_bm; + dwc2_channel_char_t* hcchar_bm = &pipe->hcchar_bm; uint8_t ch_id = find_free_channel(dwc2); TU_ASSERT(ch_id < 16); // all channel are in use @@ -362,25 +385,44 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * channel->hcintmsk = HCINT_XFER_COMPLETE | HCINT_CHANNEL_HALTED | HCINT_STALL | HCINT_AHB_ERR | HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR; - const uint16_t packet_count = tu_div_ceil(buflen, hcchar_bm->ep_size); + uint16_t packet_count = tu_div_ceil(buflen, hcchar_bm->ep_size); + if (packet_count == 0) { + packet_count = 1; // zero length packet still count as 1 + } channel->hctsiz = (pipe->next_data_toggle << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | buflen; // Control transfer always start with DATA1 for data and status stage. May has issue with ZLP - if (pipe->next_data_toggle == HCTSIZ_PID_DATA0 || tu_edpt_number(ep_addr) == 0) { + if (pipe->next_data_toggle == HCTSIZ_PID_DATA0 || ep_num == 0) { pipe->next_data_toggle = HCTSIZ_PID_DATA1; } else { pipe->next_data_toggle = HCTSIZ_PID_DATA0; } + // TODO support split transaction + channel->hcsplt = pipe->hcsplt; + + hcchar_bm->odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame + hcchar_bm->ep_dir = ep_dir; // control endpoint can switch direction + channel->hcchar = pipe->hcchar & ~HCCHAR_CHENA; // restore hcchar but don't enable yet + if (dma_host_enabled(dwc2)) { channel->hcdma = (uint32_t) buffer; } else { - TU_ASSERT(false); // not yet support - } + if (ep_dir == TUSB_DIR_IN) { + TU_ASSERT(false); // not yet support + } else { + pipe->buf = buffer; + pipe->total_len = buflen; - // TODO support split transaction - channel->hcsplt = pipe->hcsplt; - channel->hcchar = pipe->hcchar; // kick-off transfer + channel->hcchar |= HCCHAR_CHENA; // enable channel before writing to FIFO + + if (ep_dir == TUSB_DIR_OUT && buflen > 0) { + schedule_out_packet(dwc2, pipe_id, ch_id); + } else { + TU_ASSERT(false); // not yet support + } + } + } return true; } @@ -397,8 +439,8 @@ bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { // Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet[8]) { - uint8_t pipe_id = find_opened_pipe(dev_addr, 0); - TU_ASSERT(pipe_id < CFG_TUH_DWC2_CHANNEL_MAX); // no opened pipe + uint8_t pipe_id = find_opened_pipe(dev_addr, 0, TUSB_DIR_OUT); + TU_ASSERT(pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX); // no opened pipe hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; pipe->next_data_toggle = HCTSIZ_PID_SETUP; @@ -529,25 +571,57 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) { } } +bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { + bool ff_written = false; + volatile uint32_t* tx_sts = is_periodic ? &dwc2->hptxsts : &dwc2->hnptxsts; + + // find which channel have pending packet + for (uint8_t ch_id = 0; ch_id < 32; ch_id++) { + if (tu_bit_test(dwc2->haintmsk, ch_id)) { + dwc2_channel_t* channel = &dwc2->channel[ch_id]; + const dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; + if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { + uint8_t pipe_id = find_opened_pipe(hcchar_bm.dev_addr, hcchar_bm.ep_num, TUSB_DIR_OUT); + if (pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX) { + hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; + if (pipe->pending_tx) { + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + for (uint16_t i = 0; i < remain_packets; i++) { + const uint16_t remain_bytes = channel->hctsiz_bm.xfer_size; + const uint16_t packet_bytes = tu_min16(remain_bytes, hcchar_bm.ep_size); + + // check if there is enough space in FIFO + if (packet_bytes > (*tx_sts & HPTXSTS_PTXQSAV)) { + break; + } + + dfifo_write_packet(dwc2, ch_id, pipe->buf, packet_bytes); + pipe->buf += packet_bytes; + + if (channel->hctsiz_bm.xfer_size == 0) { + pipe->pending_tx = false; // all data has been written + } + + ff_written = true; + } + } + } + } + } + } + + return ff_written; +} + /* Interrupt Hierarchy - HCINTn.XferCompl HCINTMSKn.XferComplMsk HPRT ConnDetect PrtEnChng OverCurChng - | | | | | - +---------- AND --------+ +------------ OR -----------+ - | | - HAINT.CHn HAINTMSK.CHn | - | | | - +---------- AND --------+ | - | | - GINTSTS.HCInt GINTMSK.HCInt GINTSTS.PrtInt GINTMSK.PrtInt - | | | | - +---------- AND ---------+ +---------- AND ---------+ - | | - +-------------------- OR ---------------------------+ - | - GAHBCFG.GblIntrMsk - | - IRQn - */ + HCINTn HPRT + | | + HAINT.CHn | + | | + GINTSTS HCInt PrtInt NPTxFEmp PTxFEmpp RXFLVL + + +*/ void hcd_int_handler(uint8_t rhport, bool in_isr) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); const uint32_t int_mask = dwc2->gintmsk; @@ -577,6 +651,15 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { handle_channel_irq(rhport, in_isr); } + if (int_status & GINTSTS_NPTX_FIFO_EMPTY) { + // NPTX FIFO empty interrupt, this is read-only and cleared by hardware when FIFO is written + const bool ff_written = handle_txfifo_empty(dwc2, false); + if (!ff_written) { + // no more pending packet, disable interrupt + dwc2->gintmsk &= ~GINTSTS_NPTX_FIFO_EMPTY; + } + } + // RxFIFO non-empty interrupt handling. if (int_status & GINTSTS_RXFLVL) { // RXFLVL bit is read-only -- cgit v1.3.1 From 79c0a249e878c425f3a5f926f444bb5178a748ef Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 25 Oct 2024 22:56:11 +0700 Subject: got In transfer working, able to get 1st device descriptor and set address --- hw/bsp/stm32u5/family.mk | 2 +- src/portable/synopsys/dwc2/dcd_dwc2.c | 30 +++---- src/portable/synopsys/dwc2/dwc2_common.c | 3 - src/portable/synopsys/dwc2/dwc2_type.h | 40 +++++++--- src/portable/synopsys/dwc2/hcd_dwc2.c | 130 +++++++++++++++++++++---------- 5 files changed, 135 insertions(+), 70 deletions(-) (limited to 'src') diff --git a/hw/bsp/stm32u5/family.mk b/hw/bsp/stm32u5/family.mk index 0e3c54c4b..05fe4608a 100644 --- a/hw/bsp/stm32u5/family.mk +++ b/hw/bsp/stm32u5/family.mk @@ -48,7 +48,7 @@ else SRC_C += \ src/portable/synopsys/dwc2/dcd_dwc2.c \ src/portable/synopsys/dwc2/hcd_dwc2.c \ - src/portable/synopsys/dwc2/dwc2_common.c \ + src/portable/synopsys/dwc2/dwc2_common.c endif INC += \ diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index b389b0f74..a04f03b50 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -445,8 +445,11 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Enable required interrupts dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM; - // Enable global interrupt - dwc2->gahbcfg |= GAHBCFG_GINT; + // TX FIFO empty level for interrupt is complete empty + uint32_t gahbcfg = dwc2->gahbcfg; + gahbcfg |= GAHBCFG_TX_FIFO_EPMTY_LVL; + gahbcfg |= GAHBCFG_GINT; // Enable global interrupt + dwc2->gahbcfg = gahbcfg; dcd_connect(rhport); return true; @@ -671,16 +674,15 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { // Process shared receive FIFO, this interrupt is only used in Slave mode static void handle_rxflvl_irq(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - volatile uint32_t const* rx_fifo = dwc2->fifo[0]; + const volatile uint32_t* rx_fifo = dwc2->fifo[0]; // Pop control word off FIFO - uint32_t const grxstsp = dwc2->grxstsp; - uint8_t const pktsts = (grxstsp & GRXSTSP_PKTSTS_Msk) >> GRXSTSP_PKTSTS_Pos; - uint8_t const epnum = (grxstsp & GRXSTSP_EPNUM_Msk) >> GRXSTSP_EPNUM_Pos; - uint16_t const bcnt = (grxstsp & GRXSTSP_BCNT_Msk) >> GRXSTSP_BCNT_Pos; + const dwc2_grxstsp_t grxstsp_bm = dwc2->grxstsp_bm; + const uint8_t epnum = grxstsp_bm.ep_ch_num; + const uint16_t byte_count = grxstsp_bm.byte_count; dwc2_epout_t* epout = &dwc2->epout[epnum]; - switch (pktsts) { + switch (grxstsp_bm.packet_status) { // Global OUT NAK: do nothing case GRXSTS_PKTSTS_GLOBALOUTNAK: break; @@ -705,18 +707,18 @@ static void handle_rxflvl_irq(uint8_t rhport) { // Read packet off RxFIFO if (xfer->ff) { // Ring buffer - tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, bcnt); + tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, byte_count); } else { // Linear buffer - dfifo_read_packet(dwc2, xfer->buffer, bcnt); + dfifo_read_packet(dwc2, xfer->buffer, byte_count); // Increment pointer to xfer data - xfer->buffer += bcnt; + xfer->buffer += byte_count; } - // Truncate transfer length in case of short packet - if (bcnt < xfer->max_size) { - xfer->total_len -= (epout->doeptsiz & DOEPTSIZ_XFRSIZ_Msk) >> DOEPTSIZ_XFRSIZ_Pos; + // short packet, minus remaining bytes (xfer_size) + if (byte_count < xfer->max_size) { + xfer->total_len -= epout->doeptsiz_bm.xfer_size; if (epnum == 0) { xfer->total_len -= ep0_pending[TUSB_DIR_OUT]; ep0_pending[TUSB_DIR_OUT] = 0; diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index 1527932a5..66dc59b11 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -242,9 +242,6 @@ bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) { dwc2->gintmsk |= GINTMSK_RXFLVLM; } - // (non-periodic) TX FIFO empty level for interrupt is complete empty - dwc2->gahbcfg |= GAHBCFG_TX_FIFO_EPMTY_LVL; - return true; } diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index 36e917d43..9aaa8f0bd 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -145,6 +145,21 @@ enum { HCTSIZ_PID_SETUP = 3, }; +enum { + GRXSTS_PKTSTS_GLOBALOUTNAK = 1, + GRXSTS_PKTSTS_OUTRX = 2, + GRXSTS_PKTSTS_OUTDONE = 3, + GRXSTS_PKTSTS_SETUPDONE = 4, + GRXSTS_PKTSTS_SETUPRX = 6 +}; + +enum { + GRXSTS_PKTSTS_HOST_IN_RECEIVED = 2, + GRXSTS_PKTSTS_HOST_IN_XFER_COMPL = 3, + GRXSTS_PKTSTS_HOST_DATATOGGLE_ERR = 5, + GRXSTS_PKTSTS_HOST_CHANNEL_HALTED = 7 +}; + //-------------------------------------------------------------------- // Common Register Bitfield //-------------------------------------------------------------------- @@ -263,6 +278,17 @@ typedef struct TU_ATTR_PACKED { } dwc2_grstctl_t; TU_VERIFY_STATIC(sizeof(dwc2_grstctl_t) == 4, "incorrect size"); +typedef struct TU_ATTR_PACKED { + uint32_t ep_ch_num : 4; // 0..3 Endpoint/Channel Number + uint32_t byte_count :11; // 4..14 Byte Count + uint32_t dpid : 2; // 15..16 Data PID + uint32_t packet_status : 4; // 17..20 Packet Status + uint32_t frame_number : 4; // 21..24 Frame Number + uint32_t rsv25_31 : 7; // 25..31 Reserved +} dwc2_grxstsp_t; +TU_VERIFY_STATIC(sizeof(dwc2_grxstsp_t) == 4, "incorrect size"); + +// Hardware Configuration typedef struct TU_ATTR_PACKED { uint32_t op_mode : 3; // 0..2 HNP/SRP Host/Device/OTG mode uint32_t arch : 2; // 3..4 Slave/External/Internal DMA @@ -496,7 +522,10 @@ typedef struct { volatile uint32_t gintsts; // 014 Interrupt volatile uint32_t gintmsk; // 018 Interrupt Mask volatile uint32_t grxstsr; // 01c Receive Status Debug Read + union { volatile uint32_t grxstsp; // 020 Receive Status Read/Pop + volatile dwc2_grxstsp_t grxstsp_bm; + }; volatile uint32_t grxfsiz; // 024 Receive FIFO Size union { volatile uint32_t dieptxf0; // 028 EP0 Tx FIFO Size @@ -1236,17 +1265,6 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define GRXSTSP_PKTSTS_Msk (0xFUL << GRXSTSP_PKTSTS_Pos) // 0x001E0000 #define GRXSTSP_PKTSTS GRXSTSP_PKTSTS_Msk // OUT EP interrupt mask bits -#define GRXSTS_PKTSTS_GLOBALOUTNAK 1 -#define GRXSTS_PKTSTS_OUTRX 2 -#define GRXSTS_PKTSTS_HCHIN 2 -#define GRXSTS_PKTSTS_OUTDONE 3 -#define GRXSTS_PKTSTS_HCHIN_XFER_COMP 3 -#define GRXSTS_PKTSTS_SETUPDONE 4 -#define GRXSTS_PKTSTS_DATATOGGLEERR 5 -#define GRXSTS_PKTSTS_SETUPRX 6 -#define GRXSTS_PKTSTS_HCHHALTED 7 - - /******************** Bit definition for DAINTMSK register ********************/ #define DAINTMSK_IEPM_Pos (0U) #define DAINTMSK_IEPM_Msk (0xFFFFUL << DAINTMSK_IEPM_Pos) // 0x0000FFFF diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index f388776c5..7d74d7a33 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -54,7 +54,7 @@ typedef struct { dwc2_channel_split_t hcsplt_bm; }; - uint8_t* buf; + uint8_t* buffer; uint16_t total_len; uint8_t next_data_toggle; bool pending_tx; @@ -218,7 +218,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { dwc2->hprt = HPRT_POWER; // turn on VBUS // Enable required interrupts - dwc2->gintmsk |= GINTMSK_OTGINT | GINTSTS_CONIDSTSCHNG | GINTMSK_PRTIM ; // | GINTMSK_WUIM; + dwc2->gintmsk |= GINTSTS_OTGINT | GINTSTS_CONIDSTSCHNG | GINTSTS_HPRTINT | GINTSTS_HCINT; // NPTX can hold at least 2 packet, change interrupt level to half-empty uint32_t gahbcfg = dwc2->gahbcfg & ~GAHBCFG_TX_FIFO_EPMTY_LVL; @@ -354,6 +354,11 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t find_opened_pipe(uint8_t dev_addr, u return TUSB_INDEX_INVALID_8; } +TU_ATTR_ALWAYS_INLINE static inline uint8_t find_opened_pipe_by_channel(const dwc2_channel_t* channel) { + const dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; + return find_opened_pipe(hcchar_bm.dev_addr, hcchar_bm.ep_num, hcchar_bm.ep_dir); +} + void schedule_out_packet(dwc2_regs_t* dwc2, uint8_t pipe_id, uint8_t ch_id) { // To prevent conflict with other channel, we will enable periodic/non-periodic FIFO empty interrupt accordingly. // And write packet in the interrupt handler @@ -382,8 +387,12 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * dwc2->haintmsk |= TU_BIT(ch_id); dwc2_channel_t* channel = &dwc2->channel[ch_id]; - channel->hcintmsk = HCINT_XFER_COMPLETE | HCINT_CHANNEL_HALTED | HCINT_STALL | - HCINT_AHB_ERR | HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR; + uint32_t hcintmsk = HCINT_XFER_COMPLETE | HCINT_CHANNEL_HALTED | HCINT_STALL | + HCINT_AHB_ERR | HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR; + if (ep_dir == TUSB_DIR_IN) { + hcintmsk |= HCINT_NAK; + } + channel->hcintmsk = hcintmsk; uint16_t packet_count = tu_div_ceil(buflen, hcchar_bm->ep_size); if (packet_count == 0) { @@ -405,21 +414,24 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * hcchar_bm->ep_dir = ep_dir; // control endpoint can switch direction channel->hcchar = pipe->hcchar & ~HCCHAR_CHENA; // restore hcchar but don't enable yet + pipe->buffer = buffer; + pipe->total_len = buflen; + if (dma_host_enabled(dwc2)) { channel->hcdma = (uint32_t) buffer; } else { - if (ep_dir == TUSB_DIR_IN) { - TU_ASSERT(false); // not yet support - } else { - pipe->buf = buffer; - pipe->total_len = buflen; + // enable channel for: + // - OUT endpoint: it will enable corresponding FIFO channel + // - IN endpoint: it will write an IN request to the Non-periodic Request Queue, this will have dwc2 trying to send + // IN Token. If we got NAK, we have to re-enable the channel again in the interrupt. Due to the way usbh stack only + // call hcd_edpt_xfer() once, we will need to manage de-allocate/re-allocate IN channel dynamically. + channel->hcchar |= HCCHAR_CHENA; - channel->hcchar |= HCCHAR_CHENA; // enable channel before writing to FIFO + if (ep_dir == TUSB_DIR_IN) { - if (ep_dir == TUSB_DIR_OUT && buflen > 0) { + } else { + if (buflen > 0) { schedule_out_packet(dwc2, pipe_id, ch_id); - } else { - TU_ASSERT(false); // not yet support } } } @@ -460,20 +472,50 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { // HCD Event Handler //-------------------------------------------------------------------- -#if 1 static void handle_rxflvl_irq(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - // volatile uint32_t const* rx_fifo = dwc2->fifo[0]; // Pop control word off FIFO - uint32_t const grxstsp = dwc2->grxstsp; - (void) grxstsp; - // uint8_t const pktsts = (grxstsp & GRXSTSP_PKTSTS_Msk) >> GRXSTSP_PKTSTS_Pos; - // uint8_t const epnum = (grxstsp & GRXSTSP_EPNUM_Msk) >> GRXSTSP_EPNUM_Pos; - // uint16_t const bcnt = (grxstsp & GRXSTSP_BCNT_Msk) >> GRXSTSP_BCNT_Pos; - // dwc2_epout_t* epout = &dwc2->epout[epnum]; + const dwc2_grxstsp_t grxstsp_bm = dwc2->grxstsp_bm; + const uint8_t ch_id = grxstsp_bm.ep_ch_num; + dwc2_channel_t* channel = &dwc2->channel[ch_id]; + + switch (grxstsp_bm.packet_status) { + case GRXSTS_PKTSTS_HOST_IN_RECEIVED: { + // In packet received + const uint16_t byte_count = grxstsp_bm.byte_count; + const uint8_t pipe_id = find_opened_pipe_by_channel(channel); + TU_VERIFY(pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX, ); + hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; + + dfifo_read_packet(dwc2, pipe->buffer, byte_count); + pipe->buffer += byte_count; + + // short packet, minus remaining bytes (xfer_size) + if (byte_count < channel->hctsiz_bm.xfer_size) { + pipe->total_len -= channel->hctsiz_bm.xfer_size; + } + + break; + } + + case GRXSTS_PKTSTS_HOST_IN_XFER_COMPL: + // In transfer complete: After this entry is popped from the receive FIFO, dwc2 asserts a Transfer Completed + // interrupt --> handle_channel_irq() + break; + + case GRXSTS_PKTSTS_HOST_DATATOGGLE_ERR: + TU_ASSERT(0, ); // maybe try to change DToggle + break; + + case GRXSTS_PKTSTS_HOST_CHANNEL_HALTED: + // triggered when channel.hcchar_bm.disable is set + // TODO handle later + break; + + default: break; // ignore other status + } } -#endif /* Handle Host Port interrupt, possible source are: - Connection Detection @@ -548,25 +590,31 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) { uint32_t hcint = channel->hcint; hcint &= channel->hcintmsk; - xfer_result_t result = XFER_RESULT_FAILED; - if (hcint & HCINT_XFER_COMPLETE) { - result = XFER_RESULT_SUCCESS; - } - if (hcint & HCINT_STALL) { - result = XFER_RESULT_STALLED; - } - if (hcint & (HCINT_CHANNEL_HALTED | HCINT_AHB_ERR | HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR | - HCINT_BUFFER_NAK | HCINT_XCS_XACT_ERR | HCINT_DESC_ROLLOVER)) { - result = XFER_RESULT_FAILED; - } + if (hcint & HCINT_NAK) { + // NAK received, re-enable channel. Check if request queue is available + channel->hcchar |= HCCHAR_CHENA; + } else { + // transfer result interrupt + xfer_result_t result = XFER_RESULT_FAILED; + if (hcint & HCINT_XFER_COMPLETE) { + result = XFER_RESULT_SUCCESS; + } + if (hcint & HCINT_STALL) { + result = XFER_RESULT_STALLED; + } + if (hcint & (HCINT_CHANNEL_HALTED | HCINT_AHB_ERR | HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR | + HCINT_BUFFER_NAK | HCINT_XCS_XACT_ERR | HCINT_DESC_ROLLOVER)) { + result = XFER_RESULT_FAILED; + } - const uint8_t ep_addr = tu_edpt_addr(channel->hcchar_bm.ep_num, channel->hcchar_bm.ep_dir); - hcd_event_xfer_complete(channel->hcchar_bm.dev_addr, ep_addr, 0, result, in_isr); + const uint8_t ep_addr = tu_edpt_addr(channel->hcchar_bm.ep_num, channel->hcchar_bm.ep_dir); + hcd_event_xfer_complete(channel->hcchar_bm.dev_addr, ep_addr, 0, result, in_isr); - channel->hcint = hcint; // clear all interrupt flags + // de-allocate channel by clearing haintmsk + dwc2->haintmsk &= ~TU_BIT(ch_id); + } - // de-allocate channel by clearing haintmsk - dwc2->haintmsk &= ~TU_BIT(ch_id); + channel->hcint = hcint; // clear all interrupt flags } } } @@ -581,7 +629,7 @@ bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; const dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { - uint8_t pipe_id = find_opened_pipe(hcchar_bm.dev_addr, hcchar_bm.ep_num, TUSB_DIR_OUT); + uint8_t pipe_id = find_opened_pipe_by_channel(channel); if (pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX) { hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; if (pipe->pending_tx) { @@ -595,8 +643,8 @@ bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { break; } - dfifo_write_packet(dwc2, ch_id, pipe->buf, packet_bytes); - pipe->buf += packet_bytes; + dfifo_write_packet(dwc2, ch_id, pipe->buffer, packet_bytes); + pipe->buffer += packet_bytes; if (channel->hctsiz_bm.xfer_size == 0) { pipe->pending_tx = false; // all data has been written -- cgit v1.3.1 From dd99da9dcea3bd25fba44b3946f01476fc58a243 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 28 Oct 2024 17:45:44 +0700 Subject: implement hcd_edpt_abort_xfer, hcd_device_close, check request queue available before making usb attempt. Though there is no handling when queue is full. device_info example work well --- src/portable/synopsys/dwc2/dcd_dwc2.c | 20 +-- src/portable/synopsys/dwc2/dwc2_info.md | 4 +- src/portable/synopsys/dwc2/dwc2_info.py | 13 +- src/portable/synopsys/dwc2/dwc2_type.h | 38 ++++- src/portable/synopsys/dwc2/hcd_dwc2.c | 277 +++++++++++++++++++++----------- 5 files changed, 231 insertions(+), 121 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index a04f03b50..bcfdb546b 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -882,21 +882,11 @@ static void handle_epin_irq(uint8_t rhport) { /* Interrupt Hierarchy - DxEPINTn.XferCompl DxEPMSK.XferComplMsk - | | - +---------- AND --------+ - | - DAINT.xEPnInt DAINTMSK.xEPnMsk - | | - +---------- AND --------+ - | - GINTSTS.xEPInt GINTMSK.xEPIntMsk - | | - +---------- AND --------+ - | - GAHBCFG.GblIntrMsk - | - IRQn + DxEPINTn + | + DAINT.xEPn + | + GINTSTS: xEPInt Note: when OTG_MULTI_PROC_INTRPT = 1, Device Each endpoint interrupt deachint/deachmsk/diepeachmsk/doepeachmsk are combined to generate dedicated interrupt line for each endpoint. diff --git a/src/portable/synopsys/dwc2/dwc2_info.md b/src/portable/synopsys/dwc2/dwc2_info.md index 92293a072..78c2c50f8 100644 --- a/src/portable/synopsys/dwc2/dwc2_info.md +++ b/src/portable/synopsys/dwc2/dwc2_info.md @@ -16,8 +16,8 @@ | - enable_dynamic_fifo | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | | - mul_proc_intrpt | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | | - reserved21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | -| - nptx_q_depth | 2 | 2 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 0 | 2 | -| - ptx_q_depth | 2 | 2 | 2 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 0 | 2 | +| - nptx_q_depth | 8 | 8 | 4 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 | 8 | +| - ptx_q_depth | 8 | 8 | 8 | 4 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 2 | 8 | | - token_q_depth | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 0 | 8 | | - otg_enable_ic_usb | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | GHWCFG3 | 0x0FF000E8 | 0x01F204E8 | 0x00C804B5 | 0x03805EB5 | 0x020001E8 | 0x03F403E8 | 0x0200D1E8 | 0x0200D1E8 | 0x03EED2E8 | 0x03EED2E8 | 0x03B8D2E8 | 0x0200D1E8 | 0x03B882E8 | 0x00000000 | 0x027A01E5 | diff --git a/src/portable/synopsys/dwc2/dwc2_info.py b/src/portable/synopsys/dwc2/dwc2_info.py index fe08ecce9..5884cd455 100755 --- a/src/portable/synopsys/dwc2/dwc2_info.py +++ b/src/portable/synopsys/dwc2/dwc2_info.py @@ -133,7 +133,18 @@ GHWCFG2_field = { 1: "Dedicated", 2: "Shared UTMI+", 3: "Shared ULPI" - } + }, + 'nptx_q_depth': { + 0: "2", + 1: "4", + 2: "8", + }, + 'ptx_q_depth': { + 0: "2", + 1: "4", + 2: "8", + 3: "16" + }, } # mapping for specific fields in GHWCFG4 diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index 9aaa8f0bd..0d62c404c 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -160,6 +160,14 @@ enum { GRXSTS_PKTSTS_HOST_CHANNEL_HALTED = 7 }; +// Same as TUSB_XFER_* +enum { + HCCHAR_EPTYPE_CONTROL = 0, + HCCHAR_EPTYPE_ISOCHRONOUS = 1, + HCCHAR_EPTYPE_BULK = 2, + HCCHAR_EPTYPE_INTERRUPT = 3 +}; + //-------------------------------------------------------------------- // Common Register Bitfield //-------------------------------------------------------------------- @@ -355,15 +363,26 @@ TU_VERIFY_STATIC(sizeof(dwc2_ghwcfg4_t) == 4, "incorrect size"); //-------------------------------------------------------------------- typedef struct TU_ATTR_PACKED { - uint32_t fifo_available : 16; // 0..15 Number of words available in the Tx FIFO - uint32_t queue_available : 8; // 16..23 Number of spaces available in the NPT transmit request queue for both IN and OUT + uint32_t fifo_available : 16; // 0..15 Number of words available in the Tx FIFO + uint32_t req_queue_available : 8; // 16..23 Number of spaces available in the NPT transmit request queue for both IN and OU // 24..31 is top entry in the request queue that is currently being processed by the MAC - uint32_t qtop_terminate : 1; // 24 Last entry for selected channel - uint32_t qtop_token : 2; // 25..26 Token 0: In/Out 1: ZLP, 2: Ping/cspit, 3: Channel halt command - uint32_t qtop_ch_num : 4; // 27..30 Channel number + uint32_t qtop_terminate : 1; // 24 Last entry for selected channel + uint32_t qtop_type : 2; // 25..26 Token (0) In/Out (1) ZLP, (2) Ping/cspit, (3) Channel halt command + uint32_t qtop_ch_num : 4; // 27..30 Channel number } dwc2_hnptxsts_t; TU_VERIFY_STATIC(sizeof(dwc2_hnptxsts_t) == 4, "incorrect size"); +typedef struct TU_ATTR_PACKED { + uint32_t fifo_available : 16; // 0..15 Number of words available in the Tx FIFO + uint32_t req_queue_available : 7; // 16..22 Number of spaces available in the PTX transmit request queue + uint32_t qtop_terminate : 1; // 23 Last entry for selected channel + uint32_t qtop_last_period : 1; // 24 Last entry for selected channel is a periodic entry + uint32_t qtop_type : 2; // 25..26 Token (0) In/Out (1) ZLP, (2) Ping/cspit, (3) Channel halt command + uint32_t qtop_ch_num : 4; // 27..30 Channel number + uint32_t qtop_odd_frame : 1; // 31 Send in odd frame +} dwc2_hptxsts_t; +TU_VERIFY_STATIC(sizeof(dwc2_hptxsts_t) == 4, "incorrect size"); + typedef struct TU_ATTR_PACKED { uint32_t conn_status : 1; // 0 Port connect status uint32_t conn_detected : 1; // 1 Port connect detected @@ -571,7 +590,10 @@ typedef struct { volatile uint32_t hfir; // 404 Host Frame Interval volatile uint32_t hfnum; // 408 Host Frame Number / Frame Remaining uint32_t reserved40c; // 40C + union { volatile uint32_t hptxsts; // 410 Host Periodic TX FIFO / Queue Status + volatile dwc2_hptxsts_t hptxsts_bm; + }; volatile uint32_t haint; // 414 Host All Channels Interrupt volatile uint32_t haintmsk; // 418 Host All Channels Interrupt Mask volatile uint32_t hflbaddr; // 41C Host Frame List Base Address @@ -1854,9 +1876,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define HCINT_DATATOGGLE_ERR_Pos (10U) #define HCINT_DATATOGGLE_ERR_Msk (0x1UL << HCINT_DATATOGGLE_ERR_Pos) // 0x00000400 #define HCINT_DATATOGGLE_ERR HCINT_DATATOGGLE_ERR_Msk // Data toggle error -#define HCINT_BUFFER_NAK_Pos (11U) -#define HCINT_BUFFER_NAK_Msk (0x1UL << HCINT_BUFFER_NAK_Pos) // 0x00000800 -#define HCINT_BUFFER_NAK HCINT_BUFFER_NAK_Msk // Buffer not available interrupt +#define HCINT_BUFFER_NA_Pos (11U) +#define HCINT_BUFFER_NA_Msk (0x1UL << HCINT_BUFFER_NA_Pos) // 0x00000800 +#define HCINT_BUFFER_NA HCINT_BUFFER_NA_Msk // Buffer not available interrupt #define HCINT_XCS_XACT_ERR_Pos (12U) #define HCINT_XCS_XACT_ERR_Msk (0x1UL << HCINT_XCS_XACT_ERR_Pos) // 0x00001000 #define HCINT_XCS_XACT_ERR HCINT_XCS_XACT_ERR_Msk // Excessive transaction error diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 7d74d7a33..464ade9bf 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -34,16 +34,20 @@ #include "host/hcd.h" #include "dwc2_common.h" -// DWC2 has limit number of channel, in order to support all endpoints we can store channel char/split to swap later on +// Max number of endpoints application can open #ifndef CFG_TUH_DWC2_ENDPOINT_MAX -#define CFG_TUH_DWC2_ENDPOINT_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX + CFG_TUH_HUB) +#define CFG_TUH_DWC2_ENDPOINT_MAX 16 #endif +TU_VERIFY_STATIC(CFG_TUH_DWC2_ENDPOINT_MAX <= 255, "currently only use 8-bit for index"); + enum { HPRT_W1C_MASK = HPRT_CONN_DETECT | HPRT_ENABLE | HPRT_ENABLE_CHANGE | HPRT_OVER_CURRENT_CHANGE }; -// Host driver for each opened endpoint +// Host driver struct for each opened endpoint +// When making an usb transfer, we will find an inactive channel and use it by enabled haintmsk. When the transfer is +// complete, haintmsk will be cleared, and channel is deactivated/deallocated. typedef struct { union { uint32_t hcchar; @@ -57,14 +61,14 @@ typedef struct { uint8_t* buffer; uint16_t total_len; uint8_t next_data_toggle; - bool pending_tx; + bool pending_data; } hcd_pipe_t; typedef struct { hcd_pipe_t pipe[CFG_TUH_DWC2_ENDPOINT_MAX]; -} dwc2_hcd_t; +} hcd_data_t; -dwc2_hcd_t _hcd_data; +hcd_data_t _hcd_data; //-------------------------------------------------------------------- // @@ -75,7 +79,10 @@ TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t convert_hprt_speed(uint32_t hpr case HPRT_SPEED_HIGH: speed = TUSB_SPEED_HIGH; break; case HPRT_SPEED_FULL: speed = TUSB_SPEED_FULL; break; case HPRT_SPEED_LOW : speed = TUSB_SPEED_LOW ; break; - default: TU_BREAKPOINT(); break; + default: + speed = TUSB_SPEED_INVALID; + TU_BREAKPOINT(); + break; } return speed; } @@ -86,6 +93,71 @@ TU_ATTR_ALWAYS_INLINE static inline bool dma_host_enabled(const dwc2_regs_t* dwc return CFG_TUH_DWC2_DMA && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA; } +// Check if channel is periodic (interrupt/isochronous) +TU_ATTR_ALWAYS_INLINE static inline bool channel_is_periodic(dwc2_channel_char_t hcchar_bm) { + return hcchar_bm.ep_type == HCCHAR_EPTYPE_INTERRUPT || hcchar_bm.ep_type == HCCHAR_EPTYPE_ISOCHRONOUS; +} + +TU_ATTR_ALWAYS_INLINE static inline uint8_t request_queue_avail(const dwc2_regs_t* dwc2, bool is_period) { + if (is_period) { + return dwc2->hptxsts_bm.req_queue_available; + } else { + return dwc2->hnptxsts_bm.req_queue_available; + } +} + +// Find a free channel for new transfer +TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_free(dwc2_regs_t* dwc2) { + const uint8_t max_channel = tu_min8(dwc2->ghwcfg2_bm.num_host_ch, 16); + for (uint8_t ch_id=0; ch_idhaintmsk, ch_id)) { + return ch_id; + } + } + return TUSB_INDEX_INVALID_8; +} + +// Find currently enabled/active channel associated with a pipe +TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_enabled_by_pipe(dwc2_regs_t* dwc2, const hcd_pipe_t* pipe) { + const uint8_t max_channel = tu_min8(dwc2->ghwcfg2_bm.num_host_ch, 16); + for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { + if (tu_bit_test(dwc2->haintmsk, ch_id)) { + const dwc2_channel_char_t hcchar_bm = dwc2->channel[ch_id].hcchar_bm; + if (hcchar_bm.dev_addr == pipe->hcchar_bm.dev_addr && + hcchar_bm.ep_num == pipe->hcchar_bm.ep_num && + (hcchar_bm.ep_num == 0 || hcchar_bm.ep_dir == pipe->hcchar_bm.ep_dir)) { + return ch_id; + } + } + } + + return TUSB_INDEX_INVALID_8; +} + +// Find a pipe that is opened previously with hcd_edpt_open() +TU_ATTR_ALWAYS_INLINE static inline uint8_t pipe_find_opened(uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) { + for (uint8_t i = 0; i < (uint8_t)CFG_TUH_DWC2_ENDPOINT_MAX; i++) { + const dwc2_channel_char_t* hcchar_bm = &_hcd_data.pipe[i].hcchar_bm; + // find enabled pipe: note EP0 is bidirectional + if (hcchar_bm->enable && hcchar_bm->dev_addr == dev_addr && + hcchar_bm->ep_num == ep_num && (ep_num == 0 || hcchar_bm->ep_dir == ep_dir)) { + return i; + } + } + return TUSB_INDEX_INVALID_8; +} + +// Find a pipe that is opened previously with hcd_edpt_open() and associated with a channel +TU_ATTR_ALWAYS_INLINE static inline uint8_t pipe_find_opened_by_channel(const dwc2_channel_t* channel) { + const dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; + return pipe_find_opened(hcchar_bm.dev_addr, hcchar_bm.ep_num, hcchar_bm.ep_dir); +} + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + /* USB Data FIFO Layout The FIFO is split up into @@ -169,7 +241,7 @@ bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) { (void) cfg_id; (void) cfg_param; - return false; + return true; } // Initialize controller to host mode @@ -281,7 +353,12 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport) { // HCD closes all opened endpoints belong to this device void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { (void) rhport; - (void) dev_addr; + for (uint8_t i = 0; i < (uint8_t) CFG_TUH_DWC2_ENDPOINT_MAX; i++) { + hcd_pipe_t* pipe = &_hcd_data.pipe[i]; + if (pipe->hcchar_bm.enable && pipe->hcchar_bm.dev_addr == dev_addr) { + tu_memclr(pipe, sizeof(hcd_pipe_t)); + } + } } //--------------------------------------------------------------------+ @@ -308,7 +385,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const hcchar_bm->ep_num = tu_edpt_number(desc_ep->bEndpointAddress); hcchar_bm->ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress); hcchar_bm->low_speed_dev = (devtree_info.speed == TUSB_SPEED_LOW) ? 1 : 0; - hcchar_bm->ep_type = desc_ep->bmAttributes.xfer; + hcchar_bm->ep_type = desc_ep->bmAttributes.xfer; // ep_type matches TUSB_XFER_* hcchar_bm->err_multi_count = 0; hcchar_bm->dev_addr = dev_addr; hcchar_bm->odd_frame = 0; @@ -331,58 +408,18 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const return false; } -TU_ATTR_ALWAYS_INLINE static inline uint8_t find_free_channel(dwc2_regs_t* dwc2) { - const uint8_t max_channel = tu_min8(dwc2->ghwcfg2_bm.num_host_ch, 16); - for (uint8_t i=0; ihaintmsk, i)) { - return i; - } - } - return TUSB_INDEX_INVALID_8; -} - -TU_ATTR_ALWAYS_INLINE static inline uint8_t find_opened_pipe(uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) { - for (uint8_t i = 0; i < (uint8_t) CFG_TUH_DWC2_ENDPOINT_MAX; i++) { - const dwc2_channel_char_t* hcchar_bm = &_hcd_data.pipe[i].hcchar_bm; - // find enabled pipe: note EP0 is bidirectional - if (hcchar_bm->enable && hcchar_bm->dev_addr == dev_addr && - hcchar_bm->ep_num == ep_num && (ep_num == 0 || hcchar_bm->ep_dir == ep_dir)) { - return i; - } - } - return TUSB_INDEX_INVALID_8; -} - -TU_ATTR_ALWAYS_INLINE static inline uint8_t find_opened_pipe_by_channel(const dwc2_channel_t* channel) { - const dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; - return find_opened_pipe(hcchar_bm.dev_addr, hcchar_bm.ep_num, hcchar_bm.ep_dir); -} - -void schedule_out_packet(dwc2_regs_t* dwc2, uint8_t pipe_id, uint8_t ch_id) { - // To prevent conflict with other channel, we will enable periodic/non-periodic FIFO empty interrupt accordingly. - // And write packet in the interrupt handler - hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; - dwc2_channel_t* channel = &dwc2->channel[ch_id]; - (void) channel; - const uint8_t ep_type = pipe->hcchar_bm.ep_type; - const bool is_periodic = ep_type == TUSB_XFER_INTERRUPT || ep_type == TUSB_XFER_ISOCHRONOUS; - pipe->pending_tx = true; - dwc2->gintmsk |= (is_periodic ? GINTSTS_PTX_FIFO_EMPTY : GINTSTS_NPTX_FIFO_EMPTY); -} - // Submit a transfer, when complete hcd_event_xfer_complete() must be invoked bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); const uint8_t ep_num = tu_edpt_number(ep_addr); const uint8_t ep_dir = tu_edpt_dir(ep_addr); - uint8_t pipe_id = find_opened_pipe(dev_addr, ep_num, ep_dir); + uint8_t pipe_id = pipe_find_opened(dev_addr, ep_num, ep_dir); TU_ASSERT(pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX); // no opened pipe hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; dwc2_channel_char_t* hcchar_bm = &pipe->hcchar_bm; - uint8_t ch_id = find_free_channel(dwc2); + uint8_t ch_id = channel_find_free(dwc2); TU_ASSERT(ch_id < 16); // all channel are in use dwc2->haintmsk |= TU_BIT(ch_id); @@ -420,18 +457,24 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * if (dma_host_enabled(dwc2)) { channel->hcdma = (uint32_t) buffer; } else { - // enable channel for: + bool const is_period = channel_is_periodic(*hcchar_bm); + + // enable channel for slave mode: // - OUT endpoint: it will enable corresponding FIFO channel // - IN endpoint: it will write an IN request to the Non-periodic Request Queue, this will have dwc2 trying to send // IN Token. If we got NAK, we have to re-enable the channel again in the interrupt. Due to the way usbh stack only // call hcd_edpt_xfer() once, we will need to manage de-allocate/re-allocate IN channel dynamically. - channel->hcchar |= HCCHAR_CHENA; - if (ep_dir == TUSB_DIR_IN) { - + pipe->pending_data = true; + TU_ASSERT(request_queue_avail(dwc2, is_period)); + channel->hcchar |= HCCHAR_CHENA; } else { + channel->hcchar |= HCCHAR_CHENA; if (buflen > 0) { - schedule_out_packet(dwc2, pipe_id, ch_id); + // To prevent conflict with other channel, we will enable periodic/non-periodic FIFO empty interrupt accordingly. + // And write packet in the interrupt handler + pipe->pending_data = true; + dwc2->gintmsk |= (is_period ? GINTSTS_PTX_FIFO_EMPTY : GINTSTS_NPTX_FIFO_EMPTY); } } } @@ -442,16 +485,34 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * // Abort a queued transfer. Note: it can only abort transfer that has not been started // Return true if a queued transfer is aborted, false if there is no transfer to abort bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { - (void) rhport; - (void) dev_addr; - (void) ep_addr; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); - return false; + const uint8_t pipe_id = pipe_find_opened(dev_addr, tu_edpt_number(ep_addr), tu_edpt_dir(ep_addr)); + TU_VERIFY(pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX); + hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; + + hcd_int_disable(rhport); + + // Find enabled channeled and disable it, channel will be de-allocated in the interrupt handler + const uint8_t ch_id = channel_find_enabled_by_pipe(dwc2, pipe); + if (ch_id < 16) { + dwc2_channel_t* channel = &dwc2->channel[ch_id]; + // disable also require request queue + if (request_queue_avail(dwc2, channel_is_periodic(pipe->hcchar_bm))) { + channel->hcchar |= HCCHAR_CHDIS; + } else { + TU_BREAKPOINT(); + } + } + + hcd_int_enable(rhport); + + return true; } // Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet[8]) { - uint8_t pipe_id = find_opened_pipe(dev_addr, 0, TUSB_DIR_OUT); + uint8_t pipe_id = pipe_find_opened(dev_addr, 0, TUSB_DIR_OUT); TU_ASSERT(pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX); // no opened pipe hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; pipe->next_data_toggle = HCTSIZ_PID_SETUP; @@ -484,7 +545,7 @@ static void handle_rxflvl_irq(uint8_t rhport) { case GRXSTS_PKTSTS_HOST_IN_RECEIVED: { // In packet received const uint16_t byte_count = grxstsp_bm.byte_count; - const uint8_t pipe_id = find_opened_pipe_by_channel(channel); + const uint8_t pipe_id = pipe_find_opened_by_channel(channel); TU_VERIFY(pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX, ); hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; @@ -590,28 +651,53 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) { uint32_t hcint = channel->hcint; hcint &= channel->hcintmsk; + dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; + + xfer_result_t result = XFER_RESULT_INVALID; // invalid means transfer is not complete + + if (hcint & HCINT_XFER_COMPLETE) { + result = XFER_RESULT_SUCCESS; + } + if (hcint & HCINT_STALL) { + result = XFER_RESULT_STALLED; + } + // DMA related error: HCINT_BUFFER_NA | HCINT_DESC_ROLLOVER + // if (hcint & (HCINT_BUFFER_NA | HCINT_DESC_ROLLOVER)) { + // result = XFER_RESULT_FAILED; + // } + const uint32_t xact_err = hcint & (HCINT_AHB_ERR | HCINT_XACT_ERR | HCINT_BABBLE_ERR | + HCINT_DATATOGGLE_ERR | HCINT_XCS_XACT_ERR); + if (xact_err) { + result = XFER_RESULT_FAILED; + } + + if (!xact_err && (hcint & HCINT_CHANNEL_HALTED)) { + // Channel halted without error, this is a response to channel disable + result = XFER_RESULT_INVALID; + } + if (hcint & HCINT_NAK) { - // NAK received, re-enable channel. Check if request queue is available + // NAK received, re-enable channel if request queue is available + result = XFER_RESULT_INVALID; + TU_ASSERT(request_queue_avail(dwc2, channel_is_periodic(hcchar_bm)), ); channel->hcchar |= HCCHAR_CHENA; - } else { - // transfer result interrupt - xfer_result_t result = XFER_RESULT_FAILED; - if (hcint & HCINT_XFER_COMPLETE) { - result = XFER_RESULT_SUCCESS; - } - if (hcint & HCINT_STALL) { - result = XFER_RESULT_STALLED; + } + + // Transfer is complete (success, stalled, failed) or channel is disabled + if (result != XFER_RESULT_INVALID || (hcint & HCINT_CHANNEL_HALTED)) { + uint8_t pipe_id = pipe_find_opened_by_channel(channel); + if (pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX) { + hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; + pipe->pending_data = false; } - if (hcint & (HCINT_CHANNEL_HALTED | HCINT_AHB_ERR | HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR | - HCINT_BUFFER_NAK | HCINT_XCS_XACT_ERR | HCINT_DESC_ROLLOVER)) { - result = XFER_RESULT_FAILED; - } + const uint8_t ep_addr = tu_edpt_addr(hcchar_bm.ep_num, hcchar_bm.ep_dir); - const uint8_t ep_addr = tu_edpt_addr(channel->hcchar_bm.ep_num, channel->hcchar_bm.ep_dir); - hcd_event_xfer_complete(channel->hcchar_bm.dev_addr, ep_addr, 0, result, in_isr); + dwc2->haintmsk &= ~TU_BIT(ch_id); // de-allocate channel - // de-allocate channel by clearing haintmsk - dwc2->haintmsk &= ~TU_BIT(ch_id); + // notify usbh if transfer is complete (skip if channel is disabled) + if (result != XFER_RESULT_INVALID) { + hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, 0, result, in_isr); + } } channel->hcint = hcint; // clear all interrupt flags @@ -620,26 +706,28 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) { } bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { - bool ff_written = false; - volatile uint32_t* tx_sts = is_periodic ? &dwc2->hptxsts : &dwc2->hnptxsts; + // Use period txsts for both p/np to get request queue space available (1-bit difference, it is small enough) + volatile dwc2_hptxsts_t* txsts_bm = (volatile dwc2_hptxsts_t*) (is_periodic ? &dwc2->hptxsts : &dwc2->hnptxsts); // find which channel have pending packet + bool ff_written = false; for (uint8_t ch_id = 0; ch_id < 32; ch_id++) { if (tu_bit_test(dwc2->haintmsk, ch_id)) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; const dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { - uint8_t pipe_id = find_opened_pipe_by_channel(channel); + uint8_t pipe_id = pipe_find_opened_by_channel(channel); if (pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX) { hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; - if (pipe->pending_tx) { + if (pipe->pending_data) { const uint16_t remain_packets = channel->hctsiz_bm.packet_count; for (uint16_t i = 0; i < remain_packets; i++) { const uint16_t remain_bytes = channel->hctsiz_bm.xfer_size; const uint16_t packet_bytes = tu_min16(remain_bytes, hcchar_bm.ep_size); - // check if there is enough space in FIFO - if (packet_bytes > (*tx_sts & HPTXSTS_PTXQSAV)) { + // check if there is enough space in FIFO and request queue. + // the last dword written to FIFO will trigger dwc2 controller to write to RequestQueue + if ((packet_bytes > txsts_bm->fifo_available) && (txsts_bm->req_queue_available > 0)) { break; } @@ -647,7 +735,7 @@ bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { pipe->buffer += packet_bytes; if (channel->hctsiz_bm.xfer_size == 0) { - pipe->pending_tx = false; // all data has been written + pipe->pending_data = false; // all data has been written } ff_written = true; @@ -662,11 +750,11 @@ bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { } /* Interrupt Hierarchy - HCINTn HPRT - | | - HAINT.CHn | - | | - GINTSTS HCInt PrtInt NPTxFEmp PTxFEmpp RXFLVL + HCINTn HPRT + | | + HAINT.CHn | + | | + GINTSTS : HCInt PrtInt NPTxFEmp PTxFEmpp RXFLVL */ @@ -675,7 +763,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { const uint32_t int_mask = dwc2->gintmsk; const uint32_t int_status = dwc2->gintsts & int_mask; - TU_LOG1_HEX(int_status); + // TU_LOG1_HEX(int_status); if (int_status & GINTSTS_CONIDSTSCHNG) { // Connector ID status change @@ -689,13 +777,12 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { if (int_status & GINTSTS_HPRTINT) { // Host port interrupt: source is cleared in HPRT register - TU_LOG1_HEX(dwc2->hprt); + // TU_LOG1_HEX(dwc2->hprt); handle_hprt_irq(rhport, in_isr); } if (int_status & GINTSTS_HCINT) { // Host Channel interrupt: source is cleared in HCINT register - TU_LOG1_HEX(dwc2->hprt); handle_channel_irq(rhport, in_isr); } -- cgit v1.3.1 From f953b6bf9253814ea45a76739101d478ac3f9ad1 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 28 Oct 2024 18:40:21 +0700 Subject: minor rename --- examples/host/msc_file_explorer/only.txt | 1 + hw/bsp/stm32l4/family.cmake | 2 ++ src/portable/synopsys/dwc2/dwc2_type.h | 4 +-- src/portable/synopsys/dwc2/hcd_dwc2.c | 42 ++++++++++++++++---------------- 4 files changed, 26 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/examples/host/msc_file_explorer/only.txt b/examples/host/msc_file_explorer/only.txt index fee10f9e2..9b3d6994e 100644 --- a/examples/host/msc_file_explorer/only.txt +++ b/examples/host/msc_file_explorer/only.txt @@ -12,3 +12,4 @@ mcu:MSP432E4 mcu:RX65X mcu:RAXXX mcu:MAX3421 +mcu:STM32F7 diff --git a/hw/bsp/stm32l4/family.cmake b/hw/bsp/stm32l4/family.cmake index b6eae7c7f..77876cb8b 100644 --- a/hw/bsp/stm32l4/family.cmake +++ b/hw/bsp/stm32l4/family.cmake @@ -104,6 +104,8 @@ function(family_configure_example TARGET RTOS) family_add_tinyusb(${TARGET} OPT_MCU_${FAMILY_MCUS} ${RTOS}) target_sources(${TARGET}-tinyusb PUBLIC ${TOP}/src/portable/synopsys/dwc2/dcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/hcd_dwc2.c + ${TOP}/src/portable/synopsys/dwc2/dwc2_common.c ${TOP}/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c ) target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD}) diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index 0d62c404c..7296475cf 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -154,8 +154,8 @@ enum { }; enum { - GRXSTS_PKTSTS_HOST_IN_RECEIVED = 2, - GRXSTS_PKTSTS_HOST_IN_XFER_COMPL = 3, + GRXSTS_PKTSTS_RX_DATA = 2, + GRXSTS_PKTSTS_RX_COMPLETE = 3, GRXSTS_PKTSTS_HOST_DATATOGGLE_ERR = 5, GRXSTS_PKTSTS_HOST_CHANNEL_HALTED = 7 }; diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 464ade9bf..027a839bb 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -59,9 +59,9 @@ typedef struct { }; uint8_t* buffer; - uint16_t total_len; + uint16_t total_bytes; uint8_t next_data_toggle; - bool pending_data; + volatile bool pending_data; } hcd_pipe_t; typedef struct { @@ -70,6 +70,8 @@ typedef struct { hcd_data_t _hcd_data; +#define DWC2_CHANNEL_MAX(_dwc2) tu_min8((_dwc2)->ghwcfg2_bm.num_host_ch + 1, 16) + //-------------------------------------------------------------------- // //-------------------------------------------------------------------- @@ -108,8 +110,8 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t request_queue_avail(const dwc2_regs_ // Find a free channel for new transfer TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_free(dwc2_regs_t* dwc2) { - const uint8_t max_channel = tu_min8(dwc2->ghwcfg2_bm.num_host_ch, 16); - for (uint8_t ch_id=0; ch_idhaintmsk, ch_id)) { return ch_id; @@ -120,7 +122,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_free(dwc2_regs_t* dwc2) // Find currently enabled/active channel associated with a pipe TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_enabled_by_pipe(dwc2_regs_t* dwc2, const hcd_pipe_t* pipe) { - const uint8_t max_channel = tu_min8(dwc2->ghwcfg2_bm.num_host_ch, 16); + const uint8_t max_channel = DWC2_CHANNEL_MAX(dwc2); for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { if (tu_bit_test(dwc2->haintmsk, ch_id)) { const dwc2_channel_char_t hcchar_bm = dwc2->channel[ch_id].hcchar_bm; @@ -366,7 +368,6 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { //--------------------------------------------------------------------+ // Open an endpoint -// channel0 is reserved for dev0 control endpoint bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep) { (void) rhport; //dwc2_regs_t* dwc2 = DWC2_REG(rhport); @@ -452,7 +453,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * channel->hcchar = pipe->hcchar & ~HCCHAR_CHENA; // restore hcchar but don't enable yet pipe->buffer = buffer; - pipe->total_len = buflen; + pipe->total_bytes = buflen; if (dma_host_enabled(dwc2)) { channel->hcdma = (uint32_t) buffer; @@ -542,7 +543,7 @@ static void handle_rxflvl_irq(uint8_t rhport) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; switch (grxstsp_bm.packet_status) { - case GRXSTS_PKTSTS_HOST_IN_RECEIVED: { + case GRXSTS_PKTSTS_RX_DATA: { // In packet received const uint16_t byte_count = grxstsp_bm.byte_count; const uint8_t pipe_id = pipe_find_opened_by_channel(channel); @@ -554,13 +555,13 @@ static void handle_rxflvl_irq(uint8_t rhport) { // short packet, minus remaining bytes (xfer_size) if (byte_count < channel->hctsiz_bm.xfer_size) { - pipe->total_len -= channel->hctsiz_bm.xfer_size; + pipe->total_bytes -= channel->hctsiz_bm.xfer_size; } break; } - case GRXSTS_PKTSTS_HOST_IN_XFER_COMPL: + case GRXSTS_PKTSTS_RX_COMPLETE: // In transfer complete: After this entry is popped from the receive FIFO, dwc2 asserts a Transfer Completed // interrupt --> handle_channel_irq() break; @@ -645,14 +646,13 @@ TU_ATTR_ALWAYS_INLINE static inline void handle_hprt_irq(uint8_t rhport, bool in void handle_channel_irq(uint8_t rhport, bool in_isr) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - for(uint8_t ch_id=0; ch_id<32; ch_id++) { + for (uint8_t ch_id = 0; ch_id < 32; ch_id++) { if (tu_bit_test(dwc2->haint, ch_id)) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; uint32_t hcint = channel->hcint; hcint &= channel->hcintmsk; dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; - xfer_result_t result = XFER_RESULT_INVALID; // invalid means transfer is not complete if (hcint & HCINT_XFER_COMPLETE) { @@ -686,12 +686,11 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) { // Transfer is complete (success, stalled, failed) or channel is disabled if (result != XFER_RESULT_INVALID || (hcint & HCINT_CHANNEL_HALTED)) { uint8_t pipe_id = pipe_find_opened_by_channel(channel); - if (pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX) { - hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; - pipe->pending_data = false; - } + TU_ASSERT(pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX, ); + hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; const uint8_t ep_addr = tu_edpt_addr(hcchar_bm.ep_num, hcchar_bm.ep_dir); + pipe->pending_data = false; dwc2->haintmsk &= ~TU_BIT(ch_id); // de-allocate channel // notify usbh if transfer is complete (skip if channel is disabled) @@ -709,8 +708,9 @@ bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { // Use period txsts for both p/np to get request queue space available (1-bit difference, it is small enough) volatile dwc2_hptxsts_t* txsts_bm = (volatile dwc2_hptxsts_t*) (is_periodic ? &dwc2->hptxsts : &dwc2->hnptxsts); - // find which channel have pending packet + // find OUT channel with pending data bool ff_written = false; + //const uint8_t max_channel = DWC2_CHANNEL_MAX(dwc2); for (uint8_t ch_id = 0; ch_id < 32; ch_id++) { if (tu_bit_test(dwc2->haintmsk, ch_id)) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; @@ -723,16 +723,16 @@ bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { const uint16_t remain_packets = channel->hctsiz_bm.packet_count; for (uint16_t i = 0; i < remain_packets; i++) { const uint16_t remain_bytes = channel->hctsiz_bm.xfer_size; - const uint16_t packet_bytes = tu_min16(remain_bytes, hcchar_bm.ep_size); + const uint16_t xact_bytes = tu_min16(remain_bytes, hcchar_bm.ep_size); // check if there is enough space in FIFO and request queue. // the last dword written to FIFO will trigger dwc2 controller to write to RequestQueue - if ((packet_bytes > txsts_bm->fifo_available) && (txsts_bm->req_queue_available > 0)) { + if ((xact_bytes > txsts_bm->fifo_available) && (txsts_bm->req_queue_available > 0)) { break; } - dfifo_write_packet(dwc2, ch_id, pipe->buffer, packet_bytes); - pipe->buffer += packet_bytes; + dfifo_write_packet(dwc2, ch_id, pipe->buffer, xact_bytes); + pipe->buffer += xact_bytes; if (channel->hctsiz_bm.xfer_size == 0) { pipe->pending_data = false; // all data has been written -- cgit v1.3.1 From c93d3eda5f49c925855998f4e693dfcb2122228d Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 29 Oct 2024 13:01:48 +0700 Subject: restructure, add hcd endpoint, xfer to minimize footprint for managing xfer. --- src/portable/synopsys/dwc2/hcd_dwc2.c | 352 ++++++++++++++++++++-------------- 1 file changed, 203 insertions(+), 149 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 027a839bb..8dfc99692 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -34,20 +34,35 @@ #include "host/hcd.h" #include "dwc2_common.h" -// Max number of endpoints application can open +// Max number of endpoints application can open, can be larger than DWC2_CHANNEL_COUNT_MAX #ifndef CFG_TUH_DWC2_ENDPOINT_MAX #define CFG_TUH_DWC2_ENDPOINT_MAX 16 #endif +#define DWC2_CHANNEL_COUNT_MAX 16 // absolute max channel count +#define DWC2_CHANNEL_COUNT(_dwc2) tu_min8((_dwc2)->ghwcfg2_bm.num_host_ch + 1, DWC2_CHANNEL_COUNT_MAX) + TU_VERIFY_STATIC(CFG_TUH_DWC2_ENDPOINT_MAX <= 255, "currently only use 8-bit for index"); enum { HPRT_W1C_MASK = HPRT_CONN_DETECT | HPRT_ENABLE | HPRT_ENABLE_CHANGE | HPRT_OVER_CURRENT_CHANGE }; +enum { + HCD_XFER_ERROR_MAX = 3 +}; + +enum { + HCD_XFER_STATE_UNALLOCATED = 0, + HCD_XFER_STATE_ACTIVE = 1, + HCD_XFER_STATE_DISABLING = 2, +}; + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- + // Host driver struct for each opened endpoint -// When making an usb transfer, we will find an inactive channel and use it by enabled haintmsk. When the transfer is -// complete, haintmsk will be cleared, and channel is deactivated/deallocated. typedef struct { union { uint32_t hcchar; @@ -58,20 +73,26 @@ typedef struct { dwc2_channel_split_t hcsplt_bm; }; - uint8_t* buffer; - uint16_t total_bytes; uint8_t next_data_toggle; + // uint8_t resv[3]; +} hcd_endpoint_t; + +// Additional info for each channel when it is active +typedef struct { + volatile uint8_t state; volatile bool pending_data; -} hcd_pipe_t; + uint8_t err_count; + uint8_t* buffer; + uint16_t total_bytes; +} hcd_xfer_t; typedef struct { - hcd_pipe_t pipe[CFG_TUH_DWC2_ENDPOINT_MAX]; + hcd_xfer_t xfer[DWC2_CHANNEL_COUNT_MAX]; + hcd_endpoint_t edpt[CFG_TUH_DWC2_ENDPOINT_MAX]; } hcd_data_t; hcd_data_t _hcd_data; -#define DWC2_CHANNEL_MAX(_dwc2) tu_min8((_dwc2)->ghwcfg2_bm.num_host_ch + 1, 16) - //-------------------------------------------------------------------- // //-------------------------------------------------------------------- @@ -95,11 +116,6 @@ TU_ATTR_ALWAYS_INLINE static inline bool dma_host_enabled(const dwc2_regs_t* dwc return CFG_TUH_DWC2_DMA && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA; } -// Check if channel is periodic (interrupt/isochronous) -TU_ATTR_ALWAYS_INLINE static inline bool channel_is_periodic(dwc2_channel_char_t hcchar_bm) { - return hcchar_bm.ep_type == HCCHAR_EPTYPE_INTERRUPT || hcchar_bm.ep_type == HCCHAR_EPTYPE_ISOCHRONOUS; -} - TU_ATTR_ALWAYS_INLINE static inline uint8_t request_queue_avail(const dwc2_regs_t* dwc2, bool is_period) { if (is_period) { return dwc2->hptxsts_bm.req_queue_available; @@ -108,40 +124,49 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t request_queue_avail(const dwc2_regs_ } } +// Check if channel is periodic (interrupt/isochronous) +TU_ATTR_ALWAYS_INLINE static inline bool channel_is_periodic(dwc2_channel_char_t hcchar_bm) { + return hcchar_bm.ep_type == HCCHAR_EPTYPE_INTERRUPT || hcchar_bm.ep_type == HCCHAR_EPTYPE_ISOCHRONOUS; +} + // Find a free channel for new transfer -TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_free(dwc2_regs_t* dwc2) { - const uint8_t max_channel = DWC2_CHANNEL_MAX(dwc2); +TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) { + const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { - // haintmsk bit enabled means channel is currently in use - if (!tu_bit_test(dwc2->haintmsk, ch_id)) { + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + if (HCD_XFER_STATE_UNALLOCATED == xfer->state) { + tu_memclr(xfer, sizeof(hcd_xfer_t)); + xfer->state = HCD_XFER_STATE_ACTIVE; return ch_id; } } return TUSB_INDEX_INVALID_8; } -// Find currently enabled/active channel associated with a pipe -TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_enabled_by_pipe(dwc2_regs_t* dwc2, const hcd_pipe_t* pipe) { - const uint8_t max_channel = DWC2_CHANNEL_MAX(dwc2); +TU_ATTR_ALWAYS_INLINE static inline void channel_dealloc(dwc2_regs_t* dwc2, uint8_t ch_id) { + (void) dwc2; + _hcd_data.xfer[ch_id].state = HCD_XFER_STATE_UNALLOCATED; +} + +// Find currently enabled channel. Note: EP0 is bidirectional +TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_enabled(dwc2_regs_t* dwc2, uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) { + const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { - if (tu_bit_test(dwc2->haintmsk, ch_id)) { + if (_hcd_data.xfer[ch_id].state != HCD_XFER_STATE_UNALLOCATED) { const dwc2_channel_char_t hcchar_bm = dwc2->channel[ch_id].hcchar_bm; - if (hcchar_bm.dev_addr == pipe->hcchar_bm.dev_addr && - hcchar_bm.ep_num == pipe->hcchar_bm.ep_num && - (hcchar_bm.ep_num == 0 || hcchar_bm.ep_dir == pipe->hcchar_bm.ep_dir)) { + if (hcchar_bm.dev_addr == dev_addr && hcchar_bm.ep_num == ep_num && (ep_num == 0 || hcchar_bm.ep_dir == ep_dir)) { return ch_id; } } } - return TUSB_INDEX_INVALID_8; } -// Find a pipe that is opened previously with hcd_edpt_open() -TU_ATTR_ALWAYS_INLINE static inline uint8_t pipe_find_opened(uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) { +// Find a endpoint that is opened previously with hcd_edpt_open() +// Note: EP0 is bidirectional +TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_find_opened(uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) { for (uint8_t i = 0; i < (uint8_t)CFG_TUH_DWC2_ENDPOINT_MAX; i++) { - const dwc2_channel_char_t* hcchar_bm = &_hcd_data.pipe[i].hcchar_bm; - // find enabled pipe: note EP0 is bidirectional + const dwc2_channel_char_t* hcchar_bm = &_hcd_data.edpt[i].hcchar_bm; if (hcchar_bm->enable && hcchar_bm->dev_addr == dev_addr && hcchar_bm->ep_num == ep_num && (ep_num == 0 || hcchar_bm->ep_dir == ep_dir)) { return i; @@ -150,12 +175,6 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t pipe_find_opened(uint8_t dev_addr, u return TUSB_INDEX_INVALID_8; } -// Find a pipe that is opened previously with hcd_edpt_open() and associated with a channel -TU_ATTR_ALWAYS_INLINE static inline uint8_t pipe_find_opened_by_channel(const dwc2_channel_t* channel) { - const dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; - return pipe_find_opened(hcchar_bm.dev_addr, hcchar_bm.ep_num, hcchar_bm.ep_dir); -} - //-------------------------------------------------------------------- // //-------------------------------------------------------------------- @@ -356,9 +375,9 @@ tusb_speed_t hcd_port_speed_get(uint8_t rhport) { void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { (void) rhport; for (uint8_t i = 0; i < (uint8_t) CFG_TUH_DWC2_ENDPOINT_MAX; i++) { - hcd_pipe_t* pipe = &_hcd_data.pipe[i]; - if (pipe->hcchar_bm.enable && pipe->hcchar_bm.dev_addr == dev_addr) { - tu_memclr(pipe, sizeof(hcd_pipe_t)); + hcd_endpoint_t* edpt = &_hcd_data.edpt[i]; + if (edpt->hcchar_bm.enable && edpt->hcchar_bm.dev_addr == dev_addr) { + tu_memclr(edpt, sizeof(hcd_endpoint_t)); } } } @@ -368,18 +387,18 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { //--------------------------------------------------------------------+ // Open an endpoint -bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep) { +bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* desc_ep) { (void) rhport; //dwc2_regs_t* dwc2 = DWC2_REG(rhport); hcd_devtree_info_t devtree_info; hcd_devtree_get_info(dev_addr, &devtree_info); - // find a free pipe + // find a free endpoint for (uint32_t i = 0; i < CFG_TUH_DWC2_ENDPOINT_MAX; i++) { - hcd_pipe_t* pipe = &_hcd_data.pipe[i]; - dwc2_channel_char_t* hcchar_bm = &pipe->hcchar_bm; - dwc2_channel_split_t* hcsplt_bm = &pipe->hcsplt_bm; + hcd_endpoint_t* edpt = &_hcd_data.edpt[i]; + dwc2_channel_char_t* hcchar_bm = &edpt->hcchar_bm; + dwc2_channel_split_t* hcsplt_bm = &edpt->hcsplt_bm; if (hcchar_bm->enable == 0) { hcchar_bm->ep_size = tu_edpt_packet_size(desc_ep); @@ -400,7 +419,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const hcsplt_bm->split_compl = 0; hcsplt_bm->split_en = 0; - pipe->next_data_toggle = HCTSIZ_PID_DATA0; + edpt->next_data_toggle = HCTSIZ_PID_DATA0; return true; } @@ -412,48 +431,56 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const // Submit a transfer, when complete hcd_event_xfer_complete() must be invoked bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - const uint8_t ep_num = tu_edpt_number(ep_addr); const uint8_t ep_dir = tu_edpt_dir(ep_addr); - uint8_t pipe_id = pipe_find_opened(dev_addr, ep_num, ep_dir); - TU_ASSERT(pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX); // no opened pipe - hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; - dwc2_channel_char_t* hcchar_bm = &pipe->hcchar_bm; + uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir); + TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); + hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; + dwc2_channel_char_t* hcchar_bm = &edpt->hcchar_bm; + + uint8_t ch_id = channel_alloc(dwc2); + TU_ASSERT(ch_id < 16); // all channel are in used + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; - uint8_t ch_id = channel_find_free(dwc2); - TU_ASSERT(ch_id < 16); // all channel are in use dwc2->haintmsk |= TU_BIT(ch_id); dwc2_channel_t* channel = &dwc2->channel[ch_id]; - uint32_t hcintmsk = HCINT_XFER_COMPLETE | HCINT_CHANNEL_HALTED | HCINT_STALL | - HCINT_AHB_ERR | HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR; - if (ep_dir == TUSB_DIR_IN) { - hcintmsk |= HCINT_NAK; + + uint32_t hcintmsk = HCINT_NAK | HCINT_XACT_ERR | HCINT_STALL | HCINT_XFER_COMPLETE | HCINT_DATATOGGLE_ERR; + if (dma_host_enabled(dwc2)) { + TU_ASSERT(false); // not yet supported + } else { + if (hcchar_bm->ep_dir == TUSB_DIR_OUT) { + hcintmsk |= HCINT_NYET; + } else { + hcintmsk |= HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR; + } } + channel->hcintmsk = hcintmsk; uint16_t packet_count = tu_div_ceil(buflen, hcchar_bm->ep_size); if (packet_count == 0) { packet_count = 1; // zero length packet still count as 1 } - channel->hctsiz = (pipe->next_data_toggle << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | buflen; + channel->hctsiz = (edpt->next_data_toggle << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | buflen; // Control transfer always start with DATA1 for data and status stage. May has issue with ZLP - if (pipe->next_data_toggle == HCTSIZ_PID_DATA0 || ep_num == 0) { - pipe->next_data_toggle = HCTSIZ_PID_DATA1; + if (edpt->next_data_toggle == HCTSIZ_PID_DATA0 || ep_num == 0) { + edpt->next_data_toggle = HCTSIZ_PID_DATA1; } else { - pipe->next_data_toggle = HCTSIZ_PID_DATA0; + edpt->next_data_toggle = HCTSIZ_PID_DATA0; } // TODO support split transaction - channel->hcsplt = pipe->hcsplt; + channel->hcsplt = edpt->hcsplt; hcchar_bm->odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame hcchar_bm->ep_dir = ep_dir; // control endpoint can switch direction - channel->hcchar = pipe->hcchar & ~HCCHAR_CHENA; // restore hcchar but don't enable yet + channel->hcchar = edpt->hcchar & ~HCCHAR_CHENA; // restore hcchar but don't enable yet - pipe->buffer = buffer; - pipe->total_bytes = buflen; + xfer->buffer = buffer; + xfer->total_bytes = buflen; if (dma_host_enabled(dwc2)) { channel->hcdma = (uint32_t) buffer; @@ -466,15 +493,15 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * // IN Token. If we got NAK, we have to re-enable the channel again in the interrupt. Due to the way usbh stack only // call hcd_edpt_xfer() once, we will need to manage de-allocate/re-allocate IN channel dynamically. if (ep_dir == TUSB_DIR_IN) { - pipe->pending_data = true; + xfer->pending_data = true; TU_ASSERT(request_queue_avail(dwc2, is_period)); channel->hcchar |= HCCHAR_CHENA; } else { channel->hcchar |= HCCHAR_CHENA; if (buflen > 0) { - // To prevent conflict with other channel, we will enable periodic/non-periodic FIFO empty interrupt accordingly. + // To prevent conflict with other channel, we will enable periodic/non-periodic FIFO empty interrupt accordingly // And write packet in the interrupt handler - pipe->pending_data = true; + xfer->pending_data = true; dwc2->gintmsk |= (is_period ? GINTSTS_PTX_FIFO_EMPTY : GINTSTS_NPTX_FIFO_EMPTY); } } @@ -487,36 +514,37 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * // Return true if a queued transfer is aborted, false if there is no transfer to abort bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); + const uint8_t ep_num = tu_edpt_number(ep_addr); + const uint8_t ep_dir = tu_edpt_dir(ep_addr); + const uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir); + TU_VERIFY(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); + hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; - const uint8_t pipe_id = pipe_find_opened(dev_addr, tu_edpt_number(ep_addr), tu_edpt_dir(ep_addr)); - TU_VERIFY(pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX); - hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; - - hcd_int_disable(rhport); + // hcd_int_disable(rhport); // Find enabled channeled and disable it, channel will be de-allocated in the interrupt handler - const uint8_t ch_id = channel_find_enabled_by_pipe(dwc2, pipe); + const uint8_t ch_id = channel_find_enabled(dwc2, dev_addr, ep_num, ep_dir); if (ch_id < 16) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; // disable also require request queue - if (request_queue_avail(dwc2, channel_is_periodic(pipe->hcchar_bm))) { + if (request_queue_avail(dwc2, channel_is_periodic(edpt->hcchar_bm))) { channel->hcchar |= HCCHAR_CHDIS; } else { TU_BREAKPOINT(); } } - hcd_int_enable(rhport); + // hcd_int_enable(rhport); return true; } // Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet[8]) { - uint8_t pipe_id = pipe_find_opened(dev_addr, 0, TUSB_DIR_OUT); - TU_ASSERT(pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX); // no opened pipe - hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; - pipe->next_data_toggle = HCTSIZ_PID_SETUP; + uint8_t ep_id = edpt_find_opened(dev_addr, 0, TUSB_DIR_OUT); + TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); // no opened endpoint + hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; + edpt->next_data_toggle = HCTSIZ_PID_SETUP; return hcd_edpt_xfer(rhport, dev_addr, 0, (uint8_t*)(uintptr_t) setup_packet, 8); } @@ -546,16 +574,14 @@ static void handle_rxflvl_irq(uint8_t rhport) { case GRXSTS_PKTSTS_RX_DATA: { // In packet received const uint16_t byte_count = grxstsp_bm.byte_count; - const uint8_t pipe_id = pipe_find_opened_by_channel(channel); - TU_VERIFY(pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX, ); - hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; - dfifo_read_packet(dwc2, pipe->buffer, byte_count); - pipe->buffer += byte_count; + dfifo_read_packet(dwc2, xfer->buffer, byte_count); + xfer->buffer += byte_count; // short packet, minus remaining bytes (xfer_size) if (byte_count < channel->hctsiz_bm.xfer_size) { - pipe->total_bytes -= channel->hctsiz_bm.xfer_size; + xfer->total_bytes -= channel->hctsiz_bm.xfer_size; } break; @@ -644,58 +670,89 @@ TU_ATTR_ALWAYS_INLINE static inline void handle_hprt_irq(uint8_t rhport, bool in dwc2->hprt = hprt; // clear interrupt } +// DMA related error: HCINT_BUFFER_NA | HCINT_DESC_ROLLOVER +// if (hcint & (HCINT_BUFFER_NA | HCINT_DESC_ROLLOVER)) { +// result = XFER_RESULT_FAILED; +// } void handle_channel_irq(uint8_t rhport, bool in_isr) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - for (uint8_t ch_id = 0; ch_id < 32; ch_id++) { + const bool is_dma = dma_host_enabled(dwc2); + const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); + + for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { // if (tu_bit_test(dwc2->haint, ch_id)) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + uint32_t hcint = channel->hcint; hcint &= channel->hcintmsk; dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; - xfer_result_t result = XFER_RESULT_INVALID; // invalid means transfer is not complete + xfer_result_t result = XFER_RESULT_INVALID; // invalid means not DONE - if (hcint & HCINT_XFER_COMPLETE) { - result = XFER_RESULT_SUCCESS; - } - if (hcint & HCINT_STALL) { - result = XFER_RESULT_STALLED; - } - // DMA related error: HCINT_BUFFER_NA | HCINT_DESC_ROLLOVER - // if (hcint & (HCINT_BUFFER_NA | HCINT_DESC_ROLLOVER)) { - // result = XFER_RESULT_FAILED; - // } - const uint32_t xact_err = hcint & (HCINT_AHB_ERR | HCINT_XACT_ERR | HCINT_BABBLE_ERR | - HCINT_DATATOGGLE_ERR | HCINT_XCS_XACT_ERR); - if (xact_err) { - result = XFER_RESULT_FAILED; - } - - if (!xact_err && (hcint & HCINT_CHANNEL_HALTED)) { - // Channel halted without error, this is a response to channel disable - result = XFER_RESULT_INVALID; - } + if (is_dma) { - if (hcint & HCINT_NAK) { - // NAK received, re-enable channel if request queue is available - result = XFER_RESULT_INVALID; - TU_ASSERT(request_queue_avail(dwc2, channel_is_periodic(hcchar_bm)), ); - channel->hcchar |= HCCHAR_CHENA; - } + } else { + if (hcint & HCINT_XFER_COMPLETE) { + result = XFER_RESULT_SUCCESS; + channel->hcintmsk &= ~HCINT_ACK; + channel_dealloc(dwc2, ch_id); + } else if (hcint & HCINT_STALL) { + result = XFER_RESULT_STALLED; + channel->hcintmsk |= HCINT_CHANNEL_HALTED; + channel->hcchar_bm.disable = 1; + } else if (hcint & (HCINT_NAK | HCINT_XACT_ERR | HCINT_NYET)) { + if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { + // rewind buffer + } else { + if (hcint & HCINT_NAK) { + // NAK received, re-enable channel if request queue is available + TU_ASSERT(request_queue_avail(dwc2, channel_is_periodic(hcchar_bm)), ); + channel->hcchar |= HCCHAR_CHENA; + } + } - // Transfer is complete (success, stalled, failed) or channel is disabled - if (result != XFER_RESULT_INVALID || (hcint & HCINT_CHANNEL_HALTED)) { - uint8_t pipe_id = pipe_find_opened_by_channel(channel); - TU_ASSERT(pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX, ); - hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; - const uint8_t ep_addr = tu_edpt_addr(hcchar_bm.ep_num, hcchar_bm.ep_dir); + // unmask halted + // disable channel + if (hcint & HCINT_XACT_ERR) { + xfer->err_count++; + channel->hcintmsk |= HCINT_ACK; + }else { + xfer->err_count = 0; + } + } else if (hcint & HCINT_CHANNEL_HALTED) { + if (channel->hcchar_bm.err_multi_count == HCD_XFER_ERROR_MAX) { - pipe->pending_data = false; - dwc2->haintmsk &= ~TU_BIT(ch_id); // de-allocate channel + } else { + // Re-initialize Channel (Do ping protocol for HS) + } + } else if (hcint & HCINT_ACK) { + // ACK received, reset error count + xfer->err_count = 0; + channel->hcintmsk &= ~HCINT_ACK; + } - // notify usbh if transfer is complete (skip if channel is disabled) - if (result != XFER_RESULT_INVALID) { - hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, 0, result, in_isr); + // const uint32_t xact_err = hcint & (HCINT_AHB_ERR | HCINT_XACT_ERR | HCINT_BABBLE_ERR | + // HCINT_DATATOGGLE_ERR | HCINT_XCS_XACT_ERR); + // if (xact_err) { + // result = XFER_RESULT_FAILED; + // } + // if (!xact_err && (hcint & HCINT_CHANNEL_HALTED)) { + // // Channel halted without error, this is a response to channel disable + // result = XFER_RESULT_INVALID; + // } + + + // Transfer is complete (success, stalled, failed) or channel is disabled + if (result != XFER_RESULT_INVALID || (hcint & HCINT_CHANNEL_HALTED)) { + xfer->pending_data = false; + dwc2->haintmsk &= ~TU_BIT(ch_id); // de-allocate channel + + // notify usbh if transfer is complete (skip if channel is disabled) + const uint8_t ep_addr = tu_edpt_addr(hcchar_bm.ep_num, hcchar_bm.ep_dir); + if (result != XFER_RESULT_INVALID) { + hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, 0, result, in_isr); + } } } @@ -710,36 +767,33 @@ bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { // find OUT channel with pending data bool ff_written = false; - //const uint8_t max_channel = DWC2_CHANNEL_MAX(dwc2); - for (uint8_t ch_id = 0; ch_id < 32; ch_id++) { + //const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); + for (uint8_t ch_id = 0; ch_id < 32 /* 16 */ ; ch_id++) { if (tu_bit_test(dwc2->haintmsk, ch_id)) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; const dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { - uint8_t pipe_id = pipe_find_opened_by_channel(channel); - if (pipe_id < CFG_TUH_DWC2_ENDPOINT_MAX) { - hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; - if (pipe->pending_data) { - const uint16_t remain_packets = channel->hctsiz_bm.packet_count; - for (uint16_t i = 0; i < remain_packets; i++) { - const uint16_t remain_bytes = channel->hctsiz_bm.xfer_size; - const uint16_t xact_bytes = tu_min16(remain_bytes, hcchar_bm.ep_size); - - // check if there is enough space in FIFO and request queue. - // the last dword written to FIFO will trigger dwc2 controller to write to RequestQueue - if ((xact_bytes > txsts_bm->fifo_available) && (txsts_bm->req_queue_available > 0)) { - break; - } - - dfifo_write_packet(dwc2, ch_id, pipe->buffer, xact_bytes); - pipe->buffer += xact_bytes; - - if (channel->hctsiz_bm.xfer_size == 0) { - pipe->pending_data = false; // all data has been written - } - - ff_written = true; + if (xfer->pending_data) { + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + for (uint16_t i = 0; i < remain_packets; i++) { + const uint16_t remain_bytes = channel->hctsiz_bm.xfer_size; + const uint16_t xact_bytes = tu_min16(remain_bytes, hcchar_bm.ep_size); + + // check if there is enough space in FIFO and request queue. + // the last dword written to FIFO will trigger dwc2 controller to write to RequestQueue + if ((xact_bytes > txsts_bm->fifo_available) && (txsts_bm->req_queue_available > 0)) { + break; } + + dfifo_write_packet(dwc2, ch_id, xfer->buffer, xact_bytes); + xfer->buffer += xact_bytes; + + if (channel->hctsiz_bm.xfer_size == 0) { + xfer->pending_data = false; // all data has been written + } + + ff_written = true; } } } -- cgit v1.3.1 From 4797c4f508d183c949b5554ac4d32a7ea71d020c Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 29 Oct 2024 14:47:44 +0700 Subject: fix nptx fifo empty handling --- src/portable/synopsys/dwc2/hcd_dwc2.c | 63 +++++++++++++---------------------- 1 file changed, 24 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 8dfc99692..edbdce45c 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -80,7 +80,6 @@ typedef struct { // Additional info for each channel when it is active typedef struct { volatile uint8_t state; - volatile bool pending_data; uint8_t err_count; uint8_t* buffer; uint16_t total_bytes; @@ -441,9 +440,6 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * uint8_t ch_id = channel_alloc(dwc2); TU_ASSERT(ch_id < 16); // all channel are in used hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; - - dwc2->haintmsk |= TU_BIT(ch_id); - dwc2_channel_t* channel = &dwc2->channel[ch_id]; uint32_t hcintmsk = HCINT_NAK | HCINT_XACT_ERR | HCINT_STALL | HCINT_XFER_COMPLETE | HCINT_DATATOGGLE_ERR; @@ -457,8 +453,6 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * } } - channel->hcintmsk = hcintmsk; - uint16_t packet_count = tu_div_ceil(buflen, hcchar_bm->ep_size); if (packet_count == 0) { packet_count = 1; // zero length packet still count as 1 @@ -477,7 +471,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * hcchar_bm->odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame hcchar_bm->ep_dir = ep_dir; // control endpoint can switch direction - channel->hcchar = edpt->hcchar & ~HCCHAR_CHENA; // restore hcchar but don't enable yet + channel->hcchar = (edpt->hcchar & ~HCCHAR_CHENA); // restore hcchar but don't enable yet xfer->buffer = buffer; xfer->total_bytes = buflen; @@ -493,7 +487,6 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * // IN Token. If we got NAK, we have to re-enable the channel again in the interrupt. Due to the way usbh stack only // call hcd_edpt_xfer() once, we will need to manage de-allocate/re-allocate IN channel dynamically. if (ep_dir == TUSB_DIR_IN) { - xfer->pending_data = true; TU_ASSERT(request_queue_avail(dwc2, is_period)); channel->hcchar |= HCCHAR_CHENA; } else { @@ -501,12 +494,14 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * if (buflen > 0) { // To prevent conflict with other channel, we will enable periodic/non-periodic FIFO empty interrupt accordingly // And write packet in the interrupt handler - xfer->pending_data = true; dwc2->gintmsk |= (is_period ? GINTSTS_PTX_FIFO_EMPTY : GINTSTS_NPTX_FIFO_EMPTY); } } } + channel->hcintmsk = hcintmsk; + dwc2->haintmsk |= TU_BIT(ch_id); + return true; } @@ -745,7 +740,6 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) { // Transfer is complete (success, stalled, failed) or channel is disabled if (result != XFER_RESULT_INVALID || (hcint & HCINT_CHANNEL_HALTED)) { - xfer->pending_data = false; dwc2->haintmsk &= ~TU_BIT(ch_id); // de-allocate channel // notify usbh if transfer is complete (skip if channel is disabled) @@ -761,46 +755,37 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) { } } +// return true if there is still pending data and need more ISR bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { // Use period txsts for both p/np to get request queue space available (1-bit difference, it is small enough) volatile dwc2_hptxsts_t* txsts_bm = (volatile dwc2_hptxsts_t*) (is_periodic ? &dwc2->hptxsts : &dwc2->hnptxsts); - // find OUT channel with pending data - bool ff_written = false; - //const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); - for (uint8_t ch_id = 0; ch_id < 32 /* 16 */ ; ch_id++) { - if (tu_bit_test(dwc2->haintmsk, ch_id)) { + const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); + for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + if (xfer->state == HCD_XFER_STATE_ACTIVE) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; const dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; - hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { - if (xfer->pending_data) { - const uint16_t remain_packets = channel->hctsiz_bm.packet_count; - for (uint16_t i = 0; i < remain_packets; i++) { - const uint16_t remain_bytes = channel->hctsiz_bm.xfer_size; - const uint16_t xact_bytes = tu_min16(remain_bytes, hcchar_bm.ep_size); - - // check if there is enough space in FIFO and request queue. - // the last dword written to FIFO will trigger dwc2 controller to write to RequestQueue - if ((xact_bytes > txsts_bm->fifo_available) && (txsts_bm->req_queue_available > 0)) { - break; - } - - dfifo_write_packet(dwc2, ch_id, xfer->buffer, xact_bytes); - xfer->buffer += xact_bytes; - - if (channel->hctsiz_bm.xfer_size == 0) { - xfer->pending_data = false; // all data has been written - } - - ff_written = true; + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + for (uint16_t i = 0; i < remain_packets; i++) { + const uint16_t remain_bytes = channel->hctsiz_bm.xfer_size; + const uint16_t xact_bytes = tu_min16(remain_bytes, hcchar_bm.ep_size); + + // check if there is enough space in FIFO and RequestQueue. + // Packet's last word written to FIFO will trigger a request queue + if ((xact_bytes > txsts_bm->fifo_available) && (txsts_bm->req_queue_available > 0)) { + return true; } + + dfifo_write_packet(dwc2, ch_id, xfer->buffer, xact_bytes); + xfer->buffer += xact_bytes; } } } } - return ff_written; + return false; // all data written } /* Interrupt Hierarchy @@ -842,8 +827,8 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { if (int_status & GINTSTS_NPTX_FIFO_EMPTY) { // NPTX FIFO empty interrupt, this is read-only and cleared by hardware when FIFO is written - const bool ff_written = handle_txfifo_empty(dwc2, false); - if (!ff_written) { + const bool more_isr = handle_txfifo_empty(dwc2, false); + if (!more_isr) { // no more pending packet, disable interrupt dwc2->gintmsk &= ~GINTSTS_NPTX_FIFO_EMPTY; } -- cgit v1.3.1 From cef9bab4b54ec038636738cf0969800ac7dd19fe Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 29 Oct 2024 15:07:28 +0700 Subject: minor clean up --- src/portable/synopsys/dwc2/hcd_dwc2.c | 70 +++++++++++++++++------------------ 1 file changed, 33 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index edbdce45c..ce5d34cb7 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -123,11 +123,6 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t request_queue_avail(const dwc2_regs_ } } -// Check if channel is periodic (interrupt/isochronous) -TU_ATTR_ALWAYS_INLINE static inline bool channel_is_periodic(dwc2_channel_char_t hcchar_bm) { - return hcchar_bm.ep_type == HCCHAR_EPTYPE_INTERRUPT || hcchar_bm.ep_type == HCCHAR_EPTYPE_ISOCHRONOUS; -} - // Find a free channel for new transfer TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) { const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); @@ -161,6 +156,11 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_enabled(dwc2_regs_t* dw return TUSB_INDEX_INVALID_8; } +// Check if is periodic (interrupt/isochronous) +TU_ATTR_ALWAYS_INLINE static inline bool edpt_is_periodic(uint8_t ep_type) { + return ep_type == HCCHAR_EPTYPE_INTERRUPT || ep_type == HCCHAR_EPTYPE_ISOCHRONOUS; +} + // Find a endpoint that is opened previously with hcd_edpt_open() // Note: EP0 is bidirectional TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_find_opened(uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) { @@ -400,25 +400,26 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* dwc2_channel_split_t* hcsplt_bm = &edpt->hcsplt_bm; if (hcchar_bm->enable == 0) { - hcchar_bm->ep_size = tu_edpt_packet_size(desc_ep); - hcchar_bm->ep_num = tu_edpt_number(desc_ep->bEndpointAddress); - hcchar_bm->ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress); - hcchar_bm->low_speed_dev = (devtree_info.speed == TUSB_SPEED_LOW) ? 1 : 0; - hcchar_bm->ep_type = desc_ep->bmAttributes.xfer; // ep_type matches TUSB_XFER_* + hcchar_bm->ep_size = tu_edpt_packet_size(desc_ep); + hcchar_bm->ep_num = tu_edpt_number(desc_ep->bEndpointAddress); + hcchar_bm->ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress); + hcchar_bm->low_speed_dev = (devtree_info.speed == TUSB_SPEED_LOW) ? 1 : 0; + hcchar_bm->ep_type = desc_ep->bmAttributes.xfer; // ep_type matches TUSB_XFER_* hcchar_bm->err_multi_count = 0; - hcchar_bm->dev_addr = dev_addr; - hcchar_bm->odd_frame = 0; - hcchar_bm->disable = 0; - hcchar_bm->enable = 1; + hcchar_bm->dev_addr = dev_addr; + hcchar_bm->odd_frame = 0; + hcchar_bm->disable = 0; + hcchar_bm->enable = 1; - hcsplt_bm->hub_port = devtree_info.hub_port; - hcsplt_bm->hub_addr = devtree_info.hub_addr; - // TODO not support split transaction yet - hcsplt_bm->xact_pos = 0; - hcsplt_bm->split_compl = 0; - hcsplt_bm->split_en = 0; + hcsplt_bm->hub_port = devtree_info.hub_port; + hcsplt_bm->hub_addr = devtree_info.hub_addr; + hcsplt_bm->xact_pos = 0; + hcsplt_bm->split_compl = 0; + hcsplt_bm->split_en = 0; + + edpt->next_data_toggle = HCTSIZ_PID_DATA0; - edpt->next_data_toggle = HCTSIZ_PID_DATA0; + // TODO not support split transaction yet return true; } @@ -442,17 +443,6 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; - uint32_t hcintmsk = HCINT_NAK | HCINT_XACT_ERR | HCINT_STALL | HCINT_XFER_COMPLETE | HCINT_DATATOGGLE_ERR; - if (dma_host_enabled(dwc2)) { - TU_ASSERT(false); // not yet supported - } else { - if (hcchar_bm->ep_dir == TUSB_DIR_OUT) { - hcintmsk |= HCINT_NYET; - } else { - hcintmsk |= HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR; - } - } - uint16_t packet_count = tu_div_ceil(buflen, hcchar_bm->ep_size); if (packet_count == 0) { packet_count = 1; // zero length packet still count as 1 @@ -476,19 +466,23 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * xfer->buffer = buffer; xfer->total_bytes = buflen; + uint32_t hcintmsk = HCINT_NAK | HCINT_XACT_ERR | HCINT_STALL | HCINT_XFER_COMPLETE | HCINT_DATATOGGLE_ERR; if (dma_host_enabled(dwc2)) { channel->hcdma = (uint32_t) buffer; + TU_ASSERT(false); // not yet supported } else { - bool const is_period = channel_is_periodic(*hcchar_bm); + bool const is_period = edpt_is_periodic(hcchar_bm->ep_type); // enable channel for slave mode: - // - OUT endpoint: it will enable corresponding FIFO channel - // - IN endpoint: it will write an IN request to the Non-periodic Request Queue, this will have dwc2 trying to send + // - OUT: it will enable corresponding FIFO channel + // - IN : it will write an IN request to the Non-periodic Request Queue, this will have dwc2 trying to send // IN Token. If we got NAK, we have to re-enable the channel again in the interrupt. Due to the way usbh stack only // call hcd_edpt_xfer() once, we will need to manage de-allocate/re-allocate IN channel dynamically. if (ep_dir == TUSB_DIR_IN) { TU_ASSERT(request_queue_avail(dwc2, is_period)); channel->hcchar |= HCCHAR_CHENA; + + hcintmsk |= HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR; } else { channel->hcchar |= HCCHAR_CHENA; if (buflen > 0) { @@ -496,6 +490,8 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * // And write packet in the interrupt handler dwc2->gintmsk |= (is_period ? GINTSTS_PTX_FIFO_EMPTY : GINTSTS_NPTX_FIFO_EMPTY); } + + hcintmsk |= HCINT_NYET; } } @@ -522,7 +518,7 @@ bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { if (ch_id < 16) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; // disable also require request queue - if (request_queue_avail(dwc2, channel_is_periodic(edpt->hcchar_bm))) { + if (request_queue_avail(dwc2, edpt_is_periodic(edpt->hcchar_bm.ep_type))) { channel->hcchar |= HCCHAR_CHDIS; } else { TU_BREAKPOINT(); @@ -702,7 +698,7 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) { } else { if (hcint & HCINT_NAK) { // NAK received, re-enable channel if request queue is available - TU_ASSERT(request_queue_avail(dwc2, channel_is_periodic(hcchar_bm)), ); + TU_ASSERT(request_queue_avail(dwc2, edpt_is_periodic(hcchar_bm.ep_type)), ); channel->hcchar |= HCCHAR_CHENA; } } -- cgit v1.3.1 From 1e164412bf791cb682c386f9d1e8e3b806faf72a Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 29 Oct 2024 16:06:44 +0700 Subject: split handle channel slave out/in --- src/portable/synopsys/dwc2/dwc2_type.h | 6 +- src/portable/synopsys/dwc2/hcd_dwc2.c | 154 ++++++++++++++++++++------------- 2 files changed, 95 insertions(+), 65 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index 7296475cf..d2ab1e94d 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -1846,9 +1846,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define HCINT_XFER_COMPLETE_Pos (0U) #define HCINT_XFER_COMPLETE_Msk (0x1UL << HCINT_XFER_COMPLETE_Pos) // 0x00000001 #define HCINT_XFER_COMPLETE HCINT_XFER_COMPLETE_Msk // Transfer completed -#define HCINT_CHANNEL_HALTED_Pos (1U) -#define HCINT_CHANNEL_HALTED_Msk (0x1UL << HCINT_CHANNEL_HALTED_Pos) // 0x00000002 -#define HCINT_CHANNEL_HALTED HCINT_CHANNEL_HALTED_Msk // Channel halted +#define HCINT_HALTED_Pos (1U) +#define HCINT_HALTED_Msk (0x1UL << HCINT_HALTED_Pos) // 0x00000002 +#define HCINT_HALTED HCINT_HALTED_Msk // Channel halted #define HCINT_AHB_ERR_Pos (2U) #define HCINT_AHB_ERR_Msk (0x1UL << HCINT_AHB_ERR_Pos) // 0x00000004 #define HCINT_AHB_ERR HCINT_AHB_ERR_Msk // AHB error diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index ce5d34cb7..568ce0c1f 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -55,7 +55,7 @@ enum { enum { HCD_XFER_STATE_UNALLOCATED = 0, HCD_XFER_STATE_ACTIVE = 1, - HCD_XFER_STATE_DISABLING = 2, + HCD_XFER_STATE_DONE = 2, }; //-------------------------------------------------------------------- @@ -138,8 +138,16 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) { } TU_ATTR_ALWAYS_INLINE static inline void channel_dealloc(dwc2_regs_t* dwc2, uint8_t ch_id) { - (void) dwc2; _hcd_data.xfer[ch_id].state = HCD_XFER_STATE_UNALLOCATED; + dwc2->haintmsk &= ~TU_BIT(ch_id); +} + +TU_ATTR_ALWAYS_INLINE static inline void channel_disable(dwc2_channel_t* channel) { + channel->hcintmsk |= HCINT_HALTED; + + uint32_t hcchar = channel->hcchar & ~HCCHAR_CHENA; // skip w1s enabled bit + hcchar |= HCCHAR_CHDIS; + channel->hcchar = hcchar; } // Find currently enabled channel. Note: EP0 is bidirectional @@ -661,10 +669,79 @@ TU_ATTR_ALWAYS_INLINE static inline void handle_hprt_irq(uint8_t rhport, bool in dwc2->hprt = hprt; // clear interrupt } -// DMA related error: HCINT_BUFFER_NA | HCINT_DESC_ROLLOVER -// if (hcint & (HCINT_BUFFER_NA | HCINT_DESC_ROLLOVER)) { -// result = XFER_RESULT_FAILED; -// } +xfer_result_t handle_channel_slave_in(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + dwc2_channel_t* channel = &dwc2->channel[ch_id]; + xfer_result_t result = XFER_RESULT_INVALID; + + if (hcint & HCINT_XFER_COMPLETE) { + result = XFER_RESULT_SUCCESS; + channel_disable(channel); + channel->hcintmsk &= ~HCINT_ACK; + } else if (hcint & (HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_STALL)) { + channel_disable(channel); + if (hcint & HCINT_XACT_ERR) { + xfer->err_count++; + channel->hcintmsk |= HCINT_ACK; + } + } else if (hcint & HCINT_HALTED) { + channel->hcintmsk &= ~HCINT_HALTED; + if (channel->hcchar_bm.err_multi_count == HCD_XFER_ERROR_MAX) { + channel_dealloc(dwc2, ch_id); + } else { + // Re-initialize Channel + } + } else if (hcint & HCINT_ACK) { + xfer->err_count = 0; + channel->hcintmsk &= ~HCINT_ACK; + } else if (hcint & HCINT_DATATOGGLE_ERR) { + xfer->err_count = 0; + } else if (hcint & HCINT_NAK) { + // NAK received, re-enable channel if request queue is available + TU_ASSERT(request_queue_avail(dwc2, is_period), XFER_RESULT_INVALID); + channel->hcchar |= HCCHAR_CHENA; + } + + return result; +} + +xfer_result_t handle_channel_slave_out(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { + (void) is_period; + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + dwc2_channel_t* channel = &dwc2->channel[ch_id]; + xfer_result_t result = XFER_RESULT_INVALID; + + if (hcint & HCINT_XFER_COMPLETE) { + result = XFER_RESULT_SUCCESS; + channel->hcintmsk &= ~HCINT_ACK; + channel_dealloc(dwc2, ch_id); + } else if (hcint & HCINT_STALL) { + xfer->state = HCD_XFER_STATE_DONE; + channel_disable(channel); + } else if (hcint & (HCINT_NAK | HCINT_XACT_ERR | HCINT_NYET)) { + channel_disable(channel); + if (hcint & HCINT_XACT_ERR) { + xfer->err_count++; + channel->hcintmsk |= HCINT_ACK; + } else { + xfer->err_count = 0; + } + } else if (hcint & HCINT_HALTED) { + channel->hcintmsk &= ~HCINT_HALTED; + if (xfer->state == HCD_XFER_STATE_DONE || channel->hcchar_bm.err_multi_count == HCD_XFER_ERROR_MAX) { + result = (xfer->state == HCD_XFER_STATE_DONE ? XFER_RESULT_STALLED : XFER_RESULT_FAILED); + channel_dealloc(dwc2, ch_id); + } else { + // Re-initialize Channel (Do ping protocol for HS) + } + } else if (hcint & HCINT_ACK) { + xfer->err_count = 0; + channel->hcintmsk &= ~HCINT_ACK; + } + + return result; +} + void handle_channel_irq(uint8_t rhport, bool in_isr) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); const bool is_dma = dma_host_enabled(dwc2); @@ -674,75 +751,28 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) { if (tu_bit_test(dwc2->haint, ch_id)) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; + const bool is_period = edpt_is_periodic(hcchar_bm.ep_type); uint32_t hcint = channel->hcint; hcint &= channel->hcintmsk; - dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; - xfer_result_t result = XFER_RESULT_INVALID; // invalid means not DONE - if (is_dma) { } else { - if (hcint & HCINT_XFER_COMPLETE) { - result = XFER_RESULT_SUCCESS; - channel->hcintmsk &= ~HCINT_ACK; - channel_dealloc(dwc2, ch_id); - } else if (hcint & HCINT_STALL) { - result = XFER_RESULT_STALLED; - channel->hcintmsk |= HCINT_CHANNEL_HALTED; - channel->hcchar_bm.disable = 1; - } else if (hcint & (HCINT_NAK | HCINT_XACT_ERR | HCINT_NYET)) { - if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { - // rewind buffer - } else { - if (hcint & HCINT_NAK) { - // NAK received, re-enable channel if request queue is available - TU_ASSERT(request_queue_avail(dwc2, edpt_is_periodic(hcchar_bm.ep_type)), ); - channel->hcchar |= HCCHAR_CHENA; - } - } + xfer_result_t result; - // unmask halted - // disable channel - if (hcint & HCINT_XACT_ERR) { - xfer->err_count++; - channel->hcintmsk |= HCINT_ACK; - }else { - xfer->err_count = 0; - } - } else if (hcint & HCINT_CHANNEL_HALTED) { - if (channel->hcchar_bm.err_multi_count == HCD_XFER_ERROR_MAX) { - - } else { - // Re-initialize Channel (Do ping protocol for HS) - } - } else if (hcint & HCINT_ACK) { - // ACK received, reset error count - xfer->err_count = 0; - channel->hcintmsk &= ~HCINT_ACK; + if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { + result = handle_channel_slave_out(dwc2, ch_id, is_period, hcint); + } else { + result = handle_channel_slave_in(dwc2, ch_id, is_period, hcint); } - // const uint32_t xact_err = hcint & (HCINT_AHB_ERR | HCINT_XACT_ERR | HCINT_BABBLE_ERR | - // HCINT_DATATOGGLE_ERR | HCINT_XCS_XACT_ERR); - // if (xact_err) { - // result = XFER_RESULT_FAILED; - // } - // if (!xact_err && (hcint & HCINT_CHANNEL_HALTED)) { - // // Channel halted without error, this is a response to channel disable - // result = XFER_RESULT_INVALID; - // } - - // Transfer is complete (success, stalled, failed) or channel is disabled - if (result != XFER_RESULT_INVALID || (hcint & HCINT_CHANNEL_HALTED)) { - dwc2->haintmsk &= ~TU_BIT(ch_id); // de-allocate channel - + if (result != XFER_RESULT_INVALID ) { // notify usbh if transfer is complete (skip if channel is disabled) const uint8_t ep_addr = tu_edpt_addr(hcchar_bm.ep_num, hcchar_bm.ep_dir); - if (result != XFER_RESULT_INVALID) { - hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, 0, result, in_isr); - } + hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, xfer->total_bytes, result, in_isr); } } -- cgit v1.3.1 From df55d587dfe8b24d72eb20cd027cd54821d0bf09 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 29 Oct 2024 18:23:56 +0700 Subject: update handle in/out, separate allocated and xfer result to make it easier to manage. Fix channel disable/deallocated. --- src/common/tusb_types.h | 8 +- src/portable/synopsys/dwc2/hcd_dwc2.c | 191 ++++++++++++++++++---------------- 2 files changed, 103 insertions(+), 96 deletions(-) (limited to 'src') diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index 3e5aa6818..92cebc42b 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -224,10 +224,10 @@ enum { // USB 2.0 Spec Table 9-7: Test Mode Selectors typedef enum { TUSB_FEATURE_TEST_J = 1, - TUSB_FEATURE_TEST_K, - TUSB_FEATURE_TEST_SE0_NAK, - TUSB_FEATURE_TEST_PACKET, - TUSB_FEATURE_TEST_FORCE_ENABLE, + TUSB_FEATURE_TEST_K = 2, + TUSB_FEATURE_TEST_SE0_NAK = 3, + TUSB_FEATURE_TEST_PACKET = 4, + TUSB_FEATURE_TEST_FORCE_ENABLE = 5, } tusb_feature_test_mode_t; //--------------------------------------------------------------------+ diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 568ce0c1f..5d843537b 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -52,12 +52,6 @@ enum { HCD_XFER_ERROR_MAX = 3 }; -enum { - HCD_XFER_STATE_UNALLOCATED = 0, - HCD_XFER_STATE_ACTIVE = 1, - HCD_XFER_STATE_DONE = 2, -}; - //-------------------------------------------------------------------- // //-------------------------------------------------------------------- @@ -79,7 +73,8 @@ typedef struct { // Additional info for each channel when it is active typedef struct { - volatile uint8_t state; + volatile bool allocated; + uint8_t result; uint8_t err_count; uint8_t* buffer; uint16_t total_bytes; @@ -128,9 +123,9 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) { const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; - if (HCD_XFER_STATE_UNALLOCATED == xfer->state) { + if (!xfer->allocated) { tu_memclr(xfer, sizeof(hcd_xfer_t)); - xfer->state = HCD_XFER_STATE_ACTIVE; + xfer->allocated = true; return ch_id; } } @@ -138,23 +133,20 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) { } TU_ATTR_ALWAYS_INLINE static inline void channel_dealloc(dwc2_regs_t* dwc2, uint8_t ch_id) { - _hcd_data.xfer[ch_id].state = HCD_XFER_STATE_UNALLOCATED; + _hcd_data.xfer[ch_id].allocated = false; dwc2->haintmsk &= ~TU_BIT(ch_id); } TU_ATTR_ALWAYS_INLINE static inline void channel_disable(dwc2_channel_t* channel) { channel->hcintmsk |= HCINT_HALTED; - - uint32_t hcchar = channel->hcchar & ~HCCHAR_CHENA; // skip w1s enabled bit - hcchar |= HCCHAR_CHDIS; - channel->hcchar = hcchar; + channel->hcchar |= HCCHAR_CHDIS | HCCHAR_CHENA; // must set both CHDIS and CHENA } // Find currently enabled channel. Note: EP0 is bidirectional TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_enabled(dwc2_regs_t* dwc2, uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) { const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { - if (_hcd_data.xfer[ch_id].state != HCD_XFER_STATE_UNALLOCATED) { + if (_hcd_data.xfer[ch_id].allocated) { const dwc2_channel_char_t hcchar_bm = dwc2->channel[ch_id].hcchar_bm; if (hcchar_bm.dev_addr == dev_addr && hcchar_bm.ep_num == ep_num && (ep_num == 0 || hcchar_bm.ep_dir == ep_dir)) { return ch_id; @@ -169,6 +161,18 @@ TU_ATTR_ALWAYS_INLINE static inline bool edpt_is_periodic(uint8_t ep_type) { return ep_type == HCCHAR_EPTYPE_INTERRUPT || ep_type == HCCHAR_EPTYPE_ISOCHRONOUS; } +// Allocate a new endpoint +TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_alloc(void) { + for (uint32_t i = 0; i < CFG_TUH_DWC2_ENDPOINT_MAX; i++) { + hcd_endpoint_t* edpt = &_hcd_data.edpt[i]; + if (edpt->hcchar_bm.enable == 0) { + edpt->hcchar_bm.enable = 1; + return i; + } + } + return TUSB_INDEX_INVALID_8; +} + // Find a endpoint that is opened previously with hcd_edpt_open() // Note: EP0 is bidirectional TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_find_opened(uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) { @@ -402,38 +406,32 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* hcd_devtree_get_info(dev_addr, &devtree_info); // find a free endpoint - for (uint32_t i = 0; i < CFG_TUH_DWC2_ENDPOINT_MAX; i++) { - hcd_endpoint_t* edpt = &_hcd_data.edpt[i]; - dwc2_channel_char_t* hcchar_bm = &edpt->hcchar_bm; - dwc2_channel_split_t* hcsplt_bm = &edpt->hcsplt_bm; - - if (hcchar_bm->enable == 0) { - hcchar_bm->ep_size = tu_edpt_packet_size(desc_ep); - hcchar_bm->ep_num = tu_edpt_number(desc_ep->bEndpointAddress); - hcchar_bm->ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress); - hcchar_bm->low_speed_dev = (devtree_info.speed == TUSB_SPEED_LOW) ? 1 : 0; - hcchar_bm->ep_type = desc_ep->bmAttributes.xfer; // ep_type matches TUSB_XFER_* - hcchar_bm->err_multi_count = 0; - hcchar_bm->dev_addr = dev_addr; - hcchar_bm->odd_frame = 0; - hcchar_bm->disable = 0; - hcchar_bm->enable = 1; - - hcsplt_bm->hub_port = devtree_info.hub_port; - hcsplt_bm->hub_addr = devtree_info.hub_addr; - hcsplt_bm->xact_pos = 0; - hcsplt_bm->split_compl = 0; - hcsplt_bm->split_en = 0; - - edpt->next_data_toggle = HCTSIZ_PID_DATA0; - - // TODO not support split transaction yet - - return true; - } - } + const uint8_t ep_id = edpt_alloc(); + TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); + hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; - return false; + dwc2_channel_char_t* hcchar_bm = &edpt->hcchar_bm; + hcchar_bm->ep_size = tu_edpt_packet_size(desc_ep); + hcchar_bm->ep_num = tu_edpt_number(desc_ep->bEndpointAddress); + hcchar_bm->ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress); + hcchar_bm->low_speed_dev = (devtree_info.speed == TUSB_SPEED_LOW) ? 1 : 0; + hcchar_bm->ep_type = desc_ep->bmAttributes.xfer; // ep_type matches TUSB_XFER_* + hcchar_bm->err_multi_count = 0; + hcchar_bm->dev_addr = dev_addr; + hcchar_bm->odd_frame = 0; + hcchar_bm->disable = 0; + hcchar_bm->enable = 1; + + dwc2_channel_split_t* hcsplt_bm = &edpt->hcsplt_bm; + hcsplt_bm->hub_port = devtree_info.hub_port; + hcsplt_bm->hub_addr = devtree_info.hub_addr; + hcsplt_bm->xact_pos = 0; + hcsplt_bm->split_compl = 0; + hcsplt_bm->split_en = 0; + + edpt->next_data_toggle = HCTSIZ_PID_DATA0; + + return true; } // Submit a transfer, when complete hcd_event_xfer_complete() must be invoked @@ -473,12 +471,21 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * xfer->buffer = buffer; xfer->total_bytes = buflen; + xfer->result = XFER_RESULT_INVALID; - uint32_t hcintmsk = HCINT_NAK | HCINT_XACT_ERR | HCINT_STALL | HCINT_XFER_COMPLETE | HCINT_DATATOGGLE_ERR; if (dma_host_enabled(dwc2)) { channel->hcdma = (uint32_t) buffer; TU_ASSERT(false); // not yet supported } else { + uint32_t hcintmsk = HCINT_NAK | HCINT_XACT_ERR | HCINT_STALL | HCINT_XFER_COMPLETE | HCINT_DATATOGGLE_ERR; + if (ep_dir == TUSB_DIR_IN) { + hcintmsk |= HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR; + } else { + hcintmsk |= HCINT_NYET; + } + channel->hcintmsk = hcintmsk; + dwc2->haintmsk |= TU_BIT(ch_id); + bool const is_period = edpt_is_periodic(hcchar_bm->ep_type); // enable channel for slave mode: @@ -489,8 +496,6 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * if (ep_dir == TUSB_DIR_IN) { TU_ASSERT(request_queue_avail(dwc2, is_period)); channel->hcchar |= HCCHAR_CHENA; - - hcintmsk |= HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR; } else { channel->hcchar |= HCCHAR_CHENA; if (buflen > 0) { @@ -498,14 +503,9 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * // And write packet in the interrupt handler dwc2->gintmsk |= (is_period ? GINTSTS_PTX_FIFO_EMPTY : GINTSTS_NPTX_FIFO_EMPTY); } - - hcintmsk |= HCINT_NYET; } } - channel->hcintmsk = hcintmsk; - dwc2->haintmsk |= TU_BIT(ch_id); - return true; } @@ -527,7 +527,7 @@ bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; // disable also require request queue if (request_queue_avail(dwc2, edpt_is_periodic(edpt->hcchar_bm.ep_type))) { - channel->hcchar |= HCCHAR_CHDIS; + channel_disable(channel); } else { TU_BREAKPOINT(); } @@ -669,13 +669,14 @@ TU_ATTR_ALWAYS_INLINE static inline void handle_hprt_irq(uint8_t rhport, bool in dwc2->hprt = hprt; // clear interrupt } -xfer_result_t handle_channel_slave_in(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { +bool handle_channel_slave_in(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; - xfer_result_t result = XFER_RESULT_INVALID; + + bool is_done = false; if (hcint & HCINT_XFER_COMPLETE) { - result = XFER_RESULT_SUCCESS; + xfer->result = XFER_RESULT_SUCCESS; channel_disable(channel); channel->hcintmsk &= ~HCINT_ACK; } else if (hcint & (HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_STALL)) { @@ -686,8 +687,11 @@ xfer_result_t handle_channel_slave_in(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_ } } else if (hcint & HCINT_HALTED) { channel->hcintmsk &= ~HCINT_HALTED; - if (channel->hcchar_bm.err_multi_count == HCD_XFER_ERROR_MAX) { - channel_dealloc(dwc2, ch_id); + if (xfer->result != XFER_RESULT_INVALID) { + is_done = true; + } else if (channel->hcchar_bm.err_multi_count == HCD_XFER_ERROR_MAX) { + xfer->result = XFER_RESULT_FAILED; + is_done = true; } else { // Re-initialize Channel } @@ -698,25 +702,26 @@ xfer_result_t handle_channel_slave_in(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_ xfer->err_count = 0; } else if (hcint & HCINT_NAK) { // NAK received, re-enable channel if request queue is available - TU_ASSERT(request_queue_avail(dwc2, is_period), XFER_RESULT_INVALID); + TU_ASSERT(request_queue_avail(dwc2, is_period)); channel->hcchar |= HCCHAR_CHENA; } - return result; + return is_done; } -xfer_result_t handle_channel_slave_out(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { +bool handle_channel_slave_out(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { (void) is_period; hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; - xfer_result_t result = XFER_RESULT_INVALID; + + bool is_notify = false; if (hcint & HCINT_XFER_COMPLETE) { - result = XFER_RESULT_SUCCESS; + is_notify = true; + xfer->result = XFER_RESULT_SUCCESS; channel->hcintmsk &= ~HCINT_ACK; - channel_dealloc(dwc2, ch_id); } else if (hcint & HCINT_STALL) { - xfer->state = HCD_XFER_STATE_DONE; + xfer->result = XFER_RESULT_STALLED; channel_disable(channel); } else if (hcint & (HCINT_NAK | HCINT_XACT_ERR | HCINT_NYET)) { channel_disable(channel); @@ -728,9 +733,11 @@ xfer_result_t handle_channel_slave_out(dwc2_regs_t* dwc2, uint8_t ch_id, bool is } } else if (hcint & HCINT_HALTED) { channel->hcintmsk &= ~HCINT_HALTED; - if (xfer->state == HCD_XFER_STATE_DONE || channel->hcchar_bm.err_multi_count == HCD_XFER_ERROR_MAX) { - result = (xfer->state == HCD_XFER_STATE_DONE ? XFER_RESULT_STALLED : XFER_RESULT_FAILED); - channel_dealloc(dwc2, ch_id); + if (xfer->result != XFER_RESULT_INVALID) { + is_notify = true; + } else if (channel->hcchar_bm.err_multi_count == HCD_XFER_ERROR_MAX) { + xfer->result = XFER_RESULT_FAILED; + is_notify = true; } else { // Re-initialize Channel (Do ping protocol for HS) } @@ -739,7 +746,7 @@ xfer_result_t handle_channel_slave_out(dwc2_regs_t* dwc2, uint8_t ch_id, bool is channel->hcintmsk &= ~HCINT_ACK; } - return result; + return is_notify; } void handle_channel_irq(uint8_t rhport, bool in_isr) { @@ -760,19 +767,20 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) { if (is_dma) { } else { - xfer_result_t result; + bool is_done; if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { - result = handle_channel_slave_out(dwc2, ch_id, is_period, hcint); + is_done = handle_channel_slave_out(dwc2, ch_id, is_period, hcint); } else { - result = handle_channel_slave_in(dwc2, ch_id, is_period, hcint); + is_done = handle_channel_slave_in(dwc2, ch_id, is_period, hcint); } - // Transfer is complete (success, stalled, failed) or channel is disabled - if (result != XFER_RESULT_INVALID ) { - // notify usbh if transfer is complete (skip if channel is disabled) + // notify usbh if done + if (is_done) { const uint8_t ep_addr = tu_edpt_addr(hcchar_bm.ep_num, hcchar_bm.ep_dir); - hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, xfer->total_bytes, result, in_isr); + hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, xfer->total_bytes, xfer->result, in_isr); + + channel_dealloc(dwc2, ch_id); } } @@ -789,7 +797,7 @@ bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; - if (xfer->state == HCD_XFER_STATE_ACTIVE) { + if (xfer->allocated) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; const dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { @@ -846,6 +854,18 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { handle_hprt_irq(rhport, in_isr); } + // RxFIFO non-empty interrupt handling, must be handled before HCINT + if (int_status & GINTSTS_RXFLVL) { + // RXFLVL bit is read-only + dwc2->gintmsk &= ~GINTMSK_RXFLVLM; // disable RXFLVL interrupt while reading + + do { + handle_rxflvl_irq(rhport); // read all packets + } while(dwc2->gintsts & GINTSTS_RXFLVL); + + dwc2->gintmsk |= GINTMSK_RXFLVLM; + } + if (int_status & GINTSTS_HCINT) { // Host Channel interrupt: source is cleared in HCINT register handle_channel_irq(rhport, in_isr); @@ -859,19 +879,6 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { dwc2->gintmsk &= ~GINTSTS_NPTX_FIFO_EMPTY; } } - - // RxFIFO non-empty interrupt handling. - if (int_status & GINTSTS_RXFLVL) { - // RXFLVL bit is read-only - dwc2->gintmsk &= ~GINTMSK_RXFLVLM; // disable RXFLVL interrupt while reading - - do { - handle_rxflvl_irq(rhport); // read all packets - } while(dwc2->gintsts & GINTSTS_RXFLVL); - - dwc2->gintmsk |= GINTMSK_RXFLVLM; - } - } #endif -- cgit v1.3.1 From 074811c2514161972c75337601b915f21828ba91 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 29 Oct 2024 19:08:45 +0700 Subject: refactor channel_send_in_token(), support IN token for multiple transaction per transfer --- src/portable/synopsys/dwc2/hcd_dwc2.c | 58 ++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 5d843537b..4456955db 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -110,14 +110,6 @@ TU_ATTR_ALWAYS_INLINE static inline bool dma_host_enabled(const dwc2_regs_t* dwc return CFG_TUH_DWC2_DMA && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA; } -TU_ATTR_ALWAYS_INLINE static inline uint8_t request_queue_avail(const dwc2_regs_t* dwc2, bool is_period) { - if (is_period) { - return dwc2->hptxsts_bm.req_queue_available; - } else { - return dwc2->hnptxsts_bm.req_queue_available; - } -} - // Find a free channel for new transfer TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) { const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); @@ -138,10 +130,27 @@ TU_ATTR_ALWAYS_INLINE static inline void channel_dealloc(dwc2_regs_t* dwc2, uint } TU_ATTR_ALWAYS_INLINE static inline void channel_disable(dwc2_channel_t* channel) { + // disable also require request queue + // request_queue_avail(); channel->hcintmsk |= HCINT_HALTED; channel->hcchar |= HCCHAR_CHDIS | HCCHAR_CHENA; // must set both CHDIS and CHENA } +TU_ATTR_ALWAYS_INLINE static inline uint8_t request_queue_avail(const dwc2_regs_t* dwc2, bool is_period) { + if (is_period) { + return dwc2->hptxsts_bm.req_queue_available; + } else { + return dwc2->hnptxsts_bm.req_queue_available; + } +} + +// attempt to send IN token to receive data +TU_ATTR_ALWAYS_INLINE static inline bool channel_send_in_token(const dwc2_regs_t* dwc2, dwc2_channel_t* channel, bool is_period) { + TU_ASSERT(request_queue_avail(dwc2, is_period)); + channel->hcchar_bm.enable = 1; + return true; +} + // Find currently enabled channel. Note: EP0 is bidirectional TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_enabled(dwc2_regs_t* dwc2, uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) { const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); @@ -494,8 +503,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * // IN Token. If we got NAK, we have to re-enable the channel again in the interrupt. Due to the way usbh stack only // call hcd_edpt_xfer() once, we will need to manage de-allocate/re-allocate IN channel dynamically. if (ep_dir == TUSB_DIR_IN) { - TU_ASSERT(request_queue_avail(dwc2, is_period)); - channel->hcchar |= HCCHAR_CHENA; + channel_send_in_token(dwc2, channel, is_period); } else { channel->hcchar |= HCCHAR_CHENA; if (buflen > 0) { @@ -517,7 +525,7 @@ bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { const uint8_t ep_dir = tu_edpt_dir(ep_addr); const uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir); TU_VERIFY(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); - hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; + //hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; // hcd_int_disable(rhport); @@ -525,12 +533,7 @@ bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { const uint8_t ch_id = channel_find_enabled(dwc2, dev_addr, ep_num, ep_dir); if (ch_id < 16) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; - // disable also require request queue - if (request_queue_avail(dwc2, edpt_is_periodic(edpt->hcchar_bm.ep_type))) { - channel_disable(channel); - } else { - TU_BREAKPOINT(); - } + channel_disable(channel); } // hcd_int_enable(rhport); @@ -578,9 +581,17 @@ static void handle_rxflvl_irq(uint8_t rhport) { dfifo_read_packet(dwc2, xfer->buffer, byte_count); xfer->buffer += byte_count; - // short packet, minus remaining bytes (xfer_size) - if (byte_count < channel->hctsiz_bm.xfer_size) { - xfer->total_bytes -= channel->hctsiz_bm.xfer_size; + if (byte_count < channel->hcchar_bm.ep_size) { + // short packet, minus remaining bytes + const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size; + xfer->total_bytes -= remain_bytes; + } else { + // still more packet to send + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + if (remain_packets) { + bool const is_period = edpt_is_periodic(channel->hcchar_bm.ep_type); + channel_send_in_token(dwc2, channel, is_period); + } } break; @@ -702,8 +713,7 @@ bool handle_channel_slave_in(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, u xfer->err_count = 0; } else if (hcint & HCINT_NAK) { // NAK received, re-enable channel if request queue is available - TU_ASSERT(request_queue_avail(dwc2, is_period)); - channel->hcchar |= HCCHAR_CHENA; + channel_send_in_token(dwc2, channel, is_period); } return is_done; @@ -775,11 +785,9 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) { is_done = handle_channel_slave_in(dwc2, ch_id, is_period, hcint); } - // notify usbh if done if (is_done) { const uint8_t ep_addr = tu_edpt_addr(hcchar_bm.ep_num, hcchar_bm.ep_dir); hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, xfer->total_bytes, xfer->result, in_isr); - channel_dealloc(dwc2, ch_id); } } @@ -803,7 +811,7 @@ bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { const uint16_t remain_packets = channel->hctsiz_bm.packet_count; for (uint16_t i = 0; i < remain_packets; i++) { - const uint16_t remain_bytes = channel->hctsiz_bm.xfer_size; + const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size; const uint16_t xact_bytes = tu_min16(remain_bytes, hcchar_bm.ep_size); // check if there is enough space in FIFO and RequestQueue. -- cgit v1.3.1 From 616b5770f279fee1aeec7c94258124762ad0aa65 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 29 Oct 2024 21:54:21 +0700 Subject: fix pid token calculation, implement hcd_edpt_clear_stall(). msc explorer example read work, but write10 still wip --- src/portable/synopsys/dwc2/dwc2_type.h | 11 ++++--- src/portable/synopsys/dwc2/hcd_dwc2.c | 58 +++++++++++++++++++++++----------- 2 files changed, 47 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index d2ab1e94d..342a4cb2d 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -139,10 +139,13 @@ enum { }; enum { - HCTSIZ_PID_DATA0 = 0, - HCTSIZ_PID_DATA2 = 1, - HCTSIZ_PID_DATA1 = 2, - HCTSIZ_PID_SETUP = 3, + HCTSIZ_PID_DATA0 = 0, // 00b + HCTSIZ_PID_DATA2 = 1, // 01b + HCTSIZ_PID_DATA1 = 2, // 10b + HCTSIZ_PID_SETUP = 3, // 11b +}; +enum { + HCTSIZ_PID_MDATA = 3, }; enum { diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 4456955db..d64a95393 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -67,13 +67,14 @@ typedef struct { dwc2_channel_split_t hcsplt_bm; }; - uint8_t next_data_toggle; + uint8_t next_pid; // uint8_t resv[3]; } hcd_endpoint_t; // Additional info for each channel when it is active typedef struct { volatile bool allocated; + uint8_t ep_id; // associated edpt uint8_t result; uint8_t err_count; uint8_t* buffer; @@ -125,7 +126,8 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) { } TU_ATTR_ALWAYS_INLINE static inline void channel_dealloc(dwc2_regs_t* dwc2, uint8_t ch_id) { - _hcd_data.xfer[ch_id].allocated = false; + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + xfer->allocated = false; dwc2->haintmsk &= ~TU_BIT(ch_id); } @@ -195,6 +197,14 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_find_opened(uint8_t dev_addr, u return TUSB_INDEX_INVALID_8; } +TU_ATTR_ALWAYS_INLINE static inline uint8_t cal_next_pid(uint8_t pid, uint8_t packet_count) { + if (packet_count & 0x01) { + return pid ^ 0x02; // toggle DATA0 and DATA1 + } else { + return pid; + } +} + //-------------------------------------------------------------------- // //-------------------------------------------------------------------- @@ -322,7 +332,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // force host mode and wait for mode switch dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FDMOD) | GUSBCFG_FHMOD; - while( (dwc2->gintsts & GINTSTS_CMOD) != GINTSTS_CMODE_HOST) {} + while ((dwc2->gintsts & GINTSTS_CMOD) != GINTSTS_CMODE_HOST) {} // configure fixed-allocated fifo scheme dfifo_host_init(rhport); @@ -438,7 +448,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* hcsplt_bm->split_compl = 0; hcsplt_bm->split_en = 0; - edpt->next_data_toggle = HCTSIZ_PID_DATA0; + edpt->next_pid = HCTSIZ_PID_DATA0; return true; } @@ -448,6 +458,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * dwc2_regs_t* dwc2 = DWC2_REG(rhport); const uint8_t ep_num = tu_edpt_number(ep_addr); const uint8_t ep_dir = tu_edpt_dir(ep_addr); + uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir); TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; @@ -456,19 +467,21 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * uint8_t ch_id = channel_alloc(dwc2); TU_ASSERT(ch_id < 16); // all channel are in used hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + xfer->ep_id = ep_id; + dwc2_channel_t* channel = &dwc2->channel[ch_id]; uint16_t packet_count = tu_div_ceil(buflen, hcchar_bm->ep_size); if (packet_count == 0) { packet_count = 1; // zero length packet still count as 1 } - channel->hctsiz = (edpt->next_data_toggle << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | buflen; + channel->hctsiz = (edpt->next_pid << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | buflen; - // Control transfer always start with DATA1 for data and status stage. May has issue with ZLP - if (edpt->next_data_toggle == HCTSIZ_PID_DATA0 || ep_num == 0) { - edpt->next_data_toggle = HCTSIZ_PID_DATA1; + // pre-calculate next PID based on packet count, adjusted in transfer complete interrupt if short packet + if (ep_num == 0) { + edpt->next_pid = HCTSIZ_PID_DATA1; // control data and status stage always start with DATA1 } else { - edpt->next_data_toggle = HCTSIZ_PID_DATA0; + edpt->next_pid = cal_next_pid(edpt->next_pid, packet_count); } // TODO support split transaction @@ -546,7 +559,7 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet uint8_t ep_id = edpt_find_opened(dev_addr, 0, TUSB_DIR_OUT); TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); // no opened endpoint hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; - edpt->next_data_toggle = HCTSIZ_PID_SETUP; + edpt->next_pid = HCTSIZ_PID_SETUP; return hcd_edpt_xfer(rhport, dev_addr, 0, (uint8_t*)(uintptr_t) setup_packet, 8); } @@ -554,10 +567,15 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet // clear stall, data toggle is also reset to DATA0 bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { (void) rhport; - (void) dev_addr; - (void) ep_addr; + const uint8_t ep_num = tu_edpt_number(ep_addr); + const uint8_t ep_dir = tu_edpt_dir(ep_addr); + const uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir); + TU_VERIFY(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); + hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; + + edpt->next_pid = HCTSIZ_PID_DATA0; - return false; + return true; } //-------------------------------------------------------------------- @@ -581,24 +599,28 @@ static void handle_rxflvl_irq(uint8_t rhport) { dfifo_read_packet(dwc2, xfer->buffer, byte_count); xfer->buffer += byte_count; + const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size; + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; if (byte_count < channel->hcchar_bm.ep_size) { // short packet, minus remaining bytes - const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size; xfer->total_bytes -= remain_bytes; + + // update PID since we got short packet + TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,); + hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; // update PID + edpt->next_pid = cal_next_pid(edpt->next_pid, remain_packets); } else { - // still more packet to send - const uint16_t remain_packets = channel->hctsiz_bm.packet_count; if (remain_packets) { + // still more packet to send bool const is_period = edpt_is_periodic(channel->hcchar_bm.ep_type); channel_send_in_token(dwc2, channel, is_period); } } - break; } case GRXSTS_PKTSTS_RX_COMPLETE: - // In transfer complete: After this entry is popped from the receive FIFO, dwc2 asserts a Transfer Completed + // In transfer complete: After this entry is popped from the rx FIFO, dwc2 asserts a Transfer Completed // interrupt --> handle_channel_irq() break; -- cgit v1.3.1 From 3c1cb0e08f650931f95647400c1cf1e3abb03486 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 29 Oct 2024 22:48:58 +0700 Subject: correct the fifo_available comparison (words not byte) --- src/portable/synopsys/dwc2/dcd_dwc2.c | 10 ++++----- src/portable/synopsys/dwc2/hcd_dwc2.c | 42 +++++++++++++++++------------------ 2 files changed, 26 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index bcfdb546b..9b5c476e4 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -853,21 +853,21 @@ static void handle_epin_irq(uint8_t rhport) { const uint16_t remain_bytes = epin->dieptsiz_bm.xfer_size; // Packet can not be larger than ep max size - const uint16_t packet_size = tu_min16(remain_bytes, xfer->max_size); + const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->max_size); // It's only possible to write full packets into FIFO. Therefore DTXFSTS register of current // EP has to be checked if the buffer can take another WHOLE packet - if (packet_size > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) { + if (xact_bytes > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) { break; } // Push packet to Tx-FIFO if (xfer->ff) { volatile uint32_t* tx_fifo = dwc2->fifo[n]; - tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*) (uintptr_t) tx_fifo, packet_size); + tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*) (uintptr_t) tx_fifo, xact_bytes); } else { - dfifo_write_packet(dwc2, n, xfer->buffer, packet_size); - xfer->buffer += packet_size; + dfifo_write_packet(dwc2, n, xfer->buffer, xact_bytes); + xfer->buffer += xact_bytes; } } diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index d64a95393..226497b78 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -463,6 +463,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; dwc2_channel_char_t* hcchar_bm = &edpt->hcchar_bm; + bool const is_period = edpt_is_periodic(hcchar_bm->ep_type); uint8_t ch_id = channel_alloc(dwc2); TU_ASSERT(ch_id < 16); // all channel are in used @@ -487,7 +488,9 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * // TODO support split transaction channel->hcsplt = edpt->hcsplt; - hcchar_bm->odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame + if (is_period) { + hcchar_bm->odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame + } hcchar_bm->ep_dir = ep_dir; // control endpoint can switch direction channel->hcchar = (edpt->hcchar & ~HCCHAR_CHENA); // restore hcchar but don't enable yet @@ -508,8 +511,6 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * channel->hcintmsk = hcintmsk; dwc2->haintmsk |= TU_BIT(ch_id); - bool const is_period = edpt_is_periodic(hcchar_bm->ep_type); - // enable channel for slave mode: // - OUT: it will enable corresponding FIFO channel // - IN : it will write an IN request to the Non-periodic Request Queue, this will have dwc2 trying to send @@ -771,7 +772,8 @@ bool handle_channel_slave_out(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, xfer->result = XFER_RESULT_FAILED; is_notify = true; } else { - // Re-initialize Channel (Do ping protocol for HS) + // Got here due to NAK probably, retry channel (Do ping protocol for HS) + } } else if (hcint & HCINT_ACK) { xfer->err_count = 0; @@ -827,24 +829,22 @@ bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; - if (xfer->allocated) { - dwc2_channel_t* channel = &dwc2->channel[ch_id]; - const dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; - if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { - const uint16_t remain_packets = channel->hctsiz_bm.packet_count; - for (uint16_t i = 0; i < remain_packets; i++) { - const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size; - const uint16_t xact_bytes = tu_min16(remain_bytes, hcchar_bm.ep_size); - - // check if there is enough space in FIFO and RequestQueue. - // Packet's last word written to FIFO will trigger a request queue - if ((xact_bytes > txsts_bm->fifo_available) && (txsts_bm->req_queue_available > 0)) { - return true; - } - - dfifo_write_packet(dwc2, ch_id, xfer->buffer, xact_bytes); - xfer->buffer += xact_bytes; + dwc2_channel_t* channel = &dwc2->channel[ch_id]; + const dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; + if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + for (uint16_t i = 0; i < remain_packets; i++) { + const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size; + const uint16_t xact_bytes = tu_min16(remain_bytes, hcchar_bm.ep_size); + + // check if there is enough space in FIFO and RequestQueue. + // Packet's last word written to FIFO will trigger a request queue + if ((xact_bytes > (txsts_bm->fifo_available << 2)) && (txsts_bm->req_queue_available > 0)) { + return true; } + + dfifo_write_packet(dwc2, ch_id, xfer->buffer, xact_bytes); + xfer->buffer += xact_bytes; } } } -- cgit v1.3.1 From 5d74fb43b373e4def1387067eed8b4f1ac71151a Mon Sep 17 00:00:00 2001 From: Bollen Nico Date: Tue, 29 Oct 2024 20:41:13 +0100 Subject: Extend callback information --- src/class/cdc/cdc_device.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h index 3ad7c8baf..2d5ba2575 100644 --- a/src/class/cdc/cdc_device.h +++ b/src/class/cdc/cdc_device.h @@ -204,6 +204,9 @@ TU_ATTR_WEAK void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts); TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding); // Invoked when received send break +// \param[in] itf interface for which send break was received. +// \param[in] duration_ms the length of time, in milliseconds, of the break signal. If a value of FFFFh, then the +// device will send a break until another SendBreak request is received with value 0000h. TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms); //--------------------------------------------------------------------+ -- cgit v1.3.1 From 80d8157048170bcf2bbb77499b1496ea3c79c193 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 30 Oct 2024 18:11:41 +0700 Subject: handle out nak slave -> halted -> retry transfer --- src/portable/synopsys/dwc2/hcd_dwc2.c | 217 +++++++++++++++++++++------------- 1 file changed, 133 insertions(+), 84 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 226497b78..e80f8d0ba 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -75,10 +75,13 @@ typedef struct { typedef struct { volatile bool allocated; uint8_t ep_id; // associated edpt - uint8_t result; uint8_t err_count; - uint8_t* buffer; - uint16_t total_bytes; + uint8_t result; + uint16_t xferred_bytes; // bytes that accumulate transferred though USB bus for the whole hcd_edpt_xfer(), which can + // be composed of multiple channel_start_xfer() (retry with NAK/NYET) + uint8_t* buf_start; + uint16_t buf_len; + uint16_t out_fifo_bytes; // bytes written to TX FIFO (may not be transferred on USB bus). } hcd_xfer_t; typedef struct { @@ -125,30 +128,30 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) { return TUSB_INDEX_INVALID_8; } +TU_ATTR_ALWAYS_INLINE static inline uint8_t req_queue_avail(const dwc2_regs_t* dwc2, bool is_period) { + if (is_period) { + return dwc2->hptxsts_bm.req_queue_available; + } else { + return dwc2->hnptxsts_bm.req_queue_available; + } +} + TU_ATTR_ALWAYS_INLINE static inline void channel_dealloc(dwc2_regs_t* dwc2, uint8_t ch_id) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; xfer->allocated = false; dwc2->haintmsk &= ~TU_BIT(ch_id); } -TU_ATTR_ALWAYS_INLINE static inline void channel_disable(dwc2_channel_t* channel) { +TU_ATTR_ALWAYS_INLINE static inline void channel_disable(const dwc2_regs_t* dwc2, dwc2_channel_t* channel, bool is_period) { // disable also require request queue - // request_queue_avail(); + TU_ASSERT(req_queue_avail(dwc2, is_period), ); channel->hcintmsk |= HCINT_HALTED; channel->hcchar |= HCCHAR_CHDIS | HCCHAR_CHENA; // must set both CHDIS and CHENA } -TU_ATTR_ALWAYS_INLINE static inline uint8_t request_queue_avail(const dwc2_regs_t* dwc2, bool is_period) { - if (is_period) { - return dwc2->hptxsts_bm.req_queue_available; - } else { - return dwc2->hnptxsts_bm.req_queue_available; - } -} - // attempt to send IN token to receive data TU_ATTR_ALWAYS_INLINE static inline bool channel_send_in_token(const dwc2_regs_t* dwc2, dwc2_channel_t* channel, bool is_period) { - TU_ASSERT(request_queue_avail(dwc2, is_period)); + TU_ASSERT(req_queue_avail(dwc2, is_period)); channel->hcchar_bm.enable = 1; return true; } @@ -197,6 +200,14 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_find_opened(uint8_t dev_addr, u return TUSB_INDEX_INVALID_8; } +TU_ATTR_ALWAYS_INLINE static inline uint16_t cal_packet_count(uint16_t len, uint16_t ep_size) { + if (len == 0) { + return 1; + } else { + return tu_div_ceil(len, ep_size); + } +} + TU_ATTR_ALWAYS_INLINE static inline uint8_t cal_next_pid(uint8_t pid, uint8_t packet_count) { if (packet_count & 0x01) { return pid ^ 0x02; // toggle DATA0 and DATA1 @@ -453,57 +464,42 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* return true; } -// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked -bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) { - dwc2_regs_t* dwc2 = DWC2_REG(rhport); - const uint8_t ep_num = tu_edpt_number(ep_addr); - const uint8_t ep_dir = tu_edpt_dir(ep_addr); - - uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir); - TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); - hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; +bool channel_start_xfer(dwc2_regs_t* dwc2, uint8_t ch_id) { + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; dwc2_channel_char_t* hcchar_bm = &edpt->hcchar_bm; + dwc2_channel_t* channel = &dwc2->channel[ch_id]; bool const is_period = edpt_is_periodic(hcchar_bm->ep_type); - uint8_t ch_id = channel_alloc(dwc2); - TU_ASSERT(ch_id < 16); // all channel are in used - hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; - xfer->ep_id = ep_id; + // clear previous state + xfer->out_fifo_bytes = 0; - dwc2_channel_t* channel = &dwc2->channel[ch_id]; - - uint16_t packet_count = tu_div_ceil(buflen, hcchar_bm->ep_size); - if (packet_count == 0) { - packet_count = 1; // zero length packet still count as 1 + // hchar: restore but don't enable yet + if (is_period) { + hcchar_bm->odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame } - channel->hctsiz = (edpt->next_pid << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | buflen; + channel->hcchar = (edpt->hcchar & ~HCCHAR_CHENA); + + // hctsiz: zero length packet still count as 1 + const uint16_t packet_count = cal_packet_count(xfer->buf_len, hcchar_bm->ep_size); + channel->hctsiz = (edpt->next_pid << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | xfer->buf_len; // pre-calculate next PID based on packet count, adjusted in transfer complete interrupt if short packet - if (ep_num == 0) { + if (hcchar_bm->ep_num == 0) { edpt->next_pid = HCTSIZ_PID_DATA1; // control data and status stage always start with DATA1 } else { edpt->next_pid = cal_next_pid(edpt->next_pid, packet_count); } - // TODO support split transaction + // split: TODO support later channel->hcsplt = edpt->hcsplt; - if (is_period) { - hcchar_bm->odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame - } - hcchar_bm->ep_dir = ep_dir; // control endpoint can switch direction - channel->hcchar = (edpt->hcchar & ~HCCHAR_CHENA); // restore hcchar but don't enable yet - - xfer->buffer = buffer; - xfer->total_bytes = buflen; - xfer->result = XFER_RESULT_INVALID; - if (dma_host_enabled(dwc2)) { - channel->hcdma = (uint32_t) buffer; + // channel->hcdma = (uint32_t) buffer; TU_ASSERT(false); // not yet supported } else { uint32_t hcintmsk = HCINT_NAK | HCINT_XACT_ERR | HCINT_STALL | HCINT_XFER_COMPLETE | HCINT_DATATOGGLE_ERR; - if (ep_dir == TUSB_DIR_IN) { + if (hcchar_bm->ep_dir == TUSB_DIR_IN) { hcintmsk |= HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR; } else { hcintmsk |= HCINT_NYET; @@ -516,11 +512,11 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * // - IN : it will write an IN request to the Non-periodic Request Queue, this will have dwc2 trying to send // IN Token. If we got NAK, we have to re-enable the channel again in the interrupt. Due to the way usbh stack only // call hcd_edpt_xfer() once, we will need to manage de-allocate/re-allocate IN channel dynamically. - if (ep_dir == TUSB_DIR_IN) { + if (hcchar_bm->ep_dir == TUSB_DIR_IN) { channel_send_in_token(dwc2, channel, is_period); } else { - channel->hcchar |= HCCHAR_CHENA; - if (buflen > 0) { + channel->hcchar_bm.enable = 1; + if (xfer->buf_len > 0) { // To prevent conflict with other channel, we will enable periodic/non-periodic FIFO empty interrupt accordingly // And write packet in the interrupt handler dwc2->gintmsk |= (is_period ? GINTSTS_PTX_FIFO_EMPTY : GINTSTS_NPTX_FIFO_EMPTY); @@ -531,6 +527,29 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * return true; } +// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked +bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + const uint8_t ep_num = tu_edpt_number(ep_addr); + const uint8_t ep_dir = tu_edpt_dir(ep_addr); + + uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir); + TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); + hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; + + uint8_t ch_id = channel_alloc(dwc2); + TU_ASSERT(ch_id < 16); // all channel are in used + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + xfer->ep_id = ep_id; + + edpt->hcchar_bm.ep_dir = ep_dir; // control endpoint can switch direction + xfer->buf_start = buffer; + xfer->buf_len = buflen; + xfer->result = XFER_RESULT_INVALID; + + return channel_start_xfer(dwc2, ch_id); +} + // Abort a queued transfer. Note: it can only abort transfer that has not been started // Return true if a queued transfer is aborted, false if there is no transfer to abort bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { @@ -547,7 +566,7 @@ bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { const uint8_t ch_id = channel_find_enabled(dwc2, dev_addr, ep_num, ep_dir); if (ch_id < 16) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; - channel_disable(channel); + channel_disable(dwc2, channel, edpt_is_periodic(channel->hcchar_bm.ep_type)); } // hcd_int_enable(rhport); @@ -597,16 +616,16 @@ static void handle_rxflvl_irq(uint8_t rhport) { const uint16_t byte_count = grxstsp_bm.byte_count; hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; - dfifo_read_packet(dwc2, xfer->buffer, byte_count); - xfer->buffer += byte_count; + dfifo_read_packet(dwc2, xfer->buf_start + xfer->xferred_bytes, byte_count); + xfer->xferred_bytes += byte_count; const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size; const uint16_t remain_packets = channel->hctsiz_bm.packet_count; if (byte_count < channel->hcchar_bm.ep_size) { // short packet, minus remaining bytes - xfer->total_bytes -= remain_bytes; + xfer->xferred_bytes -= remain_bytes; - // update PID since we got short packet + // update PID based on remain packets count TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,); hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; // update PID edpt->next_pid = cal_next_pid(edpt->next_pid, remain_packets); @@ -703,7 +722,7 @@ TU_ATTR_ALWAYS_INLINE static inline void handle_hprt_irq(uint8_t rhport, bool in dwc2->hprt = hprt; // clear interrupt } -bool handle_channel_slave_in(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { +bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; @@ -711,10 +730,10 @@ bool handle_channel_slave_in(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, u if (hcint & HCINT_XFER_COMPLETE) { xfer->result = XFER_RESULT_SUCCESS; - channel_disable(channel); + channel_disable(dwc2, channel, is_period); channel->hcintmsk &= ~HCINT_ACK; } else if (hcint & (HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_STALL)) { - channel_disable(channel); + channel_disable(dwc2, channel, is_period); if (hcint & HCINT_XACT_ERR) { xfer->err_count++; channel->hcintmsk |= HCINT_ACK; @@ -742,45 +761,73 @@ bool handle_channel_slave_in(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, u return is_done; } -bool handle_channel_slave_out(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { +bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { (void) is_period; hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; - bool is_notify = false; + bool is_done = false; if (hcint & HCINT_XFER_COMPLETE) { - is_notify = true; + is_done = true; xfer->result = XFER_RESULT_SUCCESS; channel->hcintmsk &= ~HCINT_ACK; + TU_LOG1("xferred = %u, remain = %u, out_fifo = %u\n", + xfer->xferred_bytes, xfer->buf_len, xfer->out_fifo_bytes); } else if (hcint & HCINT_STALL) { xfer->result = XFER_RESULT_STALLED; - channel_disable(channel); + channel_disable(dwc2, channel, is_period); } else if (hcint & (HCINT_NAK | HCINT_XACT_ERR | HCINT_NYET)) { - channel_disable(channel); + TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); + hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; + edpt->next_pid = channel->hctsiz_bm.pid; // save PID + + /* Rewind buffer pointer and total bytes to retry later + * Must use the hctsiz.pktcnt field to determine how much data has been transferred. This field reflects the number + * of packets that have been transferred via the USB. This is always an integral number of packets if the transfer + * was halted before its normal completion. (Can't use the hctsiz.xfersize field because that reflects the number of + * bytes transferred via the AHB, not the USB). + */ + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + const uint16_t total_packets = cal_packet_count(xfer->buf_len, channel->hcchar_bm.ep_size); + const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size; + xfer->xferred_bytes += actual_bytes; + xfer->buf_start += actual_bytes; + xfer->buf_len -= actual_bytes; + xfer->out_fifo_bytes = 0; + TU_LOG1("xferred = %u, remain = %u, out_fifo = %u, this actual = %u\n", + xfer->xferred_bytes, xfer->buf_len, xfer->out_fifo_bytes, actual_bytes); + + channel_disable(dwc2, channel, is_period); if (hcint & HCINT_XACT_ERR) { xfer->err_count++; channel->hcintmsk |= HCINT_ACK; } else { + // NAK/NYET disable channel to flush all posted request and try again xfer->err_count = 0; } } else if (hcint & HCINT_HALTED) { channel->hcintmsk &= ~HCINT_HALTED; if (xfer->result != XFER_RESULT_INVALID) { - is_notify = true; + is_done = true; } else if (channel->hcchar_bm.err_multi_count == HCD_XFER_ERROR_MAX) { xfer->result = XFER_RESULT_FAILED; - is_notify = true; + is_done = true; } else { - // Got here due to NAK probably, retry channel (Do ping protocol for HS) - + // Got here due to NAK or NYET (need to do PING for HS) -> Retry transfer + TU_ASSERT(channel_start_xfer(dwc2, ch_id)); } } else if (hcint & HCINT_ACK) { xfer->err_count = 0; channel->hcintmsk &= ~HCINT_ACK; } - return is_notify; + if (is_done) { + xfer->xferred_bytes += xfer->out_fifo_bytes; + xfer->out_fifo_bytes = 0; + } + + return is_done; } void handle_channel_irq(uint8_t rhport, bool in_isr) { @@ -798,25 +845,26 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) { uint32_t hcint = channel->hcint; hcint &= channel->hcintmsk; - if (is_dma) { + channel->hcint = hcint; // clear all interrupt flags + if (is_dma) { + // NOTE For DMA This is flow for core with OUT NAK enhancement from v2.71a } else { bool is_done; if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { - is_done = handle_channel_slave_out(dwc2, ch_id, is_period, hcint); + is_done = handle_channel_out_slave(dwc2, ch_id, is_period, hcint); } else { - is_done = handle_channel_slave_in(dwc2, ch_id, is_period, hcint); + is_done = handle_channel_in_slave(dwc2, ch_id, is_period, hcint); } if (is_done) { const uint8_t ep_addr = tu_edpt_addr(hcchar_bm.ep_num, hcchar_bm.ep_dir); - hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, xfer->total_bytes, xfer->result, in_isr); + hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, xfer->xferred_bytes, xfer->result, in_isr); channel_dealloc(dwc2, ch_id); } } - channel->hcint = hcint; // clear all interrupt flags } } } @@ -828,14 +876,14 @@ bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { - hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; - const dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; - if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { + // skip writing to FIFO if channel is expecting halted. + if (!(channel->hcintmsk & HCINT_HALTED) && (channel->hcchar_bm.ep_dir == TUSB_DIR_OUT)) { + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; const uint16_t remain_packets = channel->hctsiz_bm.packet_count; for (uint16_t i = 0; i < remain_packets; i++) { const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size; - const uint16_t xact_bytes = tu_min16(remain_bytes, hcchar_bm.ep_size); + const uint16_t xact_bytes = tu_min16(remain_bytes, channel->hcchar_bm.ep_size); // check if there is enough space in FIFO and RequestQueue. // Packet's last word written to FIFO will trigger a request queue @@ -843,8 +891,8 @@ bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { return true; } - dfifo_write_packet(dwc2, ch_id, xfer->buffer, xact_bytes); - xfer->buffer += xact_bytes; + dfifo_write_packet(dwc2, ch_id, xfer->buf_start + xfer->out_fifo_bytes, xact_bytes); + xfer->out_fifo_bytes += xact_bytes; } } } @@ -896,11 +944,6 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { dwc2->gintmsk |= GINTMSK_RXFLVLM; } - if (int_status & GINTSTS_HCINT) { - // Host Channel interrupt: source is cleared in HCINT register - handle_channel_irq(rhport, in_isr); - } - if (int_status & GINTSTS_NPTX_FIFO_EMPTY) { // NPTX FIFO empty interrupt, this is read-only and cleared by hardware when FIFO is written const bool more_isr = handle_txfifo_empty(dwc2, false); @@ -909,6 +952,12 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { dwc2->gintmsk &= ~GINTSTS_NPTX_FIFO_EMPTY; } } + + if (int_status & GINTSTS_HCINT) { + // Host Channel interrupt: source is cleared in HCINT register + // must bee handled after TX FIFO empty + handle_channel_irq(rhport, in_isr); + } } #endif -- cgit v1.3.1 From 92e7ac6c237bdba001660894d6be4008480ad7ba Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 31 Oct 2024 10:50:09 +0700 Subject: fix bulk out and txfifo_empty(), should not rely on hcchar_bm.ep_size for OUT. msc explorer seems to work well. --- src/portable/synopsys/dwc2/hcd_dwc2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index e80f8d0ba..432aac1ea 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -882,12 +882,12 @@ bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; const uint16_t remain_packets = channel->hctsiz_bm.packet_count; for (uint16_t i = 0; i < remain_packets; i++) { - const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size; + const uint16_t remain_bytes = xfer->buf_len - xfer->out_fifo_bytes; const uint16_t xact_bytes = tu_min16(remain_bytes, channel->hcchar_bm.ep_size); - // check if there is enough space in FIFO and RequestQueue. + // skip if there is not enough space in FIFO and RequestQueue. // Packet's last word written to FIFO will trigger a request queue - if ((xact_bytes > (txsts_bm->fifo_available << 2)) && (txsts_bm->req_queue_available > 0)) { + if ((xact_bytes > (txsts_bm->fifo_available << 2)) || (txsts_bm->req_queue_available == 0)) { return true; } -- cgit v1.3.1 From 9afa64014c662eb549bfcb605b0828ca5aeb26be Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 31 Oct 2024 21:37:57 +0700 Subject: testing hcd with dma, work well after enumeration. --- examples/host/cdc_msc_hid/only.txt | 1 + src/portable/synopsys/dwc2/hcd_dwc2.c | 130 +++++++++++++++++++++++++++------- 2 files changed, 107 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/examples/host/cdc_msc_hid/only.txt b/examples/host/cdc_msc_hid/only.txt index fee10f9e2..9b3d6994e 100644 --- a/examples/host/cdc_msc_hid/only.txt +++ b/examples/host/cdc_msc_hid/only.txt @@ -12,3 +12,4 @@ mcu:MSP432E4 mcu:RX65X mcu:RAXXX mcu:MAX3421 +mcu:STM32F7 diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 432aac1ea..3b2721869 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -114,7 +114,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool dma_host_enabled(const dwc2_regs_t* dwc return CFG_TUH_DWC2_DMA && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA; } -// Find a free channel for new transfer +// Allocate a channel for new transfer TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) { const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { @@ -152,7 +152,7 @@ TU_ATTR_ALWAYS_INLINE static inline void channel_disable(const dwc2_regs_t* dwc2 // attempt to send IN token to receive data TU_ATTR_ALWAYS_INLINE static inline bool channel_send_in_token(const dwc2_regs_t* dwc2, dwc2_channel_t* channel, bool is_period) { TU_ASSERT(req_queue_avail(dwc2, is_period)); - channel->hcchar_bm.enable = 1; + channel->hcchar |= HCCHAR_CHENA; return true; } @@ -494,9 +494,20 @@ bool channel_start_xfer(dwc2_regs_t* dwc2, uint8_t ch_id) { // split: TODO support later channel->hcsplt = edpt->hcsplt; + channel->hcint = 0xFFFFFFFF; // clear all interrupts + if (dma_host_enabled(dwc2)) { - // channel->hcdma = (uint32_t) buffer; - TU_ASSERT(false); // not yet supported + uint32_t hcintmsk = HCINT_HALTED; + channel->hcintmsk = hcintmsk; + dwc2->haintmsk |= TU_BIT(ch_id); + + channel->hcdma = (uint32_t) xfer->buf_start; + + if (hcchar_bm->ep_dir == TUSB_DIR_IN) { + channel_send_in_token(dwc2, channel, is_period); + } else { + channel->hcchar |= HCCHAR_CHENA; + } } else { uint32_t hcintmsk = HCINT_NAK | HCINT_XACT_ERR | HCINT_STALL | HCINT_XFER_COMPLETE | HCINT_DATATOGGLE_ERR; if (hcchar_bm->ep_dir == TUSB_DIR_IN) { @@ -515,7 +526,7 @@ bool channel_start_xfer(dwc2_regs_t* dwc2, uint8_t ch_id) { if (hcchar_bm->ep_dir == TUSB_DIR_IN) { channel_send_in_token(dwc2, channel, is_period); } else { - channel->hcchar_bm.enable = 1; + channel->hcchar |= HCCHAR_CHENA; if (xfer->buf_len > 0) { // To prevent conflict with other channel, we will enable periodic/non-periodic FIFO empty interrupt accordingly // And write packet in the interrupt handler @@ -541,12 +552,15 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * TU_ASSERT(ch_id < 16); // all channel are in used hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; xfer->ep_id = ep_id; - - edpt->hcchar_bm.ep_dir = ep_dir; // control endpoint can switch direction xfer->buf_start = buffer; xfer->buf_len = buflen; xfer->result = XFER_RESULT_INVALID; + if (ep_num == 0) { + // update ep_dir since control endpoint can switch direction + edpt->hcchar_bm.ep_dir = ep_dir; + } + return channel_start_xfer(dwc2, ch_id); } @@ -725,7 +739,6 @@ TU_ATTR_ALWAYS_INLINE static inline void handle_hprt_irq(uint8_t rhport, bool in bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; - bool is_done = false; if (hcint & HCINT_XFER_COMPLETE) { @@ -765,15 +778,12 @@ bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, (void) is_period; hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; - bool is_done = false; if (hcint & HCINT_XFER_COMPLETE) { is_done = true; xfer->result = XFER_RESULT_SUCCESS; channel->hcintmsk &= ~HCINT_ACK; - TU_LOG1("xferred = %u, remain = %u, out_fifo = %u\n", - xfer->xferred_bytes, xfer->buf_len, xfer->out_fifo_bytes); } else if (hcint & HCINT_STALL) { xfer->result = XFER_RESULT_STALLED; channel_disable(dwc2, channel, is_period); @@ -795,8 +805,6 @@ bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, xfer->buf_start += actual_bytes; xfer->buf_len -= actual_bytes; xfer->out_fifo_bytes = 0; - TU_LOG1("xferred = %u, remain = %u, out_fifo = %u, this actual = %u\n", - xfer->xferred_bytes, xfer->buf_len, xfer->out_fifo_bytes, actual_bytes); channel_disable(dwc2, channel, is_period); if (hcint & HCINT_XACT_ERR) { @@ -830,6 +838,79 @@ bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, return is_done; } +bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { + (void) is_period; + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + dwc2_channel_t* channel = &dwc2->channel[ch_id]; + bool is_done = false; + + if (hcint & HCINT_HALTED) { + if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL | HCINT_BABBLE_ERR)) { + is_done = true; + xfer->err_count = 0; + if (hcint & HCINT_STALL) { + xfer->result = XFER_RESULT_STALLED; + } else if (hcint & HCINT_BABBLE_ERR) { + xfer->result = XFER_RESULT_FAILED; + } else { + xfer->result = XFER_RESULT_SUCCESS; + } + channel->hcintmsk &= ~HCINT_ACK; + } else if (hcint & HCINT_XACT_ERR) { + xfer->err_count++; + if (xfer->err_count >= HCD_XFER_ERROR_MAX) { + is_done = true; + xfer->result = XFER_RESULT_FAILED; + } else { + channel->hcintmsk |= HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR; + // re-init channel + TU_ASSERT(false); + } + } + } else if (hcint & (HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR)) { + xfer->err_count = 0; + channel->hcintmsk &= ~(HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR); + } + + return is_done; +} + +bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { + (void) is_period; + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + dwc2_channel_t* channel = &dwc2->channel[ch_id]; + bool is_done = false; + + if (hcint & HCINT_HALTED) { + if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL)) { + is_done = true; + xfer->result = (hcint & HCINT_STALL) ? XFER_RESULT_STALLED : XFER_RESULT_SUCCESS; + xfer->err_count = 0; + channel->hcintmsk &= ~HCINT_ACK; + } else if (hcint & HCINT_XACT_ERR) { + if (hcint & (HCINT_NAK | HCINT_NYET | HCINT_ACK)) { + xfer->err_count = 0; + // re-init channel, re-wind buffer pointer + TU_ASSERT(false); + } else { + xfer->err_count++; + if (xfer->err_count >= HCD_XFER_ERROR_MAX) { + xfer->result = XFER_RESULT_FAILED; + is_done = true; + } else { + // re-init channel, re-wind buffer pointer + TU_ASSERT(false); + } + } + } + } else if (hcint & HCINT_ACK) { + xfer->err_count = 0; + channel->hcintmsk &= ~HCINT_ACK; + } + + return is_done; +} + void handle_channel_irq(uint8_t rhport, bool in_isr) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); const bool is_dma = dma_host_enabled(dwc2); @@ -843,28 +924,29 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) { const bool is_period = edpt_is_periodic(hcchar_bm.ep_type); uint32_t hcint = channel->hcint; - hcint &= channel->hcintmsk; - - channel->hcint = hcint; // clear all interrupt flags + channel->hcint = (hcint & channel->hcintmsk); // clear enabled interrupts + bool is_done; if (is_dma) { // NOTE For DMA This is flow for core with OUT NAK enhancement from v2.71a + if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { + is_done = handle_channel_out_dma(dwc2, ch_id, is_period, hcint); + } else { + is_done = handle_channel_in_dma(dwc2, ch_id, is_period, hcint); + } } else { - bool is_done; - if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { is_done = handle_channel_out_slave(dwc2, ch_id, is_period, hcint); } else { is_done = handle_channel_in_slave(dwc2, ch_id, is_period, hcint); } - - if (is_done) { - const uint8_t ep_addr = tu_edpt_addr(hcchar_bm.ep_num, hcchar_bm.ep_dir); - hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, xfer->xferred_bytes, xfer->result, in_isr); - channel_dealloc(dwc2, ch_id); - } } + if (is_done) { + const uint8_t ep_addr = tu_edpt_addr(hcchar_bm.ep_num, hcchar_bm.ep_dir); + hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, xfer->xferred_bytes, xfer->result, in_isr); + channel_dealloc(dwc2, ch_id); + } } } } -- cgit v1.3.1 From f7f80e844ce7d3a38554609a11b8c16bb383ec21 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 31 Oct 2024 23:00:19 +0700 Subject: do ping for slave out when nak/nyet --- src/portable/synopsys/dwc2/dwc2_type.h | 1 + src/portable/synopsys/dwc2/hcd_dwc2.c | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index 342a4cb2d..89561a5e6 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -434,6 +434,7 @@ typedef struct TU_ATTR_PACKED { uint32_t xfer_size : 19; // 0..18 Transfer size in bytes uint32_t packet_count : 10; // 19..28 Number of packets uint32_t pid : 2; // 29..30 Packet ID + uint32_t do_ping : 1; // 31 Do PING } dwc2_channel_tsize_t; TU_VERIFY_STATIC(sizeof(dwc2_channel_tsize_t) == 4, "incorrect size"); diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 3b2721869..a1666b8d0 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -67,16 +67,21 @@ typedef struct { dwc2_channel_split_t hcsplt_bm; }; + uint8_t is_hs_dev; uint8_t next_pid; - // uint8_t resv[3]; + // uint8_t resv[2]; } hcd_endpoint_t; // Additional info for each channel when it is active typedef struct { volatile bool allocated; uint8_t ep_id; // associated edpt - uint8_t err_count; + union TU_ATTR_PACKED { + uint8_t err_count : 7; + uint8_t do_ping : 1; + }; uint8_t result; + uint16_t xferred_bytes; // bytes that accumulate transferred though USB bus for the whole hcd_edpt_xfer(), which can // be composed of multiple channel_start_xfer() (retry with NAK/NYET) uint8_t* buf_start; @@ -180,6 +185,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_alloc(void) { for (uint32_t i = 0; i < CFG_TUH_DWC2_ENDPOINT_MAX; i++) { hcd_endpoint_t* edpt = &_hcd_data.edpt[i]; if (edpt->hcchar_bm.enable == 0) { + tu_memclr(edpt, sizeof(hcd_endpoint_t)); edpt->hcchar_bm.enable = 1; return i; } @@ -460,6 +466,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* hcsplt_bm->split_en = 0; edpt->next_pid = HCTSIZ_PID_DATA0; + edpt->is_hs_dev = (devtree_info.speed == TUSB_SPEED_HIGH) ? 1 : 0; return true; } @@ -482,7 +489,12 @@ bool channel_start_xfer(dwc2_regs_t* dwc2, uint8_t ch_id) { // hctsiz: zero length packet still count as 1 const uint16_t packet_count = cal_packet_count(xfer->buf_len, hcchar_bm->ep_size); - channel->hctsiz = (edpt->next_pid << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | xfer->buf_len; + uint32_t hctsiz = (edpt->next_pid << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | xfer->buf_len; + if (xfer->do_ping && edpt->is_hs_dev && edpt->next_pid != HCTSIZ_PID_SETUP && hcchar_bm->ep_dir == TUSB_DIR_OUT) { + hctsiz |= HCTSIZ_DOPING; + xfer->do_ping = 0; + } + channel->hctsiz = hctsiz; // pre-calculate next PID based on packet count, adjusted in transfer complete interrupt if short packet if (hcchar_bm->ep_num == 0) { @@ -822,7 +834,8 @@ bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, xfer->result = XFER_RESULT_FAILED; is_done = true; } else { - // Got here due to NAK or NYET (need to do PING for HS) -> Retry transfer + // Got here due to NAK or NYET -> Retry transfer with do PING (for highspeed) + xfer->do_ping = 1; TU_ASSERT(channel_start_xfer(dwc2, ch_id)); } } else if (hcint & HCINT_ACK) { -- cgit v1.3.1 From 4c8ce9733aafa96c8810be3b291ff1afea6888b7 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 1 Nov 2024 16:55:12 +0700 Subject: dma host work well with bulk/control --- src/portable/synopsys/dwc2/hcd_dwc2.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index a1666b8d0..c35252a8d 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -861,6 +861,8 @@ bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uin if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL | HCINT_BABBLE_ERR)) { is_done = true; xfer->err_count = 0; + const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size; + xfer->xferred_bytes += (xfer->buf_len - remain_bytes); if (hcint & HCINT_STALL) { xfer->result = XFER_RESULT_STALLED; } else if (hcint & HCINT_BABBLE_ERR) { @@ -897,8 +899,19 @@ bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, ui if (hcint & HCINT_HALTED) { if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL)) { is_done = true; - xfer->result = (hcint & HCINT_STALL) ? XFER_RESULT_STALLED : XFER_RESULT_SUCCESS; xfer->err_count = 0; + if (hcint & HCINT_XFER_COMPLETE) { + xfer->result = XFER_RESULT_SUCCESS; + xfer->xferred_bytes += xfer->buf_len; + } else { + xfer->result = XFER_RESULT_STALLED; + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + const uint16_t total_packets = cal_packet_count(xfer->buf_len, channel->hcchar_bm.ep_size); + const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size; + xfer->xferred_bytes += actual_bytes; + xfer->buf_start += actual_bytes; + xfer->buf_len -= actual_bytes; + } channel->hcintmsk &= ~HCINT_ACK; } else if (hcint & HCINT_XACT_ERR) { if (hcint & (HCINT_NAK | HCINT_NYET | HCINT_ACK)) { -- cgit v1.3.1 From b7ff10f59c48024e65341a494c51c471d4e8337e Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 1 Nov 2024 17:54:10 +0700 Subject: rename and add both CFG_TUH_DWC2_SLAVE_ENABLE/CFG_TUH_DWC2_DMA_ENABLE better out dma handle --- src/portable/synopsys/dwc2/dcd_dwc2.c | 10 +- src/portable/synopsys/dwc2/dwc2_common.c | 9 +- src/portable/synopsys/dwc2/dwc2_common.h | 2 +- src/portable/synopsys/dwc2/hcd_dwc2.c | 232 ++++++++++++++++--------------- src/tusb_option.h | 10 +- 5 files changed, 141 insertions(+), 122 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index 9b5c476e4..4cf725bfe 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -412,8 +412,14 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Core Initialization const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_DEVICE); - const bool is_dma = dma_device_enabled(dwc2); - TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma)); + TU_ASSERT(dwc2_core_init(rhport, is_highspeed)); + + if (dma_device_enabled(dwc2)) { + // DMA seems to be only settable after a core reset, and not possible to switch on-the-fly + dwc2->gahbcfg |= GAHBCFG_DMAEN | GAHBCFG_HBSTLEN_2; + } else { + dwc2->gintmsk |= GINTSTS_RXFLVL; + } // Device Initialization dcd_disconnect(rhport); diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index 66dc59b11..a3c7e80cb 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -194,7 +194,7 @@ bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role) { * In addition, UTMI+/ULPI can be shared to run at fullspeed mode with 48Mhz * */ -bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) { +bool dwc2_core_init(uint8_t rhport, bool is_highspeed) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); // Check Synopsys ID register, failed if controller clock/power is not enabled @@ -235,13 +235,6 @@ bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) { dwc2->gintmsk = 0; - if (is_dma) { - // DMA seems to be only settable after a core reset, and not possible to switch on-the-fly - dwc2->gahbcfg |= GAHBCFG_DMAEN | GAHBCFG_HBSTLEN_2; - } else { - dwc2->gintmsk |= GINTMSK_RXFLVLM; - } - return true; } diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h index 18b93894f..3f7f23c3a 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.h +++ b/src/portable/synopsys/dwc2/dwc2_common.h @@ -73,7 +73,7 @@ TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) { } bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role); -bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma); +bool dwc2_core_init(uint8_t rhport, bool is_highspeed); void dwc2_core_handle_common_irq(uint8_t rhport, bool in_isr); //--------------------------------------------------------------------+ diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index c35252a8d..02f6fa54a 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -83,7 +83,7 @@ typedef struct { uint8_t result; uint16_t xferred_bytes; // bytes that accumulate transferred though USB bus for the whole hcd_edpt_xfer(), which can - // be composed of multiple channel_start_xfer() (retry with NAK/NYET) + // be composed of multiple channel_xfer_start() (retry with NAK/NYET) uint8_t* buf_start; uint16_t buf_len; uint16_t out_fifo_bytes; // bytes written to TX FIFO (may not be transferred on USB bus). @@ -116,7 +116,7 @@ TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t convert_hprt_speed(uint32_t hpr TU_ATTR_ALWAYS_INLINE static inline bool dma_host_enabled(const dwc2_regs_t* dwc2) { (void) dwc2; // Internal DMA only - return CFG_TUH_DWC2_DMA && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA; + return CFG_TUH_DWC2_DMA_ENABLE && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA; } // Allocate a channel for new transfer @@ -321,8 +321,14 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Core Initialization const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_HOST); - const bool is_dma = dma_host_enabled(dwc2); - TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma)); + TU_ASSERT(dwc2_core_init(rhport, is_highspeed)); + + if (dma_host_enabled(dwc2)) { + // DMA seems to be only settable after a core reset, and not possible to switch on-the-fly + dwc2->gahbcfg |= GAHBCFG_DMAEN | GAHBCFG_HBSTLEN_2; + } else { + dwc2->gintmsk |= GINTSTS_RXFLVL; + } //------------- 3.1 Host Initialization -------------// @@ -471,7 +477,29 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* return true; } -bool channel_start_xfer(dwc2_regs_t* dwc2, uint8_t ch_id) { +// clean up channel after part of transfer is done but the whole urb is not complete +static void channel_xfer_cleanup(dwc2_regs_t* dwc2, uint8_t ch_id) { + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + dwc2_channel_t* channel = &dwc2->channel[ch_id]; + TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX, ); + hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; + + edpt->next_pid = channel->hctsiz_bm.pid; // save PID + + /* Must use the hctsiz.pktcnt field to determine how much data has been transferred. This field reflects the number + * of packets that have been transferred via the USB. This is always an integral number of packets if the transfer + * was halted before its normal completion. (Can't use the hctsiz.xfersize field because that reflects the number of + * bytes transferred via the AHB, not the USB). */ + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + const uint16_t total_packets = cal_packet_count(xfer->buf_len, channel->hcchar_bm.ep_size); + const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size; + xfer->xferred_bytes += actual_bytes; + xfer->buf_start += actual_bytes; + xfer->buf_len -= actual_bytes; + xfer->out_fifo_bytes = 0; +} + +static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; dwc2_channel_char_t* hcchar_bm = &edpt->hcchar_bm; @@ -573,7 +601,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * edpt->hcchar_bm.ep_dir = ep_dir; } - return channel_start_xfer(dwc2, ch_id); + return channel_xfer_start(dwc2, ch_id); } // Abort a queued transfer. Note: it can only abort transfer that has not been started @@ -627,7 +655,7 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { //-------------------------------------------------------------------- // HCD Event Handler //-------------------------------------------------------------------- - +#if CFG_TUH_DWC2_SLAVE_ENABLE static void handle_rxflvl_irq(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); @@ -683,72 +711,7 @@ static void handle_rxflvl_irq(uint8_t rhport) { } } -/* Handle Host Port interrupt, possible source are: - - Connection Detection - - Enable Change - - Over Current Change -*/ -TU_ATTR_ALWAYS_INLINE static inline void handle_hprt_irq(uint8_t rhport, bool in_isr) { - dwc2_regs_t* dwc2 = DWC2_REG(rhport); - uint32_t hprt = dwc2->hprt & ~HPRT_W1C_MASK; - const dwc2_hprt_t hprt_bm = dwc2->hprt_bm; - - if (dwc2->hprt & HPRT_CONN_DETECT) { - // Port Connect Detect - hprt |= HPRT_CONN_DETECT; - - if (hprt_bm.conn_status) { - hcd_event_device_attach(rhport, in_isr); - } else { - hcd_event_device_remove(rhport, in_isr); - } - } - - if (dwc2->hprt & HPRT_ENABLE_CHANGE) { - // Port enable change - hprt |= HPRT_ENABLE_CHANGE; - - if (hprt_bm.enable) { - // Port enable - // Config HCFG FS/LS clock and HFIR for SOF interval according to link speed (value is in PHY clock unit) - const tusb_speed_t speed = convert_hprt_speed(hprt_bm.speed); - uint32_t hcfg = dwc2->hcfg & ~HCFG_FSLS_PHYCLK_SEL; - - const dwc2_gusbcfg_t gusbcfg_bm = dwc2->gusbcfg_bm; - uint32_t clock = 60; - if (gusbcfg_bm.phy_sel) { - // dedicated FS is 48Mhz - clock = 48; - hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ; - } else { - // UTMI+ or ULPI - if (gusbcfg_bm.ulpi_utmi_sel) { - clock = 60; // ULPI 8-bit is 60Mhz - } else if (gusbcfg_bm.phy_if16) { - clock = 30; // UTMI+ 16-bit is 30Mhz - } else { - clock = 60; // UTMI+ 8-bit is 60Mhz - } - hcfg |= HCFG_FSLS_PHYCLK_SEL_30_60MHZ; - } - - dwc2->hcfg = hcfg; - - uint32_t hfir = dwc2->hfir & ~HFIR_FRIVL_Msk; - if (speed == TUSB_SPEED_HIGH) { - hfir |= 125*clock; - } else { - hfir |= 1000*clock; - } - - dwc2->hfir = hfir; - } - } - - dwc2->hprt = hprt; // clear interrupt -} - -bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { +static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; bool is_done = false; @@ -786,7 +749,7 @@ bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, u return is_done; } -bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { +static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { (void) is_period; hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; @@ -800,24 +763,8 @@ bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, xfer->result = XFER_RESULT_STALLED; channel_disable(dwc2, channel, is_period); } else if (hcint & (HCINT_NAK | HCINT_XACT_ERR | HCINT_NYET)) { - TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); - hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; - edpt->next_pid = channel->hctsiz_bm.pid; // save PID - - /* Rewind buffer pointer and total bytes to retry later - * Must use the hctsiz.pktcnt field to determine how much data has been transferred. This field reflects the number - * of packets that have been transferred via the USB. This is always an integral number of packets if the transfer - * was halted before its normal completion. (Can't use the hctsiz.xfersize field because that reflects the number of - * bytes transferred via the AHB, not the USB). - */ - const uint16_t remain_packets = channel->hctsiz_bm.packet_count; - const uint16_t total_packets = cal_packet_count(xfer->buf_len, channel->hcchar_bm.ep_size); - const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size; - xfer->xferred_bytes += actual_bytes; - xfer->buf_start += actual_bytes; - xfer->buf_len -= actual_bytes; - xfer->out_fifo_bytes = 0; - + // clean up transfer so far, disable and start again later + channel_xfer_cleanup(dwc2, ch_id); channel_disable(dwc2, channel, is_period); if (hcint & HCINT_XACT_ERR) { xfer->err_count++; @@ -836,7 +783,7 @@ bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, } else { // Got here due to NAK or NYET -> Retry transfer with do PING (for highspeed) xfer->do_ping = 1; - TU_ASSERT(channel_start_xfer(dwc2, ch_id)); + TU_ASSERT(channel_xfer_start(dwc2, ch_id)); } } else if (hcint & HCINT_ACK) { xfer->err_count = 0; @@ -850,8 +797,10 @@ bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, return is_done; } +#endif -bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { +#if CFG_TUH_DWC2_DMA_ENABLE +static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { (void) is_period; hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; @@ -890,7 +839,7 @@ bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uin return is_done; } -bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { +static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { (void) is_period; hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; @@ -905,27 +854,24 @@ bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, ui xfer->xferred_bytes += xfer->buf_len; } else { xfer->result = XFER_RESULT_STALLED; - const uint16_t remain_packets = channel->hctsiz_bm.packet_count; - const uint16_t total_packets = cal_packet_count(xfer->buf_len, channel->hcchar_bm.ep_size); - const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size; - xfer->xferred_bytes += actual_bytes; - xfer->buf_start += actual_bytes; - xfer->buf_len -= actual_bytes; + channel_xfer_cleanup(dwc2, ch_id); } channel->hcintmsk &= ~HCINT_ACK; } else if (hcint & HCINT_XACT_ERR) { if (hcint & (HCINT_NAK | HCINT_NYET | HCINT_ACK)) { xfer->err_count = 0; - // re-init channel, re-wind buffer pointer - TU_ASSERT(false); + // clean up transfer so far and start again + channel_xfer_cleanup(dwc2, ch_id); + channel_xfer_start(dwc2, ch_id); } else { xfer->err_count++; if (xfer->err_count >= HCD_XFER_ERROR_MAX) { xfer->result = XFER_RESULT_FAILED; is_done = true; } else { - // re-init channel, re-wind buffer pointer - TU_ASSERT(false); + // clean up transfer so far and start again + channel_xfer_cleanup(dwc2, ch_id); + channel_xfer_start(dwc2, ch_id); } } } @@ -936,8 +882,9 @@ bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, ui return is_done; } +#endif -void handle_channel_irq(uint8_t rhport, bool in_isr) { +static void handle_channel_irq(uint8_t rhport, bool in_isr) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); const bool is_dma = dma_host_enabled(dwc2); const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); @@ -978,7 +925,7 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) { } // return true if there is still pending data and need more ISR -bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { +static bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { // Use period txsts for both p/np to get request queue space available (1-bit difference, it is small enough) volatile dwc2_hptxsts_t* txsts_bm = (volatile dwc2_hptxsts_t*) (is_periodic ? &dwc2->hptxsts : &dwc2->hnptxsts); @@ -1008,6 +955,71 @@ bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { return false; // all data written } +/* Handle Host Port interrupt, possible source are: + - Connection Detection + - Enable Change + - Over Current Change +*/ +static void handle_hprt_irq(uint8_t rhport, bool in_isr) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + uint32_t hprt = dwc2->hprt & ~HPRT_W1C_MASK; + const dwc2_hprt_t hprt_bm = dwc2->hprt_bm; + + if (dwc2->hprt & HPRT_CONN_DETECT) { + // Port Connect Detect + hprt |= HPRT_CONN_DETECT; + + if (hprt_bm.conn_status) { + hcd_event_device_attach(rhport, in_isr); + } else { + hcd_event_device_remove(rhport, in_isr); + } + } + + if (dwc2->hprt & HPRT_ENABLE_CHANGE) { + // Port enable change + hprt |= HPRT_ENABLE_CHANGE; + + if (hprt_bm.enable) { + // Port enable + // Config HCFG FS/LS clock and HFIR for SOF interval according to link speed (value is in PHY clock unit) + const tusb_speed_t speed = convert_hprt_speed(hprt_bm.speed); + uint32_t hcfg = dwc2->hcfg & ~HCFG_FSLS_PHYCLK_SEL; + + const dwc2_gusbcfg_t gusbcfg_bm = dwc2->gusbcfg_bm; + uint32_t clock = 60; + if (gusbcfg_bm.phy_sel) { + // dedicated FS is 48Mhz + clock = 48; + hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ; + } else { + // UTMI+ or ULPI + if (gusbcfg_bm.ulpi_utmi_sel) { + clock = 60; // ULPI 8-bit is 60Mhz + } else if (gusbcfg_bm.phy_if16) { + clock = 30; // UTMI+ 16-bit is 30Mhz + } else { + clock = 60; // UTMI+ 8-bit is 60Mhz + } + hcfg |= HCFG_FSLS_PHYCLK_SEL_30_60MHZ; + } + + dwc2->hcfg = hcfg; + + uint32_t hfir = dwc2->hfir & ~HFIR_FRIVL_Msk; + if (speed == TUSB_SPEED_HIGH) { + hfir |= 125*clock; + } else { + hfir |= 1000*clock; + } + + dwc2->hfir = hfir; + } + } + + dwc2->hprt = hprt; // clear interrupt +} + /* Interrupt Hierarchy HCINTn HPRT | | @@ -1040,17 +1052,19 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { handle_hprt_irq(rhport, in_isr); } +#if CFG_TUH_DWC2_SLAVE_ENABLE // RxFIFO non-empty interrupt handling, must be handled before HCINT if (int_status & GINTSTS_RXFLVL) { // RXFLVL bit is read-only - dwc2->gintmsk &= ~GINTMSK_RXFLVLM; // disable RXFLVL interrupt while reading + dwc2->gintmsk &= ~GINTSTS_RXFLVL; // disable RXFLVL interrupt while reading do { handle_rxflvl_irq(rhport); // read all packets } while(dwc2->gintsts & GINTSTS_RXFLVL); - dwc2->gintmsk |= GINTMSK_RXFLVLM; + dwc2->gintmsk |= GINTSTS_RXFLVL; } +#endif if (int_status & GINTSTS_NPTX_FIFO_EMPTY) { // NPTX FIFO empty interrupt, this is read-only and cleared by hardware when FIFO is written diff --git a/src/tusb_option.h b/src/tusb_option.h index f3a8bb724..b193d9c09 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -254,8 +254,14 @@ #define CFG_TUD_DWC2_DMA 0 #endif -#ifndef CFG_TUH_DWC2_DMA - #define CFG_TUH_DWC2_DMA 1 +// Enable DWC2 Slave mode for host +#ifndef CFG_TUH_DWC2_SLAVE_ENABLE + #define CFG_TUH_DWC2_SLAVE_ENABLE 1 +#endif + +// Enable DWC2 DMA for host +#ifndef CFG_TUH_DWC2_DMA_ENABLE + #define CFG_TUH_DWC2_DMA_ENABLE 1 #endif // Enable PIO-USB software host controller -- cgit v1.3.1 From b1182de8726db4080a8b1b8b4b57e1667eb81956 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 1 Nov 2024 19:04:32 +0700 Subject: clean up is_period --- src/portable/synopsys/dwc2/hcd_dwc2.c | 97 ++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 02f6fa54a..960a78ff3 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -66,16 +66,15 @@ typedef struct { uint32_t hcsplt; dwc2_channel_split_t hcsplt_bm; }; - - uint8_t is_hs_dev; uint8_t next_pid; - // uint8_t resv[2]; + uint16_t frame_interval; + uint16_t frame_countdown; } hcd_endpoint_t; // Additional info for each channel when it is active typedef struct { volatile bool allocated; - uint8_t ep_id; // associated edpt + uint8_t ep_id; union TU_ATTR_PACKED { uint8_t err_count : 7; uint8_t do_ping : 1; @@ -84,8 +83,8 @@ typedef struct { uint16_t xferred_bytes; // bytes that accumulate transferred though USB bus for the whole hcd_edpt_xfer(), which can // be composed of multiple channel_xfer_start() (retry with NAK/NYET) - uint8_t* buf_start; uint16_t buf_len; + uint8_t* buf_start; uint16_t out_fifo_bytes; // bytes written to TX FIFO (may not be transferred on USB bus). } hcd_xfer_t; @@ -133,6 +132,11 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) { return TUSB_INDEX_INVALID_8; } +// Check if is periodic (interrupt/isochronous) +TU_ATTR_ALWAYS_INLINE static inline bool edpt_is_periodic(uint8_t ep_type) { + return ep_type == HCCHAR_EPTYPE_INTERRUPT || ep_type == HCCHAR_EPTYPE_ISOCHRONOUS; +} + TU_ATTR_ALWAYS_INLINE static inline uint8_t req_queue_avail(const dwc2_regs_t* dwc2, bool is_period) { if (is_period) { return dwc2->hptxsts_bm.req_queue_available; @@ -147,16 +151,17 @@ TU_ATTR_ALWAYS_INLINE static inline void channel_dealloc(dwc2_regs_t* dwc2, uint dwc2->haintmsk &= ~TU_BIT(ch_id); } -TU_ATTR_ALWAYS_INLINE static inline void channel_disable(const dwc2_regs_t* dwc2, dwc2_channel_t* channel, bool is_period) { +TU_ATTR_ALWAYS_INLINE static inline bool channel_disable(const dwc2_regs_t* dwc2, dwc2_channel_t* channel) { // disable also require request queue - TU_ASSERT(req_queue_avail(dwc2, is_period), ); + TU_ASSERT(req_queue_avail(dwc2, edpt_is_periodic(channel->hcchar_bm.ep_type))); channel->hcintmsk |= HCINT_HALTED; channel->hcchar |= HCCHAR_CHDIS | HCCHAR_CHENA; // must set both CHDIS and CHENA + return true; } // attempt to send IN token to receive data -TU_ATTR_ALWAYS_INLINE static inline bool channel_send_in_token(const dwc2_regs_t* dwc2, dwc2_channel_t* channel, bool is_period) { - TU_ASSERT(req_queue_avail(dwc2, is_period)); +TU_ATTR_ALWAYS_INLINE static inline bool channel_send_in_token(const dwc2_regs_t* dwc2, dwc2_channel_t* channel) { + TU_ASSERT(req_queue_avail(dwc2, edpt_is_periodic(channel->hcchar_bm.ep_type))); channel->hcchar |= HCCHAR_CHENA; return true; } @@ -175,10 +180,6 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_enabled(dwc2_regs_t* dw return TUSB_INDEX_INVALID_8; } -// Check if is periodic (interrupt/isochronous) -TU_ATTR_ALWAYS_INLINE static inline bool edpt_is_periodic(uint8_t ep_type) { - return ep_type == HCCHAR_EPTYPE_INTERRUPT || ep_type == HCCHAR_EPTYPE_ISOCHRONOUS; -} // Allocate a new endpoint TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_alloc(void) { @@ -472,7 +473,15 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* hcsplt_bm->split_en = 0; edpt->next_pid = HCTSIZ_PID_DATA0; - edpt->is_hs_dev = (devtree_info.speed == TUSB_SPEED_HIGH) ? 1 : 0; + if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) { + edpt->frame_interval = 1 << (desc_ep->bInterval - 1); + } else if (desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT) { + if (devtree_info.speed == TUSB_SPEED_HIGH) { + edpt->frame_interval = 1 << (desc_ep->bInterval - 1); + } else { + edpt->frame_interval = desc_ep->bInterval; + } + } return true; } @@ -518,8 +527,12 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) { // hctsiz: zero length packet still count as 1 const uint16_t packet_count = cal_packet_count(xfer->buf_len, hcchar_bm->ep_size); uint32_t hctsiz = (edpt->next_pid << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | xfer->buf_len; - if (xfer->do_ping && edpt->is_hs_dev && edpt->next_pid != HCTSIZ_PID_SETUP && hcchar_bm->ep_dir == TUSB_DIR_OUT) { - hctsiz |= HCTSIZ_DOPING; + if (xfer->do_ping && edpt->next_pid != HCTSIZ_PID_SETUP && hcchar_bm->ep_dir == TUSB_DIR_OUT) { + hcd_devtree_info_t devtree_info; + hcd_devtree_get_info(hcchar_bm->dev_addr, &devtree_info); + if (devtree_info.speed == TUSB_SPEED_HIGH) { + hctsiz |= HCTSIZ_DOPING; + } xfer->do_ping = 0; } channel->hctsiz = hctsiz; @@ -534,7 +547,7 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) { // split: TODO support later channel->hcsplt = edpt->hcsplt; - channel->hcint = 0xFFFFFFFF; // clear all interrupts + channel->hcint = 0xFFFFFFFF; // clear all channel interrupts if (dma_host_enabled(dwc2)) { uint32_t hcintmsk = HCINT_HALTED; @@ -544,7 +557,7 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) { channel->hcdma = (uint32_t) xfer->buf_start; if (hcchar_bm->ep_dir == TUSB_DIR_IN) { - channel_send_in_token(dwc2, channel, is_period); + channel_send_in_token(dwc2, channel); } else { channel->hcchar |= HCCHAR_CHENA; } @@ -564,7 +577,7 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) { // IN Token. If we got NAK, we have to re-enable the channel again in the interrupt. Due to the way usbh stack only // call hcd_edpt_xfer() once, we will need to manage de-allocate/re-allocate IN channel dynamically. if (hcchar_bm->ep_dir == TUSB_DIR_IN) { - channel_send_in_token(dwc2, channel, is_period); + channel_send_in_token(dwc2, channel); } else { channel->hcchar |= HCCHAR_CHENA; if (xfer->buf_len > 0) { @@ -612,7 +625,6 @@ bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { const uint8_t ep_dir = tu_edpt_dir(ep_addr); const uint8_t ep_id = edpt_find_opened(dev_addr, ep_num, ep_dir); TU_VERIFY(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); - //hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; // hcd_int_disable(rhport); @@ -620,7 +632,7 @@ bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { const uint8_t ch_id = channel_find_enabled(dwc2, dev_addr, ep_num, ep_dir); if (ch_id < 16) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; - channel_disable(dwc2, channel, edpt_is_periodic(channel->hcchar_bm.ep_type)); + channel_disable(dwc2, channel); } // hcd_int_enable(rhport); @@ -685,9 +697,7 @@ static void handle_rxflvl_irq(uint8_t rhport) { edpt->next_pid = cal_next_pid(edpt->next_pid, remain_packets); } else { if (remain_packets) { - // still more packet to send - bool const is_period = edpt_is_periodic(channel->hcchar_bm.ep_type); - channel_send_in_token(dwc2, channel, is_period); + channel_send_in_token(dwc2, channel); // still more packet to send } } break; @@ -711,17 +721,17 @@ static void handle_rxflvl_irq(uint8_t rhport) { } } -static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { +static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; bool is_done = false; if (hcint & HCINT_XFER_COMPLETE) { xfer->result = XFER_RESULT_SUCCESS; - channel_disable(dwc2, channel, is_period); + channel_disable(dwc2, channel); channel->hcintmsk &= ~HCINT_ACK; } else if (hcint & (HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_STALL)) { - channel_disable(dwc2, channel, is_period); + channel_disable(dwc2, channel); if (hcint & HCINT_XACT_ERR) { xfer->err_count++; channel->hcintmsk |= HCINT_ACK; @@ -743,14 +753,13 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_pe xfer->err_count = 0; } else if (hcint & HCINT_NAK) { // NAK received, re-enable channel if request queue is available - channel_send_in_token(dwc2, channel, is_period); + channel_send_in_token(dwc2, channel); } return is_done; } -static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { - (void) is_period; +static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; bool is_done = false; @@ -761,11 +770,11 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_p channel->hcintmsk &= ~HCINT_ACK; } else if (hcint & HCINT_STALL) { xfer->result = XFER_RESULT_STALLED; - channel_disable(dwc2, channel, is_period); + channel_disable(dwc2, channel); } else if (hcint & (HCINT_NAK | HCINT_XACT_ERR | HCINT_NYET)) { // clean up transfer so far, disable and start again later channel_xfer_cleanup(dwc2, ch_id); - channel_disable(dwc2, channel, is_period); + channel_disable(dwc2, channel); if (hcint & HCINT_XACT_ERR) { xfer->err_count++; channel->hcintmsk |= HCINT_ACK; @@ -800,8 +809,7 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_p #endif #if CFG_TUH_DWC2_DMA_ENABLE -static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { - (void) is_period; +static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; bool is_done = false; @@ -834,13 +842,16 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_peri } else if (hcint & (HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR)) { xfer->err_count = 0; channel->hcintmsk &= ~(HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR); + // if (hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR)) { + // TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,); + // hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; + // } } return is_done; } -static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { - (void) is_period; +static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; bool is_done = false; @@ -894,25 +905,27 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; - const bool is_period = edpt_is_periodic(hcchar_bm.ep_type); uint32_t hcint = channel->hcint; channel->hcint = (hcint & channel->hcintmsk); // clear enabled interrupts bool is_done; if (is_dma) { - // NOTE For DMA This is flow for core with OUT NAK enhancement from v2.71a + #if CFG_TUH_DWC2_DMA_ENABLE if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { - is_done = handle_channel_out_dma(dwc2, ch_id, is_period, hcint); + is_done = handle_channel_out_dma(dwc2, ch_id, hcint); } else { - is_done = handle_channel_in_dma(dwc2, ch_id, is_period, hcint); + is_done = handle_channel_in_dma(dwc2, ch_id, hcint); } + #endif } else { + #if CFG_TUH_DWC2_SLAVE_ENABLE if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { - is_done = handle_channel_out_slave(dwc2, ch_id, is_period, hcint); + is_done = handle_channel_out_slave(dwc2, ch_id, hcint); } else { - is_done = handle_channel_in_slave(dwc2, ch_id, is_period, hcint); + is_done = handle_channel_in_slave(dwc2, ch_id, hcint); } + #endif } if (is_done) { -- cgit v1.3.1 From 4f288c030ad92f9859cbc6b78ed86d06609c2ea5 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 1 Nov 2024 20:58:29 +0700 Subject: move buffer and buflen to hcd_endpoint_t to support periodic endpoint --- src/portable/synopsys/dwc2/dwc2_common.c | 12 +-- src/portable/synopsys/dwc2/hcd_dwc2.c | 175 +++++++++++++++++++------------ 2 files changed, 109 insertions(+), 78 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index a3c7e80cb..4d62cf3c6 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -224,15 +224,9 @@ bool dwc2_core_init(uint8_t rhport, bool is_highspeed) { dfifo_flush_tx(dwc2, 0x10); // all tx fifo dfifo_flush_rx(dwc2); - // Clear all pending interrupts - uint32_t int_mask; - - int_mask = dwc2->gintsts; - dwc2->gintsts |= int_mask; - - int_mask = dwc2->gotgint; - dwc2->gotgint |= int_mask; - + // Clear pending and disable all interrupts + dwc2->gintsts = 0xFFFFFFFFU; + dwc2->gotgint = 0xFFFFFFFFU; dwc2->gintmsk = 0; return true; diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 960a78ff3..c44ea436a 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -66,9 +66,13 @@ typedef struct { uint32_t hcsplt; dwc2_channel_split_t hcsplt_bm; }; + uint8_t next_pid; uint16_t frame_interval; uint16_t frame_countdown; + + uint8_t* buffer; + uint16_t buflen; } hcd_endpoint_t; // Additional info for each channel when it is active @@ -83,8 +87,6 @@ typedef struct { uint16_t xferred_bytes; // bytes that accumulate transferred though USB bus for the whole hcd_edpt_xfer(), which can // be composed of multiple channel_xfer_start() (retry with NAK/NYET) - uint16_t buf_len; - uint8_t* buf_start; uint16_t out_fifo_bytes; // bytes written to TX FIFO (may not be transferred on USB bus). } hcd_xfer_t; @@ -490,7 +492,6 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* static void channel_xfer_cleanup(dwc2_regs_t* dwc2, uint8_t ch_id) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; - TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX, ); hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; edpt->next_pid = channel->hctsiz_bm.pid; // save PID @@ -500,12 +501,12 @@ static void channel_xfer_cleanup(dwc2_regs_t* dwc2, uint8_t ch_id) { * was halted before its normal completion. (Can't use the hctsiz.xfersize field because that reflects the number of * bytes transferred via the AHB, not the USB). */ const uint16_t remain_packets = channel->hctsiz_bm.packet_count; - const uint16_t total_packets = cal_packet_count(xfer->buf_len, channel->hcchar_bm.ep_size); + const uint16_t total_packets = cal_packet_count(edpt->buflen, channel->hcchar_bm.ep_size); const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size; xfer->xferred_bytes += actual_bytes; - xfer->buf_start += actual_bytes; - xfer->buf_len -= actual_bytes; xfer->out_fifo_bytes = 0; + edpt->buffer += actual_bytes; + edpt->buflen -= actual_bytes; } static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) { @@ -525,8 +526,8 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) { channel->hcchar = (edpt->hcchar & ~HCCHAR_CHENA); // hctsiz: zero length packet still count as 1 - const uint16_t packet_count = cal_packet_count(xfer->buf_len, hcchar_bm->ep_size); - uint32_t hctsiz = (edpt->next_pid << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | xfer->buf_len; + const uint16_t packet_count = cal_packet_count(edpt->buflen, hcchar_bm->ep_size); + uint32_t hctsiz = (edpt->next_pid << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | edpt->buflen; if (xfer->do_ping && edpt->next_pid != HCTSIZ_PID_SETUP && hcchar_bm->ep_dir == TUSB_DIR_OUT) { hcd_devtree_info_t devtree_info; hcd_devtree_get_info(hcchar_bm->dev_addr, &devtree_info); @@ -547,14 +548,14 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) { // split: TODO support later channel->hcsplt = edpt->hcsplt; - channel->hcint = 0xFFFFFFFF; // clear all channel interrupts + channel->hcint = 0xFFFFFFFFU; // clear all channel interrupts if (dma_host_enabled(dwc2)) { uint32_t hcintmsk = HCINT_HALTED; channel->hcintmsk = hcintmsk; dwc2->haintmsk |= TU_BIT(ch_id); - channel->hcdma = (uint32_t) xfer->buf_start; + channel->hcdma = (uint32_t) edpt->buffer; if (hcchar_bm->ep_dir == TUSB_DIR_IN) { channel_send_in_token(dwc2, channel); @@ -580,7 +581,7 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) { channel_send_in_token(dwc2, channel); } else { channel->hcchar |= HCCHAR_CHENA; - if (xfer->buf_len > 0) { + if (edpt->buflen > 0) { // To prevent conflict with other channel, we will enable periodic/non-periodic FIFO empty interrupt accordingly // And write packet in the interrupt handler dwc2->gintmsk |= (is_period ? GINTSTS_PTX_FIFO_EMPTY : GINTSTS_NPTX_FIFO_EMPTY); @@ -605,9 +606,9 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * TU_ASSERT(ch_id < 16); // all channel are in used hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; xfer->ep_id = ep_id; - xfer->buf_start = buffer; - xfer->buf_len = buflen; xfer->result = XFER_RESULT_INVALID; + edpt->buffer = buffer; + edpt->buflen = buflen; if (ep_num == 0) { // update ep_dir since control endpoint can switch direction @@ -681,8 +682,10 @@ static void handle_rxflvl_irq(uint8_t rhport) { // In packet received const uint16_t byte_count = grxstsp_bm.byte_count; hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,); + hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; - dfifo_read_packet(dwc2, xfer->buf_start + xfer->xferred_bytes, byte_count); + dfifo_read_packet(dwc2, edpt->buffer + xfer->xferred_bytes, byte_count); xfer->xferred_bytes += byte_count; const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size; @@ -692,8 +695,6 @@ static void handle_rxflvl_irq(uint8_t rhport) { xfer->xferred_bytes -= remain_bytes; // update PID based on remain packets count - TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,); - hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; // update PID edpt->next_pid = cal_next_pid(edpt->next_pid, remain_packets); } else { if (remain_packets) { @@ -721,6 +722,40 @@ static void handle_rxflvl_irq(uint8_t rhport) { } } +// return true if there is still pending data and need more ISR +static bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { + // Use period txsts for both p/np to get request queue space available (1-bit difference, it is small enough) + volatile dwc2_hptxsts_t* txsts_bm = (volatile dwc2_hptxsts_t*) (is_periodic ? &dwc2->hptxsts : &dwc2->hnptxsts); + + const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); + for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { + dwc2_channel_t* channel = &dwc2->channel[ch_id]; + // skip writing to FIFO if channel is expecting halted. + if (!(channel->hcintmsk & HCINT_HALTED) && (channel->hcchar_bm.ep_dir == TUSB_DIR_OUT)) { + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); + hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; + + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + for (uint16_t i = 0; i < remain_packets; i++) { + const uint16_t remain_bytes = edpt->buflen - xfer->out_fifo_bytes; + const uint16_t xact_bytes = tu_min16(remain_bytes, channel->hcchar_bm.ep_size); + + // skip if there is not enough space in FIFO and RequestQueue. + // Packet's last word written to FIFO will trigger a request queue + if ((xact_bytes > (txsts_bm->fifo_available << 2)) || (txsts_bm->req_queue_available == 0)) { + return true; + } + + dfifo_write_packet(dwc2, ch_id, edpt->buffer + xfer->out_fifo_bytes, xact_bytes); + xfer->out_fifo_bytes += xact_bytes; + } + } + } + + return false; // all data written +} + static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; @@ -812,6 +847,8 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; + hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; + bool is_done = false; if (hcint & HCINT_HALTED) { @@ -819,7 +856,7 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci is_done = true; xfer->err_count = 0; const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size; - xfer->xferred_bytes += (xfer->buf_len - remain_bytes); + xfer->xferred_bytes += (edpt->buflen - remain_bytes); if (hcint & HCINT_STALL) { xfer->result = XFER_RESULT_STALLED; } else if (hcint & HCINT_BABBLE_ERR) { @@ -838,14 +875,23 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci // re-init channel TU_ASSERT(false); } + } else if (hcint & (HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR)) { + xfer->err_count = 0; + channel->hcintmsk &= ~(HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR); + if ((hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR))) { + edpt->next_pid = channel->hctsiz_bm.pid; // save PID + + if (edpt_is_periodic(channel->hcchar_bm.ep_type)){ + // for periodic, de-allocate channel, enable SOF set frame counter for later transfer + channel_dealloc(dwc2, ch_id); + edpt->frame_countdown = edpt->frame_interval; + dwc2->gintmsk |= GINTSTS_SOF; + } else { + // for control/bulk: try again immediately + TU_ASSERT(false); + } + } } - } else if (hcint & (HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR)) { - xfer->err_count = 0; - channel->hcintmsk &= ~(HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR); - // if (hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR)) { - // TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,); - // hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; - // } } return is_done; @@ -854,6 +900,8 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; + hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; + bool is_done = false; if (hcint & HCINT_HALTED) { @@ -862,7 +910,7 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc xfer->err_count = 0; if (hcint & HCINT_XFER_COMPLETE) { xfer->result = XFER_RESULT_SUCCESS; - xfer->xferred_bytes += xfer->buf_len; + xfer->xferred_bytes += edpt->buflen; } else { xfer->result = XFER_RESULT_STALLED; channel_xfer_cleanup(dwc2, ch_id); @@ -904,6 +952,7 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) { if (tu_bit_test(dwc2->haint, ch_id)) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,); dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; uint32_t hcint = channel->hcint; @@ -937,35 +986,21 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) { } } -// return true if there is still pending data and need more ISR -static bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { - // Use period txsts for both p/np to get request queue space available (1-bit difference, it is small enough) - volatile dwc2_hptxsts_t* txsts_bm = (volatile dwc2_hptxsts_t*) (is_periodic ? &dwc2->hptxsts : &dwc2->hnptxsts); - - const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); - for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { - dwc2_channel_t* channel = &dwc2->channel[ch_id]; - // skip writing to FIFO if channel is expecting halted. - if (!(channel->hcintmsk & HCINT_HALTED) && (channel->hcchar_bm.ep_dir == TUSB_DIR_OUT)) { - hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; - const uint16_t remain_packets = channel->hctsiz_bm.packet_count; - for (uint16_t i = 0; i < remain_packets; i++) { - const uint16_t remain_bytes = xfer->buf_len - xfer->out_fifo_bytes; - const uint16_t xact_bytes = tu_min16(remain_bytes, channel->hcchar_bm.ep_size); - - // skip if there is not enough space in FIFO and RequestQueue. - // Packet's last word written to FIFO will trigger a request queue - if ((xact_bytes > (txsts_bm->fifo_available << 2)) || (txsts_bm->req_queue_available == 0)) { - return true; - } - - dfifo_write_packet(dwc2, ch_id, xfer->buf_start + xfer->out_fifo_bytes, xact_bytes); - xfer->out_fifo_bytes += xact_bytes; - } - } - } - - return false; // all data written +// SOF is enabled for scheduled periodic transfer +static void handle_sof_irq(uint8_t rhport, bool in_isr) { + (void) in_isr; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + (void) dwc2; + // for(uint8_t ep_id; ep_id < CFG_TUH_DWC2_ENDPOINT_MAX; ep_id++) { + // hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; + // if (edpt->hcchar_bm.enable && edpt_is_periodic(edpt->hcchar_bm.ep_type) && edpt->frame_countdown > 0) { + // + // edpt->frame_countdown--; + // if (edpt->frame_countdown == 0) { + // channel_xfer_start(dwc2, ep_id); + // } + // } + // } } /* Handle Host Port interrupt, possible source are: @@ -1034,22 +1069,20 @@ static void handle_hprt_irq(uint8_t rhport, bool in_isr) { } /* Interrupt Hierarchy - HCINTn HPRT - | | - HAINT.CHn | - | | - GINTSTS : HCInt PrtInt NPTxFEmp PTxFEmpp RXFLVL - - + HCINTn HPRT + | | + HAINT.CHn | + | | + GINTSTS : HCInt | PrtInt | NPTxFEmp | PTxFEmpp | RXFLVL | SOF */ void hcd_int_handler(uint8_t rhport, bool in_isr) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - const uint32_t int_mask = dwc2->gintmsk; - const uint32_t int_status = dwc2->gintsts & int_mask; + const uint32_t gintmsk = dwc2->gintmsk; + const uint32_t gintsts = dwc2->gintsts & gintmsk; // TU_LOG1_HEX(int_status); - if (int_status & GINTSTS_CONIDSTSCHNG) { + if (gintsts & GINTSTS_CONIDSTSCHNG) { // Connector ID status change dwc2->gintsts = GINTSTS_CONIDSTSCHNG; @@ -1059,7 +1092,11 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { // TODO wait for SRP if OTG } - if (int_status & GINTSTS_HPRTINT) { + if (gintsts & GINTSTS_SOF) { + handle_sof_irq(rhport, in_isr); + } + + if (gintsts & GINTSTS_HPRTINT) { // Host port interrupt: source is cleared in HPRT register // TU_LOG1_HEX(dwc2->hprt); handle_hprt_irq(rhport, in_isr); @@ -1067,7 +1104,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { #if CFG_TUH_DWC2_SLAVE_ENABLE // RxFIFO non-empty interrupt handling, must be handled before HCINT - if (int_status & GINTSTS_RXFLVL) { + if (gintsts & GINTSTS_RXFLVL) { // RXFLVL bit is read-only dwc2->gintmsk &= ~GINTSTS_RXFLVL; // disable RXFLVL interrupt while reading @@ -1077,9 +1114,8 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { dwc2->gintmsk |= GINTSTS_RXFLVL; } -#endif - if (int_status & GINTSTS_NPTX_FIFO_EMPTY) { + if (gintsts & GINTSTS_NPTX_FIFO_EMPTY) { // NPTX FIFO empty interrupt, this is read-only and cleared by hardware when FIFO is written const bool more_isr = handle_txfifo_empty(dwc2, false); if (!more_isr) { @@ -1087,8 +1123,9 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { dwc2->gintmsk &= ~GINTSTS_NPTX_FIFO_EMPTY; } } +#endif - if (int_status & GINTSTS_HCINT) { + if (gintsts & GINTSTS_HCINT) { // Host Channel interrupt: source is cleared in HCINT register // must bee handled after TX FIFO empty handle_channel_irq(rhport, in_isr); -- cgit v1.3.1 From c96e04159e3420f156a29bb1de899a379086ff4a Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 1 Nov 2024 21:31:01 +0700 Subject: implement periodic endpoint using sof --- src/portable/synopsys/dwc2/hcd_dwc2.c | 61 ++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index c44ea436a..b6509ad4e 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -69,7 +69,7 @@ typedef struct { uint8_t next_pid; uint16_t frame_interval; - uint16_t frame_countdown; + uint16_t frame_countdown; // count down to make a transfer for periodic endpoint (from interval) uint8_t* buffer; uint16_t buflen; @@ -592,6 +592,17 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) { return true; } +// kick-off transfer with an endpoint +static bool edpt_xfer_kickoff(dwc2_regs_t* dwc2, uint8_t ep_id) { + uint8_t ch_id = channel_alloc(dwc2); + TU_ASSERT(ch_id < 16); // all channel are in used + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + xfer->ep_id = ep_id; + xfer->result = XFER_RESULT_INVALID; + + return channel_xfer_start(dwc2, ch_id); +} + // Submit a transfer, when complete hcd_event_xfer_complete() must be invoked bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); @@ -602,11 +613,6 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; - uint8_t ch_id = channel_alloc(dwc2); - TU_ASSERT(ch_id < 16); // all channel are in used - hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; - xfer->ep_id = ep_id; - xfer->result = XFER_RESULT_INVALID; edpt->buffer = buffer; edpt->buflen = buflen; @@ -615,7 +621,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * edpt->hcchar_bm.ep_dir = ep_dir; } - return channel_xfer_start(dwc2, ch_id); + return edpt_xfer_kickoff(dwc2, ep_id); } // Abort a queued transfer. Note: it can only abort transfer that has not been started @@ -880,7 +886,6 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci channel->hcintmsk &= ~(HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR); if ((hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR))) { edpt->next_pid = channel->hctsiz_bm.pid; // save PID - if (edpt_is_periodic(channel->hcchar_bm.ep_type)){ // for periodic, de-allocate channel, enable SOF set frame counter for later transfer channel_dealloc(dwc2, ch_id); @@ -987,20 +992,27 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) { } // SOF is enabled for scheduled periodic transfer -static void handle_sof_irq(uint8_t rhport, bool in_isr) { +static bool handle_sof_irq(uint8_t rhport, bool in_isr) { (void) in_isr; dwc2_regs_t* dwc2 = DWC2_REG(rhport); - (void) dwc2; - // for(uint8_t ep_id; ep_id < CFG_TUH_DWC2_ENDPOINT_MAX; ep_id++) { - // hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; - // if (edpt->hcchar_bm.enable && edpt_is_periodic(edpt->hcchar_bm.ep_type) && edpt->frame_countdown > 0) { - // - // edpt->frame_countdown--; - // if (edpt->frame_countdown == 0) { - // channel_xfer_start(dwc2, ep_id); - // } - // } - // } + + bool more_isr = false; + + for(uint8_t ep_id = 0; ep_id < CFG_TUH_DWC2_ENDPOINT_MAX; ep_id++) { + hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; + if (edpt->hcchar_bm.enable && edpt_is_periodic(edpt->hcchar_bm.ep_type) && edpt->frame_countdown > 0) { + edpt->frame_countdown--; + if (edpt->frame_countdown == 0) { + if (!edpt_xfer_kickoff(dwc2, ep_id)) { + edpt->frame_countdown = 1; // failed to start (out of channel), try again next frame + } + } + + more_isr = true; + } + } + + return more_isr; } /* Handle Host Port interrupt, possible source are: @@ -1093,7 +1105,10 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { } if (gintsts & GINTSTS_SOF) { - handle_sof_irq(rhport, in_isr); + const bool more_sof = handle_sof_irq(rhport, in_isr); + if (!more_sof) { + dwc2->gintmsk &= ~GINTSTS_SOF; + } } if (gintsts & GINTSTS_HPRTINT) { @@ -1117,8 +1132,8 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { if (gintsts & GINTSTS_NPTX_FIFO_EMPTY) { // NPTX FIFO empty interrupt, this is read-only and cleared by hardware when FIFO is written - const bool more_isr = handle_txfifo_empty(dwc2, false); - if (!more_isr) { + const bool more_nptxfe = handle_txfifo_empty(dwc2, false); + if (!more_nptxfe) { // no more pending packet, disable interrupt dwc2->gintmsk &= ~GINTSTS_NPTX_FIFO_EMPTY; } -- cgit v1.3.1 From a0f75e5b6742c5365bbcea079dab75242230fe60 Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 2 Nov 2024 00:20:40 +0700 Subject: fix slave in nak & ack order --- src/portable/synopsys/dwc2/hcd_dwc2.c | 109 ++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index b6509ad4e..42a5b9fbd 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -87,7 +87,7 @@ typedef struct { uint16_t xferred_bytes; // bytes that accumulate transferred though USB bus for the whole hcd_edpt_xfer(), which can // be composed of multiple channel_xfer_start() (retry with NAK/NYET) - uint16_t out_fifo_bytes; // bytes written to TX FIFO (may not be transferred on USB bus). + uint16_t txfifo_bytes; // bytes written to TX FIFO (may not be transferred on USB bus). } hcd_xfer_t; typedef struct { @@ -489,22 +489,29 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* } // clean up channel after part of transfer is done but the whole urb is not complete -static void channel_xfer_cleanup(dwc2_regs_t* dwc2, uint8_t ch_id) { +static void channel_xfer_wrapup(dwc2_regs_t* dwc2, uint8_t ch_id) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; edpt->next_pid = channel->hctsiz_bm.pid; // save PID - /* Must use the hctsiz.pktcnt field to determine how much data has been transferred. This field reflects the number - * of packets that have been transferred via the USB. This is always an integral number of packets if the transfer - * was halted before its normal completion. (Can't use the hctsiz.xfersize field because that reflects the number of - * bytes transferred via the AHB, not the USB). */ - const uint16_t remain_packets = channel->hctsiz_bm.packet_count; - const uint16_t total_packets = cal_packet_count(edpt->buflen, channel->hcchar_bm.ep_size); - const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size; + /* Since hctsiz.xfersize field reflects the number of bytes transferred via the AHB, not the USB) + * For IN: we can use hctsiz.xfersize as remaining bytes. + * For OUT: Must use the hctsiz.pktcnt field to determine how much data has been transferred. This field reflects the + * number of packets that have been transferred via the USB. This is always an integral number of packets if the + * transfer was halted before its normal completion. + */ + uint16_t actual_bytes; + if (channel->hcchar_bm.ep_dir == TUSB_DIR_IN) { + actual_bytes = edpt->buflen - channel->hctsiz_bm.xfer_size; + } else { + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + const uint16_t total_packets = cal_packet_count(edpt->buflen, channel->hcchar_bm.ep_size); + actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size; + } + xfer->txfifo_bytes = 0; xfer->xferred_bytes += actual_bytes; - xfer->out_fifo_bytes = 0; edpt->buffer += actual_bytes; edpt->buflen -= actual_bytes; } @@ -517,7 +524,7 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) { bool const is_period = edpt_is_periodic(hcchar_bm->ep_type); // clear previous state - xfer->out_fifo_bytes = 0; + xfer->txfifo_bytes = 0; // hchar: restore but don't enable yet if (is_period) { @@ -691,19 +698,15 @@ static void handle_rxflvl_irq(uint8_t rhport) { TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,); hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; - dfifo_read_packet(dwc2, edpt->buffer + xfer->xferred_bytes, byte_count); - xfer->xferred_bytes += byte_count; - - const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size; - const uint16_t remain_packets = channel->hctsiz_bm.packet_count; - if (byte_count < channel->hcchar_bm.ep_size) { - // short packet, minus remaining bytes - xfer->xferred_bytes -= remain_bytes; + if (byte_count) { + dfifo_read_packet(dwc2, edpt->buffer + xfer->xferred_bytes, byte_count); + xfer->xferred_bytes += byte_count; - // update PID based on remain packets count - edpt->next_pid = cal_next_pid(edpt->next_pid, remain_packets); - } else { - if (remain_packets) { + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + if (byte_count < edpt->hcchar_bm.ep_size) { + // short packet: update PID based on remain packets count + edpt->next_pid = cal_next_pid(edpt->next_pid, remain_packets); + } if (remain_packets) { channel_send_in_token(dwc2, channel); // still more packet to send } } @@ -744,7 +747,7 @@ static bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { const uint16_t remain_packets = channel->hctsiz_bm.packet_count; for (uint16_t i = 0; i < remain_packets; i++) { - const uint16_t remain_bytes = edpt->buflen - xfer->out_fifo_bytes; + const uint16_t remain_bytes = edpt->buflen - xfer->txfifo_bytes; const uint16_t xact_bytes = tu_min16(remain_bytes, channel->hcchar_bm.ep_size); // skip if there is not enough space in FIFO and RequestQueue. @@ -753,8 +756,8 @@ static bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { return true; } - dfifo_write_packet(dwc2, ch_id, edpt->buffer + xfer->out_fifo_bytes, xact_bytes); - xfer->out_fifo_bytes += xact_bytes; + dfifo_write_packet(dwc2, ch_id, edpt->buffer + xfer->txfifo_bytes, xact_bytes); + xfer->txfifo_bytes += xact_bytes; } } } @@ -772,11 +775,15 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h channel_disable(dwc2, channel); channel->hcintmsk &= ~HCINT_ACK; } else if (hcint & (HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_STALL)) { - channel_disable(dwc2, channel); - if (hcint & HCINT_XACT_ERR) { + if (hcint & HCINT_STALL) { + xfer->result = XFER_RESULT_STALLED; + } else if (hcint & HCINT_BABBLE_ERR) { + xfer->result = XFER_RESULT_FAILED; + } else if (hcint & HCINT_XACT_ERR) { xfer->err_count++; channel->hcintmsk |= HCINT_ACK; } + channel_disable(dwc2, channel); } else if (hcint & HCINT_HALTED) { channel->hcintmsk &= ~HCINT_HALTED; if (xfer->result != XFER_RESULT_INVALID) { @@ -786,15 +793,17 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h is_done = true; } else { // Re-initialize Channel + TU_ASSERT(false); } - } else if (hcint & HCINT_ACK) { - xfer->err_count = 0; - channel->hcintmsk &= ~HCINT_ACK; } else if (hcint & HCINT_DATATOGGLE_ERR) { xfer->err_count = 0; + TU_ASSERT(false); } else if (hcint & HCINT_NAK) { // NAK received, re-enable channel if request queue is available channel_send_in_token(dwc2, channel); + } else if (hcint & HCINT_ACK) { + xfer->err_count = 0; + channel->hcintmsk &= ~HCINT_ACK; } return is_done; @@ -814,7 +823,7 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t channel_disable(dwc2, channel); } else if (hcint & (HCINT_NAK | HCINT_XACT_ERR | HCINT_NYET)) { // clean up transfer so far, disable and start again later - channel_xfer_cleanup(dwc2, ch_id); + channel_xfer_wrapup(dwc2, ch_id); channel_disable(dwc2, channel); if (hcint & HCINT_XACT_ERR) { xfer->err_count++; @@ -841,8 +850,8 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t } if (is_done) { - xfer->xferred_bytes += xfer->out_fifo_bytes; - xfer->out_fifo_bytes = 0; + xfer->xferred_bytes += xfer->txfifo_bytes; + xfer->txfifo_bytes = 0; } return is_done; @@ -878,8 +887,16 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci xfer->result = XFER_RESULT_FAILED; } else { channel->hcintmsk |= HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR; - // re-init channel - TU_ASSERT(false); + edpt->next_pid = channel->hctsiz_bm.pid; // save PID + if (edpt_is_periodic(channel->hcchar_bm.ep_type)){ + // for periodic, de-allocate channel, enable SOF set frame counter for later transfer + channel_dealloc(dwc2, ch_id); + edpt->frame_countdown = edpt->frame_interval; + dwc2->gintmsk |= GINTSTS_SOF; + } else { + // for control/bulk: try again immediately + TU_ASSERT(false); + } } } else if (hcint & (HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR)) { xfer->err_count = 0; @@ -918,14 +935,14 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc xfer->xferred_bytes += edpt->buflen; } else { xfer->result = XFER_RESULT_STALLED; - channel_xfer_cleanup(dwc2, ch_id); + channel_xfer_wrapup(dwc2, ch_id); } channel->hcintmsk &= ~HCINT_ACK; } else if (hcint & HCINT_XACT_ERR) { if (hcint & (HCINT_NAK | HCINT_NYET | HCINT_ACK)) { xfer->err_count = 0; // clean up transfer so far and start again - channel_xfer_cleanup(dwc2, ch_id); + channel_xfer_wrapup(dwc2, ch_id); channel_xfer_start(dwc2, ch_id); } else { xfer->err_count++; @@ -934,7 +951,7 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc is_done = true; } else { // clean up transfer so far and start again - channel_xfer_cleanup(dwc2, ch_id); + channel_xfer_wrapup(dwc2, ch_id); channel_xfer_start(dwc2, ch_id); } } @@ -961,7 +978,7 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) { dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; uint32_t hcint = channel->hcint; - channel->hcint = (hcint & channel->hcintmsk); // clear enabled interrupts + channel->hcint = hcint; bool is_done; if (is_dma) { @@ -1117,6 +1134,12 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { handle_hprt_irq(rhport, in_isr); } + if (gintsts & GINTSTS_HCINT) { + // Host Channel interrupt: source is cleared in HCINT register + // must be handled after TX FIFO empty + handle_channel_irq(rhport, in_isr); + } + #if CFG_TUH_DWC2_SLAVE_ENABLE // RxFIFO non-empty interrupt handling, must be handled before HCINT if (gintsts & GINTSTS_RXFLVL) { @@ -1139,12 +1162,6 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { } } #endif - - if (gintsts & GINTSTS_HCINT) { - // Host Channel interrupt: source is cleared in HCINT register - // must bee handled after TX FIFO empty - handle_channel_irq(rhport, in_isr); - } } #endif -- cgit v1.3.1 From b648a38ae84181757d4c8bdbd3f92001448ac805 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Fri, 1 Nov 2024 19:49:00 +0100 Subject: Fix Warning[Pe188]: enumerated type mixed with another type. --- src/tusb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/tusb.c b/src/tusb.c index e6f8055b7..3ab2004d2 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -41,7 +41,7 @@ #define TUP_USBIP_CONTROLLER_NUM 2 -static tusb_role_t _rhport_role[TUP_USBIP_CONTROLLER_NUM] = { 0 }; +static tusb_role_t _rhport_role[TUP_USBIP_CONTROLLER_NUM] = { TUSB_ROLE_INVALID }; //--------------------------------------------------------------------+ // Public API -- cgit v1.3.1 From 56f53a61329f3a3a492530e364f543f3a419297f Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Sat, 2 Nov 2024 12:45:32 +0100 Subject: Replace cache clean/invalidate by MPU config. --- hw/bsp/imxrt/family.c | 450 +++++++++++++++++++++++++++--- hw/bsp/imxrt/family.cmake | 1 + hw/bsp/imxrt/family.mk | 3 +- src/portable/chipidea/ci_hs/ci_hs_imxrt.h | 43 +-- src/portable/chipidea/ci_hs/dcd_ci_hs.c | 43 --- 5 files changed, 425 insertions(+), 115 deletions(-) (limited to 'src') diff --git a/hw/bsp/imxrt/family.c b/hw/bsp/imxrt/family.c index 6087ee37d..d3a228f0f 100644 --- a/hw/bsp/imxrt/family.c +++ b/hw/bsp/imxrt/family.c @@ -24,30 +24,43 @@ * This file is part of the TinyUSB stack. */ -#include "bsp/board_api.h" +#include "board.h" #include "board/clock_config.h" #include "board/pin_mux.h" -#include "board.h" +#include "bsp/board_api.h" // Suppress warning caused by mcu driver #ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-parameter" #endif +#include "fsl_clock.h" #include "fsl_device_registers.h" #include "fsl_gpio.h" #include "fsl_iomuxc.h" -#include "fsl_clock.h" #include "fsl_lpuart.h" #include "fsl_ocotp.h" #ifdef __GNUC__ -#pragma GCC diagnostic pop + #pragma GCC diagnostic pop #endif +/* --- Note about USB buffer RAM --- +For M7 core it's recommanded to put USB buffer in DTCM for better performance (flexspi_nor linker default) +Otherwise you have to put the buffer in a non-cacheable section by configurate MPU manually or using BOARD_ConfigMPU(): +- Define CFG_TUSB_MEM_SECTION=__attribute__((section("NonCacheable"))) +- (IAR only) Change __NCACHE_REGION_SIZE in linker script to cover the size of non-cacheable section, multiple of 2^N + +For secondary M4 core, the USB controller doesn't support transfer from DTCM so OCRAM must be used: +- __NCACHE_REGION_SIZE is defined by the linker script by default +- Define CFG_TUSB_MEM_SECTION=__attribute__((section("NonCacheable"))) +*/ + +static void BOARD_ConfigMPU(void); + // needed by fsl_flexspi_nor_boot -TU_ATTR_USED const uint8_t dcd_data[] = { 0x00 }; +TU_ATTR_USED const uint8_t dcd_data[] = {0x00}; //--------------------------------------------------------------------+ // @@ -59,20 +72,20 @@ TU_ATTR_USED const uint8_t dcd_data[] = { 0x00 }; #endif static void init_usb_phy(uint8_t usb_id) { - USBPHY_Type* usb_phy; + USBPHY_Type *usb_phy; if (usb_id == 0) { usb_phy = USBPHY1; CLOCK_EnableUsbhs0PhyPllClock(kCLOCK_Usbphy480M, BOARD_XTAL0_CLK_HZ); CLOCK_EnableUsbhs0Clock(kCLOCK_Usb480M, BOARD_XTAL0_CLK_HZ); } - #ifdef USBPHY2 +#ifdef USBPHY2 else if (usb_id == 1) { usb_phy = USBPHY2; CLOCK_EnableUsbhs1PhyPllClock(kCLOCK_Usbphy480M, BOARD_XTAL0_CLK_HZ); CLOCK_EnableUsbhs1Clock(kCLOCK_Usb480M, BOARD_XTAL0_CLK_HZ); } - #endif +#endif else { return; } @@ -91,13 +104,8 @@ static void init_usb_phy(uint8_t usb_id) { usb_phy->TX = phytx; } -void board_init(void) -{ - // make sure the dcache is on. -#if defined(__DCACHE_PRESENT) && __DCACHE_PRESENT - if (SCB_CCR_DC_Msk != (SCB_CCR_DC_Msk & SCB->CCR)) SCB_EnableDCache(); -#endif - +void board_init(void) { + BOARD_ConfigMPU(); BOARD_InitPins(); BOARD_BootClockRUN(); SystemCoreClockUpdate(); @@ -113,9 +121,9 @@ void board_init(void) #elif CFG_TUSB_OS == OPT_OS_FREERTOS // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher ) NVIC_SetPriority(USB_OTG1_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); -#ifdef USBPHY2 + #ifdef USBPHY2 NVIC_SetPriority(USB_OTG2_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); -#endif + #endif #endif board_led_write(true); @@ -127,19 +135,397 @@ void board_init(void) uart_config.enableTx = true; uart_config.enableRx = true; - if ( kStatus_Success != LPUART_Init(UART_PORT, &uart_config, UART_CLK_ROOT) ) { + if (kStatus_Success != LPUART_Init(UART_PORT, &uart_config, UART_CLK_ROOT)) { // failed to init uart, probably baudrate is not supported // TU_BREAKPOINT(); } //------------- USB -------------// // Note: RT105x RT106x and later have dual USB controllers. - init_usb_phy(0); // USB0 + init_usb_phy(0);// USB0 #ifdef USBPHY2 - init_usb_phy(1); // USB1 + init_usb_phy(1);// USB1 #endif } +/* MPU configuration. */ +#if __CORTEX_M == 7 +static void BOARD_ConfigMPU(void) { + #if defined(__CC_ARM) || defined(__ARMCC_VERSION) + extern uint32_t Image$$RW_m_ncache$$Base[]; + /* RW_m_ncache_unused is a auxiliary region which is used to get the whole size of noncache section */ + extern uint32_t Image$$RW_m_ncache_unused$$Base[]; + extern uint32_t Image$$RW_m_ncache_unused$$ZI$$Limit[]; + uint32_t nonCacheStart = (uint32_t) Image$$RW_m_ncache$$Base; + uint32_t size = ((uint32_t) Image$$RW_m_ncache_unused$$Base == nonCacheStart) ? 0 : ((uint32_t) Image$$RW_m_ncache_unused$$ZI$$Limit - nonCacheStart); + #elif defined(__MCUXPRESSO) + #if defined(__USE_SHMEM) + extern uint32_t __base_rpmsg_sh_mem; + extern uint32_t __top_rpmsg_sh_mem; + uint32_t nonCacheStart = (uint32_t) (&__base_rpmsg_sh_mem); + uint32_t size = (uint32_t) (&__top_rpmsg_sh_mem) - nonCacheStart; + #else + extern uint32_t __base_NCACHE_REGION; + extern uint32_t __top_NCACHE_REGION; + uint32_t nonCacheStart = (uint32_t) (&__base_NCACHE_REGION); + uint32_t size = (uint32_t) (&__top_NCACHE_REGION) - nonCacheStart; + #endif + #elif defined(__ICCARM__) || defined(__GNUC__) + extern uint32_t __NCACHE_REGION_START[]; + extern uint32_t __NCACHE_REGION_SIZE[]; + uint32_t nonCacheStart = (uint32_t) __NCACHE_REGION_START; + uint32_t size = (uint32_t) __NCACHE_REGION_SIZE; + #endif + volatile uint32_t i = 0; + + #if defined(__ICACHE_PRESENT) && __ICACHE_PRESENT + /* Disable I cache and D cache */ + if (SCB_CCR_IC_Msk == (SCB_CCR_IC_Msk & SCB->CCR)) { + SCB_DisableICache(); + } + #endif + #if defined(__DCACHE_PRESENT) && __DCACHE_PRESENT + if (SCB_CCR_DC_Msk == (SCB_CCR_DC_Msk & SCB->CCR)) { + SCB_DisableDCache(); + } + #endif + + /* Disable MPU */ + ARM_MPU_Disable(); + + /* MPU configure: + * Use ARM_MPU_RASR(DisableExec, AccessPermission, TypeExtField, IsShareable, IsCacheable, IsBufferable, + * SubRegionDisable, Size) + * API in mpu_armv7.h. + * param DisableExec Instruction access (XN) disable bit,0=instruction fetches enabled, 1=instruction fetches + * disabled. + * param AccessPermission Data access permissions, allows you to configure read/write access for User and + * Privileged mode. + * Use MACROS defined in mpu_armv7.h: + * ARM_MPU_AP_NONE/ARM_MPU_AP_PRIV/ARM_MPU_AP_URO/ARM_MPU_AP_FULL/ARM_MPU_AP_PRO/ARM_MPU_AP_RO + * Combine TypeExtField/IsShareable/IsCacheable/IsBufferable to configure MPU memory access attributes. + * TypeExtField IsShareable IsCacheable IsBufferable Memory Attribute Shareability Cache + * 0 x 0 0 Strongly Ordered shareable + * 0 x 0 1 Device shareable + * 0 0 1 0 Normal not shareable Outer and inner write + * through no write allocate + * 0 0 1 1 Normal not shareable Outer and inner write + * back no write allocate + * 0 1 1 0 Normal shareable Outer and inner write + * through no write allocate + * 0 1 1 1 Normal shareable Outer and inner write + * back no write allocate + * 1 0 0 0 Normal not shareable outer and inner + * noncache + * 1 1 0 0 Normal shareable outer and inner + * noncache + * 1 0 1 1 Normal not shareable outer and inner write + * back write/read acllocate + * 1 1 1 1 Normal shareable outer and inner write + * back write/read acllocate + * 2 x 0 0 Device not shareable + * Above are normal use settings, if your want to see more details or want to config different inner/outer cache + * policy. + * please refer to Table 4-55 /4-56 in arm cortex-M7 generic user guide + * param SubRegionDisable Sub-region disable field. 0=sub-region is enabled, 1=sub-region is disabled. + * param Size Region size of the region to be configured. use ARM_MPU_REGION_SIZE_xxx MACRO in + * mpu_armv7.h. + */ + + /* + * Add default region to deny access to whole address space to workaround speculative prefetch. + * Refer to Arm errata 1013783-B for more details. + * + */ + /* Region 0 setting: Instruction access disabled, No data access permission. */ + MPU->RBAR = ARM_MPU_RBAR(0, 0x00000000U); + MPU->RASR = ARM_MPU_RASR(1, ARM_MPU_AP_NONE, 0, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_4GB); + + /* Region 1 setting: Memory with Device type, not shareable, non-cacheable. */ + MPU->RBAR = ARM_MPU_RBAR(1, 0x80000000U); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_512MB); + + /* Region 2 setting: Memory with Device type, not shareable, non-cacheable. */ + MPU->RBAR = ARM_MPU_RBAR(2, 0x60000000U); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_512MB); + + /* Region 3 setting: Memory with Device type, not shareable, non-cacheable. */ + MPU->RBAR = ARM_MPU_RBAR(3, 0x00000000U); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_1GB); + + /* Region 4 setting: Memory with Normal type, not shareable, outer/inner write back */ + MPU->RBAR = ARM_MPU_RBAR(4, 0x00000000U); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_256KB); + + /* Region 5 setting: Memory with Normal type, not shareable, outer/inner write back */ + MPU->RBAR = ARM_MPU_RBAR(5, 0x20000000U); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_256KB); + + #if defined(CACHE_MODE_WRITE_THROUGH) && CACHE_MODE_WRITE_THROUGH + /* Region 6 setting: Memory with Normal type, not shareable, write through */ + MPU->RBAR = ARM_MPU_RBAR(6, 0x20200000U); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 0, 0, ARM_MPU_REGION_SIZE_1MB); + + /* Region 7 setting: Memory with Normal type, not shareable, write trough */ + MPU->RBAR = ARM_MPU_RBAR(7, 0x20300000U); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 0, 0, ARM_MPU_REGION_SIZE_512KB); + #else + /* Region 6 setting: Memory with Normal type, not shareable, outer/inner write back */ + MPU->RBAR = ARM_MPU_RBAR(6, 0x20200000U); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_1MB); + + /* Region 7 setting: Memory with Normal type, not shareable, outer/inner write back */ + MPU->RBAR = ARM_MPU_RBAR(7, 0x20300000U); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_512KB); + #endif + + #if defined(XIP_EXTERNAL_FLASH) && (XIP_EXTERNAL_FLASH == 1) + /* Region 8 setting: Memory with Normal type, not shareable, outer/inner write back. */ + MPU->RBAR = ARM_MPU_RBAR(8, 0x30000000U); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_RO, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_16MB); + #endif + + #ifdef USE_SDRAM + #if defined(CACHE_MODE_WRITE_THROUGH) && CACHE_MODE_WRITE_THROUGH + /* Region 9 setting: Memory with Normal type, not shareable, write trough */ + MPU->RBAR = ARM_MPU_RBAR(9, 0x80000000U); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 0, 0, ARM_MPU_REGION_SIZE_64MB); + #else + /* Region 9 setting: Memory with Normal type, not shareable, outer/inner write back */ + MPU->RBAR = ARM_MPU_RBAR(9, 0x80000000U); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 1, 0, ARM_MPU_REGION_SIZE_64MB); + #endif + #endif + + while ((size >> i) > 0x1U) { + i++; + } + + if (i != 0) { + /* The MPU region size should be 2^N, 5<=N<=32, region base should be multiples of size. */ + assert(!(nonCacheStart % size)); + assert(size == (uint32_t) (1 << i)); + assert(i >= 5); + + /* Region 10 setting: Memory with Normal type, not shareable, non-cacheable */ + MPU->RBAR = ARM_MPU_RBAR(10, nonCacheStart); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 1, 0, 0, 0, 0, i - 1); + } + + /* Region 11 setting: Memory with Device type, not shareable, non-cacheable */ + MPU->RBAR = ARM_MPU_RBAR(11, 0x40000000); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_16MB); + + /* Region 12 setting: Memory with Device type, not shareable, non-cacheable */ + MPU->RBAR = ARM_MPU_RBAR(12, 0x41000000); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_2MB); + + /* Region 13 setting: Memory with Device type, not shareable, non-cacheable */ + MPU->RBAR = ARM_MPU_RBAR(13, 0x41400000); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_1MB); + + /* Region 14 setting: Memory with Device type, not shareable, non-cacheable */ + MPU->RBAR = ARM_MPU_RBAR(14, 0x41800000); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_2MB); + + /* Region 15 setting: Memory with Device type, not shareable, non-cacheable */ + MPU->RBAR = ARM_MPU_RBAR(15, 0x42000000); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, ARM_MPU_REGION_SIZE_1MB); + + /* Enable MPU */ + ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk); + + /* Enable I cache and D cache */ + #if defined(__DCACHE_PRESENT) && __DCACHE_PRESENT + SCB_EnableDCache(); + #endif + #if defined(__ICACHE_PRESENT) && __ICACHE_PRESENT + SCB_EnableICache(); + #endif +} +#elif __CORTEX_M == 4 +void BOARD_ConfigMPU(void) { + #if defined(__CC_ARM) || defined(__ARMCC_VERSION) + extern uint32_t Image$$RW_m_ncache$$Base[]; + /* RW_m_ncache_unused is a auxiliary region which is used to get the whole size of noncache section */ + extern uint32_t Image$$RW_m_ncache_unused$$Base[]; + extern uint32_t Image$$RW_m_ncache_unused$$ZI$$Limit[]; + uint32_t nonCacheStart = (uint32_t) Image$$RW_m_ncache$$Base; + uint32_t nonCacheSize = ((uint32_t) Image$$RW_m_ncache_unused$$Base == nonCacheStart) ? 0 : ((uint32_t) Image$$RW_m_ncache_unused$$ZI$$Limit - nonCacheStart); + #elif defined(__MCUXPRESSO) + extern uint32_t __base_NCACHE_REGION; + extern uint32_t __top_NCACHE_REGION; + uint32_t nonCacheStart = (uint32_t) (&__base_NCACHE_REGION); + uint32_t nonCacheSize = (uint32_t) (&__top_NCACHE_REGION) - nonCacheStart; + #elif defined(__ICCARM__) || defined(__GNUC__) + extern uint32_t __NCACHE_REGION_START[]; + extern uint32_t __NCACHE_REGION_SIZE[]; + uint32_t nonCacheStart = (uint32_t) __NCACHE_REGION_START; + uint32_t nonCacheSize = (uint32_t) __NCACHE_REGION_SIZE; + #endif + #if defined(__USE_SHMEM) + #if defined(__CC_ARM) || defined(__ARMCC_VERSION) + extern uint32_t Image$$RPMSG_SH_MEM$$Base[]; + /* RPMSG_SH_MEM_unused is a auxiliary region which is used to get the whole size of RPMSG_SH_MEM section */ + extern uint32_t Image$$RPMSG_SH_MEM_unused$$Base[]; + extern uint32_t Image$$RPMSG_SH_MEM_unused$$ZI$$Limit[]; + uint32_t rpmsgShmemStart = (uint32_t) Image$$RPMSG_SH_MEM$$Base; + uint32_t rpmsgShmemSize = (uint32_t) Image$$RPMSG_SH_MEM_unused$$ZI$$Limit - rpmsgShmemStart; + #elif defined(__MCUXPRESSO) + extern uint32_t __base_rpmsg_sh_mem; + extern uint32_t __top_rpmsg_sh_mem; + uint32_t rpmsgShmemStart = (uint32_t) (&__base_rpmsg_sh_mem); + uint32_t rpmsgShmemSize = (uint32_t) (&__top_rpmsg_sh_mem) - rpmsgShmemStart; + #elif defined(__ICCARM__) || defined(__GNUC__) + extern uint32_t __RPMSG_SH_MEM_START[]; + extern uint32_t __RPMSG_SH_MEM_SIZE[]; + uint32_t rpmsgShmemStart = (uint32_t) __RPMSG_SH_MEM_START; + uint32_t rpmsgShmemSize = (uint32_t) __RPMSG_SH_MEM_SIZE; + #endif + #endif + uint32_t i = 0; + + /* Only config non-cacheable region on system bus */ + assert(nonCacheStart >= 0x20000000); + + /* Disable code bus cache */ + if (LMEM_PCCCR_ENCACHE_MASK == (LMEM_PCCCR_ENCACHE_MASK & LMEM->PCCCR)) { + /* Enable the processor code bus to push all modified lines. */ + LMEM->PCCCR |= LMEM_PCCCR_PUSHW0_MASK | LMEM_PCCCR_PUSHW1_MASK | LMEM_PCCCR_GO_MASK; + /* Wait until the cache command completes. */ + while ((LMEM->PCCCR & LMEM_PCCCR_GO_MASK) != 0U) { + } + /* As a precaution clear the bits to avoid inadvertently re-running this command. */ + LMEM->PCCCR &= ~(LMEM_PCCCR_PUSHW0_MASK | LMEM_PCCCR_PUSHW1_MASK); + /* Now disable the cache. */ + LMEM->PCCCR &= ~LMEM_PCCCR_ENCACHE_MASK; + } + + /* Disable system bus cache */ + if (LMEM_PSCCR_ENCACHE_MASK == (LMEM_PSCCR_ENCACHE_MASK & LMEM->PSCCR)) { + /* Enable the processor system bus to push all modified lines. */ + LMEM->PSCCR |= LMEM_PSCCR_PUSHW0_MASK | LMEM_PSCCR_PUSHW1_MASK | LMEM_PSCCR_GO_MASK; + /* Wait until the cache command completes. */ + while ((LMEM->PSCCR & LMEM_PSCCR_GO_MASK) != 0U) { + } + /* As a precaution clear the bits to avoid inadvertently re-running this command. */ + LMEM->PSCCR &= ~(LMEM_PSCCR_PUSHW0_MASK | LMEM_PSCCR_PUSHW1_MASK); + /* Now disable the cache. */ + LMEM->PSCCR &= ~LMEM_PSCCR_ENCACHE_MASK; + } + + /* Disable MPU */ + ARM_MPU_Disable(); + + #if defined(CACHE_MODE_WRITE_THROUGH) && CACHE_MODE_WRITE_THROUGH + /* Region 0 setting: Memory with Normal type, not shareable, write trough */ + MPU->RBAR = ARM_MPU_RBAR(0, 0x20200000U); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 0, 0, ARM_MPU_REGION_SIZE_1MB); + + /* Region 1 setting: Memory with Normal type, not shareable, write through */ + MPU->RBAR = ARM_MPU_RBAR(1, 0x20300000U); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 0, 0, ARM_MPU_REGION_SIZE_512KB); + + /* Region 2 setting: Memory with Normal type, not shareable, write through */ + MPU->RBAR = ARM_MPU_RBAR(2, 0x80000000U); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 0, 0, 1, 0, 0, ARM_MPU_REGION_SIZE_64MB); + + while ((nonCacheSize >> i) > 0x1U) { + i++; + } + + if (i != 0) { + /* The MPU region size should be 2^N, 5<=N<=32, region base should be multiples of size. */ + assert(!(nonCacheStart % nonCacheSize)); + assert(nonCacheSize == (uint32_t) (1 << i)); + assert(i >= 5); + + /* Region 3 setting: Memory with device type, not shareable, non-cacheable */ + MPU->RBAR = ARM_MPU_RBAR(3, nonCacheStart); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, i - 1); + } + + #if defined(__USE_SHMEM) + i = 0; + + while ((rpmsgShmemSize >> i) > 0x1U) { + i++; + } + + if (i != 0) { + /* The MPU region size should be 2^N, 5<=N<=32, region base should be multiples of size. */ + assert(!(rpmsgShmemStart % rpmsgShmemSize)); + assert(rpmsgShmemSize == (uint32_t) (1 << i)); + assert(i >= 5); + + /* Region 4 setting: Memory with device type, not shareable, non-cacheable */ + MPU->RBAR = ARM_MPU_RBAR(4, rpmsgShmemStart); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, i - 1); + } + #endif + #else + while ((nonCacheSize >> i) > 0x1U) { + i++; + } + + if (i != 0) { + /* The MPU region size should be 2^N, 5<=N<=32, region base should be multiples of size. */ + assert(!(nonCacheStart % nonCacheSize)); + assert(nonCacheSize == (uint32_t) (1 << i)); + assert(i >= 5); + + /* Region 0 setting: Memory with device type, not shareable, non-cacheable */ + MPU->RBAR = ARM_MPU_RBAR(0, nonCacheStart); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, i - 1); + } + + #if defined(__USE_SHMEM) + i = 0; + + while ((rpmsgShmemSize >> i) > 0x1U) { + i++; + } + + if (i != 0) { + /* The MPU region size should be 2^N, 5<=N<=32, region base should be multiples of size. */ + assert(!(rpmsgShmemStart % rpmsgShmemSize)); + assert(rpmsgShmemSize == (uint32_t) (1 << i)); + assert(i >= 5); + + /* Region 1 setting: Memory with device type, not shareable, non-cacheable */ + MPU->RBAR = ARM_MPU_RBAR(1, rpmsgShmemStart); + MPU->RASR = ARM_MPU_RASR(0, ARM_MPU_AP_FULL, 2, 0, 0, 0, 0, i - 1); + } + #endif + #endif + + /* Enable MPU */ + ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk); + + /* Enables the processor system bus to invalidate all lines in both ways. + and Initiate the processor system bus cache command. */ + LMEM->PSCCR |= LMEM_PSCCR_INVW0_MASK | LMEM_PSCCR_INVW1_MASK | LMEM_PSCCR_GO_MASK; + /* Wait until the cache command completes */ + while ((LMEM->PSCCR & LMEM_PSCCR_GO_MASK) != 0U) { + } + /* As a precaution clear the bits to avoid inadvertently re-running this command. */ + LMEM->PSCCR &= ~(LMEM_PSCCR_INVW0_MASK | LMEM_PSCCR_INVW1_MASK); + /* Now enable the system bus cache. */ + LMEM->PSCCR |= LMEM_PSCCR_ENCACHE_MASK; + + /* Enables the processor code bus to invalidate all lines in both ways. + and Initiate the processor code bus code cache command. */ + LMEM->PCCCR |= LMEM_PCCCR_INVW0_MASK | LMEM_PCCCR_INVW1_MASK | LMEM_PCCCR_GO_MASK; + /* Wait until the cache command completes. */ + while ((LMEM->PCCCR & LMEM_PCCCR_GO_MASK) != 0U) { + } + /* As a precaution clear the bits to avoid inadvertently re-running this command. */ + LMEM->PCCCR &= ~(LMEM_PCCCR_INVW0_MASK | LMEM_PCCCR_INVW1_MASK); + /* Now enable the code bus cache. */ + LMEM->PCCCR |= LMEM_PCCCR_ENCACHE_MASK; +} +#endif + //--------------------------------------------------------------------+ // USB Interrupt Handler //--------------------------------------------------------------------+ @@ -166,18 +552,18 @@ uint32_t board_button_read(void) { size_t board_get_unique_id(uint8_t id[], size_t max_len) { (void) max_len; - #if FSL_FEATURE_OCOTP_HAS_TIMING_CTRL +#if FSL_FEATURE_OCOTP_HAS_TIMING_CTRL OCOTP_Init(OCOTP, CLOCK_GetFreq(kCLOCK_IpgClk)); - #else +#else OCOTP_Init(OCOTP, 0u); - #endif +#endif // Reads shadow registers 0x01 - 0x04 (Configuration and Manufacturing Info) // into 8 bit wide destination, avoiding punning. for (int i = 0; i < 4; ++i) { uint32_t wr = OCOTP_ReadFuseShadowRegister(OCOTP, i + 1); for (int j = 0; j < 4; j++) { - id[i*4+j] = wr & 0xff; + id[i * 4 + j] = wr & 0xff; wr >>= 8; } } @@ -186,7 +572,7 @@ size_t board_get_unique_id(uint8_t id[], size_t max_len) { return 16; } -int board_uart_read(uint8_t* buf, int len) { +int board_uart_read(uint8_t *buf, int len) { int count = 0; while (count < len) { @@ -209,8 +595,8 @@ int board_uart_read(uint8_t* buf, int len) { return count; } -int board_uart_write(void const * buf, int len) { - LPUART_WriteBlocking(UART_PORT, (uint8_t const*)buf, len); +int board_uart_write(void const *buf, int len) { + LPUART_WriteBlocking(UART_PORT, (uint8_t const *) buf, len); return len; } @@ -236,10 +622,10 @@ TU_ATTR_UNUSED void _start(void) { while (1) {} } -#ifdef __clang__ -void _exit (int __status) { + #ifdef __clang__ +void _exit(int __status) { while (1) {} } -#endif + #endif #endif diff --git a/hw/bsp/imxrt/family.cmake b/hw/bsp/imxrt/family.cmake index d917e9777..436d6e6a3 100644 --- a/hw/bsp/imxrt/family.cmake +++ b/hw/bsp/imxrt/family.cmake @@ -64,6 +64,7 @@ function(add_board_target BOARD_TARGET) XIP_EXTERNAL_FLASH=1 XIP_BOOT_HEADER_ENABLE=1 __STARTUP_CLEAR_BSS + CFG_TUSB_MEM_SECTION=__attribute__((section("NonCacheable"))) ) target_include_directories(${BOARD_TARGET} PUBLIC ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD} diff --git a/hw/bsp/imxrt/family.mk b/hw/bsp/imxrt/family.mk index f00afb6a4..45c3ad31d 100644 --- a/hw/bsp/imxrt/family.mk +++ b/hw/bsp/imxrt/family.mk @@ -14,7 +14,8 @@ CFLAGS += \ -D__STARTUP_CLEAR_BSS \ -DXIP_EXTERNAL_FLASH=1 \ -DXIP_BOOT_HEADER_ENABLE=1 \ - -DCFG_TUSB_MCU=OPT_MCU_MIMXRT1XXX + -DCFG_TUSB_MCU=OPT_MCU_MIMXRT1XXX \ + -DCFG_TUSB_MEM_SECTION=__attribute__((section("NonCacheable"))) \ ifdef BOARD_TUD_RHPORT CFLAGS += -DBOARD_TUD_RHPORT=$(BOARD_TUD_RHPORT) diff --git a/src/portable/chipidea/ci_hs/ci_hs_imxrt.h b/src/portable/chipidea/ci_hs/ci_hs_imxrt.h index c59c107ff..201936e53 100644 --- a/src/portable/chipidea/ci_hs/ci_hs_imxrt.h +++ b/src/portable/chipidea/ci_hs/ci_hs_imxrt.h @@ -56,46 +56,11 @@ static const ci_hs_controller_t _ci_controller[] = #define CI_HS_REG(_port) ((ci_hs_regs_t*) _ci_controller[_port].reg_base) //------------- DCD -------------// -#define CI_DCD_INT_ENABLE(_p) NVIC_EnableIRQ (_ci_controller[_p].irqnum) -#define CI_DCD_INT_DISABLE(_p) NVIC_DisableIRQ(_ci_controller[_p].irqnum) +#define CI_DCD_INT_ENABLE(_p) NVIC_EnableIRQ ((IRQn_Type)_ci_controller[_p].irqnum) +#define CI_DCD_INT_DISABLE(_p) NVIC_DisableIRQ((IRQn_Type)_ci_controller[_p].irqnum) //------------- HCD -------------// -#define CI_HCD_INT_ENABLE(_p) NVIC_EnableIRQ (_ci_controller[_p].irqnum) -#define CI_HCD_INT_DISABLE(_p) NVIC_DisableIRQ(_ci_controller[_p].irqnum) - -//------------- DCache -------------// -TU_ATTR_ALWAYS_INLINE static inline bool imxrt_is_cache_mem(uintptr_t addr) { - return !(0x20000000 <= addr && addr < 0x20100000); -} - -TU_ATTR_ALWAYS_INLINE static inline bool imxrt_dcache_clean(void const* addr, uint32_t data_size) { - const uintptr_t addr32 = (uintptr_t) addr; - if (imxrt_is_cache_mem(addr32)) { - TU_ASSERT(tu_is_aligned32(addr32)); - SCB_CleanDCache_by_Addr((uint32_t *) addr32, (int32_t) data_size); - } - return true; -} - -TU_ATTR_ALWAYS_INLINE static inline bool imxrt_dcache_invalidate(void const* addr, uint32_t data_size) { - const uintptr_t addr32 = (uintptr_t) addr; - if (imxrt_is_cache_mem(addr32)) { - // Invalidating does not push cached changes back to RAM so we need to be - // *very* careful when we do it. If we're not aligned, then we risk resetting - // values back to their RAM state. - TU_ASSERT(tu_is_aligned32(addr32)); - SCB_InvalidateDCache_by_Addr((void*) addr32, (int32_t) data_size); - } - return true; -} - -TU_ATTR_ALWAYS_INLINE static inline bool imxrt_dcache_clean_invalidate(void const* addr, uint32_t data_size) { - const uintptr_t addr32 = (uintptr_t) addr; - if (imxrt_is_cache_mem(addr32)) { - TU_ASSERT(tu_is_aligned32(addr32)); - SCB_CleanInvalidateDCache_by_Addr((uint32_t *) addr32, (int32_t) data_size); - } - return true; -} +#define CI_HCD_INT_ENABLE(_p) NVIC_EnableIRQ ((IRQn_Type)_ci_controller[_p].irqnum) +#define CI_HCD_INT_DISABLE(_p) NVIC_DisableIRQ((IRQn_Type)_ci_controller[_p].irqnum) #endif diff --git a/src/portable/chipidea/ci_hs/dcd_ci_hs.c b/src/portable/chipidea/ci_hs/dcd_ci_hs.c index 430a0aad1..76b78cb57 100644 --- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c +++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c @@ -33,19 +33,6 @@ #if CFG_TUSB_MCU == OPT_MCU_MIMXRT1XXX #include "ci_hs_imxrt.h" - - void dcd_dcache_clean(void const* addr, uint32_t data_size) { - imxrt_dcache_clean(addr, data_size); - } - - void dcd_dcache_invalidate(void const* addr, uint32_t data_size) { - imxrt_dcache_invalidate(addr, data_size); - } - - void dcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) { - imxrt_dcache_clean_invalidate(addr, data_size); - } - #else #if TU_CHECK_MCU(OPT_MCU_LPC18XX, OPT_MCU_LPC43XX) @@ -57,18 +44,6 @@ #else #error "Unsupported MCUs" #endif - - TU_ATTR_WEAK void dcd_dcache_clean(void const* addr, uint32_t data_size) { - (void) addr; (void) data_size; - } - - TU_ATTR_WEAK void dcd_dcache_invalidate(void const* addr, uint32_t data_size) { - (void) addr; (void) data_size; - } - - TU_ATTR_WEAK void dcd_dcache_clean_invalidate(void const* addr, uint32_t data_size) { - (void) addr; (void) data_size; - } #endif //--------------------------------------------------------------------+ @@ -230,8 +205,6 @@ static void bus_reset(uint8_t rhport) _dcd_data.qhd[0][0].qtd_overlay.next = _dcd_data.qhd[0][1].qtd_overlay.next = QTD_NEXT_INVALID; _dcd_data.qhd[0][0].int_on_setup = 1; // OUT only - - dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t)); } bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { @@ -257,8 +230,6 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED; #endif - dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t)); - dcd_reg->ENDPTLISTADDR = (uint32_t) _dcd_data.qhd; // Endpoint List Address has to be 2K alignment dcd_reg->USBSTS = dcd_reg->USBSTS; dcd_reg->USBINTR = INTR_USB | INTR_ERROR | INTR_PORT_CHANGE | INTR_SUSPEND; @@ -325,10 +296,6 @@ void dcd_sof_enable(uint8_t rhport, bool en) static void qtd_init(dcd_qtd_t* p_qtd, void * data_ptr, uint16_t total_bytes) { - // Force the CPU to flush the buffer. We increase the size by 31 because the call aligns the - // address to 32-byte boundaries. Buffer must be word aligned - dcd_dcache_clean_invalidate((uint32_t*) tu_align((uint32_t) data_ptr, 4), total_bytes + 31); - tu_memclr(p_qtd, sizeof(dcd_qtd_t)); p_qtd->next = QTD_NEXT_INVALID; @@ -402,8 +369,6 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) p_qhd->qtd_overlay.next = QTD_NEXT_INVALID; - dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t)); - // Enable EP Control uint32_t const epctrl = (p_endpoint_desc->bmAttributes.xfer << ENDPTCTRL_TYPE_POS) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET; @@ -461,9 +426,6 @@ static void qhd_start_xfer(uint8_t rhport, uint8_t epnum, uint8_t dir) p_qhd->qtd_overlay.halted = false; // clear any previous error p_qhd->qtd_overlay.next = (uint32_t) p_qtd; // link qtd to qhd - // flush cache - dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t)); - if ( epnum == 0 ) { // follows UM 24.10.8.1.1 Setup packet handling using setup lockout mechanism @@ -539,8 +501,6 @@ bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16 page++; } } - - dcd_dcache_clean_invalidate((uint32_t*) tu_align((uint32_t) fifo_info.ptr_wrap, 4), total_bytes - fifo_info.len_wrap + 31); } else { @@ -652,9 +612,6 @@ void dcd_int_handler(uint8_t rhport) if (int_status & INTR_USB) { - // Make sure we read the latest version of _dcd_data. - dcd_dcache_clean_invalidate(&_dcd_data, sizeof(dcd_data_t)); - uint32_t const edpt_complete = dcd_reg->ENDPTCOMPLETE; dcd_reg->ENDPTCOMPLETE = edpt_complete; // acknowledge -- cgit v1.3.1 From 5d6d905cb063f7a4f13cf0c8160dbf6371e70e4c Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 2 Nov 2024 20:20:27 +0700 Subject: got interrupt in working with slave --- src/portable/synopsys/dwc2/hcd_dwc2.c | 71 +++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 42a5b9fbd..8bfba442b 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -80,8 +80,9 @@ typedef struct { volatile bool allocated; uint8_t ep_id; union TU_ATTR_PACKED { - uint8_t err_count : 7; + uint8_t err_count : 6; uint8_t do_ping : 1; + uint8_t sof_schedule : 1; }; uint8_t result; @@ -681,6 +682,31 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { //-------------------------------------------------------------------- // HCD Event Handler //-------------------------------------------------------------------- +static void channel_xfer_in_retry(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) { + hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; + dwc2_channel_t* channel = &dwc2->channel[ch_id]; + hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; + + if (edpt_is_periodic(channel->hcchar_bm.ep_type)){ + // for periodic, de-allocate channel, enable SOF set frame counter for later transfer + edpt->next_pid = channel->hctsiz_bm.pid; // save PID + edpt->frame_countdown = edpt->frame_interval; + dwc2->gintmsk |= GINTSTS_SOF; + + if (hcint & HCINT_HALTED) { + // already halted, de-allocate channel (called from DMA isr) + channel_dealloc(dwc2, ch_id); + } else { + // disable channel first if not halted (called slave isr) + xfer->sof_schedule = 1; + channel_disable(dwc2, channel); + } + } else { + // for control/bulk: try again immediately + channel_send_in_token(dwc2, channel); + } +} + #if CFG_TUH_DWC2_SLAVE_ENABLE static void handle_rxflvl_irq(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); @@ -770,6 +796,8 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h dwc2_channel_t* channel = &dwc2->channel[ch_id]; bool is_done = false; +// TU_LOG1("ch%u: ep = %u, hcint = 0x%04lX\r\n", ch_id, channel->hcchar_bm.ep_num, hcint); + if (hcint & HCINT_XFER_COMPLETE) { xfer->result = XFER_RESULT_SUCCESS; channel_disable(dwc2, channel); @@ -786,21 +814,23 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h channel_disable(dwc2, channel); } else if (hcint & HCINT_HALTED) { channel->hcintmsk &= ~HCINT_HALTED; - if (xfer->result != XFER_RESULT_INVALID) { + if (xfer->sof_schedule) { + // de-allocate channel but does not complete xfer, we schedule it in the SOF interrupt + channel_dealloc(dwc2, ch_id); + } else if (xfer->result != XFER_RESULT_INVALID) { is_done = true; } else if (channel->hcchar_bm.err_multi_count == HCD_XFER_ERROR_MAX) { xfer->result = XFER_RESULT_FAILED; is_done = true; } else { - // Re-initialize Channel - TU_ASSERT(false); + channel_xfer_in_retry(dwc2, ch_id, hcint); } } else if (hcint & HCINT_DATATOGGLE_ERR) { xfer->err_count = 0; TU_ASSERT(false); } else if (hcint & HCINT_NAK) { // NAK received, re-enable channel if request queue is available - channel_send_in_token(dwc2, channel); + channel_xfer_in_retry(dwc2, ch_id, hcint); } else if (hcint & HCINT_ACK) { xfer->err_count = 0; channel->hcintmsk &= ~HCINT_ACK; @@ -887,31 +917,13 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci xfer->result = XFER_RESULT_FAILED; } else { channel->hcintmsk |= HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR; - edpt->next_pid = channel->hctsiz_bm.pid; // save PID - if (edpt_is_periodic(channel->hcchar_bm.ep_type)){ - // for periodic, de-allocate channel, enable SOF set frame counter for later transfer - channel_dealloc(dwc2, ch_id); - edpt->frame_countdown = edpt->frame_interval; - dwc2->gintmsk |= GINTSTS_SOF; - } else { - // for control/bulk: try again immediately - TU_ASSERT(false); - } + channel_xfer_in_retry(dwc2, ch_id, hcint); } } else if (hcint & (HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR)) { xfer->err_count = 0; channel->hcintmsk &= ~(HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR); if ((hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR))) { - edpt->next_pid = channel->hctsiz_bm.pid; // save PID - if (edpt_is_periodic(channel->hcchar_bm.ep_type)){ - // for periodic, de-allocate channel, enable SOF set frame counter for later transfer - channel_dealloc(dwc2, ch_id); - edpt->frame_countdown = edpt->frame_interval; - dwc2->gintmsk |= GINTSTS_SOF; - } else { - // for control/bulk: try again immediately - TU_ASSERT(false); - } + channel_xfer_in_retry(dwc2, ch_id, hcint); } } } @@ -1161,6 +1173,15 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { dwc2->gintmsk &= ~GINTSTS_NPTX_FIFO_EMPTY; } } + + if (gintsts & GINTSTS_PTX_FIFO_EMPTY) { + // PTX FIFO empty interrupt, this is read-only and cleared by hardware when FIFO is written + const bool more_ptxfe = handle_txfifo_empty(dwc2, true); + if (!more_ptxfe) { + // no more pending packet, disable interrupt + dwc2->gintmsk &= ~GINTSTS_PTX_FIFO_EMPTY; + } + } #endif } -- cgit v1.3.1 From ed8d2c9594ae5b46da6c7096a384f2f091f866ef Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Sat, 2 Nov 2024 17:07:44 +0100 Subject: Fix align issue of _audiod_fct.alt_setting. Signed-off-by: HiFiPhile --- src/class/audio/audio_device.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index 8c86de433..47831725c 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -1810,7 +1810,8 @@ static bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const * uint8_t const *dummy; TU_VERIFY(audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &dummy)); - TU_VERIFY(tud_control_xfer(rhport, p_request, &_audiod_fct[func_id].alt_setting[idxItf], 1)); + _audiod_fct[func_id].ctrl_buf[0] = _audiod_fct[func_id].alt_setting[idxItf]; + TU_VERIFY(tud_control_xfer(rhport, p_request, _audiod_fct[func_id].ctrl_buf, 1)); TU_LOG2(" Get itf: %u - current alt: %u\r\n", itf, _audiod_fct[func_id].alt_setting[idxItf]); -- cgit v1.3.1 From cdc98e12c1baa0528198e7a8abc0db06e6d205ec Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Sat, 2 Nov 2024 17:09:51 +0100 Subject: Improve EP IN flow control. Signed-off-by: HiFiPhile --- examples/device/uac2_headset/src/tusb_config.h | 2 +- src/class/audio/audio_device.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/examples/device/uac2_headset/src/tusb_config.h b/examples/device/uac2_headset/src/tusb_config.h index 328e35f52..c921a37ae 100644 --- a/examples/device/uac2_headset/src/tusb_config.h +++ b/examples/device/uac2_headset/src/tusb_config.h @@ -146,7 +146,7 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX) #define CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX) -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN)*2 +#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN)*4 #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TU_MAX(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN, CFG_TUD_AUDIO_FUNC_1_FORMAT_2_EP_SZ_IN) // Maximum EP IN size for all AS alternate settings used // EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense) diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c index 47831725c..b7340d1b6 100644 --- a/src/class/audio/audio_device.c +++ b/src/class/audio/audio_device.c @@ -2138,6 +2138,13 @@ static bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const // Check if entity is present and get corresponding driver index TU_VERIFY(audiod_verify_entity_exists(itf, entityID, &func_id)); +#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL + uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue); + if (_audiod_fct[func_id].bclock_id_tx == entityID && ctrlSel == AUDIO_CS_CTRL_SAM_FREQ && p_request->bRequest == AUDIO_CS_REQ_CUR) { + _audiod_fct[func_id].sample_rate_tx = tu_unaligned_read32(_audiod_fct[func_id].ctrl_buf); + } +#endif + // Invoke callback return tud_audio_set_req_entity_cb(rhport, p_request, _audiod_fct[func_id].ctrl_buf); } -- cgit v1.3.1 From 80ad7c4e87b74b1bccc4f566a3cee8d1ff0fafc9 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 4 Nov 2024 17:20:58 +0700 Subject: add tusb_time_millis_api() and default/weak tusb_time_delay_ms_api(). Which is required for host and some device without RTOS. --- src/common/tusb_private.h | 14 ++++++++---- src/common/tusb_types.h | 2 +- src/tusb.c | 29 ++++++++++++++++-------- src/tusb.h | 7 ++++-- test/unit-test/test/device/msc/test_msc_device.c | 4 ++++ test/unit-test/test/device/usbd/test_usbd.c | 4 ++++ 6 files changed, 44 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/common/tusb_private.h b/src/common/tusb_private.h index 8a479c042..c71775abb 100644 --- a/src/common/tusb_private.h +++ b/src/common/tusb_private.h @@ -24,9 +24,8 @@ * This file is part of the TinyUSB stack. */ - -#ifndef _TUSB_PRIVATE_H_ -#define _TUSB_PRIVATE_H_ +#ifndef TUSB_PRIVATE_H_ +#define TUSB_PRIVATE_H_ // Internal Helper used by Host and Device Stack @@ -34,6 +33,13 @@ extern "C" { #endif +#define TUP_USBIP_CONTROLLER_NUM 2 +extern tusb_role_t _tusb_rhport_role[TUP_USBIP_CONTROLLER_NUM]; + +//--------------------------------------------------------------------+ +// Endpoint +//--------------------------------------------------------------------+ + typedef struct TU_ATTR_PACKED { volatile uint8_t busy : 1; volatile uint8_t stalled : 1; @@ -163,4 +169,4 @@ bool tu_edpt_stream_peek(tu_edpt_stream_t* s, uint8_t* ch) { } #endif -#endif /* _TUSB_PRIVATE_H_ */ +#endif diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index 92cebc42b..d045a3c8f 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -264,7 +264,7 @@ typedef enum { } microsoft_os_20_type_t; enum { - CONTROL_STAGE_IDLE, + CONTROL_STAGE_IDLE = 0, CONTROL_STAGE_SETUP, CONTROL_STAGE_DATA, CONTROL_STAGE_ACK diff --git a/src/tusb.c b/src/tusb.c index 6a0cc0805..a516cb9a1 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -39,14 +39,25 @@ #include "host/usbh_pvt.h" #endif -#define TUP_USBIP_CONTROLLER_NUM 2 - -static tusb_role_t _rhport_role[TUP_USBIP_CONTROLLER_NUM] = { 0 }; +tusb_role_t _tusb_rhport_role[TUP_USBIP_CONTROLLER_NUM] = { 0 }; + +//-------------------------------------------------------------------- +// Weak/Default API, can be overwritten by Application +//-------------------------------------------------------------------- + +TU_ATTR_WEAK void tusb_time_delay_ms_api(uint32_t ms) { +#if CFG_TUSB_OS != OPT_OS_NONE + osal_task_delay(ms); +#else + // delay using millis() (if implemented) and/or frame number if possible + const uint32_t time_ms = tusb_time_millis_api(); + while ((tusb_time_millis_api() - time_ms) < ms) {} +#endif +} //--------------------------------------------------------------------+ // Public API //--------------------------------------------------------------------+ - bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // backward compatible called with tusb_init(void) #if defined(TUD_OPT_RHPORT) || defined(TUH_OPT_RHPORT) @@ -58,7 +69,7 @@ bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { .speed = TUD_OPT_HIGH_SPEED ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL }; TU_ASSERT ( tud_rhport_init(rhport, &dev_init) ); - _rhport_role[TUD_OPT_RHPORT] = TUSB_ROLE_DEVICE; + _tusb_rhport_role[TUD_OPT_RHPORT] = TUSB_ROLE_DEVICE; #endif #if CFG_TUH_ENABLED && defined(TUH_OPT_RHPORT) @@ -68,7 +79,7 @@ bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { .speed = TUH_OPT_HIGH_SPEED ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL }; TU_ASSERT( tuh_rhport_init(TUH_OPT_RHPORT, &host_init) ); - _rhport_role[TUH_OPT_RHPORT] = TUSB_ROLE_HOST; + _tusb_rhport_role[TUH_OPT_RHPORT] = TUSB_ROLE_HOST; #endif return true; @@ -77,7 +88,7 @@ bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // new API with explicit rhport and role TU_ASSERT(rhport < TUP_USBIP_CONTROLLER_NUM && rh_init->role != TUSB_ROLE_INVALID); - _rhport_role[rhport] = rh_init->role; + _tusb_rhport_role[rhport] = rh_init->role; #if CFG_TUD_ENABLED if (rh_init->role == TUSB_ROLE_DEVICE) { @@ -112,14 +123,14 @@ void tusb_int_handler(uint8_t rhport, bool in_isr) { TU_VERIFY(rhport < TUP_USBIP_CONTROLLER_NUM,); #if CFG_TUD_ENABLED - if (_rhport_role[rhport] == TUSB_ROLE_DEVICE) { + if (_tusb_rhport_role[rhport] == TUSB_ROLE_DEVICE) { (void) in_isr; dcd_int_handler(rhport); } #endif #if CFG_TUH_ENABLED - if (_rhport_role[rhport] == TUSB_ROLE_HOST) { + if (_tusb_rhport_role[rhport] == TUSB_ROLE_HOST) { hcd_int_handler(rhport, in_isr); } #endif diff --git a/src/tusb.h b/src/tusb.h index 3ffaafded..cb6021b33 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -169,8 +169,11 @@ void tusb_int_handler(uint8_t rhport, bool in_isr); // API Implemented by user //--------------------------------------------------------------------+ -// Get current milliseconds, maybe required by some port with no RTOS -uint32_t tusb_time_millis(void); +// Get current milliseconds, required by some port/configuration without RTOS +uint32_t tusb_time_millis_api(void); + +// Delay in milliseconds, use tusb_time_millis_api() by default. required by some port/configuration with no RTOS +void tusb_time_delay_ms_api(uint32_t ms); #ifdef __cplusplus } diff --git a/test/unit-test/test/device/msc/test_msc_device.c b/test/unit-test/test/device/msc/test_msc_device.c index f2a4fa295..55b690313 100644 --- a/test/unit-test/test/device/msc/test_msc_device.c +++ b/test/unit-test/test/device/msc/test_msc_device.c @@ -42,6 +42,10 @@ TEST_FILE("msc_device.c") // MACRO TYPEDEF CONSTANT ENUM DECLARATION //--------------------------------------------------------------------+ +uint32_t tusb_time_millis_api(void) { + return 0; +} + enum { EDPT_CTRL_OUT = 0x00, diff --git a/test/unit-test/test/device/usbd/test_usbd.c b/test/unit-test/test/device/usbd/test_usbd.c index 9879cd4ba..e7c6a8578 100644 --- a/test/unit-test/test/device/usbd/test_usbd.c +++ b/test/unit-test/test/device/usbd/test_usbd.c @@ -39,6 +39,10 @@ TEST_FILE("usbd_control.c") // MACRO TYPEDEF CONSTANT ENUM DECLARATION //--------------------------------------------------------------------+ +uint32_t tusb_time_millis_api(void) { + return 0; +} + enum { EDPT_CTRL_OUT = 0x00, -- cgit v1.3.1 From 29262f3e24326c1a7c06137824e44ffaa6301094 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 4 Nov 2024 17:22:25 +0700 Subject: improve port0_enable(), properly configure hcfg and hfir according to port speed. Enable low power UTMI+ phy mode for ls/fs device. --- src/portable/synopsys/dwc2/dwc2_type.h | 4 ++ src/portable/synopsys/dwc2/hcd_dwc2.c | 115 +++++++++++++++++---------------- 2 files changed, 63 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index 89561a5e6..a88a2d389 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -730,6 +730,7 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define HCFG_FSLS_PHYCLK_SEL HCFG_FSLS_PHYCLK_SEL_Msk // FS/LS PHY clock select #define HCFG_FSLS_PHYCLK_SEL_30_60MHZ (0x0UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000000 #define HCFG_FSLS_PHYCLK_SEL_48MHZ (0x1UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000001 +#define HCFG_FSLS_PHYCLK_SEL_6MHZ (0x2UL << HCFG_FSLS_PHYCLK_SEL_Pos) // 0x00000002 #define HCFG_FSLS_ONLY_Pos (2U) #define HCFG_FSLS_ONLY_Msk (0x1UL << HCFG_FSLS_ONLY_Pos) // 0x00000004 @@ -848,6 +849,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define HFIR_FRIVL_Pos (0U) #define HFIR_FRIVL_Msk (0xFFFFUL << HFIR_FRIVL_Pos) // 0x0000FFFF #define HFIR_FRIVL HFIR_FRIVL_Msk // Frame interval +#define HFIR_RELOAD_CTRL_Pos (16U) // available since v2.92a +#define HFIR_RELOAD_CTRL_Msk (0x1UL << HFIR_RELOAD_CTRL_Pos) +#define HFIR_RELOAD_CTRL HFIR_RELOAD_CTRL_Msk /******************** Bit definition for HFNUM register ********************/ #define HFNUM_FRNUM_Pos (0U) diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 8bfba442b..b2ca86a36 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -45,7 +45,7 @@ TU_VERIFY_STATIC(CFG_TUH_DWC2_ENDPOINT_MAX <= 255, "currently only use 8-bit for index"); enum { - HPRT_W1C_MASK = HPRT_CONN_DETECT | HPRT_ENABLE | HPRT_ENABLE_CHANGE | HPRT_OVER_CURRENT_CHANGE + HPRT_W1_MASK = HPRT_CONN_DETECT | HPRT_ENABLE | HPRT_ENABLE_CHANGE | HPRT_OVER_CURRENT_CHANGE | HPRT_SUSPEND }; enum { @@ -79,7 +79,7 @@ typedef struct { typedef struct { volatile bool allocated; uint8_t ep_id; - union TU_ATTR_PACKED { + struct TU_ATTR_PACKED { uint8_t err_count : 6; uint8_t do_ping : 1; uint8_t sof_schedule : 1; @@ -336,26 +336,13 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { //------------- 3.1 Host Initialization -------------// - // FS/LS PHY Clock Select - uint32_t hcfg = dwc2->hcfg; - if (is_highspeed) { - hcfg &= ~HCFG_FSLS_ONLY; - } else { - hcfg &= ~HCFG_FSLS_ONLY; // since we are using FS PHY - hcfg &= ~HCFG_FSLS_PHYCLK_SEL; - - if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI && - dwc2->ghwcfg2_bm.fs_phy_type == GHWCFG2_FSPHY_DEDICATED) { - // dedicated FS PHY with 48 mhz - hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ; - } else { - // shared HS PHY running at full speed - hcfg |= HCFG_FSLS_PHYCLK_SEL_30_60MHZ; - } - } - dwc2->hcfg = hcfg; + // work at max supported speed + dwc2->hcfg &= ~HCFG_FSLS_ONLY; // Enable HFIR reload + if (dwc2->gsnpsid >= DWC2_CORE_REV_2_92a) { + dwc2->hfir |= HFIR_RELOAD_CTRL; + } // force host mode and wait for mode switch dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FDMOD) | GUSBCFG_FHMOD; @@ -364,7 +351,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // configure fixed-allocated fifo scheme dfifo_host_init(rhport); - dwc2->hprt = HPRT_W1C_MASK; // clear all write-1-clear bits + dwc2->hprt = HPRT_W1_MASK; // clear all write-1-clear bits dwc2->hprt = HPRT_POWER; // turn on VBUS // Enable required interrupts @@ -408,7 +395,7 @@ bool hcd_port_connect_status(uint8_t rhport) { // Some port would require hcd_port_reset_end() to be invoked after 10ms to complete the reset sequence. void hcd_port_reset(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - uint32_t hprt = dwc2->hprt & ~HPRT_W1C_MASK; + uint32_t hprt = dwc2->hprt & ~HPRT_W1_MASK; hprt |= HPRT_RESET; dwc2->hprt = hprt; } @@ -416,7 +403,7 @@ void hcd_port_reset(uint8_t rhport) { // Complete bus reset sequence, may be required by some controllers void hcd_port_reset_end(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - uint32_t hprt = dwc2->hprt & ~HPRT_W1C_MASK; // skip w1c bits + uint32_t hprt = dwc2->hprt & ~HPRT_W1_MASK; // skip w1c bits hprt &= ~HPRT_RESET; dwc2->hprt = hprt; } @@ -1044,6 +1031,50 @@ static bool handle_sof_irq(uint8_t rhport, bool in_isr) { return more_isr; } +// Config HCFG FS/LS clock and HFIR for SOF interval according to link speed (value is in PHY clock unit) +static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) { + uint32_t hcfg = dwc2->hcfg & ~HCFG_FSLS_PHYCLK_SEL; + + const dwc2_gusbcfg_t gusbcfg_bm = dwc2->gusbcfg_bm; + uint32_t phy_clock; + + if (gusbcfg_bm.phy_sel) { + phy_clock = 48; // dedicated FS is 48Mhz + if (speed == TUSB_SPEED_LOW) { + hcfg |= HCFG_FSLS_PHYCLK_SEL_6MHZ; + } else { + hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ; + } + } else { + if (gusbcfg_bm.ulpi_utmi_sel) { + phy_clock = 60; // ULPI 8-bit is 60Mhz + } else { + // UTMI+ 16-bit is 30Mhz, 8-bit is 60Mhz + phy_clock = gusbcfg_bm.phy_if16 ? 30 : 60; + + // Enable UTMI+ low power mode 48Mhz external clock if not highspeed + if (speed == TUSB_SPEED_HIGH) { + dwc2->gusbcfg &= ~GUSBCFG_PHYLPCS; + } else { + dwc2->gusbcfg |= GUSBCFG_PHYLPCS; + // may need to reset port + } + } + hcfg |= HCFG_FSLS_PHYCLK_SEL_30_60MHZ; + } + + dwc2->hcfg = hcfg; + + uint32_t hfir = dwc2->hfir & ~HFIR_FRIVL_Msk; + if (speed == TUSB_SPEED_HIGH) { + hfir |= 125*phy_clock; + } else { + hfir |= 1000*phy_clock; + } + + dwc2->hfir = hfir; +} + /* Handle Host Port interrupt, possible source are: - Connection Detection - Enable Change @@ -1051,7 +1082,7 @@ static bool handle_sof_irq(uint8_t rhport, bool in_isr) { */ static void handle_hprt_irq(uint8_t rhport, bool in_isr) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - uint32_t hprt = dwc2->hprt & ~HPRT_W1C_MASK; + uint32_t hprt = dwc2->hprt & ~HPRT_W1_MASK; const dwc2_hprt_t hprt_bm = dwc2->hprt_bm; if (dwc2->hprt & HPRT_CONN_DETECT) { @@ -1071,38 +1102,10 @@ static void handle_hprt_irq(uint8_t rhport, bool in_isr) { if (hprt_bm.enable) { // Port enable - // Config HCFG FS/LS clock and HFIR for SOF interval according to link speed (value is in PHY clock unit) const tusb_speed_t speed = convert_hprt_speed(hprt_bm.speed); - uint32_t hcfg = dwc2->hcfg & ~HCFG_FSLS_PHYCLK_SEL; - - const dwc2_gusbcfg_t gusbcfg_bm = dwc2->gusbcfg_bm; - uint32_t clock = 60; - if (gusbcfg_bm.phy_sel) { - // dedicated FS is 48Mhz - clock = 48; - hcfg |= HCFG_FSLS_PHYCLK_SEL_48MHZ; - } else { - // UTMI+ or ULPI - if (gusbcfg_bm.ulpi_utmi_sel) { - clock = 60; // ULPI 8-bit is 60Mhz - } else if (gusbcfg_bm.phy_if16) { - clock = 30; // UTMI+ 16-bit is 30Mhz - } else { - clock = 60; // UTMI+ 8-bit is 60Mhz - } - hcfg |= HCFG_FSLS_PHYCLK_SEL_30_60MHZ; - } - - dwc2->hcfg = hcfg; - - uint32_t hfir = dwc2->hfir & ~HFIR_FRIVL_Msk; - if (speed == TUSB_SPEED_HIGH) { - hfir |= 125*clock; - } else { - hfir |= 1000*clock; - } - - dwc2->hfir = hfir; + port0_enable(dwc2, speed); + } else { + // TU_ASSERT(false, ); } } @@ -1121,7 +1124,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { const uint32_t gintmsk = dwc2->gintmsk; const uint32_t gintsts = dwc2->gintsts & gintmsk; - // TU_LOG1_HEX(int_status); + // TU_LOG1_HEX(gintsts); if (gintsts & GINTSTS_CONIDSTSCHNG) { // Connector ID status change -- cgit v1.3.1 From 5f891e020c9fb41134babeab3ce4e8574ac47d25 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 4 Nov 2024 17:24:49 +0700 Subject: usbh use the new tusb_time_delay_ms_api() fix duplicated device attach for some devices which cause "USBH Defer Attach until current enumeration complete" include dev0 for tuh_edpt_abort_xfer() --- hw/bsp/board.c | 2 +- hw/bsp/stm32f7/family.c | 6 +-- src/host/usbh.c | 104 ++++++++++++++++++++++++------------------------ 3 files changed, 56 insertions(+), 56 deletions(-) (limited to 'src') diff --git a/hw/bsp/board.c b/hw/bsp/board.c index d4fdb7586..6d9488431 100644 --- a/hw/bsp/board.c +++ b/hw/bsp/board.c @@ -136,6 +136,6 @@ int board_getchar(void) { } -uint32_t tusb_time_millis(void) { +uint32_t tusb_time_millis_api(void) { return board_millis(); } diff --git a/hw/bsp/stm32f7/family.c b/hw/bsp/stm32f7/family.c index f1671b362..630837d8c 100644 --- a/hw/bsp/stm32f7/family.c +++ b/hw/bsp/stm32f7/family.c @@ -173,7 +173,7 @@ void board_init(void) { #endif // vbus sense //------------- rhport1: OTG_HS -------------// - #ifdef USB_HS_PHYC +#ifdef USB_HS_PHYC // MCU with built-in HS PHY such as F723, F733, F730 /* Configure DM DP Pins */ @@ -197,7 +197,7 @@ void board_init(void) { /* Enable PHYC Clocks */ __HAL_RCC_OTGPHYC_CLK_ENABLE(); - #else +#else // MCU with external ULPI PHY /* ULPI CLK */ @@ -243,7 +243,7 @@ void board_init(void) { GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Alternate = GPIO_AF10_OTG_HS; HAL_GPIO_Init(GPIOI, &GPIO_InitStruct); - #endif // USB_HS_PHYC +#endif // USB_HS_PHYC // Enable USB HS & ULPI Clocks __HAL_RCC_USB_OTG_HS_ULPI_CLK_ENABLE(); diff --git a/src/host/usbh.c b/src/host/usbh.c index 8bf40a607..b0a2b2160 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -278,15 +278,6 @@ static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hu static bool usbh_edpt_control_open(uint8_t dev_addr, uint8_t max_packet_size); static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); -#if CFG_TUSB_OS == OPT_OS_NONE -// TODO rework time-related function later -// weak and overridable -TU_ATTR_WEAK void osal_task_delay(uint32_t msec) { - const uint32_t start = hcd_frame_number(_usbh_controller); - while ( ( hcd_frame_number(_usbh_controller) - start ) < msec ) {} -} -#endif - TU_ATTR_ALWAYS_INLINE static inline bool queue_event(hcd_event_t const * event, bool in_isr) { TU_ASSERT(osal_queue_send(_usbh_q, event, in_isr)); tuh_event_hook_cb(event->rhport, event->event_id, in_isr); @@ -487,17 +478,27 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) { // due to the shared _usbh_ctrl_buf, we must complete enumerating one device before enumerating another one. // TODO better to have an separated queue for newly attached devices if (_dev0.enumerating) { - TU_LOG_USBH("[%u:] USBH Defer Attach until current enumeration complete\r\n", event.rhport); + // Some device can cause multiple duplicated attach events + // drop current enumerating and start over for a proper port reset + if (event.rhport == _dev0.rhport && event.connection.hub_addr == _dev0.hub_addr && + event.connection.hub_port == _dev0.hub_port) { + // abort/cancel current enumeration and start new one + TU_LOG1("[%u:] USBH Device Attach (duplicated)\r\n", event.rhport); + tuh_edpt_abort_xfer(0, 0); + enum_new_device(&event); + } else { + TU_LOG_USBH("[%u:] USBH Defer Attach until current enumeration complete\r\n", event.rhport); - bool is_empty = osal_queue_empty(_usbh_q); - queue_event(&event, in_isr); + bool is_empty = osal_queue_empty(_usbh_q); + queue_event(&event, in_isr); - if (is_empty) { - // Exit if this is the only event in the queue, otherwise we may loop forever - return; + if (is_empty) { + // Exit if this is the only event in the queue, otherwise we may loop forever + return; + } } } else { - TU_LOG_USBH("[%u:] USBH DEVICE ATTACH\r\n", event.rhport); + TU_LOG1("[%u:] USBH Device Attach\r\n", event.rhport); _dev0.enumerating = 1; enum_new_device(&event); } @@ -603,12 +604,12 @@ bool tuh_control_xfer (tuh_xfer_t* xfer) { TU_VERIFY(xfer->ep_addr == 0 && xfer->setup); // Check if device is still connected (enumerating for dev0) - uint8_t const daddr = xfer->daddr; - if ( daddr == 0 ) { - if (!_dev0.enumerating) return false; + const uint8_t daddr = xfer->daddr; + if (daddr == 0) { + TU_VERIFY(_dev0.enumerating); } else { - usbh_device_t const* dev = get_device(daddr); - if (dev && dev->connected == 0) return false; + const usbh_device_t* dev = get_device(daddr); + TU_VERIFY(dev && dev->connected); } // pre-check to help reducing mutex lock @@ -778,24 +779,26 @@ bool tuh_edpt_xfer(tuh_xfer_t* xfer) { } bool tuh_edpt_abort_xfer(uint8_t daddr, uint8_t ep_addr) { - usbh_device_t* dev = get_device(daddr); - TU_VERIFY(dev); - TU_LOG_USBH("[%u] Aborted transfer on EP %02X\r\n", daddr, ep_addr); - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); + const uint8_t epnum = tu_edpt_number(ep_addr); + const uint8_t dir = tu_edpt_dir(ep_addr); + + if (epnum == 0) { + // Also include dev0 for aborting enumerating + const uint8_t rhport = usbh_get_rhport(daddr); - if ( epnum == 0 ) { // control transfer: only 1 control at a time, check if we are aborting the current one TU_VERIFY(daddr == _ctrl_xfer.daddr && _ctrl_xfer.stage != CONTROL_STAGE_IDLE); - TU_VERIFY(hcd_edpt_abort_xfer(dev->rhport, daddr, ep_addr)); - // reset control transfer state to idle - _set_control_xfer_stage(CONTROL_STAGE_IDLE); + hcd_edpt_abort_xfer(rhport, daddr, ep_addr); + _set_control_xfer_stage(CONTROL_STAGE_IDLE); // reset control transfer state to idle } else { - // non-control skip if not busy - TU_VERIFY(dev->ep_status[epnum][dir].busy); - TU_VERIFY(hcd_edpt_abort_xfer(dev->rhport, daddr, ep_addr)); + usbh_device_t* dev = get_device(daddr); + TU_VERIFY(dev); + + TU_VERIFY(dev->ep_status[epnum][dir].busy); // non-control skip if not busy + hcd_edpt_abort_xfer(dev->rhport, daddr, ep_addr); + // mark as ready and release endpoint if transfer is aborted dev->ep_status[epnum][dir].busy = false; tu_edpt_release(&dev->ep_status[epnum][dir], _usbh_mutex); @@ -1281,9 +1284,9 @@ static void process_removing_device(uint8_t rhport, uint8_t hub_addr, uint8_t hu //--------------------------------------------------------------------+ enum { - ENUM_RESET_DELAY = 50, // USB specs: 10 to 50ms - ENUM_CONTACT_DEBOUNCING_DELAY = 450, // when plug/unplug a device, physical connection can be bouncing and may - // generate a series of attach/detach event. This delay wait for stable connection + ENUM_RESET_DELAY_MS = 50, // USB specs: 10 to 50ms + ENUM_DEBOUNCING_DELAY_MS = 450, // when plug/unplug a device, physical connection can be bouncing and may + // generate a series of attach/detach event. This delay wait for stable connection }; enum { @@ -1322,7 +1325,7 @@ static void process_enumeration(tuh_xfer_t* xfer) { bool retry = _dev0.enumerating && (failed_count < ATTEMPT_COUNT_MAX); if ( retry ) { failed_count++; - osal_task_delay(ATTEMPT_DELAY_MS); // delay a bit + tusb_time_delay_ms_api(ATTEMPT_DELAY_MS); // delay a bit TU_LOG1("Enumeration attempt %u\r\n", failed_count); retry = tuh_control_xfer(xfer); } @@ -1364,7 +1367,7 @@ static void process_enumeration(tuh_xfer_t* xfer) { } case ENUM_HUB_GET_STATUS_2: - osal_task_delay(ENUM_RESET_DELAY); + tusb_time_delay_ms_api(ENUM_RESET_DELAY_MS); TU_ASSERT(hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, process_enumeration, ENUM_HUB_CLEAR_RESET_2),); break; @@ -1402,7 +1405,7 @@ static void process_enumeration(tuh_xfer_t* xfer) { if (_dev0.hub_addr == 0) { // connected directly to roothub hcd_port_reset( _dev0.rhport ); - osal_task_delay(RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since + tusb_time_delay_ms_api(RESET_DELAY); // TODO may not work for no-OS on MCU that require reset_end() since // sof of controller may not running while resetting hcd_port_reset_end(_dev0.rhport); // TODO: fall through to SET ADDRESS, refactor later @@ -1424,9 +1427,9 @@ static void process_enumeration(tuh_xfer_t* xfer) { case ENUM_GET_DEVICE_DESC: { // Allow 2ms for address recovery time, Ref USB Spec 9.2.6.3 - osal_task_delay(2); + tusb_time_delay_ms_api(2); - uint8_t const new_addr = (uint8_t) tu_le16toh(xfer->setup->wValue); + const uint8_t new_addr = (uint8_t) tu_le16toh(xfer->setup->wValue); usbh_device_t* new_dev = get_device(new_addr); TU_ASSERT(new_dev,); @@ -1514,6 +1517,8 @@ static void process_enumeration(tuh_xfer_t* xfer) { } } + + static bool enum_new_device(hcd_event_t* event) { _dev0.rhport = event->rhport; _dev0.hub_addr = event->connection.hub_addr; @@ -1522,19 +1527,15 @@ static bool enum_new_device(hcd_event_t* event) { if (_dev0.hub_addr == 0) { // connected directly to roothub hcd_port_reset(_dev0.rhport); -#if CFG_TUSB_OS == OPT_OS_NONE - // Since we are in middle of rhport reset, frame number is not available for time delay - // need to depend on tusb_time_millis() instead - const uint32_t start_reset = tusb_time_millis(); - while ((tusb_time_millis() - start_reset) < ENUM_RESET_DELAY) {} -#else - osal_task_delay(ENUM_RESET_DELAY); -#endif + + // Since we are in middle of rhport reset, frame number is not available yet. + // need to depend on tusb_time_millis_api() + tusb_time_delay_ms_api(ENUM_RESET_DELAY_MS); hcd_port_reset_end(_dev0.rhport); // wait until device connection is stable TODO non blocking - osal_task_delay(ENUM_CONTACT_DEBOUNCING_DELAY); + tusb_time_delay_ms_api(ENUM_DEBOUNCING_DELAY_MS); // device unplugged while delaying if (!hcd_port_connect_status(_dev0.rhport)) { @@ -1557,10 +1558,9 @@ static bool enum_new_device(hcd_event_t* event) { else { // connected via external hub // wait until device connection is stable TODO non blocking - osal_task_delay(ENUM_CONTACT_DEBOUNCING_DELAY); + tusb_time_delay_ms_api(ENUM_DEBOUNCING_DELAY_MS); // ENUM_HUB_GET_STATUS - //TU_ASSERT( hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, enum_hub_get_status0_complete, 0) ); TU_ASSERT(hub_port_get_status(_dev0.hub_addr, _dev0.hub_port, _usbh_ctrl_buf, process_enumeration, ENUM_HUB_CLEAR_RESET_1)); } -- cgit v1.3.1 From 372db1e19a28458110b250a3149b585f8dabbfff Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 5 Nov 2024 10:37:39 +0700 Subject: implement split transaction, got control working --- src/portable/synopsys/dwc2/hcd_dwc2.c | 89 +++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index b2ca86a36..3527f682e 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -432,8 +432,8 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { // Open an endpoint bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* desc_ep) { - (void) rhport; - //dwc2_regs_t* dwc2 = DWC2_REG(rhport); + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + const tusb_speed_t rh_speed = convert_hprt_speed(dwc2->hprt_bm.speed); hcd_devtree_info_t devtree_info; hcd_devtree_get_info(dev_addr, &devtree_info); @@ -460,7 +460,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* hcsplt_bm->hub_addr = devtree_info.hub_addr; hcsplt_bm->xact_pos = 0; hcsplt_bm->split_compl = 0; - hcsplt_bm->split_en = 0; + hcsplt_bm->split_en = (rh_speed == TUSB_SPEED_HIGH && devtree_info.speed != TUSB_SPEED_HIGH) ? 1 : 0; edpt->next_pid = HCTSIZ_PID_DATA0; if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) { @@ -477,7 +477,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* } // clean up channel after part of transfer is done but the whole urb is not complete -static void channel_xfer_wrapup(dwc2_regs_t* dwc2, uint8_t ch_id) { +static void channel_xfer_out_wrapup(dwc2_regs_t* dwc2, uint8_t ch_id) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; @@ -490,14 +490,10 @@ static void channel_xfer_wrapup(dwc2_regs_t* dwc2, uint8_t ch_id) { * number of packets that have been transferred via the USB. This is always an integral number of packets if the * transfer was halted before its normal completion. */ - uint16_t actual_bytes; - if (channel->hcchar_bm.ep_dir == TUSB_DIR_IN) { - actual_bytes = edpt->buflen - channel->hctsiz_bm.xfer_size; - } else { - const uint16_t remain_packets = channel->hctsiz_bm.packet_count; - const uint16_t total_packets = cal_packet_count(edpt->buflen, channel->hcchar_bm.ep_size); - actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size; - } + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + const uint16_t total_packets = cal_packet_count(edpt->buflen, channel->hcchar_bm.ep_size); + const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size; + xfer->txfifo_bytes = 0; xfer->xferred_bytes += actual_bytes; edpt->buffer += actual_bytes; @@ -540,9 +536,7 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) { edpt->next_pid = cal_next_pid(edpt->next_pid, packet_count); } - // split: TODO support later channel->hcsplt = edpt->hcsplt; - channel->hcint = 0xFFFFFFFFU; // clear all channel interrupts if (dma_host_enabled(dwc2)) { @@ -690,6 +684,9 @@ static void channel_xfer_in_retry(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci } } else { // for control/bulk: try again immediately + if (channel->hcsplt_bm.split_en) { + channel->hcsplt_bm.split_compl = 0; // retry with start split + } channel_send_in_token(dwc2, channel); } } @@ -840,7 +837,7 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t channel_disable(dwc2, channel); } else if (hcint & (HCINT_NAK | HCINT_XACT_ERR | HCINT_NYET)) { // clean up transfer so far, disable and start again later - channel_xfer_wrapup(dwc2, ch_id); + channel_xfer_out_wrapup(dwc2, ch_id); channel_disable(dwc2, channel); if (hcint & HCINT_XACT_ERR) { xfer->err_count++; @@ -883,19 +880,33 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci bool is_done = false; + // TU_LOG1("in hcint = %02lX\r\n", hcint); + if (hcint & HCINT_HALTED) { if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL | HCINT_BABBLE_ERR)) { - is_done = true; - xfer->err_count = 0; const uint16_t remain_bytes = (uint16_t) channel->hctsiz_bm.xfer_size; - xfer->xferred_bytes += (edpt->buflen - remain_bytes); + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + const uint16_t actual_len = edpt->buflen - remain_bytes; + xfer->xferred_bytes += actual_len; + + is_done = true; + if (hcint & HCINT_STALL) { xfer->result = XFER_RESULT_STALLED; } else if (hcint & HCINT_BABBLE_ERR) { xfer->result = XFER_RESULT_FAILED; + } else if (channel->hcsplt_bm.split_en && remain_packets && actual_len == edpt->hcchar_bm.ep_size) { + // Split can only complete 1 transaction (up to 1 packet) at a time, schedule more + is_done = false; + edpt->buffer += actual_len; + edpt->buflen -= actual_len; + + channel_xfer_in_retry(dwc2, ch_id, hcint); } else { xfer->result = XFER_RESULT_SUCCESS; } + + xfer->err_count = 0; channel->hcintmsk &= ~HCINT_ACK; } else if (hcint & HCINT_XACT_ERR) { xfer->err_count++; @@ -906,12 +917,27 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci channel->hcintmsk |= HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR; channel_xfer_in_retry(dwc2, ch_id, hcint); } - } else if (hcint & (HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR)) { + } else if (hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR)) { xfer->err_count = 0; - channel->hcintmsk &= ~(HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR); + channel->hcintmsk &= ~(HCINT_NAK | HCINT_DATATOGGLE_ERR); if ((hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR))) { channel_xfer_in_retry(dwc2, ch_id, hcint); } + } else if (hcint & HCINT_ACK) { + xfer->err_count = 0; + channel->hcintmsk &= ~HCINT_ACK; + + if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) { + // start split is ACK --> do complete split + channel->hcsplt_bm.split_compl = 1; + channel_send_in_token(dwc2, channel); + } + } else if (hcint & HCINT_NYET) { + if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl) { + // split not yet mean hub has no data, retry complete split + channel->hcsplt_bm.split_compl = 1; + channel_send_in_token(dwc2, channel); + } } } @@ -925,6 +951,8 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc bool is_done = false; + // TU_LOG1("out hcint = %02lX\r\n", hcint); + if (hcint & HCINT_HALTED) { if (hcint & (HCINT_XFER_COMPLETE | HCINT_STALL)) { is_done = true; @@ -934,14 +962,14 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc xfer->xferred_bytes += edpt->buflen; } else { xfer->result = XFER_RESULT_STALLED; - channel_xfer_wrapup(dwc2, ch_id); + channel_xfer_out_wrapup(dwc2, ch_id); } channel->hcintmsk &= ~HCINT_ACK; } else if (hcint & HCINT_XACT_ERR) { if (hcint & (HCINT_NAK | HCINT_NYET | HCINT_ACK)) { xfer->err_count = 0; // clean up transfer so far and start again - channel_xfer_wrapup(dwc2, ch_id); + channel_xfer_out_wrapup(dwc2, ch_id); channel_xfer_start(dwc2, ch_id); } else { xfer->err_count++; @@ -950,10 +978,23 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc is_done = true; } else { // clean up transfer so far and start again - channel_xfer_wrapup(dwc2, ch_id); + channel_xfer_out_wrapup(dwc2, ch_id); channel_xfer_start(dwc2, ch_id); } } + } else if (hcint & HCINT_ACK) { + xfer->err_count = 0; + if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) { + // start split is ACK --> do complete split + channel->hcsplt_bm.split_compl = 1; + channel->hcchar |= HCCHAR_CHENA; + } + } else if (hcint & HCINT_NYET) { + if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl) { + // split not yet mean hub has no data, retry complete split + channel->hcsplt_bm.split_compl = 1; + channel->hcchar |= HCCHAR_CHENA; + } } } else if (hcint & HCINT_ACK) { xfer->err_count = 0; @@ -969,7 +1010,7 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) { const bool is_dma = dma_host_enabled(dwc2); const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); - for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { // + for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { if (tu_bit_test(dwc2->haint, ch_id)) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; -- cgit v1.3.1 From fbc193647a69c749c43b6ea69733f144000c8a89 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 5 Nov 2024 16:26:56 +0700 Subject: split interrupt work with fs mouse --- src/portable/synopsys/dwc2/hcd_dwc2.c | 115 ++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 3527f682e..fa8269132 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -52,6 +52,10 @@ enum { HCD_XFER_ERROR_MAX = 3 }; +enum { + HCD_XFER_PERIOD_SPLIT_NYET_MAX = 3 +}; + //-------------------------------------------------------------------- // //-------------------------------------------------------------------- @@ -67,9 +71,12 @@ typedef struct { dwc2_channel_split_t hcsplt_bm; }; - uint8_t next_pid; - uint16_t frame_interval; - uint16_t frame_countdown; // count down to make a transfer for periodic endpoint (from interval) + struct TU_ATTR_PACKED { + uint32_t uframe_interval : 18; // micro-frame interval + uint32_t next_pid : 2; + }; + + uint32_t uframe_countdown; // micro-frame count down to transfer for periodic, only need 18-bit uint8_t* buffer; uint16_t buflen; @@ -80,7 +87,8 @@ typedef struct { volatile bool allocated; uint8_t ep_id; struct TU_ATTR_PACKED { - uint8_t err_count : 6; + uint8_t err_count : 3; + uint8_t period_split_nyet_count : 3; uint8_t do_ping : 1; uint8_t sof_schedule : 1; }; @@ -101,9 +109,9 @@ hcd_data_t _hcd_data; //-------------------------------------------------------------------- // //-------------------------------------------------------------------- -TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t convert_hprt_speed(uint32_t hprt_speed) { +TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t hprt_speed_get(dwc2_regs_t* dwc2) { tusb_speed_t speed; - switch(hprt_speed) { + switch(dwc2->hprt_bm.speed) { case HPRT_SPEED_HIGH: speed = TUSB_SPEED_HIGH; break; case HPRT_SPEED_FULL: speed = TUSB_SPEED_FULL; break; case HPRT_SPEED_LOW : speed = TUSB_SPEED_LOW ; break; @@ -411,7 +419,7 @@ void hcd_port_reset_end(uint8_t rhport) { // Get port link speed tusb_speed_t hcd_port_speed_get(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - const tusb_speed_t speed = convert_hprt_speed(dwc2->hprt_bm.speed); + const tusb_speed_t speed = hprt_speed_get(dwc2); return speed; } @@ -433,7 +441,7 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { // Open an endpoint bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* desc_ep) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - const tusb_speed_t rh_speed = convert_hprt_speed(dwc2->hprt_bm.speed); + const tusb_speed_t rh_speed = hprt_speed_get(dwc2); hcd_devtree_info_t devtree_info; hcd_devtree_get_info(dev_addr, &devtree_info); @@ -464,12 +472,15 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* edpt->next_pid = HCTSIZ_PID_DATA0; if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) { - edpt->frame_interval = 1 << (desc_ep->bInterval - 1); + edpt->uframe_interval = 1 << (desc_ep->bInterval - 1); + if (devtree_info.speed == TUSB_SPEED_FULL) { + edpt->uframe_interval <<= 3; + } } else if (desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT) { if (devtree_info.speed == TUSB_SPEED_HIGH) { - edpt->frame_interval = 1 << (desc_ep->bInterval - 1); + edpt->uframe_interval = 1 << (desc_ep->bInterval - 1); } else { - edpt->frame_interval = desc_ep->bInterval; + edpt->uframe_interval = desc_ep->bInterval << 3; } } @@ -669,9 +680,22 @@ static void channel_xfer_in_retry(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; if (edpt_is_periodic(channel->hcchar_bm.ep_type)){ + // retry immediately for periodic split nyet if haven't reach max retry + if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl && (hcint & HCINT_NYET)) { + xfer->period_split_nyet_count++; + if (xfer->period_split_nyet_count < HCD_XFER_PERIOD_SPLIT_NYET_MAX) { + channel->hcchar_bm.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame + channel_send_in_token(dwc2, channel); + return; + } else { + // too many NYET, de-allocate channel with below code + xfer->period_split_nyet_count = 0; + } + } + // for periodic, de-allocate channel, enable SOF set frame counter for later transfer edpt->next_pid = channel->hctsiz_bm.pid; // save PID - edpt->frame_countdown = edpt->frame_interval; + edpt->uframe_countdown = edpt->uframe_interval; dwc2->gintmsk |= GINTSTS_SOF; if (hcint & HCINT_HALTED) { @@ -683,10 +707,7 @@ static void channel_xfer_in_retry(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci channel_disable(dwc2, channel); } } else { - // for control/bulk: try again immediately - if (channel->hcsplt_bm.split_en) { - channel->hcsplt_bm.split_compl = 0; // retry with start split - } + // for control/bulk: retry immediately channel_send_in_token(dwc2, channel); } } @@ -901,6 +922,7 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci edpt->buffer += actual_len; edpt->buflen -= actual_len; + channel->hcsplt_bm.split_compl = 0; channel_xfer_in_retry(dwc2, ch_id, hcint); } else { xfer->result = XFER_RESULT_SUCCESS; @@ -915,29 +937,37 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci xfer->result = XFER_RESULT_FAILED; } else { channel->hcintmsk |= HCINT_ACK | HCINT_NAK | HCINT_DATATOGGLE_ERR; + channel->hcsplt_bm.split_compl = 0; channel_xfer_in_retry(dwc2, ch_id, hcint); } - } else if (hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR)) { - xfer->err_count = 0; - channel->hcintmsk &= ~(HCINT_NAK | HCINT_DATATOGGLE_ERR); - if ((hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR))) { + } else if (hcint & HCINT_NYET) { + // Must handle nyet before nak or ack. Could get a nyet at the same time as either of those on a BULK/CONTROL + // OUT that started with a PING. The nyet takes precedence. + if (channel->hcsplt_bm.split_en) { + // split not yet mean hub has no data, retry complete split + channel->hcsplt_bm.split_compl = 1; channel_xfer_in_retry(dwc2, ch_id, hcint); } } else if (hcint & HCINT_ACK) { xfer->err_count = 0; channel->hcintmsk &= ~HCINT_ACK; - - if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) { + if (channel->hcsplt_bm.split_en) { // start split is ACK --> do complete split + // TODO: for ISO must use xact_pos to plan complete split based on microframe (up to 187.5 bytes/uframe) channel->hcsplt_bm.split_compl = 1; + if (edpt_is_periodic(channel->hcchar_bm.ep_type)) { + channel->hcchar_bm.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame + } channel_send_in_token(dwc2, channel); } - } else if (hcint & HCINT_NYET) { - if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl) { - // split not yet mean hub has no data, retry complete split - channel->hcsplt_bm.split_compl = 1; - channel_send_in_token(dwc2, channel); - } + } else if (hcint & (HCINT_NAK | HCINT_DATATOGGLE_ERR)) { + xfer->err_count = 0; + channel->hcintmsk &= ~(HCINT_NAK | HCINT_DATATOGGLE_ERR); + channel->hcsplt_bm.split_compl = 0; // restart with start-split + channel_xfer_in_retry(dwc2, ch_id, hcint); + } else if (hcint & HCINT_FARME_OVERRUN) { + // retry start-split in next binterval + channel_xfer_in_retry(dwc2, ch_id, hcint); } } @@ -982,6 +1012,12 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc channel_xfer_start(dwc2, ch_id); } } + } else if (hcint & HCINT_NYET) { + if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl) { + // split not yet mean hub has no data, retry complete split + channel->hcsplt_bm.split_compl = 1; + channel->hcchar |= HCCHAR_CHENA; + } } else if (hcint & HCINT_ACK) { xfer->err_count = 0; if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) { @@ -989,12 +1025,6 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc channel->hcsplt_bm.split_compl = 1; channel->hcchar |= HCCHAR_CHENA; } - } else if (hcint & HCINT_NYET) { - if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl) { - // split not yet mean hub has no data, retry complete split - channel->hcsplt_bm.split_compl = 1; - channel->hcchar |= HCCHAR_CHENA; - } } } else if (hcint & HCINT_ACK) { xfer->err_count = 0; @@ -1055,13 +1085,16 @@ static bool handle_sof_irq(uint8_t rhport, bool in_isr) { bool more_isr = false; + // If highspeed then SOF is 125us, else 1ms + const uint32_t ucount = (hprt_speed_get(dwc2) == TUSB_SPEED_HIGH ? 1 : 8); + for(uint8_t ep_id = 0; ep_id < CFG_TUH_DWC2_ENDPOINT_MAX; ep_id++) { hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; - if (edpt->hcchar_bm.enable && edpt_is_periodic(edpt->hcchar_bm.ep_type) && edpt->frame_countdown > 0) { - edpt->frame_countdown--; - if (edpt->frame_countdown == 0) { + if (edpt->hcchar_bm.enable && edpt_is_periodic(edpt->hcchar_bm.ep_type) && edpt->uframe_countdown > 0) { + edpt->uframe_countdown -= tu_min32(ucount, edpt->uframe_countdown); + if (edpt->uframe_countdown == 0) { if (!edpt_xfer_kickoff(dwc2, ep_id)) { - edpt->frame_countdown = 1; // failed to start (out of channel), try again next frame + edpt->uframe_countdown = ucount; // failed to start, try again next frame } } @@ -1073,7 +1106,8 @@ static bool handle_sof_irq(uint8_t rhport, bool in_isr) { } // Config HCFG FS/LS clock and HFIR for SOF interval according to link speed (value is in PHY clock unit) -static void port0_enable(dwc2_regs_t* dwc2, tusb_speed_t speed) { +static void port0_enable(dwc2_regs_t* dwc2) { + const tusb_speed_t speed = hprt_speed_get(dwc2); uint32_t hcfg = dwc2->hcfg & ~HCFG_FSLS_PHYCLK_SEL; const dwc2_gusbcfg_t gusbcfg_bm = dwc2->gusbcfg_bm; @@ -1143,8 +1177,7 @@ static void handle_hprt_irq(uint8_t rhport, bool in_isr) { if (hprt_bm.enable) { // Port enable - const tusb_speed_t speed = convert_hprt_speed(hprt_bm.speed); - port0_enable(dwc2, speed); + port0_enable(dwc2); } else { // TU_ASSERT(false, ); } -- cgit v1.3.1 From fe79a93594c6ec994443e61317ea6bcc9fdf36b4 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 6 Nov 2024 12:09:32 +0700 Subject: implement split for slave, got mouse working --- src/portable/synopsys/dwc2/dwc2_type.h | 9 ++ src/portable/synopsys/dwc2/hcd_dwc2.c | 176 +++++++++++++++++++++++---------- src/tusb_option.h | 2 +- 3 files changed, 136 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index a88a2d389..dfd61bc32 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -438,6 +438,12 @@ typedef struct TU_ATTR_PACKED { } dwc2_channel_tsize_t; TU_VERIFY_STATIC(sizeof(dwc2_channel_tsize_t) == 4, "incorrect size"); +typedef struct TU_ATTR_PACKED { + uint32_t num : 16; // 0..15 Frame number + uint32_t remainning : 16; // 16..31 Frame remaining +} dwc2_hfnum_t; +TU_VERIFY_STATIC(sizeof(dwc2_hfnum_t) == 4, "incorrect size"); + // Host Channel typedef struct { union { @@ -592,7 +598,10 @@ typedef struct { //------------ Host -------------// volatile uint32_t hcfg; // 400 Host Configuration volatile uint32_t hfir; // 404 Host Frame Interval + union { volatile uint32_t hfnum; // 408 Host Frame Number / Frame Remaining + volatile dwc2_hfnum_t hfnum_bm; + }; uint32_t reserved40c; // 40C union { volatile uint32_t hptxsts; // 410 Host Periodic TX FIFO / Queue Status diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index fa8269132..aabe1c062 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -73,7 +73,10 @@ typedef struct { struct TU_ATTR_PACKED { uint32_t uframe_interval : 18; // micro-frame interval + uint32_t speed : 2; uint32_t next_pid : 2; + uint32_t do_ping : 1; + // uint32_t : 9; }; uint32_t uframe_countdown; // micro-frame count down to transfer for periodic, only need 18-bit @@ -89,14 +92,14 @@ typedef struct { struct TU_ATTR_PACKED { uint8_t err_count : 3; uint8_t period_split_nyet_count : 3; - uint8_t do_ping : 1; - uint8_t sof_schedule : 1; + uint8_t halted_nyet : 1; + uint8_t halted_sof_schedule : 1; }; uint8_t result; uint16_t xferred_bytes; // bytes that accumulate transferred though USB bus for the whole hcd_edpt_xfer(), which can // be composed of multiple channel_xfer_start() (retry with NAK/NYET) - uint16_t txfifo_bytes; // bytes written to TX FIFO (may not be transferred on USB bus). + uint16_t fifo_bytes; // bytes written/read from/to FIFO (may not be transferred on USB bus). } hcd_xfer_t; typedef struct { @@ -470,6 +473,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* hcsplt_bm->split_compl = 0; hcsplt_bm->split_en = (rh_speed == TUSB_SPEED_HIGH && devtree_info.speed != TUSB_SPEED_HIGH) ? 1 : 0; + edpt->speed = devtree_info.speed; edpt->next_pid = HCTSIZ_PID_DATA0; if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) { edpt->uframe_interval = 1 << (desc_ep->bInterval - 1); @@ -505,7 +509,7 @@ static void channel_xfer_out_wrapup(dwc2_regs_t* dwc2, uint8_t ch_id) { const uint16_t total_packets = cal_packet_count(edpt->buflen, channel->hcchar_bm.ep_size); const uint16_t actual_bytes = (total_packets - remain_packets) * channel->hcchar_bm.ep_size; - xfer->txfifo_bytes = 0; + xfer->fifo_bytes = 0; xfer->xferred_bytes += actual_bytes; edpt->buffer += actual_bytes; edpt->buflen -= actual_bytes; @@ -519,7 +523,7 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) { bool const is_period = edpt_is_periodic(hcchar_bm->ep_type); // clear previous state - xfer->txfifo_bytes = 0; + xfer->fifo_bytes = 0; // hchar: restore but don't enable yet if (is_period) { @@ -530,15 +534,12 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) { // hctsiz: zero length packet still count as 1 const uint16_t packet_count = cal_packet_count(edpt->buflen, hcchar_bm->ep_size); uint32_t hctsiz = (edpt->next_pid << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | edpt->buflen; - if (xfer->do_ping && edpt->next_pid != HCTSIZ_PID_SETUP && hcchar_bm->ep_dir == TUSB_DIR_OUT) { - hcd_devtree_info_t devtree_info; - hcd_devtree_get_info(hcchar_bm->dev_addr, &devtree_info); - if (devtree_info.speed == TUSB_SPEED_HIGH) { - hctsiz |= HCTSIZ_DOPING; - } - xfer->do_ping = 0; + if (edpt->do_ping && edpt->speed == TUSB_SPEED_HIGH && + edpt->next_pid != HCTSIZ_PID_SETUP && hcchar_bm->ep_dir == TUSB_DIR_OUT) { + hctsiz |= HCTSIZ_DOPING; } channel->hctsiz = hctsiz; + edpt->do_ping = 0; // pre-calculate next PID based on packet count, adjusted in transfer complete interrupt if short packet if (hcchar_bm->ep_num == 0) { @@ -565,9 +566,12 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) { } else { uint32_t hcintmsk = HCINT_NAK | HCINT_XACT_ERR | HCINT_STALL | HCINT_XFER_COMPLETE | HCINT_DATATOGGLE_ERR; if (hcchar_bm->ep_dir == TUSB_DIR_IN) { - hcintmsk |= HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR; + hcintmsk |= HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR | HCINT_ACK; } else { hcintmsk |= HCINT_NYET; + if (edpt->hcsplt_bm.split_en) { + hcintmsk |= HCINT_ACK; + } } channel->hcintmsk = hcintmsk; dwc2->haintmsk |= TU_BIT(ch_id); @@ -680,9 +684,10 @@ static void channel_xfer_in_retry(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; if (edpt_is_periodic(channel->hcchar_bm.ep_type)){ - // retry immediately for periodic split nyet if haven't reach max retry - if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl && (hcint & HCINT_NYET)) { + // retry immediately for periodic split NYET if we haven't reach max retry + if (channel->hcsplt_bm.split_en && channel->hcsplt_bm.split_compl && (hcint & HCINT_NYET || xfer->halted_nyet)) { xfer->period_split_nyet_count++; + xfer->halted_nyet = 0; if (xfer->period_split_nyet_count < HCD_XFER_PERIOD_SPLIT_NYET_MAX) { channel->hcchar_bm.odd_frame = 1 - (dwc2->hfnum & 1); // transfer on next frame channel_send_in_token(dwc2, channel); @@ -703,7 +708,7 @@ static void channel_xfer_in_retry(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci channel_dealloc(dwc2, ch_id); } else { // disable channel first if not halted (called slave isr) - xfer->sof_schedule = 1; + xfer->halted_sof_schedule = 1; channel_disable(dwc2, channel); } } else { @@ -712,6 +717,24 @@ static void channel_xfer_in_retry(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci } } +#if CFG_TUSB_DEBUG +TU_ATTR_ALWAYS_INLINE static inline void print_hcint(uint32_t hcint) { + const char* str[] = { + "XFRC", "HALTED", "AHBERR", "STALL", + "NAK", "ACK", "NYET", "XERR", + "BBLERR", "FRMOR", "DTERR", "BNA", + "XCSERR", "DESC_LST" + }; + + for(uint32_t i=0; i<14; i++) { + if (hcint & TU_BIT(i)) { + TU_LOG1("%s ", str[i]); + } + } + TU_LOG1("\r\n"); +} +#endif + #if CFG_TUH_DWC2_SLAVE_ENABLE static void handle_rxflvl_irq(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); @@ -719,11 +742,11 @@ static void handle_rxflvl_irq(uint8_t rhport) { // Pop control word off FIFO const dwc2_grxstsp_t grxstsp_bm = dwc2->grxstsp_bm; const uint8_t ch_id = grxstsp_bm.ep_ch_num; - dwc2_channel_t* channel = &dwc2->channel[ch_id]; + // dwc2_channel_t* channel = &dwc2->channel[ch_id]; switch (grxstsp_bm.packet_status) { case GRXSTS_PKTSTS_RX_DATA: { - // In packet received + // In packet received, pop this entry --> ACK interrupt const uint16_t byte_count = grxstsp_bm.byte_count; hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,); @@ -732,14 +755,7 @@ static void handle_rxflvl_irq(uint8_t rhport) { if (byte_count) { dfifo_read_packet(dwc2, edpt->buffer + xfer->xferred_bytes, byte_count); xfer->xferred_bytes += byte_count; - - const uint16_t remain_packets = channel->hctsiz_bm.packet_count; - if (byte_count < edpt->hcchar_bm.ep_size) { - // short packet: update PID based on remain packets count - edpt->next_pid = cal_next_pid(edpt->next_pid, remain_packets); - } if (remain_packets) { - channel_send_in_token(dwc2, channel); // still more packet to send - } + xfer->fifo_bytes = byte_count; } break; } @@ -778,7 +794,7 @@ static bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { const uint16_t remain_packets = channel->hctsiz_bm.packet_count; for (uint16_t i = 0; i < remain_packets; i++) { - const uint16_t remain_bytes = edpt->buflen - xfer->txfifo_bytes; + const uint16_t remain_bytes = edpt->buflen - xfer->fifo_bytes; const uint16_t xact_bytes = tu_min16(remain_bytes, channel->hcchar_bm.ep_size); // skip if there is not enough space in FIFO and RequestQueue. @@ -787,26 +803,41 @@ static bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { return true; } - dfifo_write_packet(dwc2, ch_id, edpt->buffer + xfer->txfifo_bytes, xact_bytes); - xfer->txfifo_bytes += xact_bytes; + dfifo_write_packet(dwc2, ch_id, edpt->buffer + xfer->fifo_bytes, xact_bytes); + xfer->fifo_bytes += xact_bytes; } } } - return false; // all data written + return false; // no channel has pending data } static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; + hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; bool is_done = false; -// TU_LOG1("ch%u: ep = %u, hcint = 0x%04lX\r\n", ch_id, channel->hcchar_bm.ep_num, hcint); + // if (channel->hcsplt_bm.split_en) { + // if (edpt->hcchar_bm.ep_num == 1) { + // TU_LOG1("Frame %u, ch %u: ep %u, hcint 0x%04lX ", dwc2->hfnum_bm.num, ch_id, channel->hcchar_bm.ep_num, hcint); + // print_hcint(hcint); + // } if (hcint & HCINT_XFER_COMPLETE) { - xfer->result = XFER_RESULT_SUCCESS; + if (edpt->hcchar_bm.ep_num != 0) { + edpt->next_pid = channel->hctsiz_bm.pid; // save pid (already toggled) + } + + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + if (channel->hcsplt_bm.split_en && remain_packets && xfer->fifo_bytes == edpt->hcchar_bm.ep_size) { + // Split can only complete 1 transaction (up to 1 packet) at a time, schedule more + channel->hcsplt_bm.split_compl = 0; + } else { + xfer->result = XFER_RESULT_SUCCESS; + } + channel_disable(dwc2, channel); - channel->hcintmsk &= ~HCINT_ACK; } else if (hcint & (HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_STALL)) { if (hcint & HCINT_STALL) { xfer->result = XFER_RESULT_STALLED; @@ -816,37 +847,66 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h xfer->err_count++; channel->hcintmsk |= HCINT_ACK; } + + channel_disable(dwc2, channel); + } else if (hcint & HCINT_NYET) { + // restart complete split + channel->hcsplt_bm.split_compl = 1; + xfer->halted_nyet = 1; + channel_disable(dwc2, channel); + } else if (hcint & HCINT_NAK) { + // NAK received, re-enable channel if request queue is available + if (channel->hcsplt_bm.split_en) { + channel->hcsplt_bm.split_compl = 0; // restart with start-split + } + channel_disable(dwc2, channel); + } else if (hcint & HCINT_ACK) { + xfer->err_count = 0; + + if (channel->hcsplt_bm.split_en) { + if (!channel->hcsplt_bm.split_compl) { + // start split is ACK --> do complete split + channel->hcintmsk |= HCINT_NYET; + channel->hcsplt_bm.split_compl = 1; + channel_send_in_token(dwc2, channel); + } else { + // do nothing for complete split with DATA, this will trigger XferComplete and handled there + } + } else { + // ACK with data + const uint16_t remain_packets = channel->hctsiz_bm.packet_count; + if (remain_packets) { + // still more packet to receive, also reset to start split + channel->hcsplt_bm.split_compl = 0; + channel_send_in_token(dwc2, channel); + } + } } else if (hcint & HCINT_HALTED) { channel->hcintmsk &= ~HCINT_HALTED; - if (xfer->sof_schedule) { + if (xfer->halted_sof_schedule) { // de-allocate channel but does not complete xfer, we schedule it in the SOF interrupt channel_dealloc(dwc2, ch_id); } else if (xfer->result != XFER_RESULT_INVALID) { is_done = true; - } else if (channel->hcchar_bm.err_multi_count == HCD_XFER_ERROR_MAX) { + } else if (xfer->err_count == HCD_XFER_ERROR_MAX) { xfer->result = XFER_RESULT_FAILED; is_done = true; } else { + // got here due to NAK or NYET channel_xfer_in_retry(dwc2, ch_id, hcint); } } else if (hcint & HCINT_DATATOGGLE_ERR) { xfer->err_count = 0; TU_ASSERT(false); - } else if (hcint & HCINT_NAK) { - // NAK received, re-enable channel if request queue is available - channel_xfer_in_retry(dwc2, ch_id, hcint); - } else if (hcint & HCINT_ACK) { - xfer->err_count = 0; - channel->hcintmsk &= ~HCINT_ACK; } - return is_done; } static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hcint) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; + hcd_endpoint_t* edpt = &_hcd_data.edpt[xfer->ep_id]; bool is_done = false; if (hcint & HCINT_XFER_COMPLETE) { @@ -856,7 +916,18 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t } else if (hcint & HCINT_STALL) { xfer->result = XFER_RESULT_STALLED; channel_disable(dwc2, channel); - } else if (hcint & (HCINT_NAK | HCINT_XACT_ERR | HCINT_NYET)) { + } else if (hcint & HCINT_NYET) { + xfer->err_count = 0; + if (channel->hcsplt_bm.split_en) { + // retry complete split + channel->hcsplt_bm.split_compl = 1; + channel->hcchar |= HCCHAR_CHENA; + } else { + edpt->do_ping = 1; + channel_xfer_out_wrapup(dwc2, ch_id); + channel_disable(dwc2, channel); + } + } else if (hcint & (HCINT_NAK | HCINT_XACT_ERR)) { // clean up transfer so far, disable and start again later channel_xfer_out_wrapup(dwc2, ch_id); channel_disable(dwc2, channel); @@ -864,29 +935,34 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t xfer->err_count++; channel->hcintmsk |= HCINT_ACK; } else { - // NAK/NYET disable channel to flush all posted request and try again + // NAK disable channel to flush all posted request and try again + edpt->do_ping = 1; xfer->err_count = 0; } } else if (hcint & HCINT_HALTED) { channel->hcintmsk &= ~HCINT_HALTED; if (xfer->result != XFER_RESULT_INVALID) { is_done = true; - } else if (channel->hcchar_bm.err_multi_count == HCD_XFER_ERROR_MAX) { + } else if (xfer->err_count == HCD_XFER_ERROR_MAX) { xfer->result = XFER_RESULT_FAILED; is_done = true; } else { - // Got here due to NAK or NYET -> Retry transfer with do PING (for highspeed) - xfer->do_ping = 1; + // Got here due to NAK or NYET TU_ASSERT(channel_xfer_start(dwc2, ch_id)); } } else if (hcint & HCINT_ACK) { xfer->err_count = 0; channel->hcintmsk &= ~HCINT_ACK; + if (channel->hcsplt_bm.split_en && !channel->hcsplt_bm.split_compl) { + // start split is ACK --> do complete split + channel->hcsplt_bm.split_compl = 1; + channel->hcchar |= HCCHAR_CHENA; + } } if (is_done) { - xfer->xferred_bytes += xfer->txfifo_bytes; - xfer->txfifo_bytes = 0; + xfer->xferred_bytes += xfer->fifo_bytes; + xfer->fifo_bytes = 0; } return is_done; @@ -1230,7 +1306,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { } #if CFG_TUH_DWC2_SLAVE_ENABLE - // RxFIFO non-empty interrupt handling, must be handled before HCINT + // RxFIFO non-empty interrupt handling if (gintsts & GINTSTS_RXFLVL) { // RXFLVL bit is read-only dwc2->gintmsk &= ~GINTSTS_RXFLVL; // disable RXFLVL interrupt while reading diff --git a/src/tusb_option.h b/src/tusb_option.h index b193d9c09..688976e55 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -261,7 +261,7 @@ // Enable DWC2 DMA for host #ifndef CFG_TUH_DWC2_DMA_ENABLE - #define CFG_TUH_DWC2_DMA_ENABLE 1 + #define CFG_TUH_DWC2_DMA_ENABLE 0 #endif // Enable PIO-USB software host controller -- cgit v1.3.1 From 48b32f5d1ec17f80db6ae960e535e7f5bfe5329b Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 7 Nov 2024 15:15:24 +0700 Subject: enable host dwc2 dma by default --- .idea/cmake.xml | 6 +++++- .idea/runConfigurations/k64f.xml | 5 +++-- .idea/runConfigurations/kl25.xml | 5 +++-- .idea/runConfigurations/lpc1857.xml | 5 +++-- .idea/runConfigurations/lpc4088.xml | 5 +++-- .idea/runConfigurations/lpc54628.xml | 5 +++-- .idea/runConfigurations/lpc55s69.xml | 5 +++-- .idea/runConfigurations/mcx947.xml | 5 +++-- .idea/runConfigurations/nrf52840.xml | 5 +++-- .idea/runConfigurations/nrf5340.xml | 5 +++-- .idea/runConfigurations/ra2a1.xml | 5 +++-- .idea/runConfigurations/ra4m1.xml | 5 +++-- .idea/runConfigurations/ra6m1.xml | 5 +++-- .idea/runConfigurations/ra6m5.xml | 5 +++-- .idea/runConfigurations/rt1010.xml | 5 +++-- .idea/runConfigurations/rt1060.xml | 5 +++-- .idea/runConfigurations/samd21g18.xml | 5 +++-- .idea/runConfigurations/samd51j19.xml | 5 +++-- .idea/runConfigurations/stm32g474.xml | 2 +- .idea/runConfigurations/stm32h563.xml | 2 +- .idea/runConfigurations/stm32h743.xml | 2 +- .idea/runConfigurations/stm32u5a5.xml | 2 +- .idea/runConfigurations/uno_r4.xml | 5 +++-- src/tusb_option.h | 2 +- 24 files changed, 64 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/.idea/cmake.xml b/.idea/cmake.xml index 05dceda5a..625bfa916 100644 --- a/.idea/cmake.xml +++ b/.idea/cmake.xml @@ -92,13 +92,17 @@ + + + - + + diff --git a/.idea/runConfigurations/k64f.xml b/.idea/runConfigurations/k64f.xml index 80ca22d40..6db0dd74e 100644 --- a/.idea/runConfigurations/k64f.xml +++ b/.idea/runConfigurations/k64f.xml @@ -1,7 +1,8 @@ - - + + +