bump up tinyusb to commit e41a63c60dbebd9579698e6444bc62cfa518e15c

This commit is contained in:
hathach 2025-05-21 16:00:29 +07:00
parent f05bd099c3
commit 6fa11c4d5e
No known key found for this signature in database
GPG key ID: 26FAB84F615C3C52
22 changed files with 414 additions and 196 deletions

View file

@ -38,7 +38,7 @@ jobs:
run: bash ci/doxy_gen_and_deploy.sh
# ---------------------------------------
# Main
# build
# ---------------------------------------
build:
runs-on: ubuntu-latest
@ -83,53 +83,3 @@ jobs:
- name: test platforms
run: python3 ci/build_platform.py ${{ matrix.arduino-platform }}
# ---------------------------------------
# Build ESP32 v2
# ---------------------------------------
build-esp32-v2:
if: false
runs-on: ubuntu-latest
needs: pre-commit
strategy:
fail-fast: false
matrix:
arduino-platform:
- 'feather_esp32s2'
- 'feather_esp32s3'
esp32-version:
- '2.0.17'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Checkout adafruit/ci-arduino
uses: actions/checkout@v4
with:
repository: adafruit/ci-arduino
path: ci
- name: pre-install
run: bash ci/actions_install.sh
- name: Install arduino-esp32 v2 and Libraries
env:
BSP_URLS: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
run: |
arduino-cli core install esp32:esp32@${{ matrix.esp32-version }} --additional-urls $BSP_URLS
arduino-cli lib install ${{ env.ARDUINO_LIBS }}
arduino-cli core list
arduino-cli lib list
- name: Create custom build script
working-directory: ${{ github.workspace }}/ci
run: |
echo 'import build_platform' > build_esp32_v2.py
echo 'build_platform.test_examples_in_folder("'${{ matrix.arduino-platform }}'", build_platform.BUILD_DIR)' >> build_esp32_v2.py
echo 'exit(build_platform.success)' >> build_esp32_v2.py
cat build_esp32_v2.py
- name: test platforms
run: |
python3 ci/build_esp32_v2.py

View file

@ -672,7 +672,7 @@ void cdch_close(uint8_t daddr) {
bool cdch_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes) {
// TODO handle stall response, retry failed transfer ...
TU_ASSERT(event == XFER_RESULT_SUCCESS);
TU_VERIFY(event == XFER_RESULT_SUCCESS);
uint8_t const idx = get_idx_by_ep_addr(daddr, ep_addr);
cdch_interface_t * p_cdc = get_itf(idx);

View file

@ -444,7 +444,7 @@ bool hidh_xfer_cb(uint8_t daddr, uint8_t ep_addr, xfer_result_t result, uint32_t
hidh_epbuf_t* epbuf = get_hid_epbuf(idx);
if (dir == TUSB_DIR_IN) {
TU_LOG_DRV(" Get Report callback (%u, %u)\r\n", daddr, idx);
TU_LOG_DRV(" [idx=%u] Get Report callback\r\n", idx);
TU_LOG3_MEM(epbuf->epin, xferred_bytes, 2);
tuh_hid_report_received_cb(daddr, idx, epbuf->epin, (uint16_t) xferred_bytes);
} else {
@ -461,7 +461,9 @@ void hidh_close(uint8_t daddr) {
hidh_interface_t* p_hid = &_hidh_itf[i];
if (p_hid->daddr == daddr) {
TU_LOG_DRV(" HIDh close addr = %u index = %u\r\n", daddr, i);
if (tuh_hid_umount_cb) tuh_hid_umount_cb(daddr, i);
if (tuh_hid_umount_cb) {
tuh_hid_umount_cb(daddr, i);
}
tu_memclr(p_hid, sizeof(hidh_interface_t));
}
}

View file

@ -196,8 +196,8 @@ void vendord_reset(uint8_t rhport) {
uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint16_t max_len) {
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == desc_itf->bInterfaceClass, 0);
const uint8_t* desc_end = (const uint8_t*)desc_itf + max_len;
const uint8_t* p_desc = tu_desc_next(desc_itf);
const uint8_t* desc_end = (uint8_t const*)desc_itf + max_len;
// Find available interface
vendord_interface_t* p_vendor = NULL;
@ -210,26 +210,26 @@ uint16_t vendord_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uin
TU_VERIFY(p_vendor, 0);
p_vendor->itf_num = desc_itf->bInterfaceNumber;
uint8_t found_ep = 0;
while (found_ep < desc_itf->bNumEndpoints) {
// skip non-endpoint descriptors
while ( (TUSB_DESC_ENDPOINT != tu_desc_type(p_desc)) && (p_desc < desc_end) ) {
p_desc = tu_desc_next(p_desc);
}
if (p_desc >= desc_end) {
break;
}
while (tu_desc_is_valid(p_desc, desc_end)) {
const uint8_t desc_type = tu_desc_type(p_desc);
if (desc_type == TUSB_DESC_INTERFACE || desc_type == TUSB_DESC_INTERFACE_ASSOCIATION) {
break; // end of this interface
} else if (desc_type == TUSB_DESC_ENDPOINT) {
const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
found_ep++;
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
tu_edpt_stream_open(&p_vendor->tx.stream, desc_ep);
tud_vendor_n_write_flush((uint8_t)(p_vendor - _vendord_itf));
} else {
tu_edpt_stream_open(&p_vendor->rx.stream, desc_ep);
TU_ASSERT(tu_edpt_stream_read_xfer(rhport, &p_vendor->rx.stream) > 0, 0); // prepare for incoming data
// open endpoint stream, skip if already opened
if (tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
if (p_vendor->tx.stream.ep_addr == 0) {
tu_edpt_stream_open(&p_vendor->tx.stream, desc_ep);
tud_vendor_n_write_flush((uint8_t)(p_vendor - _vendord_itf));
}
} else {
if (p_vendor->rx.stream.ep_addr == 0) {
tu_edpt_stream_open(&p_vendor->rx.stream, desc_ep);
TU_ASSERT(tu_edpt_stream_read_xfer(rhport, &p_vendor->rx.stream) > 0, 0); // prepare for incoming data
}
}
}
p_desc = tu_desc_next(p_desc);

View file

@ -369,6 +369,10 @@
#define TUP_DCD_ENDPOINT_MAX 7 // only 5 TX FIFO for endpoint IN
#define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
#if CFG_TUSB_MCU == OPT_MCU_ESP32S3
#define TUP_MCU_MULTIPLE_CORE 1
#endif
// Disable slave if DMA is enabled
#define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUD_DWC2_DMA_ENABLE
#define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUH_DWC2_DMA_ENABLE
@ -381,6 +385,8 @@
#define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/
#define TUP_MCU_MULTIPLE_CORE 1
// Disable slave if DMA is enabled
#define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUD_DWC2_DMA_ENABLE
#define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUH_DWC2_DMA_ENABLE
@ -410,6 +416,7 @@
#elif TU_CHECK_MCU(OPT_MCU_RP2040)
#define TUP_DCD_EDPT_ISO_ALLOC
#define TUP_DCD_ENDPOINT_MAX 16
#define TUP_MCU_MULTIPLE_CORE 1
#define TU_ATTR_FAST_FUNC __attribute__((section(".time_critical.tinyusb")))

View file

@ -586,6 +586,12 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_subtype(void const* desc) {
return ((uint8_t const*) desc)[DESC_OFFSET_SUBTYPE];
}
TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_desc_is_valid(void const* desc, uint8_t const* desc_end) {
const uint8_t* desc8 = (uint8_t const*) desc;
return (desc8 < desc_end) && (tu_desc_next(desc) <= desc_end);
}
// find descriptor that match byte1 (type)
uint8_t const * tu_desc_find(uint8_t const* desc, uint8_t const* end, uint8_t byte1);

View file

@ -342,15 +342,16 @@ TU_ATTR_ALWAYS_INLINE static inline usbd_class_driver_t const * get_driver(uint8
enum { RHPORT_INVALID = 0xFFu };
tu_static uint8_t _usbd_rhport = RHPORT_INVALID;
// Event queue
// usbd_int_set() is used as mutex in OS NONE config
static OSAL_SPINLOCK_DEF(_usbd_spin, usbd_int_set);
// Event queue: usbd_int_set() is used as mutex in OS NONE config
OSAL_QUEUE_DEF(usbd_int_set, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t);
tu_static osal_queue_t _usbd_q;
static osal_queue_t _usbd_q;
// Mutex for claiming endpoint
#if OSAL_MUTEX_REQUIRED
tu_static osal_mutex_def_t _ubsd_mutexdef;
tu_static osal_mutex_t _usbd_mutex;
static osal_mutex_def_t _ubsd_mutexdef;
static osal_mutex_t _usbd_mutex;
#else
#define _usbd_mutex NULL
#endif
@ -468,7 +469,7 @@ bool tud_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
TU_ASSERT(rh_init);
TU_LOG_USBD("USBD init on controller %u, speed = %s\r\n", rhport,
rh_init->speed == TUSB_SPEED_HIGH ? "High" : "Full");
rh_init->speed == TUSB_SPEED_HIGH ? "High" : "Full");
TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(usbd_device_t));
TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(dcd_event_t));
TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_fifo_t));
@ -477,6 +478,8 @@ bool tud_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
tu_varclr(&_usbd_dev);
_usbd_queued_setup = 0;
osal_spin_init(&_usbd_spin);
#if OSAL_MUTEX_REQUIRED
// Init device mutex
_usbd_mutex = osal_mutex_create(&_ubsd_mutexdef);
@ -1248,17 +1251,21 @@ TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr)
// USBD API For Class Driver
//--------------------------------------------------------------------+
void usbd_int_set(bool enabled)
{
if (enabled)
{
void usbd_int_set(bool enabled) {
if (enabled) {
dcd_int_enable(_usbd_rhport);
}else
{
} else {
dcd_int_disable(_usbd_rhport);
}
}
void usbd_spin_lock(bool in_isr) {
osal_spin_lock(&_usbd_spin, in_isr);
}
void usbd_spin_unlock(bool in_isr) {
osal_spin_unlock(&_usbd_spin, in_isr);
}
// Parse consecutive endpoint descriptors (IN & OUT)
bool usbd_open_edpt_pair(uint8_t rhport, uint8_t const* p_desc, uint8_t ep_count, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in)
{

View file

@ -68,6 +68,8 @@ usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* driver_count) TU_ATTR
typedef bool (*usbd_control_xfer_cb_t)(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
void usbd_int_set(bool enabled);
void usbd_spin_lock(bool in_isr);
void usbd_spin_unlock(bool in_isr);
//--------------------------------------------------------------------+
// USBD Endpoint API

View file

@ -201,7 +201,6 @@ bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp,
bool hub_port_get_status_local(uint8_t hub_addr, uint8_t hub_port, hub_port_status_response_t* resp) {
(void) hub_port;
TU_VERIFY(hub_addr > CFG_TUH_DEVICE_MAX);
hub_interface_t* p_hub = get_hub_itf(hub_addr);
*resp = p_hub->port_status;
return true;

View file

@ -147,6 +147,9 @@ static osal_mutex_t _usbh_mutex;
#define _usbh_mutex NULL
#endif
// Spinlock for interrupt handler
static OSAL_SPINLOCK_DEF(_usbh_spin, usbh_int_set);
// Event queue: usbh_int_set() is used as mutex in OS NONE config
OSAL_QUEUE_DEF(usbh_int_set, _usbh_qdef, CFG_TUH_TASK_QUEUE_SZ, hcd_event_t);
static osal_queue_t _usbh_q;
@ -424,6 +427,8 @@ bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
TU_LOG_INT_USBH(sizeof(tu_fifo_t));
TU_LOG_INT_USBH(sizeof(tu_edpt_stream_t));
osal_spin_init(&_usbh_spin);
// Event queue
_usbh_q = osal_queue_create(&_usbh_qdef);
TU_ASSERT(_usbh_q != NULL);
@ -547,7 +552,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
// TODO better to have an separated queue for newly attached devices
if (_usbh_data.enumerating_daddr == TUSB_INDEX_INVALID_8) {
// New device attached and we are ready
TU_LOG1("[%u:] USBH Device Attach\r\n", event.rhport);
TU_LOG_USBH("[%u:] USBH Device Attach\r\n", event.rhport);
_usbh_data.enumerating_daddr = 0; // enumerate new device with address 0
enum_new_device(&event);
} else {
@ -562,7 +567,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
break;
case HCD_EVENT_DEVICE_REMOVE:
TU_LOG1("[%u:%u:%u] USBH DEVICE REMOVED\r\n", event.rhport, event.connection.hub_addr, event.connection.hub_port);
TU_LOG_USBH("[%u:%u:%u] USBH DEVICE REMOVED\r\n", event.rhport, event.connection.hub_addr, event.connection.hub_port);
if (_usbh_data.enumerating_daddr == 0 &&
event.rhport == _usbh_data.dev0_bus.rhport &&
event.connection.hub_addr == _usbh_data.dev0_bus.hub_addr &&
@ -579,7 +584,8 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
TU_LOG_USBH("on EP %02X with %u bytes: %s\r\n", ep_addr, (unsigned int) event.xfer_complete.len, tu_str_xfer_result[event.xfer_complete.result]);
TU_LOG_USBH("[:%u] on EP %02X with %u bytes: %s\r\n",
event.dev_addr, ep_addr, (unsigned int) event.xfer_complete.len, tu_str_xfer_result[event.xfer_complete.result]);
if (event.dev_addr == 0) {
// device 0 only has control endpoint
@ -618,7 +624,7 @@ void tuh_task_ext(uint32_t timeout_ms, bool in_isr) {
uint8_t drv_id = dev->ep2drv[epnum][ep_dir];
usbh_class_driver_t const* driver = get_driver(drv_id);
if (driver) {
TU_LOG_USBH("%s xfer callback\r\n", driver->name);
TU_LOG_USBH(" %s xfer callback\r\n", driver->name);
driver->xfer_cb(event.dev_addr, ep_addr, (xfer_result_t) event.xfer_complete.result,
event.xfer_complete.len);
} else {
@ -894,6 +900,14 @@ void usbh_int_set(bool enabled) {
}
}
void usbh_spin_lock(bool in_isr) {
osal_spin_lock(&_usbh_spin, in_isr);
}
void usbh_spin_unlock(bool in_isr) {
osal_spin_unlock(&_usbh_spin, in_isr);
}
void usbh_defer_func(osal_task_func_t func, void *param, bool in_isr) {
hcd_event_t event = { 0 };
event.event_id = USBH_EVENT_FUNC_CALL;
@ -1463,7 +1477,7 @@ static void process_enumeration(tuh_xfer_t* xfer) {
bool retry = (_usbh_data.enumerating_daddr != TUSB_INDEX_INVALID_8) && (failed_count < ATTEMPT_COUNT_MAX);
if (retry) {
tusb_time_delay_ms_api(ATTEMPT_DELAY_MS); // delay a bit
TU_LOG1("Enumeration attempt %u/%u\r\n", failed_count+1, ATTEMPT_COUNT_MAX);
TU_LOG_USBH("Enumeration attempt %u/%u\r\n", failed_count+1, ATTEMPT_COUNT_MAX);
retry = tuh_control_xfer(xfer);
}

View file

@ -71,6 +71,9 @@ void usbh_int_set(bool enabled);
void usbh_defer_func(osal_task_func_t func, void *param, bool in_isr);
void usbh_spin_lock(bool in_isr);
void usbh_spin_unlock(bool in_isr);
//--------------------------------------------------------------------+
// USBH Endpoint API
//--------------------------------------------------------------------+

View file

@ -75,6 +75,10 @@ typedef void (*osal_task_func_t)( void * );
// OSAL Porting API
// Should be implemented as static inline function in osal_port.h header
/*
void osal_spin_init(osal_spinlock_t *ctx);
void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr)
void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr);
osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t* semdef);
bool osal_semaphore_delete(osal_semaphore_t semd_hdl);
bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr);

View file

@ -42,20 +42,20 @@ extern "C" {
//--------------------------------------------------------------------+
#if configSUPPORT_STATIC_ALLOCATION
typedef StaticSemaphore_t osal_semaphore_def_t;
typedef StaticSemaphore_t osal_mutex_def_t;
typedef StaticSemaphore_t osal_semaphore_def_t;
typedef StaticSemaphore_t osal_mutex_def_t;
#else
// not used therefore defined to smallest possible type to save space
typedef uint8_t osal_semaphore_def_t;
typedef uint8_t osal_mutex_def_t;
// not used therefore defined to the smallest possible type to save space
typedef uint8_t osal_semaphore_def_t;
typedef uint8_t osal_mutex_def_t;
#endif
typedef SemaphoreHandle_t osal_semaphore_t;
typedef SemaphoreHandle_t osal_mutex_t;
typedef QueueHandle_t osal_queue_t;
typedef struct
{
typedef struct {
uint16_t depth;
uint16_t item_sz;
void* buf;
@ -83,16 +83,14 @@ typedef struct
//--------------------------------------------------------------------+
// TASK API
//--------------------------------------------------------------------+
TU_ATTR_ALWAYS_INLINE static inline uint32_t _osal_ms2tick(uint32_t msec) {
if ( msec == OSAL_TIMEOUT_WAIT_FOREVER ) return portMAX_DELAY;
if ( msec == 0 ) return 0;
if (msec == OSAL_TIMEOUT_WAIT_FOREVER) { return portMAX_DELAY; }
if (msec == 0) { return 0; }
uint32_t ticks = pdMS_TO_TICKS(msec);
// configTICK_RATE_HZ is less than 1000 and 1 tick > 1 ms
// we still need to delay at least 1 tick
if ( ticks == 0 ) ticks = 1;
// If configTICK_RATE_HZ is less than 1000 and 1 tick > 1 ms, we still need to delay at least 1 tick
if (ticks == 0) { ticks = 1; }
return ticks;
}
@ -101,10 +99,71 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
vTaskDelay(pdMS_TO_TICKS(msec));
}
//--------------------------------------------------------------------+
// Spinlock API
//--------------------------------------------------------------------+
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
osal_spinlock_t _name
#if TUSB_MCU_VENDOR_ESPRESSIF
// Espressif critical take spinlock as argument and does not use in_isr
typedef portMUX_TYPE osal_spinlock_t;
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
spinlock_initialize(ctx);
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
return; // single core MCU does not need to lock in ISR
}
portENTER_CRITICAL(ctx);
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
return; // single core MCU does not need to lock in ISR
}
portEXIT_CRITICAL(ctx);
}
#else
typedef UBaseType_t osal_spinlock_t;
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
(void) ctx;
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
if (in_isr) {
if (!TUP_MCU_MULTIPLE_CORE) {
(void) ctx;
return; // single core MCU does not need to lock in ISR
}
*ctx = taskENTER_CRITICAL_FROM_ISR();
} else {
taskENTER_CRITICAL();
}
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
if (in_isr) {
if (!TUP_MCU_MULTIPLE_CORE) {
(void) ctx;
return; // single core MCU does not need to lock in ISR
}
taskEXIT_CRITICAL_FROM_ISR(*ctx);
} else {
taskEXIT_CRITICAL();
}
}
#endif
//--------------------------------------------------------------------+
// Semaphore API
//--------------------------------------------------------------------+
TU_ATTR_ALWAYS_INLINE static inline osal_semaphore_t osal_semaphore_create(osal_semaphore_def_t *semdef) {
#if configSUPPORT_STATIC_ALLOCATION
return xSemaphoreCreateBinaryStatic(semdef);
@ -120,19 +179,12 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_delete(osal_semaphore_t
}
TU_ATTR_ALWAYS_INLINE static inline bool osal_semaphore_post(osal_semaphore_t sem_hdl, bool in_isr) {
if ( !in_isr ) {
if (!in_isr) {
return xSemaphoreGive(sem_hdl) != 0;
} else {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
BaseType_t res = xSemaphoreGiveFromISR(sem_hdl, &xHigherPriorityTaskWoken);
#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3
// not needed after https://github.com/espressif/esp-idf/commit/c5fd79547ac9b7bae06fa660e9f814d18d3390b7
if ( xHigherPriorityTaskWoken ) portYIELD_FROM_ISR();
#else
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
#endif
return res != 0;
}
}
@ -148,7 +200,6 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_semaphore_reset(osal_semaphore_t c
//--------------------------------------------------------------------+
// MUTEX API (priority inheritance)
//--------------------------------------------------------------------+
TU_ATTR_ALWAYS_INLINE static inline osal_mutex_t osal_mutex_create(osal_mutex_def_t *mdef) {
#if configSUPPORT_STATIC_ALLOCATION
return xSemaphoreCreateMutexStatic(mdef);
@ -174,7 +225,6 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_mutex_unlock(osal_mutex_t mutex_hd
//--------------------------------------------------------------------+
// QUEUE API
//--------------------------------------------------------------------+
TU_ATTR_ALWAYS_INLINE static inline osal_queue_t osal_queue_create(osal_queue_def_t* qdef) {
osal_queue_t q;
@ -201,19 +251,12 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_receive(osal_queue_t qhdl, v
}
TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void const *data, bool in_isr) {
if ( !in_isr ) {
if (!in_isr) {
return xQueueSendToBack(qhdl, data, OSAL_TIMEOUT_WAIT_FOREVER) != 0;
} else {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
BaseType_t res = xQueueSendToBackFromISR(qhdl, data, &xHigherPriorityTaskWoken);
#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3
// not needed after https://github.com/espressif/esp-idf/commit/c5fd79547ac9b7bae06fa660e9f814d18d3390b7 (IDF v5)
if ( xHigherPriorityTaskWoken ) portYIELD_FROM_ISR();
#else
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
#endif
return res != 0;
}
}

View file

@ -40,6 +40,32 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
os_time_delay( os_time_ms_to_ticks32(msec) );
}
//--------------------------------------------------------------------+
// Spinlock API
//--------------------------------------------------------------------+
typedef os_sr_t osal_spinlock_t;
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
osal_spinlock_t _name
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
(void) ctx;
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
return; // single core MCU does not need to lock in ISR
}
OS_ENTER_CRITICAL(*ctx);
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
return; // single core MCU does not need to lock in ISR
}
OS_ENTER_CRITICAL(*ctx);
}
//--------------------------------------------------------------------+
// Semaphore API
//--------------------------------------------------------------------+

View file

@ -40,6 +40,33 @@ extern "C" {
TU_ATTR_WEAK void osal_task_delay(uint32_t msec);
#endif
//--------------------------------------------------------------------+
// Spinlock API
//--------------------------------------------------------------------+
typedef struct {
void (* interrupt_set)(bool);
} osal_spinlock_t;
// For SMP, spinlock must be locked by hardware, cannot just use interrupt
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
osal_spinlock_t _name = { .interrupt_set = _int_set }
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
(void) ctx;
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
if (!in_isr) {
ctx->interrupt_set(false);
}
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
if (!in_isr) {
ctx->interrupt_set(true);
}
}
//--------------------------------------------------------------------+
// Binary Semaphore API
//--------------------------------------------------------------------+

View file

@ -43,6 +43,27 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
sleep_ms(msec);
}
//--------------------------------------------------------------------+
// Spinlock API
//--------------------------------------------------------------------+
typedef critical_section_t osal_spinlock_t; // pico implement critical section with spinlock
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
osal_spinlock_t _name
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
critical_section_init(ctx);
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
(void) in_isr;
critical_section_enter_blocking(ctx);
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
(void) in_isr;
critical_section_exit(ctx);
}
//--------------------------------------------------------------------+
// Binary Semaphore API
//--------------------------------------------------------------------+

View file

@ -42,6 +42,32 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
rt_thread_mdelay(msec);
}
//--------------------------------------------------------------------+
// Spinlock API
//--------------------------------------------------------------------+
typedef struct rt_spinlock osal_spinlock_t;
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
osal_spinlock_t _name
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
rt_spin_lock_init(ctx);
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
return; // single core MCU does not need to lock in ISR
}
rt_spin_lock(ctx);
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
return; // single core MCU does not need to lock in ISR
}
rt_spin_unlock(ctx);
}
//--------------------------------------------------------------------+
// Semaphore API
//--------------------------------------------------------------------+

View file

@ -56,6 +56,25 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t msec2wait(uint32_t msec) {
}
}
//--------------------------------------------------------------------+
// Spinlock API, stub not implemented
//--------------------------------------------------------------------+
typedef uint8_t osal_spinlock_t;
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
osal_spinlock_t _name
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
(void) ctx;
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
(void) ctx; (void) in_isr;
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
(void) ctx; (void) in_isr;
}
//--------------------------------------------------------------------+
// Semaphore API
//--------------------------------------------------------------------+

View file

@ -35,6 +35,35 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
k_msleep(msec);
}
//--------------------------------------------------------------------+
// Spinlock API
//--------------------------------------------------------------------+
typedef struct {
struct k_spinlock lock;
k_spinlock_key_t key;
} osal_spinlock_t;
#define OSAL_SPINLOCK_DEF(_name, _int_set) \
osal_spinlock_t _name
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_init(osal_spinlock_t *ctx) {
(void) ctx;
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_lock(osal_spinlock_t *ctx, bool in_isr) {
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
return; // single core MCU does not need to lock in ISR
}
ctx->key = k_spin_lock(&ctx->lock);
}
TU_ATTR_ALWAYS_INLINE static inline void osal_spin_unlock(osal_spinlock_t *ctx, bool in_isr) {
if (!TUP_MCU_MULTIPLE_CORE && in_isr) {
return; // single core MCU does not need to lock in ISR
}
k_spin_unlock(&ctx->lock, ctx->key);
}
//--------------------------------------------------------------------+
// Binary Semaphore API
//--------------------------------------------------------------------+

View file

@ -28,9 +28,9 @@
#if CFG_TUH_ENABLED && defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
#include <stdatomic.h>
#include "host/hcd.h"
#include "host/usbh.h"
#include "host/usbh_pvt.h"
//--------------------------------------------------------------------+
//
@ -233,13 +233,7 @@ typedef struct {
uint8_t hxfr;
}sndfifo_owner;
#if CFG_TUSB_MCU == OPT_MCU_RP2040
// currently has undefined reference to `__atomic_test_and_set' with rp2040 on Arduino with gcc 14.2
// temporarily use native semaphore instead. TODO rework osal semaphore/mutex later on
semaphore_t busy; // busy transferring
#else
atomic_flag busy; // busy transferring
#endif
bool busy_lock; // busy transferring
#if OSAL_MUTEX_REQUIRED
OSAL_MUTEX_DEF(spi_mutexdef);
@ -258,25 +252,6 @@ static tuh_configure_max3421_t _tuh_cfg = {
.pinctl = 0, // default: negative edge interrupt
};
#if CFG_TUSB_MCU == OPT_MCU_RP2040
TU_ATTR_ALWAYS_INLINE static inline bool usb_xfer_test_and_set(void) {
return !sem_try_acquire(&_hcd_data.busy);
}
TU_ATTR_ALWAYS_INLINE static inline void usb_xfer_clear(void) {
sem_release(&_hcd_data.busy);
}
#else
TU_ATTR_ALWAYS_INLINE static inline bool usb_xfer_test_and_set(void) {
return atomic_flag_test_and_set(&_hcd_data.busy);
}
TU_ATTR_ALWAYS_INLINE static inline void usb_xfer_clear(void) {
atomic_flag_clear(&_hcd_data.busy);
}
#endif
//--------------------------------------------------------------------+
// SPI Commands and Helper
//--------------------------------------------------------------------+
@ -352,7 +327,9 @@ TU_ATTR_ALWAYS_INLINE static inline void mode_write(uint8_t rhport, uint8_t data
}
TU_ATTR_ALWAYS_INLINE static inline void peraddr_write(uint8_t rhport, uint8_t data, bool in_isr) {
if ( _hcd_data.peraddr == data ) return; // no need to change address
if (_hcd_data.peraddr == data) {
return; // no need to change address
}
_hcd_data.peraddr = data;
reg_write(rhport, PERADDR_ADDR, data, in_isr);
@ -398,7 +375,7 @@ TU_ATTR_ALWAYS_INLINE static inline void hwfifo_setup(uint8_t rhport, const uint
static void hwfifo_receive(uint8_t rhport, uint8_t * buffer, uint16_t len, bool in_isr) {
uint8_t hirq;
uint8_t const reg = RCVVFIFO_ADDR;
const uint8_t reg = RCVVFIFO_ADDR;
max3421_spi_lock(rhport, in_isr);
@ -414,7 +391,7 @@ static void hwfifo_receive(uint8_t rhport, uint8_t * buffer, uint16_t len, bool
//--------------------------------------------------------------------+
static max3421_ep_t* find_ep_not_addr0(uint8_t daddr, uint8_t ep_num, uint8_t ep_dir) {
uint8_t const is_out = 1-ep_dir;
const uint8_t is_out = 1-ep_dir;
for(size_t i=1; i<CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
max3421_ep_t* ep = &_hcd_data.ep[i];
// control endpoint is bi-direction (skip check)
@ -509,10 +486,6 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
tu_memclr(&_hcd_data, sizeof(_hcd_data));
_hcd_data.peraddr = 0xff; // invalid
#if CFG_TUSB_MCU == OPT_MCU_RP2040
sem_init(&_hcd_data.busy, 1, 1);
#endif
#if OSAL_MUTEX_REQUIRED
_hcd_data.spi_mutex = osal_mutex_create(&_hcd_data.spi_mutexdef);
#endif
@ -570,10 +543,6 @@ bool hcd_deinit(uint8_t rhport) {
_hcd_data.spi_mutex = NULL;
#endif
#if CFG_TUSB_MCU == OPT_MCU_RP2040
sem_reset(&_hcd_data.busy, 1);
#endif
return true;
}
@ -760,8 +729,8 @@ static void xact_generic(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool
// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked
bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) {
uint8_t const ep_num = tu_edpt_number(ep_addr);
uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
const uint8_t ep_num = tu_edpt_number(ep_addr);
const uint8_t ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
TU_VERIFY(ep);
@ -777,8 +746,17 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buf
ep->xferred_len = 0;
ep->state = EP_STATE_ATTEMPT_1;
bool has_xfer = false;
usbh_spin_lock(false);
if (!_hcd_data.busy_lock) {
_hcd_data.busy_lock = true;
has_xfer = true;
}
usbh_spin_unlock(false);
// carry out transfer if not busy
if (!usb_xfer_test_and_set()) {
if (has_xfer) {
xact_generic(rhport, ep, true, false);
}
@ -814,8 +792,17 @@ bool hcd_setup_send(uint8_t rhport, uint8_t daddr, uint8_t const setup_packet[8]
ep->xferred_len = 0;
ep->state = EP_STATE_ATTEMPT_1;
bool has_xfer = false;
usbh_spin_lock(false);
if (!_hcd_data.busy_lock) {
_hcd_data.busy_lock = true;
has_xfer = true;
}
usbh_spin_unlock(false);
// carry out transfer if not busy
if (!usb_xfer_test_and_set()) {
if (has_xfer) {
xact_setup(rhport, ep, false);
}
@ -881,8 +868,8 @@ static void handle_connect_irq(uint8_t rhport, bool in_isr) {
}
static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t result, uint8_t hrsl, bool in_isr) {
uint8_t const ep_dir = 1-ep->hxfr_bm.is_out;
uint8_t const ep_addr = tu_edpt_addr(ep->hxfr_bm.ep_num, ep_dir);
const uint8_t ep_dir = 1 - ep->hxfr_bm.is_out;
const uint8_t ep_addr = tu_edpt_addr(ep->hxfr_bm.ep_num, ep_dir);
// save data toggle
if (ep_dir) {
@ -900,7 +887,9 @@ static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t re
xact_generic(rhport, next_ep, true, in_isr);
}else {
// no more pending
usb_xfer_clear();
usbh_spin_lock(in_isr);
_hcd_data.busy_lock = false;
usbh_spin_unlock(in_isr);
}
}
@ -939,7 +928,9 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) {
xact_generic(rhport, next_ep, true, in_isr);
} else {
// no more pending in this frame -> clear busy
usb_xfer_clear();
usbh_spin_lock(in_isr);
_hcd_data.busy_lock = false;
usbh_spin_unlock(in_isr);
}
return;
@ -1030,8 +1021,8 @@ void print_hirq(uint8_t hirq) {
// Interrupt handler
void hcd_int_handler(uint8_t rhport, bool in_isr) {
uint8_t hirq = reg_read(rhport, HIRQ_ADDR, in_isr) & _hcd_data.hien;
if (!hirq) return;
// print_hirq(hirq);
if (!hirq) { return; }
// print_hirq(hirq);
if (hirq & HIRQ_FRAME_IRQ) {
_hcd_data.frame_count++;
@ -1050,8 +1041,19 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
}
// start usb transfer if not busy
if (ep_retry != NULL && !usb_xfer_test_and_set()) {
xact_generic(rhport, ep_retry, true, in_isr);
if (ep_retry != NULL) {
bool has_xfer = false;
usbh_spin_lock(in_isr);
if (!_hcd_data.busy_lock) {
_hcd_data.busy_lock = true;
has_xfer = true;
}
usbh_spin_unlock(in_isr);
if (has_xfer) {
xact_generic(rhport, ep_retry, true, in_isr);
}
}
}

View file

@ -39,6 +39,7 @@
#define DWC2_DEBUG 2
#include "device/dcd.h"
#include "device/usbd_pvt.h"
#include "dwc2_common.h"
//--------------------------------------------------------------------+
@ -52,6 +53,7 @@ typedef struct {
uint8_t interval;
} xfer_ctl_t;
// This variable is modified from ISR context, so it must be protected by critical section
static xfer_ctl_t xfer_status[DWC2_EP_MAX][2];
#define XFER_CTL_BASE(_ep, _dir) (&xfer_status[_ep][_dir])
@ -321,6 +323,9 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
}
}
// Since this function returns void, it is not possible to return a boolean success message
// We must make sure that this function is not called when the EP is disabled
// Must be called from critical section
static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uint8_t dir) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, dir);
@ -531,6 +536,8 @@ void dcd_edpt_close_all(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
usbd_spin_lock(false);
_dcd_data.allocated_epin_count = 0;
// Disable non-control interrupt
@ -548,8 +555,9 @@ void dcd_edpt_close_all(uint8_t rhport) {
dfifo_flush_tx(dwc2, 0x10); // all tx fifo
dfifo_flush_rx(dwc2);
dfifo_device_init(rhport); // re-init dfifo
usbd_spin_unlock(false);
}
bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
@ -567,21 +575,31 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpo
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
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;
xfer->total_len = total_bytes;
bool ret;
// EP0 can only handle one packet
if (epnum == 0) {
_dcd_data.ep0_pending[dir] = total_bytes;
usbd_spin_lock(false);
if (xfer->max_size == 0) {
ret = false; // Endpoint is closed
} else {
xfer->buffer = buffer;
xfer->ff = NULL;
xfer->total_len = total_bytes;
// EP0 can only handle one packet
if (epnum == 0) {
_dcd_data.ep0_pending[dir] = total_bytes;
}
// Schedule packets to be sent within interrupt
edpt_schedule_packets(rhport, epnum, dir);
ret = true;
}
// Schedule packets to be sent within interrupt
edpt_schedule_packets(rhport, epnum, dir);
usbd_spin_unlock(false);
return true;
return ret;
}
// The number of bytes has to be given explicitly to allow more flexible control of how many
@ -594,17 +612,27 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t
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 = NULL;
xfer->ff = ff;
xfer->total_len = total_bytes;
bool ret;
// Schedule packets to be sent within interrupt
// TODO xfer fifo may only available for slave mode
edpt_schedule_packets(rhport, epnum, dir);
usbd_spin_lock(false);
return true;
if (xfer->max_size == 0) {
ret = false; // Endpoint is closed
} else {
xfer->buffer = NULL;
xfer->ff = ff;
xfer->total_len = total_bytes;
// Schedule packets to be sent within interrupt
// TODO xfer fifo may only available for slave mode
edpt_schedule_packets(rhport, epnum, dir);
ret = true;
}
usbd_spin_unlock(false);
return ret;
}
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
@ -631,6 +659,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
//--------------------------------------------------------------------
// 7.4.1 Initialization on USB Reset
// Must be called from critical section
static void handle_bus_reset(uint8_t rhport) {
dwc2_regs_t *dwc2 = DWC2_REG(rhport);
const uint8_t ep_count = dwc2_ep_count(dwc2);
@ -983,14 +1012,16 @@ static void handle_ep_irq(uint8_t rhport, uint8_t dir) {
*/
void dcd_int_handler(uint8_t rhport) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
const uint32_t gintmask = dwc2->gintmsk;
const uint32_t gintsts = dwc2->gintsts & gintmask;
if (gintsts & GINTSTS_USBRST) {
// USBRST is start of reset.
dwc2->gintsts = GINTSTS_USBRST;
usbd_spin_lock(true);
handle_bus_reset(rhport);
usbd_spin_unlock(true);
}
if (gintsts & GINTSTS_ENUMDNE) {

View file

@ -55,8 +55,8 @@ static const dwc2_controller_t _dwc2_controller[] = {
// On ESP32 for consistency we associate
// - Port0 to OTG_FS, and Port1 to OTG_HS
static const dwc2_controller_t _dwc2_controller[] = {
{ .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_CH0_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 },
{ .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTG_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .ep_fifo_size = 4096 }
{ .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_CH0_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .ep_fifo_size = 1024 },
{ .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTG_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .ep_fifo_size = 4096 }
};
#endif