summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhathach <[email protected]>2025-06-11 16:23:54 +0700
committerhathach <[email protected]>2025-06-11 16:23:54 +0700
commitd3d505e8f7872871185029b91588ae41ac037288 (patch)
tree92a2ea3f40efd05f71abd6819dbe5a71a32b27fd /src
parent6346fbec16739d91fc9ece57216d40ca1c091936 (diff)
parent8e252203697bc74f622d31a0de113e0ad01c9704 (diff)
Merge branch 'master' into fork/HiFiPhile/h7rs
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--src/class/net/ecm_rndis_device.c51
-rw-r--r--src/class/net/ncm_device.c36
-rw-r--r--src/class/net/net_device.h5
-rw-r--r--src/class/vendor/vendor_device.c40
-rw-r--r--src/common/tusb_mcu.h7
-rw-r--r--src/common/tusb_types.h6
-rw-r--r--src/device/usbd.c31
-rw-r--r--src/device/usbd_pvt.h2
-rw-r--r--src/host/usbh.c13
-rw-r--r--src/host/usbh.h2
-rw-r--r--src/host/usbh_pvt.h3
-rw-r--r--src/osal/osal.h4
-rw-r--r--src/osal/osal_freertos.h107
-rw-r--r--src/osal/osal_mynewt.h26
-rw-r--r--src/osal/osal_none.h27
-rw-r--r--src/osal/osal_pico.h21
-rw-r--r--src/osal/osal_rtthread.h26
-rw-r--r--src/osal/osal_rtx4.h19
-rw-r--r--src/osal/osal_zephyr.h29
-rw-r--r--src/portable/analog/max3421/hcd_max3421.c69
-rw-r--r--src/portable/chipidea/ci_hs/dcd_ci_hs.c4
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c71
-rw-r--r--src/portable/synopsys/dwc2/dwc2_esp32.h4
-rw-r--r--src/tusb_option.h10
25 files changed, 499 insertions, 119 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 55c52033c..99d3059fc 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,9 +1,6 @@
-# TODO more docs and example on how to use this file
-# TINYUSB_TARGET_PREFIX and TINYUSB_TARGET_SUFFIX can be used to change the name of the target
-
cmake_minimum_required(VERSION 3.20)
-# Add tinyusb to a existing target
+# Add tinyusb to a existing target, DCD and HCD drivers are not included
function(tinyusb_target_add TARGET)
target_sources(${TARGET} PRIVATE
# common
diff --git a/src/class/net/ecm_rndis_device.c b/src/class/net/ecm_rndis_device.c
index a54e6d662..299eb97c8 100644
--- a/src/class/net/ecm_rndis_device.c
+++ b/src/class/net/ecm_rndis_device.c
@@ -81,6 +81,7 @@ typedef struct {
static netd_interface_t _netd_itf;
CFG_TUD_MEM_SECTION static netd_epbuf_t _netd_epbuf;
static bool can_xmit;
+static bool ecm_link_is_up = true; // Store link state for ECM mode
void tud_network_recv_renew(void) {
usbd_edpt_xfer(0, _netd_itf.ep_out, _netd_epbuf.rx, NETD_PACKET_SIZE);
@@ -95,7 +96,11 @@ void netd_report(uint8_t *buf, uint16_t len) {
const uint8_t rhport = 0;
len = tu_min16(len, sizeof(ecm_notify_t));
- TU_VERIFY(usbd_edpt_claim(rhport, _netd_itf.ep_notif), );
+ if (!usbd_edpt_claim(rhport, _netd_itf.ep_notif)) {
+ TU_LOG1("ECM: Failed to claim notification endpoint\n");
+ return;
+ }
+
memcpy(_netd_epbuf.notify, buf, len);
usbd_edpt_xfer(rhport, _netd_itf.ep_notif, _netd_epbuf.notify, len);
}
@@ -181,8 +186,6 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
// Open endpoint pair for RNDIS
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in), 0);
- tud_network_init_cb();
-
// we are ready to transmit a packet
can_xmit = true;
@@ -196,11 +199,11 @@ uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
}
static void ecm_report(bool nc) {
- const ecm_notify_t ecm_notify_nc = {
+ ecm_notify_t ecm_notify_nc = {
.header = {
.bmRequestType = 0xA1,
.bRequest = 0, /* NETWORK_CONNECTION aka NetworkConnection */
- .wValue = 1, /* Connected */
+ .wValue = ecm_link_is_up ? 1 : 0, /* Use current link state */
.wLength = 0,
},
};
@@ -259,7 +262,6 @@ bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
// TODO should be merge with RNDIS's after endpoint opened
// Also should have opposite callback for application to disable network !!
- tud_network_init_cb();
can_xmit = true; // we are ready to transmit a packet
tud_network_recv_renew(); // prepare for incoming packets
}
@@ -286,7 +288,10 @@ bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
/* the only required CDC-ECM Management Element Request is SetEthernetPacketFilter */
if (0x43 /* SET_ETHERNET_PACKET_FILTER */ == request->bRequest) {
tud_control_xfer(rhport, request, NULL, 0);
- ecm_report(true);
+ // Only send connection notification if link is up
+ if (ecm_link_is_up) {
+ ecm_report(true);
+ }
}
} else {
if (request->bmRequestType_bit.direction == TUSB_DIR_IN) {
@@ -363,9 +368,8 @@ bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
}
if (_netd_itf.ecm_mode && (ep_addr == _netd_itf.ep_notif)) {
- if (sizeof(tusb_control_request_t) == xferred_bytes) {
- ecm_report(false);
- }
+ // Notification transfer complete - endpoint is now free
+ // Don't automatically send speed change notification after link state changes
}
return true;
@@ -398,4 +402,31 @@ void tud_network_xmit(void *ref, uint16_t arg) {
do_in_xfer(_netd_epbuf.tx, len);
}
+// Set the network link state (up/down) and notify the host
+void tud_network_link_state(uint8_t rhport, bool is_up) {
+ (void)rhport;
+
+ if (_netd_itf.ecm_mode) {
+ ecm_link_is_up = is_up;
+
+ // For ECM mode, send network connection notification only
+ // Don't trigger speed change notification for link state changes
+ ecm_notify_t notify = {
+ .header = {
+ .bmRequestType = 0xA1,
+ .bRequest = 0, /* NETWORK_CONNECTION */
+ .wValue = is_up ? 1 : 0, /* 0 = disconnected, 1 = connected */
+ .wLength = 0,
+ },
+ };
+ notify.header.wIndex = _netd_itf.itf_num;
+ netd_report((uint8_t *)&notify, sizeof(notify.header));
+ } else {
+ // For RNDIS mode, we would need to implement RNDIS status indication
+ // This is more complex and requires RNDIS_INDICATE_STATUS_MSG
+ // For now, RNDIS doesn't support dynamic link state changes
+ (void)is_up;
+ }
+}
+
#endif
diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c
index f9fda0698..02833c5f1 100644
--- a/src/class/net/ncm_device.c
+++ b/src/class/net/ncm_device.c
@@ -110,6 +110,7 @@ typedef struct {
NOTIFICATION_DONE
} notification_xmit_state; // state of notification transmission
bool notification_xmit_is_running; // notification is currently transmitted
+ bool link_is_up; // current link state
// misc
bool tud_network_recv_renew_active; // tud_network_recv_renew() is active (avoid recursive invocations)
@@ -218,7 +219,7 @@ static void notification_xmit(uint8_t rhport, bool force_next) {
.direction = TUSB_DIR_IN
},
.bRequest = CDC_NOTIF_NETWORK_CONNECTION,
- .wValue = 1 /* Connected */,
+ .wValue = ncm_interface.link_is_up ? 1 : 0, /* Dynamic link state */
.wIndex = ncm_interface.itf_num,
.wLength = 0,
},
@@ -232,6 +233,7 @@ static void notification_xmit(uint8_t rhport, bool force_next) {
ncm_interface.notification_xmit_is_running = true;
} else {
TU_LOG_DRV(" NOTIFICATION_FINISHED\n");
+ ncm_interface.notification_xmit_is_running = false;
}
} // notification_xmit
@@ -755,6 +757,32 @@ static void tud_network_recv_renew_r(uint8_t rhport) {
tud_network_recv_renew();
} // tud_network_recv_renew
+/**
+ * Set the link state and send notification to host
+ */
+void tud_network_link_state(uint8_t rhport, bool is_up) {
+ TU_LOG_DRV("tud_network_link_state(%d, %d)\n", rhport, is_up);
+
+ if (ncm_interface.link_is_up == is_up) {
+ // No change in link state
+ return;
+ }
+
+ ncm_interface.link_is_up = is_up;
+
+ // Only send notification if we have an active data interface
+ if (ncm_interface.itf_data_alt != 1) {
+ TU_LOG_DRV(" link state notification skipped (interface not active)\n");
+ return;
+ }
+
+ // Reset notification state to send link state update
+ ncm_interface.notification_xmit_state = NOTIFICATION_CONNECTED;
+
+ // Trigger notification transmission
+ notification_xmit(rhport, false);
+}
+
//-----------------------------------------------------------------------------
//
// all the netd_*() stuff (interface TinyUSB -> driver)
@@ -774,6 +802,12 @@ void netd_init(void) {
for (int i = 0; i < RECV_NTB_N; ++i) {
ncm_interface.recv_free_ntb[i] = &ncm_epbuf.recv[i].ntb;
}
+ // Default link state - can be configured via CFG_TUD_NCM_DEFAULT_LINK_UP
+ #ifdef CFG_TUD_NCM_DEFAULT_LINK_UP
+ ncm_interface.link_is_up = CFG_TUD_NCM_DEFAULT_LINK_UP;
+ #else
+ ncm_interface.link_is_up = true; // Default to link up if not set.
+ #endif
} // netd_init
/**
diff --git a/src/class/net/net_device.h b/src/class/net/net_device.h
index 4c9a92f2d..fff2623b7 100644
--- a/src/class/net/net_device.h
+++ b/src/class/net/net_device.h
@@ -87,6 +87,11 @@ void tud_network_init_cb(void);
// TODO removed later since it is not part of tinyusb stack
extern uint8_t tud_network_mac_address[6];
+//------------- NCM -------------//
+
+// Set the network link state (up/down) and notify the host
+void tud_network_link_state(uint8_t rhport, bool is_up);
+
//--------------------------------------------------------------------+
// INTERNAL USBD-CLASS DRIVER API
//--------------------------------------------------------------------+
diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c
index 2fc0ac944..7f1fd8c41 100644
--- a/src/class/vendor/vendor_device.c
+++ b/src/class/vendor/vendor_device.c
@@ -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;
- }
-
- const tusb_desc_endpoint_t* desc_ep = (const tusb_desc_endpoint_t*) p_desc;
- TU_ASSERT(usbd_edpt_open(rhport, desc_ep));
- found_ep++;
+ 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));
- 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);
diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h
index a0175d664..6678265b5 100644
--- a/src/common/tusb_mcu.h
+++ b/src/common/tusb_mcu.h
@@ -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")))
diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h
index e000a4bd3..fd7f01b67 100644
--- a/src/common/tusb_types.h
+++ b/src/common/tusb_types.h
@@ -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);
diff --git a/src/device/usbd.c b/src/device/usbd.c
index 9c381d5e0..6e5fcf3b6 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -340,15 +340,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
@@ -466,7 +467,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));
@@ -475,6 +476,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);
@@ -1242,17 +1245,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)
{
diff --git a/src/device/usbd_pvt.h b/src/device/usbd_pvt.h
index 190d6fd7f..5c6f9dbee 100644
--- a/src/device/usbd_pvt.h
+++ b/src/device/usbd_pvt.h
@@ -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
diff --git a/src/host/usbh.c b/src/host/usbh.c
index b7d5a05f2..f2e5c1f0e 100644
--- a/src/host/usbh.c
+++ b/src/host/usbh.c
@@ -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);
@@ -895,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;
diff --git a/src/host/usbh.h b/src/host/usbh.h
index 6f34d8bb3..13eede869 100644
--- a/src/host/usbh.h
+++ b/src/host/usbh.h
@@ -42,7 +42,7 @@
//--------------------------------------------------------------------+
// Endpoint Bulk size depending on host mx speed
-#define TUH_EPSIZE_BULK_MPS (TUD_OPT_HIGH_SPEED ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS)
+#define TUH_EPSIZE_BULK_MPS (TUH_OPT_HIGH_SPEED ? TUSB_EPSIZE_BULK_HS : TUSB_EPSIZE_BULK_FS)
// forward declaration
struct tuh_xfer_s;
diff --git a/src/host/usbh_pvt.h b/src/host/usbh_pvt.h
index 61b012493..cb092e5f3 100644
--- a/src/host/usbh_pvt.h
+++ b/src/host/usbh_pvt.h
@@ -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
//--------------------------------------------------------------------+
diff --git a/src/osal/osal.h b/src/osal/osal.h
index 38d45da44..a33280425 100644
--- a/src/osal/osal.h
+++ b/src/osal/osal.h
@@ -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);
diff --git a/src/osal/osal_freertos.h b/src/osal/osal_freertos.h
index a3a0f3a3f..bde5ec010 100644
--- a/src/osal/osal_freertos.h
+++ b/src/osal/osal_freertos.h
@@ -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;
}
@@ -102,9 +100,70 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t msec) {
}
//--------------------------------------------------------------------+
-// Semaphore API
+// 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;
}
}
diff --git a/src/osal/osal_mynewt.h b/src/osal/osal_mynewt.h
index 16def0d2a..ee95e684f 100644
--- a/src/osal/osal_mynewt.h
+++ b/src/osal/osal_mynewt.h
@@ -41,6 +41,32 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t 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
//--------------------------------------------------------------------+
typedef struct os_sem osal_semaphore_def_t;
diff --git a/src/osal/osal_none.h b/src/osal/osal_none.h
index 40e9bb83a..a8eb1042b 100644
--- a/src/osal/osal_none.h
+++ b/src/osal/osal_none.h
@@ -41,6 +41,33 @@ 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
//--------------------------------------------------------------------+
typedef struct {
diff --git a/src/osal/osal_pico.h b/src/osal/osal_pico.h
index 315de0950..ace5907d7 100644
--- a/src/osal/osal_pico.h
+++ b/src/osal/osal_pico.h
@@ -44,6 +44,27 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t 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
//--------------------------------------------------------------------+
typedef struct semaphore osal_semaphore_def_t, * osal_semaphore_t;
diff --git a/src/osal/osal_rtthread.h b/src/osal/osal_rtthread.h
index c27814835..a778f5425 100644
--- a/src/osal/osal_rtthread.h
+++ b/src/osal/osal_rtthread.h
@@ -43,6 +43,32 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t 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
//--------------------------------------------------------------------+
typedef struct rt_semaphore osal_semaphore_def_t;
diff --git a/src/osal/osal_rtx4.h b/src/osal/osal_rtx4.h
index 35909e4d6..35860ddd5 100644
--- a/src/osal/osal_rtx4.h
+++ b/src/osal/osal_rtx4.h
@@ -57,6 +57,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
//--------------------------------------------------------------------+
typedef OS_SEM osal_semaphore_def_t;
diff --git a/src/osal/osal_zephyr.h b/src/osal/osal_zephyr.h
index 8ecb13c6d..91f225f79 100644
--- a/src/osal/osal_zephyr.h
+++ b/src/osal/osal_zephyr.h
@@ -36,6 +36,35 @@ TU_ATTR_ALWAYS_INLINE static inline void osal_task_delay(uint32_t 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
//--------------------------------------------------------------------+
typedef struct k_sem osal_semaphore_def_t, * osal_semaphore_t;
diff --git a/src/portable/analog/max3421/hcd_max3421.c b/src/portable/analog/max3421/hcd_max3421.c
index bb33200f2..971dbd62e 100644
--- a/src/portable/analog/max3421/hcd_max3421.c
+++ b/src/portable/analog/max3421/hcd_max3421.c
@@ -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,7 +233,7 @@ typedef struct {
uint8_t hxfr;
}sndfifo_owner;
- atomic_flag busy; // busy transferring
+ bool busy_lock; // busy transferring
#if OSAL_MUTEX_REQUIRED
OSAL_MUTEX_DEF(spi_mutexdef);
@@ -327,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);
@@ -373,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);
@@ -389,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)
@@ -727,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);
@@ -744,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 (!atomic_flag_test_and_set(&_hcd_data.busy)) {
+ if (has_xfer) {
xact_generic(rhport, ep, true, false);
}
@@ -781,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 (!atomic_flag_test_and_set(&_hcd_data.busy)) {
+ if (has_xfer) {
xact_setup(rhport, ep, false);
}
@@ -848,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) {
@@ -867,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
- atomic_flag_clear(&_hcd_data.busy);
+ usbh_spin_lock(in_isr);
+ _hcd_data.busy_lock = false;
+ usbh_spin_unlock(in_isr);
}
}
@@ -906,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
- atomic_flag_clear(&_hcd_data.busy);
+ usbh_spin_lock(in_isr);
+ _hcd_data.busy_lock = false;
+ usbh_spin_unlock(in_isr);
}
return;
@@ -997,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++;
@@ -1017,8 +1041,19 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
}
// start usb transfer if not busy
- if (ep_retry != NULL && !atomic_flag_test_and_set(&_hcd_data.busy)) {
- 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);
+ }
}
}
diff --git a/src/portable/chipidea/ci_hs/dcd_ci_hs.c b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
index a716dc24c..244f5a2d4 100644
--- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c
+++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
@@ -239,7 +239,11 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
usbmode |= USBMODE_CM_DEVICE;
dcd_reg->USBMODE = usbmode;
+#ifdef CFG_TUD_CI_HS_VBUS_CHARGE
+ dcd_reg->OTGSC = OTGSC_VBUS_CHARGE | OTGSC_OTG_TERMINATION;
+#else
dcd_reg->OTGSC = OTGSC_VBUS_DISCHARGE | OTGSC_OTG_TERMINATION;
+#endif
#if !TUD_OPT_HIGH_SPEED
dcd_reg->PORTSC1 = PORTSC1_FORCE_FULL_SPEED;
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index 52d675611..865c51894 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -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) {
diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h
index 3309760ff..49b8c54cb 100644
--- a/src/portable/synopsys/dwc2/dwc2_esp32.h
+++ b/src/portable/synopsys/dwc2/dwc2_esp32.h
@@ -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
diff --git a/src/tusb_option.h b/src/tusb_option.h
index 98f1a91b5..104f669c9 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -195,6 +195,7 @@
// Analog Devices
#define OPT_MCU_MAX32690 2400 ///< ADI MAX32690
+#define OPT_MCU_MAX32665 2401 ///< ADI MAX32666/5
#define OPT_MCU_MAX32666 2401 ///< ADI MAX32666/5
#define OPT_MCU_MAX32650 2402 ///< ADI MAX32650/1/2
#define OPT_MCU_MAX78002 2403 ///< ADI MAX78002
@@ -267,6 +268,15 @@
#define CFG_TUD_DWC2_DMA_ENABLE CFG_TUD_DWC2_DMA_ENABLE_DEFAULT
#endif
+// Enable CI_HS VBUS Charge. Set this to 1 if the USB_VBUS pin is not connected to 5V VBUS (note: 3.3V is insufficient).
+#ifndef CFG_TUD_CI_HS_VBUS_CHARGE
+ #ifndef CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT
+ #define CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT 0
+ #endif
+
+ #define CFG_TUD_CI_HS_VBUS_CHARGE CFG_TUD_CI_HS_VBUS_CHARGE_DEFAULT
+#endif
+
// Enable DWC2 Slave mode for host
#ifndef CFG_TUH_DWC2_SLAVE_ENABLE
#ifndef CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT