Merge remote-tracking branch 'tinyusb/master' into dwc2_ep0

This commit is contained in:
Mengsk
2025-10-06 17:13:56 +02:00
15 changed files with 246 additions and 951 deletions

View File

@ -67,3 +67,12 @@ Before building, it's recommended to run pre-commit to ensure code quality:
- hw/: Board support packages and MCU drivers
- examples/: Reference examples for device/host/dual
- test/: Unit tests and hardware integration tests
## Release Process
To prepare a new release:
1. Update the `version` variable in `tools/make_release.py` to the new version number
2. Run the release script: `python tools/make_release.py`
- This will update version numbers in `src/tusb_option.h`, `repository.yml`, and `library.json`
- It will also regenerate documentation
3. Update `docs/info/changelog.rst` with release notes
4. Commit changes and create release tag

View File

@ -122,7 +122,7 @@ Supported CPUs
+--------------+-----------------------------+--------+------+-----------+------------------------+-------------------+
| Dialog | DA1469x | ✔ | ✖ | ✖ | da146xx | |
+--------------+-----------------------------+--------+------+-----------+------------------------+-------------------+
| Espressif | S2, S3 | ✔ | ✔ | ✖ | dwc2 or esp32sx | |
| Espressif | S2, S3 | ✔ | ✔ | ✖ | dwc2 | |
| ESP32 +-----------------------------+--------+------+-----------+------------------------+-------------------+
| | P4 | ✔ | ✔ | ✔ | dwc2 | |
| +-----------------------------+--------+------+-----------+------------------------+-------------------+

View File

@ -2,6 +2,198 @@
Changelog
*********
0.19.0
======
General
-------
- New MCUs and Boards:
- Add ESP32-H4, ESP32-C5, ESP32-C61 support
- Add STM32U0, STM32WBA, STM32N6
- Add AT32F405, AT32F403A, AT32F415, AT32F423 support
- Add CH32V305 support and CH32V20x USB host support
- Add MCXA156 SDK 2.16 support and FRDM-MCXA156 board
API Changes
-----------
- Core APIs
- Add weak callbacks with new syntax for better compiler compatibility
- Add ``tusb_deinit()`` to cleanup stack
- Add time functions: ``tusb_time_millis_api()`` and ``tusb_time_delay_ms_api()``
- Add ``osal_critical`` APIs for critical section handling
- Introduce ``xfer_isr()`` callback for ISO transfer optimization in device classes
- Device APIs
- CDC: Add notification support ``tud_cdc_configure()``, ``tud_cdc_n_notify_uart_state()``,
``tud_cdc_n_notify_conn_speed_change()``, ``tud_cdc_notify_complete_cb()``
- MSC: Add ``tud_msc_inquiry2_cb()`` with bufsize parameter, update ``tud_msc_async_io_done()``
with ``in_isr`` parameter
- Audio: Add ``tud_audio_n_mounted()`` and various FIFO access functions
- MTP: Add ``tud_mtp_mounted()``, ``tud_mtp_data_send()``, ``tud_mtp_data_receive()``,
``tud_mtp_response_send()``, ``tud_mtp_event_send()``
- Host APIs
- Core: Add ``tuh_edpt_close()``, ``tuh_address_set()``, ``tuh_descriptor_get_device_local()``,
``tuh_descriptor_get_string_langid()``, ``tuh_connected()``, ``tuh_bus_info_get()``
- Add enumeration callbacks: ``tuh_enum_descriptor_device_cb()``,
``tuh_enum_descriptor_configuration_cb()``
- CDC: Add ``tuh_cdc_get_control_line_state_local()``, ``tuh_cdc_get/set_dtr/rts()``,
``tuh_cdc_connect/disconnect()`` and sync versions of all control APIs
- MIDI: Add ``tuh_midi_itf_get_info()``, ``tuh_midi_packet_read_n()``,
``tuh_midi_packet_write_n()``, ``tuh_midi_read_available()``, ``tuh_midi_write_flush()``,
``tuh_midi_descriptor_cb()``
Controller Driver (DCD & HCD)
-----------------------------
- DWC2
- Support DWC2 v4.30a with improved reset procedure
- Fix core reset: wait for AHB idle before reset
- Add STM32 DWC2 data cache support with proper alignment
- Host improvements:
- Fix disconnect detection and SOF flag handling
- Fix HFIR timing off-by-one error
- Retry IN token immediately for bInterval=1
- Proper attach debouncing (200ms)
- Fix all retry intervals
- Resume OUT transfer when PING ACKed
- Fix enumeration racing conditions
- Refactor bitfields for better code generation
- FSDEV (STM32)
- Fix AT32 compile issues after single-buffered endpoint changes
- Add configurable single-buffered isochronous endpoints
- Fix STM32H7 recurrent suspend ISR
- Fix STM32L4 GPIOD clock enable for variants without GPIOD
- Fix STM32 PHYC PLL stability wait
- Improve PMA size handling for STM32U0
- EHCI
- Fix removed QHD getting reused
- Fix NXP USBPHY disconnection detection
- Chipidea/NXP
- Fix race condition with spinlock
- Improve iMXRT support: fix build, disable BOARD_ConfigMPU, fix attach debouncing on port1 highspeed
- Fix iMXRT1064 and add to HIL test pool
- MAX3421E
- Use spinlock for thread safety instead of atomic flag
- Implement ``hcd_edpt_close()``
- RP2040
- Fix audio ISO transfer: reset state before notifying stack
- Fix CMake RTOS cache variable
- Abort transfer if active in ``iso_activate()``
- SAMD
- Add host controller driver support
Device Stack
------------
- USBD Core
- Introduce ``xfer_isr()`` callback for interrupt-time transfer handling
- Add ``usbd_edpt_xfer_fifo()`` stub
- Revert endpoint busy/claim status if ``xfer_isr()`` defers to ``xfer_cb()``
- Audio
- Major simplification of UAC driver and alt settings management
- Move ISO transfers into ``xfer_isr()`` for better performance
- Remove FIFO mutex (single producer/consumer optimization)
- Add implicit feedback support for data IN endpoints
- Fix alignment issues
- Update buffer macros with cache line size alignment
- CDC
- Add notification support: ``CFG_TUD_CDC_NOTIFY``, ``tud_cdc_n_notify_conn_speed_change()``, ``tud_cdc_notify_complete_cb()``
- Reduce default bInterval from 16ms to 1ms for better responsiveness
- Rename ``tud_cdc_configure_fifo()`` to ``tud_cdc_configure()`` and add ``tx_overwritable_if_not_connected`` option
- Fix web serial robustness with major overhaul and logic cleanup
- HID
- Add Usage Page and Table for Power Devices (0x84 - 0x85)
- Fix HID descriptor parser variable size and 4-byte item handling
- Add consumer page configurations
- MIDI
- Fix MIDI interface descriptor handling after audio streaming interface
- Skip RX data with all zeroes
- MSC
- Add async I/O support for MSC using ``tud_msc_async_io_done()``
- Add ``tud_msc_inquiry2_cb()`` with bufsize for full inquiry response
- MTP
- Add new Media Transfer Protocol (MTP) device class driver
- Support MTP operations: GetDeviceInfo, SendObjectInfo, SendObject
- Add MTP event support with ``tud_mtp_event_send()``
- Implement filesystem example with callbacks
- Add hardware-in-the-loop testing support
- NCM
- Add USB NCM link state control support
- Fix DHCP offer/ACK destination
- USBTMC
- Add vendor-specific message support
- Vendor
- Fix vendor device reset and open issues
- Fix descriptor parsing for ``CFG_TUD_VENDOR > 1``
- Fix vendor FIFO argument calculation
Host Stack
----------
- USBH Core
- Major enumeration improvements:
- Fix enumeration racing conditions
- Add proper attach debouncing with hub/rootport handling (200ms delay)
- Reduce ``ENUM_DEBOUNCING_DELAY_MS`` to 200ms
- Always get language ID, manufacturer, product, and serial strings during enumeration
- Always get first 2 bytes of string descriptor to determine length (prevents buffer overflow)
- Support devices with multiple configurations
- Add ``tuh_enum_descriptor_device_cb()`` and ``tuh_enum_descriptor_configuration_cb()`` callbacks
- Add ``tuh_descriptor_get_string_langid()`` API
- Hub improvements:
- Check status before getting first device descriptor
- Properly handle port status and change detection
- Queue status endpoint for detach/remove events
- Fix hub status change endpoint handling
- Fix endpoint management:
- ``hcd_edpt_open()`` returns false if endpoint already opened
- Add ``hcd_edpt_close()`` implementation
- Abort pending transfers on close
- Add roothub debouncing flag to ignore attach/remove during debouncing
- Move address setting and bus info management to separate structures
- Force removed devices in same bus info before setting address
- CDC Serial Host
- Major refactor to generalize CDC serial drivers (FTDI, CP210x, CH34x, PL2303, ACM)
- Add explicit ``sync()`` API with ``TU_API_SYNC()`` returning ``tusb_xfer_result_t``
- Rename ``tuh_cdc_get_local_line_coding()`` to ``tuh_cdc_get_line_coding_local()``
- Add ``tuh_cdc_get_control_line_state_local()``
- Implement ``tuh_cdc_get/set_dtr/rts()`` as inline functions
- MIDI Host
- Major API changes:
- Rename ``tuh_midi_stream_flush()`` to ``tuh_midi_write_flush()``
- Add ``tuh_midi_packet_read_n()`` and ``tuh_midi_packet_write_n()``
- Add ``CFG_TUH_MIDI_STREAM_API`` to opt out of stream API
- Change API to use index instead of device address (supports multiple MIDI per device)
- Rename ``tuh_midi_get_num_rx/tx_cables()`` to ``tuh_midi_get_rx/tx_cable_count()``
- Add ``tuh_midi_descriptor_cb()`` and ``tuh_midi_itf_get_info()``
- MSC Host
- Continue async I/O improvements
- HID Host
- Fix version string to actually show version
0.18.0
======

View File

@ -4,9 +4,9 @@ Dependencies
MCU low-level peripheral driver and external libraries for building TinyUSB examples
======================================== ================================================================ ======================================== ======================================================================================================================================================================================================================================================================================================================================================
======================================== ================================================================ ======================================== ======================================================================================================================================================================================================================================================================================================================================================================
Local Path Repo Commit Required by
======================================== ================================================================ ======================================== ======================================================================================================================================================================================================================================================================================================================================================
======================================== ================================================================ ======================================== ======================================================================================================================================================================================================================================================================================================================================================================
hw/mcu/allwinner https://github.com/hathach/allwinner_driver.git 8e5e89e8e132c0fd90e72d5422e5d3d68232b756 fc100s
hw/mcu/analog/msdk https://github.com/analogdevicesinc/msdk.git b20b398d3e5e2007594e54a74ba3d2a2e50ddd75 maxim
hw/mcu/artery/at32f402_405 https://github.com/ArteryTek/AT32F402_405_Firmware_Library.git 4424515c2663e82438654e0947695295df2abdfe at32f402_405
@ -31,6 +31,7 @@ hw/mcu/renesas/fsp https://github.com/renesas/fsp.git
hw/mcu/renesas/rx https://github.com/kkitayam/rx_device.git 706b4e0cf485605c32351e2f90f5698267996023 rx
hw/mcu/silabs/cmsis-dfp-efm32gg12b https://github.com/cmsis-packs/cmsis-dfp-efm32gg12b.git f1c31b7887669cb230b3ea63f9b56769078960bc efm32
hw/mcu/sony/cxd56/spresense-exported-sdk https://github.com/sonydevworld/spresense-exported-sdk.git 2ec2a1538362696118dc3fdf56f33dacaf8f4067 spresense
hw/mcu/st/cmsis-device-u0 https://github.com/STMicroelectronics/cmsis-device-u0.git e3a627c6a5bc4eb2388e1885a95cc155e1672253 stm32u0
hw/mcu/st/cmsis-device-wba https://github.com/STMicroelectronics/cmsis-device-wba.git 647d8522e5fd15049e9a1cc30ed19d85e5911eaf stm32wba
hw/mcu/st/cmsis_device_c0 https://github.com/STMicroelectronics/cmsis_device_c0.git 517611273f835ffe95318947647bc1408f69120d stm32c0
hw/mcu/st/cmsis_device_f0 https://github.com/STMicroelectronics/cmsis_device_f0.git cbb5da5d48b4b5f2efacdc2f033be30f9d29889f stm32f0
@ -70,6 +71,7 @@ hw/mcu/st/stm32l1xx_hal_driver https://github.com/STMicroelectronics/
hw/mcu/st/stm32l4xx_hal_driver https://github.com/STMicroelectronics/stm32l4xx_hal_driver.git 3e039bbf62f54bbd834d578185521cff80596efe stm32l4
hw/mcu/st/stm32l5xx_hal_driver https://github.com/STMicroelectronics/stm32l5xx_hal_driver.git 3340b9a597bcf75cc173345a90a74aa2a4a37510 stm32l5
hw/mcu/st/stm32n6xx_hal_driver https://github.com/STMicroelectronics/stm32n6xx-hal-driver.git bc6c41f8f67d61b47af26695d0bf67762a000666 stm32n6
hw/mcu/st/stm32u0xx_hal_driver https://github.com/STMicroelectronics/stm32u0xx-hal-driver.git cbfb5ac654256445237fd32b3587ac6a238d24f1 stm32u0
hw/mcu/st/stm32u5xx_hal_driver https://github.com/STMicroelectronics/stm32u5xx_hal_driver.git 2c5e2568fbdb1900a13ca3b2901fdd302cac3444 stm32u5
hw/mcu/st/stm32wbaxx_hal_driver https://github.com/STMicroelectronics/stm32wbaxx_hal_driver.git 9442fbb71f855ff2e64fbf662b7726beba511a24 stm32wba
hw/mcu/st/stm32wbxx_hal_driver https://github.com/STMicroelectronics/stm32wbxx_hal_driver.git d60dd46996876506f1d2e9abd6b1cc110c8004cd stm32wb
@ -78,10 +80,10 @@ hw/mcu/wch/ch32f20x https://github.com/openwch/ch32f20x.gi
hw/mcu/wch/ch32v103 https://github.com/openwch/ch32v103.git 7578cae0b21f86dd053a1f781b2fc6ab99d0ec17 ch32v10x
hw/mcu/wch/ch32v20x https://github.com/openwch/ch32v20x.git c4c38f507e258a4e69b059ccc2dc27dde33cea1b ch32v20x
hw/mcu/wch/ch32v307 https://github.com/openwch/ch32v307.git 184f21b852cb95eed58e86e901837bc9fff68775 ch32v30x
lib/CMSIS_5 https://github.com/ARM-software/CMSIS_5.git 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c imxrt kinetis_k32l2 kinetis_kl lpc51 lpc54 lpc55 mcx mm32 msp432e4 nrf saml2x lpc11 lpc13 lpc15 lpc17 lpc18 lpc40 lpc43 stm32c0 stm32f0 stm32f1 stm32f2 stm32f3 stm32f4 stm32f7 stm32g0 stm32g4 stm32h5 stm32h7 stm32h7rs stm32l0 stm32l1 stm32l4 stm32l5 stm32n6 stm32u5 stm32wb sam3x samd11 samd21 samd51 samd5x_e5x same5x same7x saml2x samg tm4c
lib/CMSIS_5 https://github.com/ARM-software/CMSIS_5.git 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c imxrt kinetis_k32l2 kinetis_kl lpc51 lpc54 lpc55 mcx mm32 msp432e4 nrf saml2x lpc11 lpc13 lpc15 lpc17 lpc18 lpc40 lpc43 stm32c0 stm32f0 stm32f1 stm32f2 stm32f3 stm32f4 stm32f7 stm32g0 stm32g4 stm32h5 stm32h7 stm32h7rs stm32l0 stm32l1 stm32l4 stm32l5 stm32n6 stm32u0 stm32u5 stm32wb stm32wbasam3x samd11 samd21 samd51 samd5x_e5x same5x same7x saml2x samg tm4c
lib/CMSIS_6 https://github.com/ARM-software/CMSIS_6.git b0bbb0423b278ca632cfe1474eb227961d835fd2 ra
lib/FreeRTOS-Kernel https://github.com/FreeRTOS/FreeRTOS-Kernel.git cc0e0707c0c748713485b870bb980852b210877f all
lib/lwip https://github.com/lwip-tcpip/lwip.git 159e31b689577dbf69cf0683bbaffbd71fa5ee10 all
lib/sct_neopixel https://github.com/gsteiert/sct_neopixel.git e73e04ca63495672d955f9268e003cffe168fcd8 lpc55
tools/uf2 https://github.com/microsoft/uf2.git c594542b2faa01cc33a2b97c9fbebc38549df80a all
======================================== ================================================================ ======================================== ======================================================================================================================================================================================================================================================================================================================================================
======================================== ================================================================ ======================================== ======================================================================================================================================================================================================================================================================================================================================================================

View File

@ -195,43 +195,32 @@ static void send_hid_report(uint8_t report_id, uint32_t btn)
}
}
break;
case REPORT_ID_STYLUS_PEN: {
static bool touch_state = false;
hid_stylus_report_t report = {
.attr = 0,
.x = 0,
.y = 0
};
if (btn) {
report.attr = STYLUS_ATTR_TIP_SWITCH | STYLUS_ATTR_IN_RANGE;
report.x = 100;
report.y = 100;
tud_hid_report(REPORT_ID_STYLUS_PEN, &report, sizeof(report));
touch_state = true;
} else {
report.attr = 0;
if (touch_state) tud_hid_report(REPORT_ID_STYLUS_PEN, &report, sizeof(report));
touch_state = false;
}
}
break;
default: break;
}
}
/* use this to send stylus touch signal through USB. */
static void send_stylus_touch(uint16_t x, uint16_t y, bool state)
{
// skip if hid is not ready yet
if ( !tud_hid_ready() ) return;
static bool has_stylus_pen = false;
hid_stylus_report_t report =
{
.attr = 0,
.x = 0,
.y = 0
};
report.x = x;
report.y = y;
if (state)
{
report.attr = STYLUS_ATTR_TIP_SWITCH | STYLUS_ATTR_IN_RANGE;
tud_hid_report(REPORT_ID_STYLUS_PEN, &report, sizeof(report));
has_stylus_pen = true;
}else
{
report.attr = 0;
if (has_stylus_pen) tud_hid_report(REPORT_ID_STYLUS_PEN, &report, sizeof(report));
has_stylus_pen = false;
}
}
// Every 10ms, we will sent 1 report for each HID profile (keyboard, mouse etc ..)
// tud_hid_report_complete_cb() is used to send the next report after previous one is complete
void hid_task(void)
@ -239,14 +228,6 @@ void hid_task(void)
// Poll every 10ms
const uint32_t interval_ms = 10;
static uint32_t start_ms = 0;
static uint32_t touch_ms = 0;
static bool touch_state = false;
if (board_millis() - touch_ms < 100) {
touch_ms = board_millis();
send_stylus_touch(0, 0, touch_state = !touch_state);
return;
}
if ( board_millis() - start_ms < interval_ms) return; // not enough time
start_ms += interval_ms;

View File

@ -59,11 +59,11 @@ storage_info_t storage_info = {
.free_space_in_bytes = 0, // calculated at runtime
.free_space_in_objects = 0, // calculated at runtime
.storage_description = {
.count = (TU_FIELD_SZIE(storage_info_t, storage_description)-1) / sizeof(uint16_t),
.count = (TU_FIELD_SIZE(storage_info_t, storage_description)-1) / sizeof(uint16_t),
.utf16 = STORAGE_DESCRIPTRION
},
.volume_identifier = {
.count = (TU_FIELD_SZIE(storage_info_t, volume_identifier)-1) / sizeof(uint16_t),
.count = (TU_FIELD_SIZE(storage_info_t, volume_identifier)-1) / sizeof(uint16_t),
.utf16 = VOLUME_IDENTIFIER
}
};

View File

@ -1,6 +1,6 @@
{
"name": "TinyUSB",
"version": "0.18.0",
"version": "0.19.0",
"description": "TinyUSB is an open-source cross-platform USB Host/Device stack for embedded system, designed to be memory-safe with no dynamic allocation and thread-safe with all interrupt events are deferred then handled in the non-ISR task function.",
"keywords": "usb, host, device",
"repository":

View File

@ -16,5 +16,6 @@ repo.versions:
"0.16.0": "0.16.0"
"0.17.0": "0.17.0"
"0.18.0": "0.18.0"
"0-latest": "0.18.0"
"0.19.0": "0.19.0"
"0-latest": "0.19.0"
"0-dev": "0.0.0"

View File

@ -79,10 +79,9 @@
// Linear buffer in case target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer
// is available or driver is would need to be changed dramatically
// Only STM32 and dcd_transdimension use non-linear buffer for now
// dwc2 except esp32sx (since it may use dcd_esp32sx)
// Only STM32 and ChipIdea HS use non-linear buffer for now
// Ring buffer is incompatible with dcache, since neither address nor size is aligned to cache line
#if (defined(TUP_USBIP_DWC2) && !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)) || \
#if defined(TUP_USBIP_DWC2) || \
defined(TUP_USBIP_FSDEV) || \
CFG_TUSB_MCU == OPT_MCU_RX63X || \
CFG_TUSB_MCU == OPT_MCU_RX65X || \

View File

@ -267,14 +267,14 @@ void tud_hid_report_failed_cb(uint8_t instance, hid_report_type_t report_type, u
// Stylus Pen Report Descriptor Template
#define TUD_HID_REPORT_DESC_STYLUS_PEN(...) \
HID_USAGE_PAGE ( HID_USAGE_PAGE_DIGITIZER ) , \
HID_USAGE ( HID_USAGE_DIGITIZER_TOUCH_SCREEN ) , \
HID_USAGE ( HID_USAGE_DIGITIZER_PEN ) , \
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) , \
/* Report ID if any */\
__VA_ARGS__ \
HID_USAGE ( HID_USAGE_DIGITIZER_STYLUS ) , \
HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) , \
HID_USAGE_PAGE ( HID_USAGE_DIGITIZER_TIP_SWITCH ) , \
HID_USAGE_PAGE ( HID_USAGE_DIGITIZER_IN_RANGE ) , \
HID_USAGE ( HID_USAGE_DIGITIZER_STYLUS ), \
HID_COLLECTION ( HID_COLLECTION_PHYSICAL ), \
HID_USAGE ( HID_USAGE_DIGITIZER_TIP_SWITCH ), \
HID_USAGE ( HID_USAGE_DIGITIZER_IN_RANGE ), \
HID_LOGICAL_MIN ( 0 ), \
HID_LOGICAL_MAX ( 1 ), \
HID_REPORT_SIZE ( 1 ), \

View File

@ -35,7 +35,7 @@
// Macros Helper
//--------------------------------------------------------------------+
#define TU_ARRAY_SIZE(_arr) ( sizeof(_arr) / sizeof(_arr[0]) )
#define TU_FIELD_SZIE(_type, _field) (sizeof(((_type *)0)->_field))
#define TU_FIELD_SIZE(_type, _field) (sizeof(((_type *)0)->_field))
#define TU_MIN(_x, _y) ( ( (_x) < (_y) ) ? (_x) : (_y) )
#define TU_MAX(_x, _y) ( ( (_x) > (_y) ) ? (_x) : (_y) )
#define TU_DIV_CEIL(n, d) (((n) + (d) - 1) / (d))

View File

@ -1,889 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2018 Scott Shawcroft, 2019 William D. Jones for Adafruit Industries
* Copyright (c) 2019 Ha Thach (tinyusb.org)
* Additions Copyright (c) 2020, Espressif Systems (Shanghai) Co. Ltd.
*
* 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_TUSB_MCU == OPT_MCU_ESP32S2) || (CFG_TUSB_MCU == OPT_MCU_ESP32S3)) && CFG_TUD_ENABLED)
// Espressif
#include "xtensa/xtensa_api.h"
#include "esp_intr_alloc.h"
#include "esp_log.h"
#include "soc/dport_reg.h"
#include "soc/gpio_sig_map.h"
#include "soc/usb_periph.h"
#include "soc/usb_reg.h"
#include "soc/usb_struct.h"
#include "soc/periph_defs.h" // for interrupt source
#include "device/dcd.h"
#ifndef USB_OUT_EP_NUM
#define USB_OUT_EP_NUM ((int) (sizeof(USB0.out_ep_reg) / sizeof(USB0.out_ep_reg[0])))
#endif
#ifndef USB_IN_EP_NUM
#define USB_IN_EP_NUM ((int) (sizeof(USB0.in_ep_reg) / sizeof(USB0.in_ep_reg[0])))
#endif
// Max number of bi-directional endpoints including EP0
// Note: ESP32S2 specs say there are only up to 5 IN active endpoints include EP0
// We should probably prohibit enabling Endpoint IN > 4 (not done yet)
#define EP_MAX USB_OUT_EP_NUM
// FIFO size in bytes
#define EP_FIFO_SIZE 1024
// Max number of IN EP FIFOs
#define EP_FIFO_NUM 5
typedef struct {
uint8_t *buffer;
// tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API
uint16_t total_len;
uint16_t queued_len;
uint16_t max_size;
bool short_packet;
uint8_t interval;
} xfer_ctl_t;
static const char *TAG = "TUSB:DCD";
static intr_handle_t usb_ih;
static uint32_t _setup_packet[2];
#define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
static xfer_ctl_t xfer_status[EP_MAX][2];
// Keep count of how many FIFOs are in use
static uint8_t _allocated_fifos = 1; //FIFO0 is always in use
// Will either return an unused FIFO number, or 0 if all are used.
static uint8_t get_free_fifo(void)
{
if (_allocated_fifos < EP_FIFO_NUM) return _allocated_fifos++;
return 0;
}
// Setup the control endpoint 0.
static void bus_reset(void)
{
for (int ep_num = 0; ep_num < USB_OUT_EP_NUM; ep_num++) {
USB0.out_ep_reg[ep_num].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK
}
// clear device address
USB0.dcfg &= ~USB_DEVADDR_M;
USB0.daintmsk = USB_OUTEPMSK0_M | USB_INEPMSK0_M;
USB0.doepmsk = USB_SETUPMSK_M | USB_XFERCOMPLMSK;
USB0.diepmsk = USB_TIMEOUTMSK_M | USB_DI_XFERCOMPLMSK_M /*| USB_INTKNTXFEMPMSK_M*/;
// "USB Data FIFOs" section in reference manual
// Peripheral FIFO architecture
//
// --------------- 320 or 1024 ( 1280 or 4096 bytes )
// | IN FIFO MAX |
// ---------------
// | ... |
// --------------- y + x + 16 + GRXFSIZ
// | IN FIFO 2 |
// --------------- x + 16 + GRXFSIZ
// | IN FIFO 1 |
// --------------- 16 + GRXFSIZ
// | IN FIFO 0 |
// --------------- GRXFSIZ
// | OUT FIFO |
// | ( Shared ) |
// --------------- 0
//
// According to "FIFO RAM allocation" section in RM, FIFO RAM are allocated as follows (each word 32-bits):
// - Each EP IN needs at least max packet size, 16 words is sufficient for EP0 IN
//
// - All EP OUT shared a unique OUT FIFO which uses
// * 10 locations in hardware for setup packets + setup control words (up to 3 setup packets).
// * 2 locations for OUT endpoint control words.
// * 16 for largest packet size of 64 bytes. ( TODO Highspeed is 512 bytes)
// * 1 location for global NAK (not required/used here).
// * It is recommended to allocate 2 times the largest packet size, therefore
// Recommended value = 10 + 1 + 2 x (16+2) = 47 --> Let's make it 52
USB0.grstctl |= 0x10 << USB_TXFNUM_S; // fifo 0x10,
USB0.grstctl |= USB_TXFFLSH_M; // Flush fifo
USB0.grxfsiz = 52;
// Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word )
USB0.gnptxfsiz = (16 << USB_NPTXFDEP_S) | (USB0.grxfsiz & 0x0000ffffUL);
// Ready to receive SETUP packet
USB0.out_ep_reg[0].doeptsiz |= USB_SUPCNT0_M;
USB0.gintmsk |= USB_IEPINTMSK_M | USB_OEPINTMSK_M;
}
static void enum_done_processing(void)
{
ESP_EARLY_LOGV(TAG, "dcd_int_handler - Speed enumeration done! Sending DCD_EVENT_BUS_RESET then");
// On current silicon on the Full Speed core, speed is fixed to Full Speed.
// However, keep for debugging and in case Low Speed is ever supported.
uint32_t enum_spd = (USB0.dsts >> USB_ENUMSPD_S) & (USB_ENUMSPD_V);
// Maximum packet size for EP 0 is set for both directions by writing DIEPCTL
if (enum_spd == 0x03) { // Full-Speed (PHY on 48 MHz)
USB0.in_ep_reg[0].diepctl &= ~USB_D_MPS0_V; // 64 bytes
USB0.in_ep_reg[0].diepctl &= ~USB_D_STALL0_M; // clear Stall
xfer_status[0][TUSB_DIR_OUT].max_size = 64;
xfer_status[0][TUSB_DIR_IN].max_size = 64;
} else {
USB0.in_ep_reg[0].diepctl |= USB_D_MPS0_V; // 8 bytes
USB0.in_ep_reg[0].diepctl &= ~USB_D_STALL0_M; // clear Stall
xfer_status[0][TUSB_DIR_OUT].max_size = 8;
xfer_status[0][TUSB_DIR_IN].max_size = 8;
}
}
/*------------------------------------------------------------------*/
/* Controller API
*------------------------------------------------------------------*/
bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rh_init;
ESP_LOGV(TAG, "DCD init - Start");
// A. Disconnect
ESP_LOGV(TAG, "DCD init - Soft DISCONNECT and Setting up");
USB0.dctl |= USB_SFTDISCON_M; // Soft disconnect
// B. Programming DCFG
/* If USB host misbehaves during status portion of control xfer
(non zero-length packet), send STALL back and discard. Full speed. */
USB0.dcfg |= USB_NZSTSOUTHSHK_M | // NonZero .... STALL
(3 << 0); // dev speed: fullspeed 1.1 on 48 mhz // TODO no value in usb_reg.h (IDF-1476)
USB0.gahbcfg |= USB_NPTXFEMPLVL_M | USB_GLBLLNTRMSK_M; // Global interruptions ON
USB0.gusbcfg |= USB_FORCEDEVMODE_M; // force devmode
USB0.gotgctl &= ~(USB_BVALIDOVVAL_M | USB_BVALIDOVEN_M | USB_VBVALIDOVVAL_M); //no overrides
// C. Setting SNAKs, then connect
for (int n = 0; n < USB_OUT_EP_NUM; n++) {
USB0.out_ep_reg[n].doepctl |= USB_DO_SNAK0_M; // DOEPCTL0_SNAK
}
// D. Interruption masking
USB0.gintmsk = 0; //mask all
USB0.gotgint = ~0U; //clear OTG ints
USB0.gintsts = ~0U; //clear pending ints
USB0.gintmsk = USB_OTGINTMSK_M |
USB_MODEMISMSK_M |
USB_RXFLVIMSK_M |
USB_ERLYSUSPMSK_M |
USB_USBSUSPMSK_M |
USB_USBRSTMSK_M |
USB_ENUMDONEMSK_M |
USB_RESETDETMSK_M |
USB_WKUPINT_M |
USB_DISCONNINTMSK_M; // host most only
dcd_connect(rhport);
return true;
}
void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
{
(void)rhport;
ESP_LOGV(TAG, "DCD init - Set address : %u", dev_addr);
USB0.dcfg |= ((dev_addr & USB_DEVADDR_V) << USB_DEVADDR_S);
// Response with status after changing device address
dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
}
void dcd_remote_wakeup(uint8_t rhport)
{
(void)rhport;
// set remote wakeup
USB0.dctl |= USB_RMTWKUPSIG_M;
// enable SOF to detect bus resume
USB0.gintsts = USB_SOF_M;
USB0.gintmsk |= USB_SOFMSK_M;
// Per specs: remote wakeup signal bit must be clear within 1-15ms
vTaskDelay(pdMS_TO_TICKS(1));
USB0.dctl &= ~USB_RMTWKUPSIG_M;
}
// connect by enabling internal pull-up resistor on D+/D-
void dcd_connect(uint8_t rhport)
{
(void) rhport;
USB0.dctl &= ~USB_SFTDISCON_M;
}
// disconnect by disabling internal pull-up resistor on D+/D-
void dcd_disconnect(uint8_t rhport)
{
(void) rhport;
USB0.dctl |= USB_SFTDISCON_M;
}
void dcd_sof_enable(uint8_t rhport, bool en)
{
(void) rhport;
(void) en;
// TODO implement later
}
/*------------------------------------------------------------------*/
/* DCD Endpoint port
*------------------------------------------------------------------*/
bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt)
{
ESP_LOGV(TAG, "DCD endpoint opened");
(void)rhport;
usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
TU_ASSERT(epnum < EP_MAX);
xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir);
xfer->max_size = tu_edpt_packet_size(desc_edpt);
xfer->interval = desc_edpt->bInterval;
if (dir == TUSB_DIR_OUT) {
out_ep[epnum].doepctl |= USB_USBACTEP1_M |
desc_edpt->bmAttributes.xfer << USB_EPTYPE1_S |
(desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? USB_DO_SETD0PID1_M : 0) |
xfer->max_size << USB_MPS1_S;
USB0.daintmsk |= (1 << (16 + epnum));
} else {
// "USB Data FIFOs" section in reference manual
// Peripheral FIFO architecture
//
// --------------- 320 or 1024 ( 1280 or 4096 bytes )
// | IN FIFO MAX |
// ---------------
// | ... |
// --------------- y + x + 16 + GRXFSIZ
// | IN FIFO 2 |
// --------------- x + 16 + GRXFSIZ
// | IN FIFO 1 |
// --------------- 16 + GRXFSIZ
// | IN FIFO 0 |
// --------------- GRXFSIZ
// | OUT FIFO |
// | ( Shared ) |
// --------------- 0
//
// Since OUT FIFO = GRXFSIZ, FIFO 0 = 16, for simplicity, we equally allocated for the rest of endpoints
// - Size : (FIFO_SIZE/4 - GRXFSIZ - 16) / (EP_MAX-1)
// - Offset: GRXFSIZ + 16 + Size*(epnum-1)
// - IN EP 1 gets FIFO 1, IN EP "n" gets FIFO "n".
uint8_t fifo_num = get_free_fifo();
TU_ASSERT(fifo_num != 0);
in_ep[epnum].diepctl &= ~(USB_D_TXFNUM1_M | USB_D_EPTYPE1_M | USB_DI_SETD0PID1 | USB_D_MPS1_M);
in_ep[epnum].diepctl |= USB_D_USBACTEP1_M |
fifo_num << USB_D_TXFNUM1_S |
desc_edpt->bmAttributes.xfer << USB_D_EPTYPE1_S |
(desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? (1 << USB_DI_SETD0PID1_S) : 0) |
xfer->max_size << 0;
USB0.daintmsk |= (1 << (0 + epnum));
// Both TXFD and TXSA are in unit of 32-bit words.
// IN FIFO 0 was configured during enumeration, hence the "+ 16".
uint16_t const allocated_size = (USB0.grxfsiz & 0x0000ffff) + 16;
uint16_t const fifo_size = (EP_FIFO_SIZE/4 - allocated_size) / (EP_FIFO_NUM-1);
uint32_t const fifo_offset = allocated_size + fifo_size*(fifo_num-1);
// DIEPTXF starts at FIFO #1.
USB0.dieptxf[epnum - 1] = (fifo_size << USB_NPTXFDEP_S) | fifo_offset;
}
return true;
}
void dcd_edpt_close_all(uint8_t rhport)
{
(void) rhport;
usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
// Disable non-control interrupt
USB0.daintmsk = USB_OUTEPMSK0_M | USB_INEPMSK0_M;
for(uint8_t n = 1; n < EP_MAX; n++)
{
// disable OUT endpoint
out_ep[n].doepctl = 0;
xfer_status[n][TUSB_DIR_OUT].max_size = 0;
// disable IN endpoint
in_ep[n].diepctl = 0;
xfer_status[n][TUSB_DIR_IN].max_size = 0;
}
_allocated_fifos = 1;
}
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes)
{
(void)rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
xfer->buffer = buffer;
// xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = total_bytes;
xfer->queued_len = 0;
xfer->short_packet = false;
uint16_t num_packets = (total_bytes / xfer->max_size);
uint8_t short_packet_size = total_bytes % xfer->max_size;
// Zero-size packet is special case.
if (short_packet_size > 0 || (total_bytes == 0)) {
num_packets++;
}
ESP_LOGV(TAG, "Transfer <-> EP%i, %s, pkgs: %i, bytes: %i",
epnum, ((dir == TUSB_DIR_IN) ? "USB0.HOST (in)" : "HOST->DEV (out)"),
num_packets, total_bytes);
// IN and OUT endpoint xfers are interrupt-driven, we just schedule them
// here.
if (dir == TUSB_DIR_IN) {
// A full IN transfer (multiple packets, possibly) triggers XFRC.
USB0.in_ep_reg[epnum].dieptsiz = (num_packets << USB_D_PKTCNT0_S) | total_bytes;
USB0.in_ep_reg[epnum].diepctl |= USB_D_EPENA1_M | USB_D_CNAK1_M; // Enable | CNAK
// For ISO endpoint with interval=1 set correct DATA0/DATA1 bit for next frame
if ((USB0.in_ep_reg[epnum].diepctl & USB_D_EPTYPE0_M) == (1 << USB_D_EPTYPE1_S) && xfer->interval == 1) {
// Take odd/even bit from frame counter.
uint32_t const odd_frame_now = (USB0.dsts & (1u << USB_SOFFN_S));
USB0.in_ep_reg[epnum].diepctl |= (odd_frame_now ? USB_DI_SETD0PID1 : USB_DI_SETD1PID1);
}
// Enable fifo empty interrupt only if there are something to put in the fifo.
if(total_bytes != 0) {
USB0.dtknqr4_fifoemptymsk |= (1 << epnum);
}
} else {
// Each complete packet for OUT xfers triggers XFRC.
USB0.out_ep_reg[epnum].doeptsiz |= USB_PKTCNT0_M | ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S);
USB0.out_ep_reg[epnum].doepctl |= USB_EPENA0_M | USB_CNAK0_M;
// For ISO endpoint with interval=1 set correct DATA0/DATA1 bit for next frame
if ((USB0.out_ep_reg[epnum].doepctl & USB_D_EPTYPE0_M) == (1 << USB_D_EPTYPE1_S) && xfer->interval == 1) {
// Take odd/even bit from frame counter.
uint32_t const odd_frame_now = (USB0.dsts & (1u << USB_SOFFN_S));
USB0.out_ep_reg[epnum].doepctl |= (odd_frame_now ? USB_DO_SETD0PID1 : USB_DO_SETD1PID1);
}
}
return true;
}
#if 0 // TODO support dcd_edpt_xfer_fifo API
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
(void)rhport;
}
#endif
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void)rhport;
usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
if (dir == TUSB_DIR_IN) {
// Only disable currently enabled non-control endpoint
if ((epnum == 0) || !(in_ep[epnum].diepctl & USB_D_EPENA1_M)) {
in_ep[epnum].diepctl |= (USB_DI_SNAK1_M | USB_D_STALL1_M);
} else {
// Stop transmitting packets and NAK IN xfers.
in_ep[epnum].diepctl |= USB_DI_SNAK1_M;
while ((in_ep[epnum].diepint & USB_DI_SNAK1_M) == 0) ;
// Disable the endpoint. Note that both SNAK and STALL are set here.
in_ep[epnum].diepctl |= (USB_DI_SNAK1_M | USB_D_STALL1_M | USB_D_EPDIS1_M);
while ((in_ep[epnum].diepint & USB_D_EPDISBLD0_M) == 0) ;
in_ep[epnum].diepint = USB_D_EPDISBLD0_M;
}
// Flush the FIFO, and wait until we have confirmed it cleared.
uint8_t const fifo_num = ((in_ep[epnum].diepctl >> USB_D_TXFNUM1_S) & USB_D_TXFNUM1_V);
USB0.grstctl |= (fifo_num << USB_TXFNUM_S);
USB0.grstctl |= USB_TXFFLSH_M;
while ((USB0.grstctl & USB_TXFFLSH_M) != 0) ;
} else {
// Only disable currently enabled non-control endpoint
if ((epnum == 0) || !(out_ep[epnum].doepctl & USB_EPENA0_M)) {
out_ep[epnum].doepctl |= USB_STALL0_M;
} else {
// Asserting GONAK is required to STALL an OUT endpoint.
// Simpler to use polling here, we don't use the "B"OUTNAKEFF interrupt
// anyway, and it can't be cleared by user code. If this while loop never
// finishes, we have bigger problems than just the stack.
USB0.dctl |= USB_SGOUTNAK_M;
while ((USB0.gintsts & USB_GOUTNAKEFF_M) == 0) ;
// Ditto here- disable the endpoint. Note that only STALL and not SNAK
// is set here.
out_ep[epnum].doepctl |= (USB_STALL0_M | USB_EPDIS0_M);
while ((out_ep[epnum].doepint & USB_EPDISBLD0_M) == 0) ;
out_ep[epnum].doepint = USB_EPDISBLD0_M;
// Allow other OUT endpoints to keep receiving.
USB0.dctl |= USB_CGOUTNAK_M;
}
}
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
{
(void)rhport;
usb_out_endpoint_t *out_ep = &(USB0.out_ep_reg[0]);
usb_in_endpoint_t *in_ep = &(USB0.in_ep_reg[0]);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
if (dir == TUSB_DIR_IN) {
in_ep[epnum].diepctl &= ~USB_D_STALL1_M;
uint8_t eptype = (in_ep[epnum].diepctl & USB_D_EPTYPE1_M) >> USB_D_EPTYPE1_S;
// Required by USB spec to reset DATA toggle bit to DATA0 on interrupt
// and bulk endpoints.
if (eptype == 2 || eptype == 3) {
in_ep[epnum].diepctl |= USB_DI_SETD0PID1_M;
}
} else {
out_ep[epnum].doepctl &= ~USB_STALL1_M;
uint8_t eptype = (out_ep[epnum].doepctl & USB_EPTYPE1_M) >> USB_EPTYPE1_S;
// Required by USB spec to reset DATA toggle bit to DATA0 on interrupt
// and bulk endpoints.
if (eptype == 2 || eptype == 3) {
out_ep[epnum].doepctl |= USB_DO_SETD0PID1_M;
}
}
}
/*------------------------------------------------------------------*/
static void receive_packet(xfer_ctl_t *xfer, /* usb_out_endpoint_t * out_ep, */ uint16_t xfer_size)
{
ESP_EARLY_LOGV(TAG, "USB - receive_packet");
volatile uint32_t *rx_fifo = USB0.fifo[0];
// See above TODO
// uint16_t remaining = (out_ep->DOEPTSIZ & UsbDOEPTSIZ_XFRSIZ_Msk) >> UsbDOEPTSIZ_XFRSIZ_Pos;
// xfer->queued_len = xfer->total_len - remaining;
uint16_t remaining = xfer->total_len - xfer->queued_len;
uint16_t to_recv_size;
if (remaining <= xfer->max_size) {
// Avoid buffer overflow.
to_recv_size = (xfer_size > remaining) ? remaining : xfer_size;
} else {
// Room for full packet, choose recv_size based on what the microcontroller
// claims.
to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size;
}
// Common buffer read
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff)
{
// Ring buffer
tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void *) rx_fifo, to_recv_size);
}
else
#endif
{
uint8_t to_recv_rem = to_recv_size % 4;
uint16_t to_recv_size_aligned = to_recv_size - to_recv_rem;
// Do not assume xfer buffer is aligned.
uint8_t *base = (xfer->buffer + xfer->queued_len);
// This for loop always runs at least once- skip if less than 4 bytes
// to collect.
if (to_recv_size >= 4) {
for (uint16_t i = 0; i < to_recv_size_aligned; i += 4) {
uint32_t tmp = (*rx_fifo);
base[i] = tmp & 0x000000FF;
base[i + 1] = (tmp & 0x0000FF00) >> 8;
base[i + 2] = (tmp & 0x00FF0000) >> 16;
base[i + 3] = (tmp & 0xFF000000) >> 24;
}
}
// Do not read invalid bytes from RX FIFO.
if (to_recv_rem != 0) {
uint32_t tmp = (*rx_fifo);
uint8_t *last_32b_bound = base + to_recv_size_aligned;
last_32b_bound[0] = tmp & 0x000000FF;
if (to_recv_rem > 1) {
last_32b_bound[1] = (tmp & 0x0000FF00) >> 8;
}
if (to_recv_rem > 2) {
last_32b_bound[2] = (tmp & 0x00FF0000) >> 16;
}
}
}
xfer->queued_len += xfer_size;
// Per USB spec, a short OUT packet (including length 0) is always
// indicative of the end of a transfer (at least for ctl, bulk, int).
xfer->short_packet = (xfer_size < xfer->max_size);
}
static void transmit_packet(xfer_ctl_t *xfer, volatile usb_in_endpoint_t *in_ep, uint8_t fifo_num)
{
ESP_EARLY_LOGV(TAG, "USB - transmit_packet");
volatile uint32_t *tx_fifo = USB0.fifo[fifo_num];
uint16_t remaining = (in_ep->dieptsiz & 0x7FFFFU) >> USB_D_XFERSIZE0_S;
xfer->queued_len = xfer->total_len - remaining;
uint16_t to_xfer_size = (remaining > xfer->max_size) ? xfer->max_size : remaining;
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff)
{
tu_fifo_read_n_const_addr_full_words(xfer->ff, (void *) tx_fifo, to_xfer_size);
}
else
#endif
{
uint8_t to_xfer_rem = to_xfer_size % 4;
uint16_t to_xfer_size_aligned = to_xfer_size - to_xfer_rem;
// Buffer might not be aligned to 32b, so we need to force alignment
// by copying to a temp var.
uint8_t *base = (xfer->buffer + xfer->queued_len);
// This for loop always runs at least once- skip if less than 4 bytes
// to send off.
if (to_xfer_size >= 4) {
for (uint16_t i = 0; i < to_xfer_size_aligned; i += 4) {
uint32_t tmp = base[i] | (base[i + 1] << 8) |
(base[i + 2] << 16) | (base[i + 3] << 24);
(*tx_fifo) = tmp;
}
}
// Do not read beyond end of buffer if not divisible by 4.
if (to_xfer_rem != 0) {
uint32_t tmp = 0;
uint8_t *last_32b_bound = base + to_xfer_size_aligned;
tmp |= last_32b_bound[0];
if (to_xfer_rem > 1) {
tmp |= (last_32b_bound[1] << 8);
}
if (to_xfer_rem > 2) {
tmp |= (last_32b_bound[2] << 16);
}
(*tx_fifo) = tmp;
}
}
}
static void read_rx_fifo(void)
{
// Pop control word off FIFO (completed xfers will have 2 control words,
// we only pop one ctl word each interrupt).
uint32_t const ctl_word = USB0.grxstsp;
uint8_t const pktsts = (ctl_word & USB_PKTSTS_M) >> USB_PKTSTS_S;
uint8_t const epnum = (ctl_word & USB_CHNUM_M ) >> USB_CHNUM_S;
uint16_t const bcnt = (ctl_word & USB_BCNT_M ) >> USB_BCNT_S;
switch (pktsts) {
case 0x01: // Global OUT NAK (Interrupt)
ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Global OUT NAK");
break;
case 0x02: { // Out packet recvd
ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Out packet");
xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
receive_packet(xfer, bcnt);
}
break;
case 0x03: // Out packet done (Interrupt)
ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX type : Out packet done");
break;
case 0x04: // Step 2: Setup transaction completed (Interrupt)
// After this event, OEPINT interrupt will occur with SETUP bit set
ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX : Setup packet done");
USB0.out_ep_reg[epnum].doeptsiz |= USB_SUPCNT0_M;
break;
case 0x06: { // Step1: Setup data packet received
volatile uint32_t *rx_fifo = USB0.fifo[0];
// We can receive up to three setup packets in succession, but
// only the last one is valid. Therefore we just overwrite it
_setup_packet[0] = (*rx_fifo);
_setup_packet[1] = (*rx_fifo);
ESP_EARLY_LOGV(TAG, "TUSB IRQ - RX : Setup packet : 0x%08x 0x%08x", _setup_packet[0], _setup_packet[1]);
}
break;
default: // Invalid, do something here, like breakpoint?
TU_BREAKPOINT();
break;
}
}
static void handle_epout_ints(void)
{
// GINTSTS will be cleared with DAINT == 0
// DAINT for a given EP clears when DOEPINTx is cleared.
// DOEPINT will be cleared when DAINT's out bits are cleared.
for (int n = 0; n < USB_OUT_EP_NUM; n++) {
xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT);
if (USB0.daint & (1 << (16 + n))) {
// SETUP packet Setup Phase done.
if ((USB0.out_ep_reg[n].doepint & USB_SETUP0_M)) {
USB0.out_ep_reg[n].doepint = USB_STUPPKTRCVD0_M | USB_SETUP0_M; // clear
dcd_event_setup_received(0, (uint8_t *)&_setup_packet[0], true);
}
// OUT XFER complete (single packet).q
if (USB0.out_ep_reg[n].doepint & USB_XFERCOMPL0_M) {
ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP OUT - XFER complete (single packet)");
USB0.out_ep_reg[n].doepint = USB_XFERCOMPL0_M;
// Transfer complete if short packet or total len is transferred
if (xfer->short_packet || (xfer->queued_len == xfer->total_len)) {
xfer->short_packet = false;
dcd_event_xfer_complete(0, n, xfer->queued_len, XFER_RESULT_SUCCESS, true);
} else {
// Schedule another packet to be received.
USB0.out_ep_reg[n].doeptsiz |= USB_PKTCNT0_M | ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S);
USB0.out_ep_reg[n].doepctl |= USB_EPENA0_M | USB_CNAK0_M;
}
}
}
}
}
static void handle_epin_ints(void)
{
// GINTSTS will be cleared with DAINT == 0
// DAINT for a given EP clears when DIEPINTx is cleared.
// IEPINT will be cleared when DAINT's out bits are cleared.
for (uint32_t n = 0; n < USB_IN_EP_NUM; n++) {
xfer_ctl_t *xfer = &xfer_status[n][TUSB_DIR_IN];
if (USB0.daint & (1 << (0 + n))) {
ESP_EARLY_LOGV(TAG, "TUSB IRQ - EP IN %u", n);
// IN XFER complete (entire xfer).
if (USB0.in_ep_reg[n].diepint & USB_D_XFERCOMPL0_M) {
ESP_EARLY_LOGV(TAG, "TUSB IRQ - IN XFER complete!");
USB0.in_ep_reg[n].diepint = USB_D_XFERCOMPL0_M;
dcd_event_xfer_complete(0, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
}
// XFER FIFO empty
if (USB0.in_ep_reg[n].diepint & USB_D_TXFEMP0_M) {
ESP_EARLY_LOGV(TAG, "TUSB IRQ - IN XFER FIFO empty!");
USB0.in_ep_reg[n].diepint = USB_D_TXFEMP0_M;
transmit_packet(xfer, &USB0.in_ep_reg[n], n);
// Turn off TXFE if all bytes are written.
if (xfer->queued_len == xfer->total_len)
{
USB0.dtknqr4_fifoemptymsk &= ~(1 << n);
}
}
// XFER Timeout
if (USB0.in_ep_reg[n].diepint & USB_D_TIMEOUT0_M) {
// Clear interrupt or endpoint will hang.
USB0.in_ep_reg[n].diepint = USB_D_TIMEOUT0_M;
// Maybe retry?
}
}
}
}
static void _dcd_int_handler(void* arg)
{
(void) arg;
uint8_t const rhport = 0;
const uint32_t int_msk = USB0.gintmsk;
const uint32_t int_status = USB0.gintsts & int_msk;
if (int_status & USB_USBRST_M) {
// start of reset
ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset");
USB0.gintsts = USB_USBRST_M;
// FIFOs will be reassigned when the endpoints are reopen
_allocated_fifos = 1;
bus_reset();
}
if (int_status & USB_RESETDET_M) {
ESP_EARLY_LOGV(TAG, "dcd_int_handler - reset while suspend");
USB0.gintsts = USB_RESETDET_M;
bus_reset();
}
if (int_status & USB_ENUMDONE_M) {
// ENUMDNE detects speed of the link. For full-speed, we
// always expect the same value. This interrupt is considered
// the end of reset.
USB0.gintsts = USB_ENUMDONE_M;
enum_done_processing();
dcd_event_bus_reset(rhport, TUSB_SPEED_FULL, true);
}
if(int_status & USB_USBSUSP_M)
{
USB0.gintsts = USB_USBSUSP_M;
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
}
if(int_status & USB_WKUPINT_M)
{
USB0.gintsts = USB_WKUPINT_M;
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
}
if (int_status & USB_OTGINT_M)
{
// OTG INT bit is read-only
ESP_EARLY_LOGV(TAG, "dcd_int_handler - disconnected");
uint32_t const otg_int = USB0.gotgint;
if (otg_int & USB_SESENDDET_M)
{
dcd_event_bus_signal(rhport, DCD_EVENT_UNPLUGGED, true);
}
USB0.gotgint = otg_int;
}
if (int_status & USB_SOF_M) {
USB0.gintsts = USB_SOF_M;
// Disable SOF interrupt since currently only used for remote wakeup detection
USB0.gintmsk &= ~USB_SOFMSK_M;
dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
}
if (int_status & USB_RXFLVI_M) {
// RXFLVL bit is read-only
ESP_EARLY_LOGV(TAG, "dcd_int_handler - rx!");
// Mask out RXFLVL while reading data from FIFO
USB0.gintmsk &= ~USB_RXFLVIMSK_M;
read_rx_fifo();
USB0.gintmsk |= USB_RXFLVIMSK_M;
}
// OUT endpoint interrupt handling.
if (int_status & USB_OEPINT_M) {
// OEPINT is read-only
ESP_EARLY_LOGV(TAG, "dcd_int_handler - OUT endpoint!");
handle_epout_ints();
}
// IN endpoint interrupt handling.
if (int_status & USB_IEPINT_M) {
// IEPINT bit read-only
ESP_EARLY_LOGV(TAG, "dcd_int_handler - IN endpoint!");
handle_epin_ints();
}
// Without handling
USB0.gintsts |= USB_CURMOD_INT_M |
USB_MODEMIS_M |
USB_OTGINT_M |
USB_NPTXFEMP_M |
USB_GINNAKEFF_M |
USB_GOUTNAKEFF |
USB_ERLYSUSP_M |
USB_USBSUSP_M |
USB_ISOOUTDROP_M |
USB_EOPF_M |
USB_EPMIS_M |
USB_INCOMPISOIN_M |
USB_INCOMPIP_M |
USB_FETSUSP_M |
USB_PTXFEMP_M;
}
void dcd_int_enable (uint8_t rhport)
{
(void) rhport;
esp_intr_alloc(ETS_USB_INTR_SOURCE, ESP_INTR_FLAG_LOWMED, (intr_handler_t) _dcd_int_handler, NULL, &usb_ih);
}
void dcd_int_disable (uint8_t rhport)
{
(void) rhport;
esp_intr_free(usb_ih);
}
#endif // #if OPT_MCU_ESP32S2 || OPT_MCU_ESP32S3

View File

@ -31,7 +31,7 @@
// Version is release as major.minor.revision eg 1.0.0
#define TUSB_VERSION_MAJOR 0
#define TUSB_VERSION_MINOR 18
#define TUSB_VERSION_MINOR 19
#define TUSB_VERSION_REVISION 0
#define TUSB_VERSION_NUMBER (TUSB_VERSION_MAJOR * 10000 + TUSB_VERSION_MINOR * 100 + TUSB_VERSION_REVISION)

View File

@ -74,7 +74,7 @@ def ListPath(path, blacklist=[]):
print('</group>')
def List():
ListPath('src', [ 'template.c', 'dcd_synopsys.c', 'dcd_esp32sx.c' ])
ListPath('src', [ 'template.c' ])
ListPath('lib/SEGGER_RTT')
if __name__ == "__main__":

View File

@ -2,7 +2,7 @@
import re
import gen_doc
version = '0.18.0'
version = '0.19.0'
print('version {}'.format(version))
ver_id = version.split('.')