summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Kconfig1
-rw-r--r--SConscript2
-rw-r--r--class/adb/usbd_adb.c2
-rw-r--r--class/audio/usb_audio.h40
-rw-r--r--class/audio/usbh_audio.c4
-rw-r--r--class/cdc/usb_cdc.h3
-rw-r--r--class/cdc/usbd_cdc_ncm.c545
-rw-r--r--class/cdc/usbd_cdc_ncm.h41
-rw-r--r--class/hub/usbh_hub.c13
-rw-r--r--class/msc/usbd_msc.c41
-rw-r--r--class/serial/usbh_serial.c68
-rw-r--r--class/serial/usbh_serial.h13
-rw-r--r--common/usb_util.h20
-rw-r--r--core/usbd_core.c8
-rw-r--r--core/usbh_core.c8
-rw-r--r--core/usbotg_core.c9
-rw-r--r--demo/cdc_ncm_template.c384
-rw-r--r--osal/usb_osal_zephyr.c16
-rw-r--r--platform/daplink/dap_main.c41
-rw-r--r--platform/idf/usbh_fatfs.c5
-rw-r--r--platform/rtthread/rt_usbd_adb.c1
-rw-r--r--platform/rtthread/rt_usbd_serial.c1
-rw-r--r--platform/zephyr/usbh_msc_disk.c126
-rw-r--r--port/ch32/README.md2
-rw-r--r--port/ch32/ch32fs/usb_dc_usbfs.c3
-rw-r--r--port/dwc2/usb_hc_dwc2.c1
-rw-r--r--port/ehci/usb_hc_ehci.c5
-rw-r--r--port/fsdev/README.md8
-rw-r--r--port/fsdev/usb_dc_fsdev.c36
-rw-r--r--port/fsdev/usb_fsdev_reg.h381
-rw-r--r--port/fsdev/usb_glue_nation.c191
-rw-r--r--port/hpmicro/usb_dc_hpm.c5
-rw-r--r--port/musb/usb_hc_musb.c1
-rw-r--r--port/ohci/usb_hc_ohci.c1
-rw-r--r--port/rp2040/usb_hc_rp2040.c5
35 files changed, 1901 insertions, 130 deletions
diff --git a/Kconfig b/Kconfig
index ad48c93e..807165be 100644
--- a/Kconfig
+++ b/Kconfig
@@ -122,7 +122,6 @@ if CHERRYUSB
config CHERRYUSB_DEVICE_CDC_NCM
bool
prompt "Enable usb cdc ncm device"
- depends on !IDF_CMAKE
default n
config CHERRYUSB_DEVICE_MTP
diff --git a/SConscript b/SConscript
index b1ea4906..2eeed782 100644
--- a/SConscript
+++ b/SConscript
@@ -105,7 +105,7 @@ if GetDepend(['PKG_CHERRYUSB_DEVICE']):
src += Glob('port/renesas/device/r_usb_device.c')
src += Glob('port/renesas/device/usb_dc_rensas.c')
if GetDepend(['PKG_CHERRYUSB_DEVICE_CH32']):
- if GetDepend(['PKG_CHERRYUSB_DEVICE_HS']):
+ if GetDepend(['PKG_CHERRYUSB_DEVICE_SPEED_HS']):
src += Glob('port/ch32/usb_dc_usbhs.c')
else:
src += Glob('port/ch32/usb_dc_usbfs.c')
diff --git a/class/adb/usbd_adb.c b/class/adb/usbd_adb.c
index dc664309..d147834f 100644
--- a/class/adb/usbd_adb.c
+++ b/class/adb/usbd_adb.c
@@ -115,7 +115,7 @@ void usbd_adb_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
if (adb_client.common_state == ADB_STATE_READ_MSG) {
USB_ASSERT(nbytes == sizeof(struct adb_msg));
- USB_ASSERT(rx_packet.msg.data_length <= sizeof(rx_packet.payload));
+ USB_ASSERT(rx_packet.msg.data_length < sizeof(rx_packet.payload));
USB_LOG_DBG("command:%x arg0:%x arg1:%x len:%d\r\n",
rx_packet.msg.command,
diff --git a/class/audio/usb_audio.h b/class/audio/usb_audio.h
index 95609765..efb758ae 100644
--- a/class/audio/usb_audio.h
+++ b/class/audio/usb_audio.h
@@ -687,6 +687,10 @@ struct audio_cs_if_as_general_descriptor {
#define AUDIO_SIZEOF_AS_GENERAL_DESC (7)
+#ifndef CONFIG_USBHOST_AUDIO_MAX_SAMPLE_FREQ_NUM
+#define CONFIG_USBHOST_AUDIO_MAX_SAMPLE_FREQ_NUM 8
+#endif
+
struct audio_cs_if_as_format_type_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
@@ -696,7 +700,7 @@ struct audio_cs_if_as_format_type_descriptor {
uint8_t bSubframeSize;
uint8_t bBitResolution;
uint8_t bSamFreqType;
- uint8_t tSamFreq[3];
+ uint8_t tSamFreq[3 * CONFIG_USBHOST_AUDIO_MAX_SAMPLE_FREQ_NUM];
} __PACKED;
#define AUDIO_SIZEOF_FORMAT_TYPE_DESC(bSamFreqType) (8 + 3 * (bSamFreqType))
@@ -1344,19 +1348,27 @@ struct audio_v2_control_range3_param_block {
#define AUDIO_SAMPLE_FREQ_4B(frq) (uint8_t)(frq), (uint8_t)((frq >> 8)), \
(uint8_t)((frq >> 16)), (uint8_t)((frq >> 24))
-/* format 10.14 */
-#define AUDIO_FREQ_TO_FEEDBACK_FS(freq) ((freq << 10) / 1000)
-#define AUDIO_FEEDBACK_TO_BUF_FS(buf, feedback) \
- buf[0] = ((feedback << 4) & 0xFFU); \
- buf[1] = (((feedback << 4) >> 8U) & 0xFFU); \
- buf[2] = (((feedback << 4) >> 16U) & 0xFFU)
+/* FS feedback uses 10.14 samples per audio data service interval. */
+#define AUDIO_FREQ_TO_FEEDBACK_FS(freq) ((uint32_t)(((uint64_t)(freq) << 10U) / 1000U))
+#define AUDIO_FEEDBACK_TO_FREQ_FS(feedback) ((uint32_t)(((uint64_t)(feedback) * 1000U) >> 10U))
+#define AUDIO_FEEDBACK_TO_BUF_FS_INTERVAL(buf, feedback, interval) do { \
+ uint32_t audio_feedback_value = (uint32_t)((uint64_t)(feedback) << ((interval) + 3U)); \
+ (buf)[0] = (uint8_t)(audio_feedback_value); \
+ (buf)[1] = (uint8_t)(audio_feedback_value >> 8U); \
+ (buf)[2] = (uint8_t)(audio_feedback_value >> 16U); \
+} while (0)
+#define AUDIO_FEEDBACK_TO_BUF_FS(buf, feedback) AUDIO_FEEDBACK_TO_BUF_FS_INTERVAL(buf, feedback, 1U)
-/* format 16.16 */
-#define AUDIO_FREQ_TO_FEEDBACK_HS(freq) ((freq << 13) / 1000)
-#define AUDIO_FEEDBACK_TO_BUF_HS(buf, feedback) \
- buf[0] = (((feedback & 0x00001FFFu) << 3) & 0xFFu); \
- buf[1] = ((((feedback & 0x00001FFFu) << 3) >> 8) & 0xFFu); \
- buf[2] = (((feedback & 0x01FFE000u) >> 13) & 0xFFu); \
- buf[3] = (((feedback & 0x01FFE000u) >> 21) & 0xFFu)
+/* Keep HS feedback in 16.16 samples per microframe, then scale it by the audio data endpoint interval when serialized. */
+#define AUDIO_FREQ_TO_FEEDBACK_HS(freq) ((uint32_t)(((uint64_t)(freq) << 16U) / 8000U))
+#define AUDIO_FEEDBACK_TO_FREQ_HS(feedback) ((uint32_t)(((uint64_t)(feedback) * 8000U) >> 16U))
+#define AUDIO_FEEDBACK_TO_BUF_HS_INTERVAL(buf, feedback, interval) do { \
+ uint32_t audio_feedback_value = (uint32_t)((uint64_t)(feedback) << ((interval) - 1U)); \
+ (buf)[0] = (uint8_t)(audio_feedback_value); \
+ (buf)[1] = (uint8_t)(audio_feedback_value >> 8U); \
+ (buf)[2] = (uint8_t)(audio_feedback_value >> 16U); \
+ (buf)[3] = (uint8_t)(audio_feedback_value >> 24U); \
+} while (0)
+#define AUDIO_FEEDBACK_TO_BUF_HS(buf, feedback) AUDIO_FEEDBACK_TO_BUF_HS_INTERVAL(buf, feedback, 4U)
#endif /* USB_AUDIO_H */
diff --git a/class/audio/usbh_audio.c b/class/audio/usbh_audio.c
index ee0a3349..185e4f3e 100644
--- a/class/audio/usbh_audio.c
+++ b/class/audio/usbh_audio.c
@@ -694,12 +694,12 @@ static int usbh_audio_ctrl_connect(struct usbh_hubport *hport, uint8_t intf)
case AUDIO_STREAMING_FORMAT_TYPE: {
struct audio_cs_if_as_format_type_descriptor *desc = (struct audio_cs_if_as_format_type_descriptor *)p;
- USB_ASSERT(desc->bSamFreqType == 1);
+ USB_ASSERT(desc->bSamFreqType <= CONFIG_USBHOST_AUDIO_MAX_SAMPLE_FREQ_NUM && desc->bSamFreqType > 0);
USB_ASSERT(desc->bNrChannels <= CONFIG_USBHOST_AUDIO_MAX_CHANNELS);
USB_ASSERT(cur_alt_setting < CONFIG_USBHOST_MAX_INTF_ALTSETTINGS);
audio_class->as_msg_table[cur_iface - audio_class->ctrl_intf - 1].bNrChannels = desc->bNrChannels;
- memcpy(&audio_class->as_msg_table[cur_iface - audio_class->ctrl_intf - 1].as_format[cur_alt_setting], desc, AUDIO_SIZEOF_FORMAT_TYPE_DESC(1));
+ memcpy(&audio_class->as_msg_table[cur_iface - audio_class->ctrl_intf - 1].as_format[cur_alt_setting], desc, AUDIO_SIZEOF_FORMAT_TYPE_DESC(desc->bSamFreqType));
} break;
default:
break;
diff --git a/class/cdc/usb_cdc.h b/class/cdc/usb_cdc.h
index a21bc3b2..c80a107e 100644
--- a/class/cdc/usb_cdc.h
+++ b/class/cdc/usb_cdc.h
@@ -275,6 +275,9 @@
#define CDC_ECM_NOTIFY_CODE_RESPONSE_AVAILABLE 0x01
#define CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE 0x2A
+#define CDC_NCM_CONNECT_SPEED_UPSTREAM 0x004C4B40U /* 5Mbps */
+#define CDC_NCM_CONNECT_SPEED_DOWNSTREAM 0x004C4B40U /* 5Mbps */
+
#define CDC_NCM_NTH16_SIGNATURE 0x484D434E
#define CDC_NCM_NDP16_SIGNATURE_NCM0 0x304D434E
#define CDC_NCM_NDP16_SIGNATURE_NCM1 0x314D434E
diff --git a/class/cdc/usbd_cdc_ncm.c b/class/cdc/usbd_cdc_ncm.c
new file mode 100644
index 00000000..68a272ee
--- /dev/null
+++ b/class/cdc/usbd_cdc_ncm.c
@@ -0,0 +1,545 @@
+/*
+ * Copyright (c) 2026, 54fire
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include <string.h>
+#include "usbd_core.h"
+#include "usbd_cdc_ncm.h"
+
+#define CDC_NCM_OUT_EP_IDX 0
+#define CDC_NCM_IN_EP_IDX 1
+#define CDC_NCM_INT_EP_IDX 2
+
+#define CONFIG_CDC_NCM_ETH_MAX_SEGSZE 1514U
+#define CONFIG_CDC_NCM_NTB_MAX_SIZE 2048U
+#define CDC_NCM_NTH16_LEN 12U
+#define CDC_NCM_NDP16_LEN 16U
+#define CDC_NCM_DATAGRAM_OFFSET 16U
+
+static struct usbd_endpoint cdc_ncm_ep_data[3];
+
+#ifdef CONFIG_USBDEV_CDC_NCM_USING_LWIP
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_rx_buffer[USB_ALIGN_UP(CONFIG_CDC_NCM_NTB_MAX_SIZE, CONFIG_USB_ALIGN_SIZE)];
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_tx_buffer[USB_ALIGN_UP(CONFIG_CDC_NCM_NTB_MAX_SIZE, CONFIG_USB_ALIGN_SIZE)];
+#endif
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_ctrl_buf[USB_ALIGN_UP(32, CONFIG_USB_ALIGN_SIZE)];
+static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_notify_buf[USB_ALIGN_UP(16, CONFIG_USB_ALIGN_SIZE)];
+
+static volatile uint32_t g_cdc_ncm_rx_ntb_length = 0;
+static volatile uint32_t g_cdc_ncm_tx_ntb_length = 0;
+static volatile bool g_cdc_ncm_data_alt_active = false;
+static uint8_t *g_cdc_ncm_rx_data_buffer = NULL;
+static uint32_t g_cdc_ncm_rx_total_length = 0;
+static uint16_t g_cdc_ncm_rx_datagram_pos = 0;
+static uint16_t g_cdc_ncm_rx_ndp_index = 0;
+static uint16_t g_cdc_ncm_rx_sequence = 0;
+static uint16_t g_cdc_ncm_tx_sequence = 0;
+static uint16_t g_cdc_ncm_max_datagram_size = CONFIG_CDC_NCM_ETH_MAX_SEGSZE;
+static uint32_t g_cdc_ncm_ntb_in_max_size = CONFIG_CDC_NCM_NTB_MAX_SIZE;
+static uint32_t g_cdc_ncm_ntb_out_max_size = CONFIG_CDC_NCM_NTB_MAX_SIZE;
+static uint16_t g_cdc_ncm_ntb_format = 0;
+static uint16_t g_cdc_ncm_packet_filter = 0;
+static volatile uint8_t g_current_net_status = 0;
+static volatile uint8_t g_cmd_intf = 0;
+static uint8_t g_cdc_ncm_mac[6] = { 0 };
+static bool g_cdc_ncm_ep_registered = false;
+
+static uint32_t g_connect_speed_table[2] = { CDC_NCM_CONNECT_SPEED_UPSTREAM, CDC_NCM_CONNECT_SPEED_DOWNSTREAM };
+
+static void usbd_cdc_ncm_send_notify(uint8_t notifycode, uint8_t value, uint32_t *speed)
+{
+ struct cdc_eth_notification *notify = (struct cdc_eth_notification *)g_cdc_ncm_notify_buf;
+ uint8_t bytes2send = 0;
+
+ memset(g_cdc_ncm_notify_buf, 0, 16);
+ notify->bmRequestType = CDC_ECM_BMREQUEST_TYPE_ECM;
+ notify->bNotificationType = notifycode;
+ notify->wIndex = g_cmd_intf;
+
+ switch (notifycode) {
+ case CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION:
+ notify->wValue = value;
+ bytes2send = 8;
+ break;
+ case CDC_ECM_NOTIFY_CODE_RESPONSE_AVAILABLE:
+ bytes2send = 8;
+ break;
+ case CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE:
+ notify->wLength = 8;
+ if (speed) {
+ memcpy(notify->data, speed, 8);
+ }
+ bytes2send = 16;
+ break;
+ default:
+ break;
+ }
+
+ if (usb_device_is_configured(0) && bytes2send) {
+ usbd_ep_start_write(0, cdc_ncm_ep_data[CDC_NCM_INT_EP_IDX].ep_addr, g_cdc_ncm_notify_buf, bytes2send);
+ }
+}
+
+static void usbd_cdc_ncm_fill_ntb_parameters(void)
+{
+ memset(g_cdc_ncm_ctrl_buf, 0, 32);
+ SET_LE16(&g_cdc_ncm_ctrl_buf[0], 28); /* wLength */
+ SET_LE16(&g_cdc_ncm_ctrl_buf[2], 0x0001); /* NTB16 only */
+ SET_LE32(&g_cdc_ncm_ctrl_buf[4], g_cdc_ncm_ntb_in_max_size); /* device-to-host NTB */
+ SET_LE16(&g_cdc_ncm_ctrl_buf[8], 4); /* wNdbInDivisor */
+ SET_LE16(&g_cdc_ncm_ctrl_buf[10], 0); /* wNdbInPayloadRemainder */
+ SET_LE16(&g_cdc_ncm_ctrl_buf[12], 4); /* wNdbInAlignment */
+ SET_LE16(&g_cdc_ncm_ctrl_buf[14], 0); /* wReserved */
+ SET_LE32(&g_cdc_ncm_ctrl_buf[16], g_cdc_ncm_ntb_out_max_size);/* host-to-device NTB */
+ SET_LE16(&g_cdc_ncm_ctrl_buf[20], 4); /* wNdbOutDivisor */
+ SET_LE16(&g_cdc_ncm_ctrl_buf[22], 0); /* wNdbOutPayloadRemainder */
+ SET_LE16(&g_cdc_ncm_ctrl_buf[24], 4); /* wNdbOutAlignment */
+ SET_LE16(&g_cdc_ncm_ctrl_buf[26], 1); /* one datagram preferred */
+}
+
+static int cdc_ncm_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ (void)busid;
+
+ g_cmd_intf = LO_BYTE(setup->wIndex);
+
+ switch (setup->bRequest) {
+ case CDC_REQUEST_GET_NTB_PARAMETERS:
+ usbd_cdc_ncm_fill_ntb_parameters();
+ *data = g_cdc_ncm_ctrl_buf;
+ *len = 28;
+ break;
+ case CDC_REQUEST_GET_NTB_FORMAT:
+ SET_LE16(g_cdc_ncm_ctrl_buf, g_cdc_ncm_ntb_format);
+ *data = g_cdc_ncm_ctrl_buf;
+ *len = 2;
+ break;
+ case CDC_REQUEST_SET_NTB_FORMAT:
+ if (setup->wValue != 0) {
+ return -1;
+ }
+ g_cdc_ncm_ntb_format = 0;
+ break;
+ case CDC_REQUEST_GET_NTB_INPUT_SIZE:
+ SET_LE32(g_cdc_ncm_ctrl_buf, g_cdc_ncm_ntb_in_max_size);
+ *data = g_cdc_ncm_ctrl_buf;
+ *len = 4;
+ break;
+ case CDC_REQUEST_SET_NTB_INPUT_SIZE:
+ if (setup->wLength >= 4 && data && *data) {
+ uint32_t ntb_size = GET_LE32(*data);
+ if (ntb_size >= (CDC_NCM_DATAGRAM_OFFSET + CDC_NCM_NDP16_LEN) && ntb_size <= CONFIG_CDC_NCM_NTB_MAX_SIZE) {
+ g_cdc_ncm_ntb_in_max_size = ntb_size;
+ }
+ }
+ break;
+ case CDC_REQUEST_GET_MAX_DATAGRAM_SIZE:
+ SET_LE16(g_cdc_ncm_ctrl_buf, g_cdc_ncm_max_datagram_size);
+ *data = g_cdc_ncm_ctrl_buf;
+ *len = 2;
+ break;
+ case CDC_REQUEST_SET_MAX_DATAGRAM_SIZE:
+ if (setup->wLength >= 2 && data && *data) {
+ uint16_t max_datagram_size = GET_LE16(*data);
+ if (max_datagram_size >= 64 && max_datagram_size <= CONFIG_CDC_NCM_ETH_MAX_SEGSZE) {
+ g_cdc_ncm_max_datagram_size = max_datagram_size;
+ }
+ } else if (setup->wValue >= 64 && setup->wValue <= CONFIG_CDC_NCM_ETH_MAX_SEGSZE) {
+ g_cdc_ncm_max_datagram_size = setup->wValue;
+ }
+ break;
+ case CDC_REQUEST_GET_CRC_MODE:
+ SET_LE16(g_cdc_ncm_ctrl_buf, 0);
+ *data = g_cdc_ncm_ctrl_buf;
+ *len = 2;
+ break;
+ case CDC_REQUEST_SET_CRC_MODE:
+ if (setup->wValue != 0) {
+ return -1;
+ }
+ break;
+ case CDC_REQUEST_GET_NET_ADDRESS:
+ memcpy(g_cdc_ncm_ctrl_buf, g_cdc_ncm_mac, 6);
+ *data = g_cdc_ncm_ctrl_buf;
+ *len = 6;
+ break;
+ case CDC_REQUEST_SET_NET_ADDRESS:
+ if (setup->wLength >= 6 && data && *data) {
+ memcpy(g_cdc_ncm_mac, *data, 6);
+ }
+ break;
+ case CDC_REQUEST_SET_ETHERNET_PACKET_FILTER:
+ g_cdc_ncm_packet_filter = setup->wValue;
+ g_connect_speed_table[0] = 100000000;
+ g_connect_speed_table[1] = 100000000;
+ usbd_cdc_ncm_set_connect(g_cdc_ncm_packet_filter != 0, g_connect_speed_table);
+ break;
+ case CDC_REQUEST_SET_ETHERNET_MULTICAST_FILTERS:
+ case CDC_REQUEST_SET_ETHERNET_PMP_FILTER:
+ break;
+ default:
+ USB_LOG_WRN("Unhandled CDC NCM Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void cdc_ncm_notify_handler(uint8_t busid, uint8_t event, void *arg)
+{
+ (void)busid;
+
+ switch (event) {
+ case USBD_EVENT_RESET:
+ case USBD_EVENT_DISCONNECTED:
+ g_current_net_status = 0;
+ g_cdc_ncm_rx_ntb_length = 0;
+ g_cdc_ncm_tx_ntb_length = 0;
+ g_cdc_ncm_data_alt_active = false;
+ g_cdc_ncm_rx_datagram_pos = 0;
+ g_cdc_ncm_rx_ndp_index = 0;
+ break;
+ case USBD_EVENT_SET_INTERFACE: {
+#ifdef CONFIG_USBDEV_CDC_NCM_USING_LWIP
+ struct usb_interface_descriptor *desc = (struct usb_interface_descriptor *)arg;
+
+ if (desc && desc->bAlternateSetting == 1) {
+ g_cdc_ncm_data_alt_active = true;
+ usbd_cdc_ncm_start_read(g_cdc_ncm_rx_buffer, g_cdc_ncm_ntb_out_max_size);
+ } else {
+ g_cdc_ncm_data_alt_active = false;
+ g_cdc_ncm_tx_ntb_length = 0;
+ }
+#else
+ (void)arg;
+#endif
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void cdc_ncm_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
+{
+ (void)busid;
+ (void)ep;
+
+ g_cdc_ncm_rx_ntb_length = nbytes;
+ g_cdc_ncm_rx_datagram_pos = 0;
+ g_cdc_ncm_rx_ndp_index = 0;
+ usbd_cdc_ncm_data_recv_done(g_cdc_ncm_rx_ntb_length);
+}
+
+static void cdc_ncm_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
+{
+ (void)busid;
+
+ if ((nbytes % usbd_get_ep_mps(0, ep)) == 0 && nbytes) {
+ usbd_ep_start_write(0, ep, NULL, 0);
+ } else {
+ usbd_cdc_ncm_data_send_done(g_cdc_ncm_tx_ntb_length);
+ g_cdc_ncm_tx_ntb_length = 0;
+ }
+}
+
+static void cdc_ncm_int_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
+{
+ (void)busid;
+ (void)ep;
+ (void)nbytes;
+
+ if (g_current_net_status == 2) {
+ g_current_net_status = 3;
+ usbd_cdc_ncm_send_notify(CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE, 0, g_connect_speed_table);
+ } else {
+ g_current_net_status = 0;
+ }
+}
+
+int usbd_cdc_ncm_start_write(uint8_t *buf, uint32_t len)
+{
+ int ret;
+
+ if (!usb_device_is_configured(0) || !g_cdc_ncm_data_alt_active) {
+ return -USB_ERR_NOTCONN;
+ }
+
+ if (g_cdc_ncm_tx_ntb_length > 0) {
+ return -USB_ERR_BUSY;
+ }
+
+ /* mark busy before submitting: the transfer may complete in interrupt
+ context before usbd_ep_start_write() returns */
+ g_cdc_ncm_tx_ntb_length = len;
+ ret = usbd_ep_start_write(0, cdc_ncm_ep_data[CDC_NCM_IN_EP_IDX].ep_addr, buf, len);
+ if (ret != 0) {
+ g_cdc_ncm_tx_ntb_length = 0;
+ return ret;
+ }
+
+ USB_LOG_DBG("txlen:%d\r\n", g_cdc_ncm_tx_ntb_length);
+ return 0;
+}
+
+int usbd_cdc_ncm_start_read(uint8_t *buf, uint32_t len)
+{
+ if (!usb_device_is_configured(0)) {
+ return -USB_ERR_NOTCONN;
+ }
+
+ g_cdc_ncm_rx_data_buffer = buf;
+ g_cdc_ncm_rx_total_length = len;
+ g_cdc_ncm_rx_ntb_length = 0;
+ g_cdc_ncm_rx_datagram_pos = 0;
+ g_cdc_ncm_rx_ndp_index = 0;
+ return usbd_ep_start_read(0, cdc_ncm_ep_data[CDC_NCM_OUT_EP_IDX].ep_addr, buf, len);
+}
+
+#ifdef CONFIG_USBDEV_CDC_NCM_USING_LWIP
+static bool cdc_ncm_check_ndp16(uint16_t ndp_index)
+{
+ struct cdc_ncm_ndp16 *ndp16;
+ uint32_t signature;
+ uint16_t ndp_len;
+
+ if (ndp_index == 0 || (uint32_t)ndp_index + sizeof(struct cdc_ncm_ndp16) > g_cdc_ncm_rx_ntb_length) {
+ return false;
+ }
+
+ ndp16 = (struct cdc_ncm_ndp16 *)&g_cdc_ncm_rx_data_buffer[ndp_index];
+ signature = GET_LE32((uint8_t *)&ndp16->dwSignature);
+ ndp_len = GET_LE16((uint8_t *)&ndp16->wLength);
+
+ if ((signature != CDC_NCM_NDP16_SIGNATURE_NCM0) &&
+ (signature != CDC_NCM_NDP16_SIGNATURE_NCM1)) {
+ USB_LOG_WRN("invalid ncm ndp16 signature\r\n");
+ return false;
+ }
+
+ if (ndp_len < 12 || (uint32_t)ndp_index + ndp_len > g_cdc_ncm_rx_ntb_length) {
+ USB_LOG_WRN("invalid ncm ndp16 len\r\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool cdc_ncm_prepare_rx_ndp(void)
+{
+ struct cdc_ncm_nth16 *nth16;
+ uint16_t ndp_index;
+
+ if (g_cdc_ncm_rx_ntb_length < (CDC_NCM_NTH16_LEN + CDC_NCM_NDP16_LEN)) {
+ return false;
+ }
+
+ nth16 = (struct cdc_ncm_nth16 *)g_cdc_ncm_rx_data_buffer;
+ ndp_index = GET_LE16((uint8_t *)&nth16->wNdpIndex);
+ if (GET_LE32((uint8_t *)&nth16->dwSignature) != CDC_NCM_NTH16_SIGNATURE ||
+ GET_LE16((uint8_t *)&nth16->wHeaderLength) != CDC_NCM_NTH16_LEN ||
+ (uint32_t)GET_LE16((uint8_t *)&nth16->wBlockLength) > g_cdc_ncm_rx_ntb_length ||
+ !cdc_ncm_check_ndp16(ndp_index)) {
+ USB_LOG_WRN("invalid ncm nth16\r\n");
+ return false;
+ }
+
+ g_cdc_ncm_rx_ndp_index = ndp_index;
+ g_cdc_ncm_rx_sequence = GET_LE16((uint8_t *)&nth16->wSequence);
+ (void)g_cdc_ncm_rx_sequence;
+ return true;
+}
+
+struct pbuf *usbd_cdc_ncm_eth_rx(void)
+{
+ struct cdc_ncm_ndp16 *ndp16;
+ uint16_t ndp_len;
+ uint16_t next_ndp_index;
+
+ if (g_cdc_ncm_rx_ntb_length == 0 || g_cdc_ncm_rx_data_buffer == NULL) {
+ return NULL;
+ }
+
+ if (g_cdc_ncm_rx_ndp_index == 0 && !cdc_ncm_prepare_rx_ndp()) {
+ usbd_cdc_ncm_start_read(g_cdc_ncm_rx_buffer, g_cdc_ncm_ntb_out_max_size);
+ return NULL;
+ }
+
+ for (;;) {
+ ndp16 = (struct cdc_ncm_ndp16 *)&g_cdc_ncm_rx_data_buffer[g_cdc_ncm_rx_ndp_index];
+ ndp_len = GET_LE16((uint8_t *)&ndp16->wLength);
+
+ while ((8 + 4 * (g_cdc_ncm_rx_datagram_pos + 1)) <= ndp_len) {
+ struct cdc_ncm_ndp16_datagram *datagram;
+ uint16_t index;
+ uint16_t length;
+ struct pbuf *p;
+
+ datagram = (struct cdc_ncm_ndp16_datagram *)&g_cdc_ncm_rx_data_buffer[g_cdc_ncm_rx_ndp_index + 8 + 4 * g_cdc_ncm_rx_datagram_pos];
+ g_cdc_ncm_rx_datagram_pos++;
+ index = GET_LE16((uint8_t *)&datagram->wDatagramIndex);
+ length = GET_LE16((uint8_t *)&datagram->wDatagramLength);
+
+ if (index == 0 || length == 0) {
+ break;
+ }
+ if ((uint32_t)index + length > g_cdc_ncm_rx_ntb_length || length > g_cdc_ncm_max_datagram_size) {
+ USB_LOG_WRN("invalid ncm datagram index:%u len:%u\r\n", index, length);
+ continue;
+ }
+
+ p = pbuf_alloc(PBUF_RAW, length, PBUF_POOL);
+ if (p == NULL) {
+ USB_LOG_WRN("ncm pbuf alloc failed\r\n");
+ usbd_cdc_ncm_start_read(g_cdc_ncm_rx_buffer, g_cdc_ncm_ntb_out_max_size);
+ return NULL;
+ }
+ if (pbuf_take(p, &g_cdc_ncm_rx_data_buffer[index], length) != ERR_OK) {
+ pbuf_free(p);
+ continue;
+ }
+
+ USB_LOG_DBG("rxlen:%d\r\n", length);
+ return p;
+ }
+
+ /* current ndp is exhausted, follow the ndp chain if present */
+ next_ndp_index = GET_LE16((uint8_t *)&ndp16->wNextNdpIndex);
+ if (next_ndp_index == 0 || !cdc_ncm_check_ndp16(next_ndp_index)) {
+ break;
+ }
+ g_cdc_ncm_rx_ndp_index = next_ndp_index;
+ g_cdc_ncm_rx_datagram_pos = 0;
+ }
+
+ usbd_cdc_ncm_start_read(g_cdc_ncm_rx_buffer, g_cdc_ncm_ntb_out_max_size);
+ return NULL;
+}
+
+int usbd_cdc_ncm_eth_tx(struct pbuf *p)
+{
+ struct cdc_ncm_nth16 *nth16;
+ struct cdc_ncm_ndp16 *ndp16;
+ struct cdc_ncm_ndp16_datagram *datagram;
+ struct pbuf *q;
+ uint8_t *buffer;
+ uint16_t payload_len;
+ uint16_t aligned_payload_len;
+ uint16_t ndp_index;
+ uint16_t block_len;
+
+ if (!usb_device_is_configured(0)) {
+ return -USB_ERR_NOTCONN;
+ }
+
+ if (g_cdc_ncm_tx_ntb_length > 0) {
+ return -USB_ERR_BUSY;
+ }
+
+ if (p->tot_len > g_cdc_ncm_max_datagram_size) {
+ USB_LOG_WRN("ncm tx frame too large:%u\r\n", p->tot_len);
+ return -USB_ERR_INVAL;
+ }
+
+ payload_len = p->tot_len;
+ aligned_payload_len = USB_ALIGN_UP(payload_len, 4);
+ ndp_index = CDC_NCM_DATAGRAM_OFFSET + aligned_payload_len;
+ block_len = ndp_index + CDC_NCM_NDP16_LEN;
+ if (block_len > sizeof(g_cdc_ncm_tx_buffer) || block_len > g_cdc_ncm_ntb_in_max_size) {
+ return -USB_ERR_INVAL;
+ }
+
+ memset(g_cdc_ncm_tx_buffer, 0, block_len);
+ buffer = &g_cdc_ncm_tx_buffer[CDC_NCM_DATAGRAM_OFFSET];
+ for (q = p; q != NULL && payload_len > 0; q = q->next) {
+ uint16_t copy_len = MIN(q->len, payload_len);
+ usb_memcpy(buffer, q->payload, copy_len);
+ buffer += copy_len;
+ payload_len -= copy_len;
+ }
+ payload_len = p->tot_len;
+
+ nth16 = (struct cdc_ncm_nth16 *)&g_cdc_ncm_tx_buffer[0];
+ SET_LE32((uint8_t *)&nth16->dwSignature, CDC_NCM_NTH16_SIGNATURE);
+ SET_LE16((uint8_t *)&nth16->wHeaderLength, CDC_NCM_NTH16_LEN);
+ SET_LE16((uint8_t *)&nth16->wSequence, g_cdc_ncm_tx_sequence++);
+ SET_LE16((uint8_t *)&nth16->wBlockLength, block_len);
+ SET_LE16((uint8_t *)&nth16->wNdpIndex, ndp_index);
+
+ ndp16 = (struct cdc_ncm_ndp16 *)&g_cdc_ncm_tx_buffer[ndp_index];
+ SET_LE32((uint8_t *)&ndp16->dwSignature, CDC_NCM_NDP16_SIGNATURE_NCM0);
+ SET_LE16((uint8_t *)&ndp16->wLength, CDC_NCM_NDP16_LEN);
+ SET_LE16((uint8_t *)&ndp16->wNextNdpIndex, 0);
+
+ datagram = (struct cdc_ncm_ndp16_datagram *)&g_cdc_ncm_tx_buffer[ndp_index + 8];
+ SET_LE16((uint8_t *)&datagram[0].wDatagramIndex, CDC_NCM_DATAGRAM_OFFSET);
+ SET_LE16((uint8_t *)&datagram[0].wDatagramLength, payload_len);
+ datagram[1].wDatagramIndex = 0;
+ datagram[1].wDatagramLength = 0;
+
+ return usbd_cdc_ncm_start_write(g_cdc_ncm_tx_buffer, block_len);
+}
+#endif
+
+struct usbd_interface *usbd_cdc_ncm_init_intf(struct usbd_interface *intf,
+ const uint8_t int_ep,
+ const uint8_t out_ep,
+ const uint8_t in_ep,
+ uint8_t mac[6])
+{
+ memcpy(g_cdc_ncm_mac, mac, 6);
+
+ intf->class_interface_handler = cdc_ncm_class_interface_request_handler;
+ intf->class_endpoint_handler = NULL;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = cdc_ncm_notify_handler;
+
+ /* control and data interfaces share the same endpoints, register them only once */
+ if (!g_cdc_ncm_ep_registered) {
+ g_cdc_ncm_ep_registered = true;
+
+ cdc_ncm_ep_data[CDC_NCM_OUT_EP_IDX].ep_addr = out_ep;
+ cdc_ncm_ep_data[CDC_NCM_OUT_EP_IDX].ep_cb = cdc_ncm_bulk_out;
+ cdc_ncm_ep_data[CDC_NCM_IN_EP_IDX].ep_addr = in_ep;
+ cdc_ncm_ep_data[CDC_NCM_IN_EP_IDX].ep_cb = cdc_ncm_bulk_in;
+ cdc_ncm_ep_data[CDC_NCM_INT_EP_IDX].ep_addr = int_ep;
+ cdc_ncm_ep_data[CDC_NCM_INT_EP_IDX].ep_cb = cdc_ncm_int_in;
+
+ usbd_add_endpoint(0, &cdc_ncm_ep_data[CDC_NCM_OUT_EP_IDX]);
+ usbd_add_endpoint(0, &cdc_ncm_ep_data[CDC_NCM_IN_EP_IDX]);
+ usbd_add_endpoint(0, &cdc_ncm_ep_data[CDC_NCM_INT_EP_IDX]);
+ }
+
+ return intf;
+}
+
+int usbd_cdc_ncm_set_connect(bool connect, uint32_t speed[2])
+{
+ if (!usb_device_is_configured(0)) {
+ return -USB_ERR_NOTCONN;
+ }
+
+ if (connect) {
+ g_current_net_status = 2;
+ if (speed) {
+ memcpy(g_connect_speed_table, speed, 8);
+ }
+ usbd_cdc_ncm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, NULL);
+ } else {
+ g_current_net_status = 1;
+ usbd_cdc_ncm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_DISCONNECTED, NULL);
+ }
+
+ return 0;
+}
+
+__WEAK void usbd_cdc_ncm_data_recv_done(uint32_t len)
+{
+ (void)len;
+}
+
+__WEAK void usbd_cdc_ncm_data_send_done(uint32_t len)
+{
+ (void)len;
+}
diff --git a/class/cdc/usbd_cdc_ncm.h b/class/cdc/usbd_cdc_ncm.h
new file mode 100644
index 00000000..7a232c32
--- /dev/null
+++ b/class/cdc/usbd_cdc_ncm.h
@@ -0,0 +1,41 @@
+/*
+ * CherryUSB CDC NCM device class glue
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBD_CDC_NCM_H
+#define USBD_CDC_NCM_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "usb_cdc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct usbd_interface *usbd_cdc_ncm_init_intf(struct usbd_interface *intf,
+ const uint8_t int_ep,
+ const uint8_t out_ep,
+ const uint8_t in_ep,
+ uint8_t mac[6]);
+
+int usbd_cdc_ncm_set_connect(bool connect, uint32_t speed[2]);
+
+void usbd_cdc_ncm_data_recv_done(uint32_t len);
+void usbd_cdc_ncm_data_send_done(uint32_t len);
+int usbd_cdc_ncm_start_write(uint8_t *buf, uint32_t len);
+int usbd_cdc_ncm_start_read(uint8_t *buf, uint32_t len);
+
+#ifdef CONFIG_USBDEV_CDC_NCM_USING_LWIP
+#include "lwip/netif.h"
+#include "lwip/pbuf.h"
+struct pbuf *usbd_cdc_ncm_eth_rx(void);
+int usbd_cdc_ncm_eth_tx(struct pbuf *p);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBD_CDC_NCM_H */
diff --git a/class/hub/usbh_hub.c b/class/hub/usbh_hub.c
index 5538de86..26601d01 100644
--- a/class/hub/usbh_hub.c
+++ b/class/hub/usbh_hub.c
@@ -731,21 +731,21 @@ int usbh_hub_initialize(struct usbh_bus *bus)
bus->hub_mq = usb_osal_mq_create(7);
if (bus->hub_mq == NULL) {
- USB_LOG_ERR("Failed to create hub mq\r\n");
- return -1;
+ return -USB_ERR_NOMEM;
}
bus->hub_sem = usb_osal_sem_create(0);
if (bus->hub_sem == NULL) {
- USB_LOG_ERR("Failed to create hub sem\r\n");
- return -1;
+ usb_osal_mq_delete(bus->hub_mq);
+ return -USB_ERR_NOMEM;
}
snprintf(thread_name, 32, "usbh_hub%u", bus->busid);
bus->hub_thread = usb_osal_thread_create(thread_name, CONFIG_USBHOST_PSC_STACKSIZE, CONFIG_USBHOST_PSC_PRIO, usbh_hub_thread, bus);
if (bus->hub_thread == NULL) {
- USB_LOG_ERR("Failed to create hub thread\r\n");
- return -1;
+ usb_osal_mq_delete(bus->hub_mq);
+ usb_osal_sem_delete(bus->hub_sem);
+ return -USB_ERR_NOMEM;
}
return 0;
}
@@ -759,6 +759,7 @@ int usbh_hub_deinitialize(struct usbh_bus *bus)
usb_osal_mq_send(bus->hub_mq, (uintptr_t)NULL);
usb_osal_sem_take(bus->hub_sem, USB_OSAL_WAITING_FOREVER);
usb_osal_sem_delete(bus->hub_sem);
+ usb_osal_mq_delete(bus->hub_mq);
bus->hub_mq = NULL;
bus->hub_sem = NULL;
diff --git a/class/msc/usbd_msc.c b/class/msc/usbd_msc.c
index 6523ffb9..58e78c2c 100644
--- a/class/msc/usbd_msc.c
+++ b/class/msc/usbd_msc.c
@@ -103,11 +103,12 @@ void msc_storage_notify_handler(uint8_t busid, uint8_t event, void *arg)
#if defined(CONFIG_USBDEV_MSC_THREAD)
g_usbd_msc[busid].usbd_msc_mq = usb_osal_mq_create(1);
if (g_usbd_msc[busid].usbd_msc_mq == NULL) {
- USB_LOG_ERR("No memory to alloc for g_usbd_msc[busid].usbd_msc_mq\r\n");
+ return;
}
g_usbd_msc[busid].usbd_msc_thread = usb_osal_thread_create("usbd_msc", CONFIG_USBDEV_MSC_STACKSIZE, CONFIG_USBDEV_MSC_PRIO, usbdev_msc_thread, (void *)(uint32_t)busid);
if (g_usbd_msc[busid].usbd_msc_thread == NULL) {
- USB_LOG_ERR("No memory to alloc for g_usbd_msc[busid].usbd_msc_thread\r\n");
+ usb_osal_mq_delete(g_usbd_msc[busid].usbd_msc_mq);
+ return;
}
#elif defined(CONFIG_USBDEV_MSC_POLLING)
g_usbd_msc[busid].event = 0;
@@ -504,6 +505,39 @@ static bool SCSI_readCapacity10(uint8_t busid, uint8_t **data, uint32_t *len)
return true;
}
+static bool SCSI_readCapacity16(uint8_t busid, uint8_t **data, uint32_t *len)
+{
+ uint8_t capacity16[32];
+ uint32_t last_lba;
+ uint32_t blk_size;
+
+ /* service action 0x10 = READ CAPACITY(16); other service actions unsupported */
+ if ((g_usbd_msc[busid].cbw.dDataLength == 0U) ||
+ ((g_usbd_msc[busid].cbw.CB[1] & 0x1fU) != 0x10U)) {
+ SCSI_SetSenseData(busid, SCSI_KCQIR_INVALIDCOMMAND);
+ return false;
+ }
+
+ last_lba = g_usbd_msc[busid].scsi_blk_nbr[g_usbd_msc[busid].cbw.bLUN] - 1U;
+ blk_size = g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN];
+
+ memset(capacity16, 0, sizeof(capacity16));
+ /* 8-byte LBA (project volume < 4G, upper 4 bytes stay 0) */
+ capacity16[4] = (uint8_t)((last_lba >> 24) & 0xff);
+ capacity16[5] = (uint8_t)((last_lba >> 16) & 0xff);
+ capacity16[6] = (uint8_t)((last_lba >> 8) & 0xff);
+ capacity16[7] = (uint8_t)(last_lba & 0xff);
+ /* 4-byte block length */
+ capacity16[8] = (uint8_t)((blk_size >> 24) & 0xff);
+ capacity16[9] = (uint8_t)((blk_size >> 16) & 0xff);
+ capacity16[10] = (uint8_t)((blk_size >> 8) & 0xff);
+ capacity16[11] = (uint8_t)(blk_size & 0xff);
+
+ memcpy(*data, (uint8_t *)capacity16, sizeof(capacity16));
+ *len = sizeof(capacity16);
+ return true;
+}
+
static bool SCSI_read10(uint8_t busid, uint8_t **data, uint32_t *len)
{
(void)data;
@@ -727,6 +761,9 @@ static bool SCSI_CBWDecode(uint8_t busid, uint32_t nbytes)
case SCSI_CMD_READCAPACITY10:
ret = SCSI_readCapacity10(busid, &buf2send, &len2send);
break;
+ case SCSI_CMD_READCAPACITY16:
+ ret = SCSI_readCapacity16(busid, &buf2send, &len2send);
+ break;
case SCSI_CMD_READ10:
ret = SCSI_read10(busid, NULL, 0);
break;
diff --git a/class/serial/usbh_serial.c b/class/serial/usbh_serial.c
index 66aa51fc..5cc96fd1 100644
--- a/class/serial/usbh_serial.c
+++ b/class/serial/usbh_serial.c
@@ -19,7 +19,9 @@ static struct usbh_serial g_serial_class[CONFIG_USBHOST_MAX_SERIAL_CLASS];
static uint32_t g_devinuse = 0;
static uint32_t g_cdcacm_devinuse = 0;
-USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_serial_iobuffer[CONFIG_USBHOST_MAX_SERIAL_CLASS][USB_ALIGN_UP((USBH_SERIAL_RX2_NOCACHE_OFFSET + USBH_SERIAL_RX2_NOCACHE_SIZE), CONFIG_USB_ALIGN_SIZE)];
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_serial_iobuffer[CONFIG_USBHOST_MAX_SERIAL_CLASS][USB_ALIGN_UP((USBH_SERIAL_RX2_NOCACHE_OFFSET + CONFIG_USBHOST_SERIAL_BULKIN_SIZE), CONFIG_USB_ALIGN_SIZE)];
+
+static void usbh_serial_callback(void *arg, int nbytes);
static struct usbh_serial *usbh_serial_alloc(bool is_cdcacm)
{
@@ -35,6 +37,11 @@ static struct usbh_serial *usbh_serial_alloc(bool is_cdcacm)
g_serial_class[devno].iobuffer = g_serial_iobuffer[devno];
g_serial_class[devno].rx_complete_sem = usb_osal_sem_create(0);
+ if (g_serial_class[devno].rx_complete_sem == NULL) {
+ g_devinuse &= ~(1U << devno);
+ return NULL;
+ }
+
if (is_cdcacm) {
for (devno2 = 0; devno2 < CONFIG_USBHOST_MAX_SERIAL_CLASS; devno2++) {
if ((g_cdcacm_devinuse & (1U << devno2)) == 0) {
@@ -67,15 +74,28 @@ static void usbh_serial_free(struct usbh_serial *serial)
if (g_serial_class[devno].rx_complete_sem) {
usb_osal_sem_delete(g_serial_class[devno].rx_complete_sem);
+ g_serial_class[devno].rx_complete_sem = NULL;
}
}
+static int usbh_serial_rx_restart(struct usbh_serial *serial)
+{
+ /* resubmit the read urb */
+ serial->rx_errorcode = 0;
+ usbh_bulk_urb_fill(&serial->bulkin_urb, serial->hport, serial->bulkin, &serial->iobuffer[serial->rx_buf_index ? USBH_SERIAL_RX2_NOCACHE_OFFSET : USBH_SERIAL_RX_NOCACHE_OFFSET], CONFIG_USBHOST_SERIAL_BULKIN_SIZE,
+ 0, usbh_serial_callback, serial);
+ return usbh_submit_urb(&serial->bulkin_urb);
+}
+
static void usbh_serial_callback(void *arg, int nbytes)
{
struct usbh_serial *serial = (struct usbh_serial *)arg;
- uint32_t free_space;
+ uint8_t prev_buf_index;
int ret;
+ if (!serial)
+ return;
+
if (nbytes < 0) {
if (nbytes != -USB_ERR_SHUTDOWN) {
USB_LOG_ERR("serial transfer error: %d\n", nbytes);
@@ -93,30 +113,29 @@ static void usbh_serial_callback(void *arg, int nbytes)
}
if (nbytes >= serial->driver->ignore_rx_header) {
- /* resubmit the read urb */
- usbh_bulk_urb_fill(&serial->bulkin_urb, serial->hport, serial->bulkin, &serial->iobuffer[serial->rx_buf_index ? USBH_SERIAL_RX_NOCACHE_OFFSET : USBH_SERIAL_RX2_NOCACHE_OFFSET], serial->bulkin->wMaxPacketSize,
- 0, usbh_serial_callback, serial);
- ret = usbh_submit_urb(&serial->bulkin_urb);
- if (ret < 0) {
- USB_LOG_ERR("serial submit failed: %d\n", ret);
- serial->rx_errorcode = ret;
- usb_osal_sem_give(serial->rx_complete_sem);
- return;
- }
-
- free_space = usb_ringbuffer_get_free(&serial->rx_rb);
- if (free_space < (nbytes - serial->driver->ignore_rx_header)) {
- USB_LOG_ERR("serial rx ringbuffer overflow, free space: %u, rx size: %u\n", free_space, (nbytes - serial->driver->ignore_rx_header));
+ prev_buf_index = serial->rx_buf_index;
+ serial->rx_buf_index ^= 1;
+ // next ringbuffer write will cause overflow, so don't resubmit the read urb
+ if (((nbytes - serial->driver->ignore_rx_header) + CONFIG_USBHOST_SERIAL_BULKIN_SIZE) > usb_ringbuffer_get_free(&serial->rx_rb)) {
+ serial->rx_pending = true;
+ } else {
+ serial->rx_pending = false;
+ ret = usbh_serial_rx_restart(serial);
+ if (ret < 0) {
+ USB_LOG_ERR("serial submit failed: %d\n", ret);
+ serial->rx_errorcode = ret;
+ usb_osal_sem_give(serial->rx_complete_sem);
+ return;
+ }
}
usb_ringbuffer_write(&serial->rx_rb,
- &serial->iobuffer[(serial->rx_buf_index ? USBH_SERIAL_RX2_NOCACHE_OFFSET : USBH_SERIAL_RX_NOCACHE_OFFSET) + serial->driver->ignore_rx_header],
+ &serial->iobuffer[(prev_buf_index ? USBH_SERIAL_RX2_NOCACHE_OFFSET : USBH_SERIAL_RX_NOCACHE_OFFSET) + serial->driver->ignore_rx_header],
(nbytes - serial->driver->ignore_rx_header));
if (serial->rx_complete_callback) {
serial->rx_complete_callback(serial, nbytes - serial->driver->ignore_rx_header);
}
- serial->rx_buf_index ^= 1;
serial->rx_errorcode = 0;
usb_osal_sem_give(serial->rx_complete_sem);
}
@@ -375,10 +394,7 @@ int usbh_serial_control(struct usbh_serial *serial, int cmd, void *arg)
usb_ringbuffer_reset(&serial->rx_rb);
usb_osal_sem_reset(serial->rx_complete_sem);
serial->rx_buf_index = 0;
- usbh_bulk_urb_fill(&serial->bulkin_urb, serial->hport, serial->bulkin, &serial->iobuffer[serial->rx_buf_index ? USBH_SERIAL_RX2_NOCACHE_OFFSET : USBH_SERIAL_RX_NOCACHE_OFFSET], serial->bulkin->wMaxPacketSize,
- 0, usbh_serial_callback, serial);
- ret = usbh_submit_urb(&serial->bulkin_urb);
-
+ ret = usbh_serial_rx_restart(serial);
return ret;
} break;
case USBH_SERIAL_CMD_GET_ATTR: {
@@ -480,6 +496,14 @@ int usbh_serial_read(struct usbh_serial *serial, void *buffer, uint32_t buflen)
return -USB_ERR_NODEV;
}
+ if (usb_ringbuffer_get_free(&serial->rx_rb) >= CONFIG_USBHOST_SERIAL_BULKIN_SIZE && serial->rx_pending) {
+ serial->rx_pending = false;
+ ret = usbh_serial_rx_restart(serial);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
if (serial->open_flags & USBH_SERIAL_O_NONBLOCK) {
return usb_ringbuffer_read(&serial->rx_rb, buffer, buflen);
} else {
diff --git a/class/serial/usbh_serial.h b/class/serial/usbh_serial.h
index 06a31709..17d0bd43 100644
--- a/class/serial/usbh_serial.h
+++ b/class/serial/usbh_serial.h
@@ -9,14 +9,20 @@
#include "usb_cdc.h"
+#ifndef CONFIG_USBHOST_SERIAL_BULKIN_SIZE
+#define CONFIG_USBHOST_SERIAL_BULKIN_SIZE 512
+#endif
+
#define USBH_SERIAL_CTRL_NOCACHE_OFFSET 0
#define USBH_SERIAL_CTRL_NOCACHE_SIZE 32
#define USBH_SERIAL_INT_NOCACHE_OFFSET USB_ALIGN_UP(USBH_SERIAL_CTRL_NOCACHE_SIZE, CONFIG_USB_ALIGN_SIZE)
#define USBH_SERIAL_INT_NOCACHE_SIZE 32
#define USBH_SERIAL_RX_NOCACHE_OFFSET USB_ALIGN_UP((USBH_SERIAL_INT_NOCACHE_OFFSET + USBH_SERIAL_INT_NOCACHE_SIZE), CONFIG_USB_ALIGN_SIZE)
-#define USBH_SERIAL_RX_NOCACHE_SIZE 512
-#define USBH_SERIAL_RX2_NOCACHE_OFFSET USB_ALIGN_UP((USBH_SERIAL_RX_NOCACHE_OFFSET + USBH_SERIAL_RX_NOCACHE_SIZE), CONFIG_USB_ALIGN_SIZE)
-#define USBH_SERIAL_RX2_NOCACHE_SIZE 512
+#define USBH_SERIAL_RX2_NOCACHE_OFFSET USB_ALIGN_UP((USBH_SERIAL_RX_NOCACHE_OFFSET + CONFIG_USBHOST_SERIAL_BULKIN_SIZE), CONFIG_USB_ALIGN_SIZE)
+
+#if CONFIG_USBHOST_SERIAL_RX_SIZE < CONFIG_USBHOST_SERIAL_BULKIN_SIZE
+#error "CONFIG_USBHOST_SERIAL_RX_SIZE must be greater than or equal to CONFIG_USBHOST_SERIAL_BULKIN_SIZE"
+#endif
#define USBH_SERIAL_DATABITS_5 5
#define USBH_SERIAL_DATABITS_6 6
@@ -142,6 +148,7 @@ struct usbh_serial {
uint8_t rx_buf_index;
int rx_errorcode;
usbh_serial_rx_complete_callback_t rx_complete_callback;
+ bool rx_pending;
void *priv; /* Private Data */
void *user_data; /* User Data */
diff --git a/common/usb_util.h b/common/usb_util.h
index 521bf27f..97e0917c 100644
--- a/common/usb_util.h
+++ b/common/usb_util.h
@@ -185,6 +185,26 @@
(field)[3] = (uint8_t)((value) >> 0); \
} while (0)
+#define GET_LE16(field) \
+ (((uint16_t)(field)[0] << 0) | ((uint16_t)(field)[1] << 8))
+
+#define GET_LE32(field) \
+ (((uint32_t)(field)[0] << 0) | ((uint32_t)(field)[1] << 8) | ((uint32_t)(field)[2] << 16) | ((uint32_t)(field)[3] << 24))
+
+#define SET_LE16(field, value) \
+ do { \
+ (field)[0] = (uint8_t)((value) >> 0); \
+ (field)[1] = (uint8_t)((value) >> 8); \
+ } while (0)
+
+#define SET_LE32(field, value) \
+ do { \
+ (field)[0] = (uint8_t)((value) >> 0); \
+ (field)[1] = (uint8_t)((value) >> 8); \
+ (field)[2] = (uint8_t)((value) >> 16); \
+ (field)[3] = (uint8_t)((value) >> 24); \
+ } while (0)
+
#define WBVAL(x) ((x) & 0xFF), (((x) >> 8) & 0xFF)
#define DBVAL(x) ((x) & 0xFF), (((x) >> 8) & 0xFF), (((x) >> 16) & 0xFF), (((x) >> 24) & 0xFF)
diff --git a/core/usbd_core.c b/core/usbd_core.c
index a9ad67c4..d88fc5f4 100644
--- a/core/usbd_core.c
+++ b/core/usbd_core.c
@@ -1351,14 +1351,12 @@ int usbd_initialize(uint8_t busid, uintptr_t reg_base, void (*event_handler)(uin
#ifdef CONFIG_USBDEV_EP0_THREAD
g_usbd_core[busid].usbd_ep0_mq = usb_osal_mq_create(1);
if (g_usbd_core[busid].usbd_ep0_mq == NULL) {
- USB_LOG_ERR("No memory to alloc for g_usbd_core[busid].usbd_ep0_mq\r\n");
- while (1) {
- }
+ return -USB_ERR_NOMEM;
}
g_usbd_core[busid].usbd_ep0_thread = usb_osal_thread_create("usbd_ep0", CONFIG_USBDEV_EP0_STACKSIZE, CONFIG_USBDEV_EP0_PRIO, usbdev_ep0_thread, (void *)(uint32_t)busid);
if (g_usbd_core[busid].usbd_ep0_thread == NULL) {
- USB_LOG_ERR("No memory to alloc for g_usbd_core[busid].usbd_ep0_thread\r\n");
- while (1) {
+ usb_osal_mq_delete(g_usbd_core[busid].usbd_ep0_mq);
+ return -USB_ERR_NOMEM;
}
}
#endif
diff --git a/core/usbh_core.c b/core/usbh_core.c
index b7f762ea..8aa92588 100644
--- a/core/usbh_core.c
+++ b/core/usbh_core.c
@@ -242,7 +242,7 @@ static int parse_config_descriptor(struct usbh_hubport *hport, struct usb_config
return -USB_ERR_NOMEM;
}
- if (cur_ep_num >= CONFIG_USBHOST_MAX_ENDPOINTS) {
+ if (cur_ep_num > CONFIG_USBHOST_MAX_ENDPOINTS) {
USB_LOG_ERR("Endpoint num %d overflow\r\n", cur_ep_num);
return -USB_ERR_NOMEM;
}
@@ -272,6 +272,12 @@ static int parse_config_descriptor(struct usbh_hubport *hport, struct usb_config
return -USB_ERR_INVAL;
}
ep_desc = (struct usb_endpoint_descriptor *)p;
+
+ if (cur_ep >= CONFIG_USBHOST_MAX_ENDPOINTS) {
+ USB_LOG_ERR("Endpoint num %d overflow\r\n", cur_ep);
+ return -USB_ERR_NOMEM;
+ }
+
memcpy(&hport->config.intf[cur_iface].altsetting[cur_alt_setting].ep[cur_ep].ep_desc, ep_desc, 7);
cur_ep++;
break;
diff --git a/core/usbotg_core.c b/core/usbotg_core.c
index 54237e69..cf83f53a 100644
--- a/core/usbotg_core.c
+++ b/core/usbotg_core.c
@@ -88,17 +88,14 @@ int usbotg_initialize(uint8_t busid, uint32_t reg_base, usbd_event_handler_t dev
g_usbotg_core[busid].change_sem = usb_osal_sem_create(0);
if (g_usbotg_core[busid].change_sem == NULL) {
- USB_LOG_ERR("Failed to create change_sem\r\n");
- while (1) {
- }
+ return -USB_ERR_NOMEM;
}
snprintf(thread_name, 32, "usbotg%u", busid);
g_usbotg_core[busid].change_thread = usb_osal_thread_create(thread_name, 2048, 10, usbotg_rolechange_thread, (void *)(uintptr_t)busid);
if (g_usbotg_core[busid].change_thread == NULL) {
- USB_LOG_ERR("Failed to create usbotg thread\r\n");
- while (1) {
- }
+ usb_osal_sem_delete(g_usbotg_core[busid].change_sem);
+ return -USB_ERR_NOMEM;
}
usbotg_trigger_role_change(busid, default_role);
diff --git a/demo/cdc_ncm_template.c b/demo/cdc_ncm_template.c
new file mode 100644
index 00000000..17327716
--- /dev/null
+++ b/demo/cdc_ncm_template.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2026, 54fire
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+#include "usbd_cdc_ncm.h"
+
+#ifndef CONFIG_USBDEV_CDC_NCM_USING_LWIP
+#error "Please enable CONFIG_USBDEV_CDC_NCM_USING_LWIP for this demo"
+#endif
+
+/*!< endpoint address */
+#define CDC_IN_EP 0x81
+#define CDC_OUT_EP 0x02
+#define CDC_INT_EP 0x83
+
+#define USBD_VID 0xFFFF
+#define USBD_PID 0xFFFF
+#define USBD_MAX_POWER 100
+#define USBD_LANGID_STRING 1033
+
+/*!< config descriptor size */
+#define USB_CONFIG_SIZE (9 + CDC_NCM_DESCRIPTOR_LEN)
+
+#ifdef CONFIG_USB_HS
+#define CDC_MAX_MPS 512
+#else
+#define CDC_MAX_MPS 64
+#endif
+
+/* str idx = 4 is for mac address: aa:bb:cc:dd:ee:ff*/
+#define CDC_NCM_MAC_STRING_INDEX 4
+
+static const uint8_t device_descriptor[] = {
+ USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0xEF, 0x02, 0x01, USBD_VID, USBD_PID, 0x0100, 0x01)
+};
+
+static const uint8_t config_descriptor[] = {
+ USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x02, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
+ CDC_NCM_DESCRIPTOR_INIT(0x00, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, CDC_MAX_MPS, CDC_NCM_MAC_STRING_INDEX)
+};
+
+static const uint8_t device_quality_descriptor[] = {
+ ///////////////////////////////////////
+ /// device qualifier descriptor
+ ///////////////////////////////////////
+ 0x0a,
+ USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER,
+ 0x00,
+ 0x02,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x40,
+ 0x00,
+ 0x00,
+};
+
+static const char *string_descriptors[] = {
+ (const char[]){ 0x09, 0x04 }, /* Langid */
+ "CherryUSB", /* Manufacturer */
+ "CherryUSB CDC NCM DEMO", /* Product */
+ "2026123456", /* Serial Number */
+ "aabbccddeeff", /* ncm mac address */
+};
+
+static const uint8_t *device_descriptor_callback(uint8_t speed)
+{
+ return device_descriptor;
+}
+
+static const uint8_t *config_descriptor_callback(uint8_t speed)
+{
+ return config_descriptor;
+}
+
+static const uint8_t *device_quality_descriptor_callback(uint8_t speed)
+{
+ return device_quality_descriptor;
+}
+
+static const char *string_descriptor_callback(uint8_t speed, uint8_t index)
+{
+ if (index >= (sizeof(string_descriptors) / sizeof(char *))) {
+ return NULL;
+ }
+ return string_descriptors[index];
+}
+
+const struct usb_descriptor cdc_ncm_descriptor = {
+ .device_descriptor_callback = device_descriptor_callback,
+ .config_descriptor_callback = config_descriptor_callback,
+ .device_quality_descriptor_callback = device_quality_descriptor_callback,
+ .string_descriptor_callback = string_descriptor_callback
+};
+
+uint8_t mac[6] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
+
+#ifdef RT_USING_LWIP
+
+#ifndef RT_LWIP_DHCP
+#error cdc_ncm must enable RT_LWIP_DHCP
+#endif
+
+#ifndef LWIP_USING_DHCPD
+#error cdc_ncm must enable LWIP_USING_DHCPD
+#endif
+
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <netif/ethernetif.h>
+#include <dhcp_server.h>
+
+#define CDC_NCM_TX_TIMEOUT_MS 1000
+
+struct eth_device cdc_ncm_dev;
+static struct rt_semaphore cdc_ncm_tx_sem;
+
+void usbd_cdc_ncm_data_send_done(uint32_t len)
+{
+ (void)len;
+ rt_sem_release(&cdc_ncm_tx_sem);
+}
+
+static rt_err_t rt_usbd_cdc_ncm_control(rt_device_t dev, int cmd, void *args)
+{
+ switch (cmd) {
+ case NIOCTL_GADDR:
+
+ /* get mac address */
+ if (args) {
+ uint8_t *mac_dev = (uint8_t *)args;
+ rt_memcpy(mac_dev, mac, 6);
+ mac_dev[5] = ~mac_dev[5]; /* device mac can't same as host. */
+ } else
+ return -RT_ERROR;
+
+ break;
+
+ default:
+ break;
+ }
+
+ return RT_EOK;
+}
+
+struct pbuf *rt_usbd_cdc_ncm_eth_rx(rt_device_t dev)
+{
+ return usbd_cdc_ncm_eth_rx();
+}
+
+rt_err_t rt_usbd_cdc_ncm_eth_tx(rt_device_t dev, struct pbuf *p)
+{
+ int ret;
+
+ ret = usbd_cdc_ncm_eth_tx(p);
+ if (ret == 0) {
+ if (rt_sem_take(&cdc_ncm_tx_sem, rt_tick_from_millisecond(CDC_NCM_TX_TIMEOUT_MS)) != RT_EOK) {
+ return -RT_ETIMEOUT;
+ }
+ return RT_EOK;
+ } else
+ return -RT_ERROR;
+}
+
+void cdc_ncm_lwip_init(void)
+{
+ rt_sem_init(&cdc_ncm_tx_sem, "ncm_tx", 0, RT_IPC_FLAG_PRIO);
+
+ cdc_ncm_dev.parent.control = rt_usbd_cdc_ncm_control;
+ cdc_ncm_dev.eth_rx = rt_usbd_cdc_ncm_eth_rx;
+ cdc_ncm_dev.eth_tx = rt_usbd_cdc_ncm_eth_tx;
+
+ eth_device_init(&cdc_ncm_dev, "u0");
+
+ eth_device_linkchange(&cdc_ncm_dev, RT_TRUE);
+ dhcpd_start("u0");
+}
+
+void usbd_cdc_ncm_data_recv_done(uint32_t len)
+{
+ eth_device_ready(&cdc_ncm_dev);
+}
+
+#else
+#include "netif/etharp.h"
+#include "lwip/init.h"
+#include "lwip/netif.h"
+#include "lwip/pbuf.h"
+
+#include "dhserver.h"
+#include "dnserver.h"
+
+/* rough bounded wait for the polled demo; in a real application use an os semaphore with timeout */
+#define CDC_NCM_TX_TIMEOUT_LOOPS 1000000UL
+
+volatile bool cdc_ncm_tx_done = false;
+
+void usbd_cdc_ncm_data_send_done(uint32_t len)
+{
+ (void)len;
+ cdc_ncm_tx_done = true;
+}
+
+/*Static IP ADDRESS: IP_ADDR0.IP_ADDR1.IP_ADDR2.IP_ADDR3 */
+#define IP_ADDR0 (uint8_t)192
+#define IP_ADDR1 (uint8_t)168
+#define IP_ADDR2 (uint8_t)7
+#define IP_ADDR3 (uint8_t)1
+
+/*NETMASK*/
+#define NETMASK_ADDR0 (uint8_t)255
+#define NETMASK_ADDR1 (uint8_t)255
+#define NETMASK_ADDR2 (uint8_t)255
+#define NETMASK_ADDR3 (uint8_t)0
+
+/*Gateway Address*/
+#define GW_ADDR0 (uint8_t)0
+#define GW_ADDR1 (uint8_t)0
+#define GW_ADDR2 (uint8_t)0
+#define GW_ADDR3 (uint8_t)0
+
+const ip_addr_t ipaddr = IPADDR4_INIT_BYTES(IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
+const ip_addr_t netmask = IPADDR4_INIT_BYTES(NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
+const ip_addr_t gateway = IPADDR4_INIT_BYTES(GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
+
+#define NUM_DHCP_ENTRY 3
+
+static dhcp_entry_t entries[NUM_DHCP_ENTRY] = {
+ /* mac ip address subnet mask lease time */
+ { { 0 }, { 192, 168, 7, 2 }, { 255, 255, 255, 0 }, 24 * 60 * 60 },
+ { { 0 }, { 192, 168, 7, 3 }, { 255, 255, 255, 0 }, 24 * 60 * 60 },
+ { { 0 }, { 192, 168, 7, 4 }, { 255, 255, 255, 0 }, 24 * 60 * 60 }
+};
+
+static dhcp_config_t dhcp_config = {
+ { 192, 168, 7, 1 }, /* server address */
+ 67, /* port */
+ { 192, 168, 7, 1 }, /* dns server */
+ "cherry", /* dns suffix */
+ NUM_DHCP_ENTRY, /* num entry */
+ entries /* entries */
+};
+
+static bool dns_query_proc(const char *name, ip_addr_t *addr)
+{
+ if (strcmp(name, "cdc_ncm.cherry") == 0 || strcmp(name, "www.cdc_ncm.cherry") == 0) {
+ addr->addr = ipaddr.addr;
+ return true;
+ }
+ return false;
+}
+
+static struct netif cdc_ncm_netif; //network interface
+
+/* Network interface name */
+#define IFNAME0 'N'
+#define IFNAME1 'X'
+
+err_t linkoutput_fn(struct netif *netif, struct pbuf *p)
+{
+ int ret;
+ uint32_t timeout = CDC_NCM_TX_TIMEOUT_LOOPS;
+
+ cdc_ncm_tx_done = false;
+ ret = usbd_cdc_ncm_eth_tx(p);
+ if (ret == 0) {
+ while (!cdc_ncm_tx_done) {
+ if (--timeout == 0) {
+ return ERR_TIMEOUT;
+ }
+ }
+ return ERR_OK;
+ } else
+ return ERR_BUF;
+}
+
+err_t cdc_ncm_if_init(struct netif *netif)
+{
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+
+ netif->mtu = 1500;
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP;
+ netif->state = NULL;
+ netif->name[0] = IFNAME0;
+ netif->name[1] = IFNAME1;
+ netif->output = etharp_output;
+ netif->linkoutput = linkoutput_fn;
+ return ERR_OK;
+}
+
+err_t cdc_ncm_if_input(struct netif *netif)
+{
+ err_t err;
+ struct pbuf *p;
+
+ p = usbd_cdc_ncm_eth_rx();
+ if (p != NULL) {
+ err = netif->input(p, netif);
+ if (err != ERR_OK) {
+ pbuf_free(p);
+ }
+ } else {
+ return ERR_BUF;
+ }
+ return err;
+}
+
+void cdc_ncm_lwip_init(void)
+{
+ struct netif *netif = &cdc_ncm_netif;
+
+ lwip_init();
+
+ netif->hwaddr_len = 6;
+ memcpy(netif->hwaddr, mac, 6);
+ netif->hwaddr[5] = ~netif->hwaddr[5]; /* device mac can't same as host. */
+
+ netif = netif_add(netif, &ipaddr, &netmask, &gateway, NULL, cdc_ncm_if_init, netif_input);
+ netif_set_default(netif);
+ while (!netif_is_up(netif)) {
+ }
+
+ while (dhserv_init(&dhcp_config)) {
+ }
+
+ while (dnserv_init(IP_ADDR_ANY, 53, dns_query_proc)) {
+ }
+}
+
+void usbd_cdc_ncm_data_recv_done(uint32_t len)
+{
+}
+
+void cdc_ncm_input_poll(void)
+{
+ cdc_ncm_if_input(&cdc_ncm_netif);
+}
+#endif
+
+static void usbd_event_handler(uint8_t busid, uint8_t event)
+{
+ switch (event) {
+ case USBD_EVENT_RESET:
+ break;
+ case USBD_EVENT_CONNECTED:
+ break;
+ case USBD_EVENT_DISCONNECTED:
+ break;
+ case USBD_EVENT_RESUME:
+ break;
+ case USBD_EVENT_SUSPEND:
+ break;
+ case USBD_EVENT_CONFIGURED:
+ break;
+ case USBD_EVENT_SET_REMOTE_WAKEUP:
+ break;
+ case USBD_EVENT_CLR_REMOTE_WAKEUP:
+ break;
+
+ default:
+ break;
+ }
+}
+
+struct usbd_interface intf0;
+struct usbd_interface intf1;
+
+/* ncm is supported in linux, windows and macos. In linux, you should input the following command
+ *
+ * sudo ifconfig enxaabbccddeeff up
+ * sudo dhclient enxaabbccddeeff
+*/
+void cdc_ncm_init(uint8_t busid, uintptr_t reg_base)
+{
+ cdc_ncm_lwip_init();
+
+ usbd_desc_register(busid, &cdc_ncm_descriptor);
+
+ usbd_add_interface(busid, usbd_cdc_ncm_init_intf(&intf0, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, mac));
+ usbd_add_interface(busid, usbd_cdc_ncm_init_intf(&intf1, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, mac));
+ usbd_initialize(busid, reg_base, usbd_event_handler);
+}
diff --git a/osal/usb_osal_zephyr.c b/osal/usb_osal_zephyr.c
index fe2dc641..6b2a0fa4 100644
--- a/osal/usb_osal_zephyr.c
+++ b/osal/usb_osal_zephyr.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, sakumisu
+ * Copyright (c) 2025-2026 sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -48,8 +48,12 @@ usb_osal_thread_t usb_osal_thread_create(const char *name, uint32_t stack_size,
static void release_thread_handler(struct k_work *work)
{
struct release_thread_work *release_work = (struct release_thread_work *)work;
- k_free(release_work->thread);
- k_work_cancel(work);
+
+ /* The workqueue may preempt the thread that queued this work. Wait until
+ * k_thread_abort() has completed before freeing its TCB and stack. */
+ if (k_thread_join(release_work->thread, K_FOREVER) == 0) {
+ k_free(release_work->thread);
+ }
k_free(release_work);
}
@@ -63,6 +67,10 @@ void usb_osal_thread_delete(usb_osal_thread_t thread)
thread = z_current_get();
#endif
release_work = k_malloc(sizeof(struct release_thread_work));
+ if (release_work == NULL) {
+ k_thread_abort(thread);
+ return;
+ }
release_work->thread = thread;
k_work_init(&release_work->work, release_thread_handler);
k_work_submit(&release_work->work);
@@ -309,4 +317,4 @@ void *usb_osal_malloc(size_t size)
void usb_osal_free(void *ptr)
{
k_free(ptr);
-} \ No newline at end of file
+}
diff --git a/platform/daplink/dap_main.c b/platform/daplink/dap_main.c
index 27e0355b..b145c223 100644
--- a/platform/daplink/dap_main.c
+++ b/platform/daplink/dap_main.c
@@ -335,17 +335,16 @@ static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t USB_Request[DAP_PACKET_COU
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t USB_Response[DAP_PACKET_COUNT][DAP_PACKET_SIZE]; // Response Buffer
static uint16_t USB_RespSize[DAP_PACKET_COUNT]; // Response Size
-volatile struct cdc_line_coding g_cdc_lincoding;
-volatile uint8_t config_uart = 0;
-volatile uint8_t config_uart_transfer = 0;
-
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t uartrx_ringbuffer[CONFIG_UARTRX_RINGBUF_SIZE];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t usbrx_ringbuffer[CONFIG_USBRX_RINGBUF_SIZE];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t usb_tmpbuffer[DAP_PACKET_SIZE];
+static volatile struct cdc_line_coding g_cdc_lincoding;
+static volatile uint8_t config_uart = 0;
+static volatile uint8_t config_uart_transfer = 0;
static volatile uint8_t usbrx_idle_flag = 0;
-static volatile uint8_t usbtx_idle_flag = 0;
-static volatile uint8_t uarttx_idle_flag = 0;
+static volatile uint8_t usbtx_idle_flag = 1;
+static volatile uint8_t uarttx_idle_flag = 1;
USB_NOCACHE_RAM_SECTION chry_ringbuffer_t g_uartrx;
USB_NOCACHE_RAM_SECTION chry_ringbuffer_t g_usbrx;
@@ -356,9 +355,6 @@ void usbd_event_handler(uint8_t busid, uint8_t event)
switch (event) {
case USBD_EVENT_RESET:
usbrx_idle_flag = 0;
- usbtx_idle_flag = 0;
- uarttx_idle_flag = 0;
- config_uart_transfer = 0;
break;
case USBD_EVENT_CONNECTED:
break;
@@ -437,20 +433,13 @@ void usbd_cdc_acm_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
void usbd_cdc_acm_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
(void)busid;
- uint32_t size;
- uint8_t *buffer;
chry_ringbuffer_linear_read_done(&g_uartrx, nbytes);
if ((nbytes % DAP_PACKET_SIZE) == 0 && nbytes) {
/* send zlp */
usbd_ep_start_write(0, CDC_IN_EP, NULL, 0);
} else {
- if (chry_ringbuffer_get_used(&g_uartrx)) {
- buffer = chry_ringbuffer_linear_read_setup(&g_uartrx, &size);
- usbd_ep_start_write(0, CDC_IN_EP, buffer, size);
- } else {
- usbtx_idle_flag = 1;
- }
+ usbtx_idle_flag = 1;
}
}
@@ -624,7 +613,8 @@ void chry_dap_handle(void)
void usbd_cdc_acm_set_line_coding(uint8_t busid, uint8_t intf, struct cdc_line_coding *line_coding)
{
(void)busid;
- if (memcmp(line_coding, (uint8_t *)&g_cdc_lincoding, sizeof(struct cdc_line_coding)) != 0) {
+
+ if (memcmp((uint8_t *)&g_cdc_lincoding, line_coding, sizeof(struct cdc_line_coding)) != 0 && line_coding->dwDTERate != 0) {
memcpy((uint8_t *)&g_cdc_lincoding, line_coding, sizeof(struct cdc_line_coding));
config_uart = 1;
config_uart_transfer = 0;
@@ -642,6 +632,10 @@ void chry_dap_usb2uart_handle(void)
uint32_t size;
uint8_t *buffer;
+ if (usb_device_is_configured(0) == 0) {
+ return;
+ }
+
if (config_uart) {
/* disable irq here */
config_uart = 0;
@@ -700,16 +694,9 @@ __WEAK void chry_dap_usb2uart_uart_config_callback(struct cdc_line_coding *line_
/* called by user */
void chry_dap_usb2uart_uart_send_complete(uint32_t size)
{
- uint8_t *buffer;
-
chry_ringbuffer_linear_read_done(&g_usbrx, size);
- if (chry_ringbuffer_get_used(&g_usbrx)) {
- buffer = chry_ringbuffer_linear_read_setup(&g_usbrx, &size);
- chry_dap_usb2uart_uart_send_bydma(buffer, size);
- } else {
- uarttx_idle_flag = 1;
- }
+ uarttx_idle_flag = 1;
}
/* implment by user */
@@ -746,4 +733,4 @@ int usbd_msc_sector_write(uint32_t sector, uint8_t *buffer, uint32_t length)
memcpy(mass_block[sector].BlockSpace, buffer, length);
return 0;
}
-#endif
+#endif \ No newline at end of file
diff --git a/platform/idf/usbh_fatfs.c b/platform/idf/usbh_fatfs.c
index fef0798f..ca2713ae 100644
--- a/platform/idf/usbh_fatfs.c
+++ b/platform/idf/usbh_fatfs.c
@@ -9,6 +9,7 @@
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
+#include "esp_idf_version.h"
#include "esp_log.h"
#include "esp_check.h"
#include "diskio_impl.h"
@@ -215,12 +216,16 @@ esp_err_t msc_host_vfs_register(struct usbh_msc *msc_class,
strcpy(vfs->base_path, base_path);
vfs->pdrv = pdrv;
+#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(6, 0, 0)
esp_vfs_fat_conf_t conf = {
.base_path = base_path,
.fat_drive = drive,
.max_files = mount_config->max_files,
};
ret = esp_vfs_fat_register(&conf, &fs);
+#else
+ ret = esp_vfs_fat_register(base_path, drive, mount_config->max_files, &fs);
+#endif
ESP_GOTO_ON_ERROR(ret, fail, TAG, "Failed to register filesystem, error=%s", esp_err_to_name(ret));
vfs->fs = fs;
diff --git a/platform/rtthread/rt_usbd_adb.c b/platform/rtthread/rt_usbd_adb.c
index 4ab7f59a..4ab7bbe1 100644
--- a/platform/rtthread/rt_usbd_adb.c
+++ b/platform/rtthread/rt_usbd_adb.c
@@ -126,6 +126,7 @@ void usbd_adb_shell_init(uint8_t in_ep, uint8_t out_ep)
#endif
g_usbd_adb_shell.tx_done = usb_osal_sem_create(0);
+ USB_ASSERT(g_usbd_adb_shell.tx_done != NULL);
rt_ringbuffer_init(&g_usbd_adb_shell.rx_rb, g_usbd_adb_shell.rx_rb_buffer, sizeof(g_usbd_adb_shell.rx_rb_buffer));
}
diff --git a/platform/rtthread/rt_usbd_serial.c b/platform/rtthread/rt_usbd_serial.c
index 41fb01af..138f4cc8 100644
--- a/platform/rtthread/rt_usbd_serial.c
+++ b/platform/rtthread/rt_usbd_serial.c
@@ -258,6 +258,7 @@ void usbd_cdc_acm_serial_init(uint8_t busid, uint8_t in_ep, uint8_t out_ep)
serial->in_ep = in_ep;
serial->out_ep = out_ep;
serial->tx_done = usb_osal_sem_create(0);
+ USB_ASSERT(serial->tx_done != NULL);
usbd_add_interface(busid, usbd_cdc_acm_init_intf(busid, &serial->intf_ctrl));
usbd_add_interface(busid, usbd_cdc_acm_init_intf(busid, &serial->intf_data));
diff --git a/platform/zephyr/usbh_msc_disk.c b/platform/zephyr/usbh_msc_disk.c
index 6890f985..129cbfe4 100644
--- a/platform/zephyr/usbh_msc_disk.c
+++ b/platform/zephyr/usbh_msc_disk.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2025, sakumisu
+ * Copyright (c) 2025-2026 sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -13,16 +13,37 @@
#endif
#endif
-struct usbh_msc *active_msc_class;
+struct usbh_msc_disk_context {
+ struct usbh_msc *msc_class;
+ struct disk_info disk;
+ char name[CONFIG_USBHOST_DEV_NAMELEN];
+ char devname[CONFIG_USBHOST_DEV_NAMELEN];
+};
+
+static struct usbh_msc_disk_context
+ msc_disk_context[CONFIG_USBHOST_MAX_MSC_CLASS];
+
+static struct usbh_msc_disk_context *disk_msc_context(struct disk_info *disk)
+{
+ return CONTAINER_OF(disk, struct usbh_msc_disk_context, disk);
+}
+
+static bool disk_msc_connected(const struct usbh_msc *msc_class)
+{
+ return msc_class != NULL && msc_class->hport != NULL &&
+ msc_class->hport->connected;
+}
static int disk_msc_access_init(struct disk_info *disk)
{
- active_msc_class = (struct usbh_msc *)usbh_find_class_instance("/dev/sda");
- if (active_msc_class == NULL) {
- printf("do not find /dev/sda\r\n");
+ struct usbh_msc_disk_context *ctx = disk_msc_context(disk);
+
+ ctx->msc_class = (struct usbh_msc *)usbh_find_class_instance(ctx->devname);
+ if (ctx->msc_class == NULL) {
+ printf("do not find %s\r\n", ctx->devname);
return -ENODEV;
}
- if (usbh_msc_scsi_init(active_msc_class) < 0) {
+ if (usbh_msc_scsi_init(ctx->msc_class) < 0) {
return -EIO;
}
return 0;
@@ -30,6 +51,12 @@ static int disk_msc_access_init(struct disk_info *disk)
static int disk_msc_access_status(struct disk_info *disk)
{
+ struct usbh_msc_disk_context *ctx = disk_msc_context(disk);
+
+ if (!disk_msc_connected(ctx->msc_class)) {
+ return DISK_STATUS_NOMEDIA;
+ }
+
return DISK_STATUS_OK;
}
@@ -38,25 +65,30 @@ static int disk_msc_access_read(struct disk_info *disk, uint8_t *buff,
{
int ret;
uint8_t *align_buf;
+ struct usbh_msc_disk_context *ctx = disk_msc_context(disk);
+
+ if (!disk_msc_connected(ctx->msc_class)) {
+ return -ENODEV;
+ }
align_buf = (uint8_t *)buff;
#ifdef CONFIG_DCACHE
if ((uint32_t)buff & (CONFIG_USB_ALIGN_SIZE - 1)) {
- align_buf = (uint8_t *)k_aligned_alloc(CONFIG_USB_ALIGN_SIZE, count * active_msc_class->blocksize);
+ align_buf = (uint8_t *)k_aligned_alloc(CONFIG_USB_ALIGN_SIZE, count * ctx->msc_class->blocksize);
if (!align_buf) {
printf("msc get align buf failed\r\n");
return -ENOMEM;
}
}
#endif
- if (usbh_msc_scsi_read10(active_msc_class, sector, align_buf, count) < 0) {
+ if (usbh_msc_scsi_read10(ctx->msc_class, sector, align_buf, count) < 0) {
ret = -EIO;
} else {
ret = 0;
}
#ifdef CONFIG_DCACHE
if ((uint32_t)buff & (CONFIG_USB_ALIGN_SIZE - 1)) {
- usb_memcpy(buff, align_buf, count * active_msc_class->blocksize);
+ usb_memcpy(buff, align_buf, count * ctx->msc_class->blocksize);
k_free(align_buf);
}
#endif
@@ -68,19 +100,24 @@ static int disk_msc_access_write(struct disk_info *disk, const uint8_t *buff,
{
int ret;
uint8_t *align_buf;
+ struct usbh_msc_disk_context *ctx = disk_msc_context(disk);
+
+ if (!disk_msc_connected(ctx->msc_class)) {
+ return -ENODEV;
+ }
align_buf = (uint8_t *)buff;
#ifdef CONFIG_DCACHE
if ((uint32_t)buff & (CONFIG_USB_ALIGN_SIZE - 1)) {
- align_buf = (uint8_t *)k_aligned_alloc(CONFIG_USB_ALIGN_SIZE, count * active_msc_class->blocksize);
+ align_buf = (uint8_t *)k_aligned_alloc(CONFIG_USB_ALIGN_SIZE, count * ctx->msc_class->blocksize);
if (!align_buf) {
printf("msc get align buf failed\r\n");
return -ENOMEM;
}
- usb_memcpy(align_buf, buff, count * active_msc_class->blocksize);
+ usb_memcpy(align_buf, buff, count * ctx->msc_class->blocksize);
}
#endif
- if (usbh_msc_scsi_write10(active_msc_class, sector, align_buf, count) < 0) {
+ if (usbh_msc_scsi_write10(ctx->msc_class, sector, align_buf, count) < 0) {
ret = -EIO;
} else {
ret = 0;
@@ -95,14 +132,22 @@ static int disk_msc_access_write(struct disk_info *disk, const uint8_t *buff,
static int disk_msc_access_ioctl(struct disk_info *disk, uint8_t cmd, void *buff)
{
+ struct usbh_msc_disk_context *ctx = disk_msc_context(disk);
+
switch (cmd) {
case DISK_IOCTL_CTRL_SYNC:
break;
case DISK_IOCTL_GET_SECTOR_COUNT:
- *(uint32_t *)buff = active_msc_class->blocknum;
+ if (!disk_msc_connected(ctx->msc_class)) {
+ return -ENODEV;
+ }
+ *(uint32_t *)buff = ctx->msc_class->blocknum;
break;
case DISK_IOCTL_GET_SECTOR_SIZE:
- *(uint32_t *)buff = active_msc_class->blocksize;
+ if (!disk_msc_connected(ctx->msc_class)) {
+ return -ENODEV;
+ }
+ *(uint32_t *)buff = ctx->msc_class->blocksize;
break;
case DISK_IOCTL_GET_ERASE_BLOCK_SZ:
*(uint32_t *)buff = 1U;
@@ -126,17 +171,56 @@ static const struct disk_operations msc_disk_ops = {
.ioctl = disk_msc_access_ioctl,
};
-static struct disk_info usbh_msc_disk = {
- .name = "USB",
- .ops = &msc_disk_ops,
-};
+__WEAK void usbh_msc_app_run(struct usbh_msc *msc_class)
+{
+ (void)msc_class;
+}
+
+__WEAK void usbh_msc_app_stop(struct usbh_msc *msc_class)
+{
+ (void)msc_class;
+}
void usbh_msc_run(struct usbh_msc *msc_class)
{
- disk_access_register(&usbh_msc_disk);
+ uint8_t index;
+ struct usbh_msc_disk_context *ctx;
+
+ if (msc_class == NULL || msc_class->sdchar < 'a' ||
+ msc_class->sdchar >= 'a' + CONFIG_USBHOST_MAX_MSC_CLASS) {
+ return;
+ }
+
+ index = msc_class->sdchar - 'a';
+ ctx = &msc_disk_context[index];
+ memset(ctx, 0, sizeof(*ctx));
+ snprintf(ctx->name, sizeof(ctx->name), "USB%u", index);
+ snprintf(ctx->devname, sizeof(ctx->devname), "/dev/sd%c", msc_class->sdchar);
+ ctx->msc_class = msc_class;
+ ctx->disk.name = ctx->name;
+ ctx->disk.ops = &msc_disk_ops;
+
+ if (disk_access_register(&ctx->disk) < 0) {
+ return;
+ }
+
+ usbh_msc_app_run(msc_class);
}
void usbh_msc_stop(struct usbh_msc *msc_class)
{
- disk_access_unregister(&usbh_msc_disk);
-} \ No newline at end of file
+ uint8_t index;
+ struct usbh_msc_disk_context *ctx;
+
+ usbh_msc_app_stop(msc_class);
+
+ if (msc_class == NULL || msc_class->sdchar < 'a' ||
+ msc_class->sdchar >= 'a' + CONFIG_USBHOST_MAX_MSC_CLASS) {
+ return;
+ }
+
+ index = msc_class->sdchar - 'a';
+ ctx = &msc_disk_context[index];
+ disk_access_unregister(&ctx->disk);
+ ctx->msc_class = NULL;
+}
diff --git a/port/ch32/README.md b/port/ch32/README.md
index 68a1ab76..3c6461c8 100644
--- a/port/ch32/README.md
+++ b/port/ch32/README.md
@@ -4,7 +4,7 @@
## CH32FS
-- CH32V30X/CH32V20X/CH32V10X/CH32X035
+- CH32V30X/CH32V20X(USBFS, USBD 用 fsdev)/CH32V10X/CH32L103
## CH32HS
diff --git a/port/ch32/ch32fs/usb_dc_usbfs.c b/port/ch32/ch32fs/usb_dc_usbfs.c
index 8047d42d..f4761f67 100644
--- a/port/ch32/ch32fs/usb_dc_usbfs.c
+++ b/port/ch32/ch32fs/usb_dc_usbfs.c
@@ -271,7 +271,8 @@ void USBD_IRQHandler(uint8_t busid)
switch (token) {
case USBFS_UIS_TOKEN_SETUP:
- USB_SET_RX_CTRL(ep_idx, USBFS_UEP_R_RES_NAK);
+ USB_SET_TX_CTRL(0, USBFS_UEP_T_RES_NAK);
+ USB_SET_RX_CTRL(0, USBFS_UEP_R_RES_NAK);
usbd_event_ep0_setup_complete_handler(0, (uint8_t *)&g_ch32_usbfs_udc.setup);
break;
diff --git a/port/dwc2/usb_hc_dwc2.c b/port/dwc2/usb_hc_dwc2.c
index 11616c76..2362390d 100644
--- a/port/dwc2/usb_hc_dwc2.c
+++ b/port/dwc2/usb_hc_dwc2.c
@@ -696,6 +696,7 @@ int usb_hc_init(struct usbh_bus *bus)
for (uint8_t chidx = 0; chidx < g_dwc2_hcd[bus->hcd.hcd_id].hw_params.host_channels; chidx++) {
g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[chidx].waitsem = usb_osal_sem_create(0);
+ USB_ASSERT(g_dwc2_hcd[bus->hcd.hcd_id].chan_pool[chidx].waitsem != NULL);
}
USB_LOG_INFO("dwc2 has %d channels and dfifo depth(32-bit words) is %d\r\n",
diff --git a/port/ehci/usb_hc_ehci.c b/port/ehci/usb_hc_ehci.c
index fd72907f..955bbbc0 100644
--- a/port/ehci/usb_hc_ehci.c
+++ b/port/ehci/usb_hc_ehci.c
@@ -137,6 +137,10 @@ static inline void ehci_qh_add_head(struct ehci_qh_hw *head, struct ehci_qh_hw *
n->hw.hlp = head->hw.hlp;
usb_ehci_qh_qtd_flush(n);
+ if (n->urb->setup) {
+ usb_dcache_clean((uintptr_t)n->urb->setup, USB_ALIGN_UP(8, CONFIG_USB_ALIGN_SIZE));
+ }
+
usb_dcache_flush((uintptr_t)n->urb->transfer_buffer, USB_ALIGN_UP(n->urb->transfer_buffer_length, CONFIG_USB_ALIGN_SIZE));
head->hw.hlp = QH_HLP_QH(n);
@@ -799,6 +803,7 @@ int usb_hc_init(struct usbh_bus *bus)
for (uint8_t index = 0; index < CONFIG_USB_EHCI_QH_NUM; index++) {
qh = &ehci_qh_pool[bus->hcd.hcd_id][index];
qh->waitsem = usb_osal_sem_create(0);
+ USB_ASSERT(qh->waitsem != NULL);
}
memset(&g_async_qh_head[bus->hcd.hcd_id], 0, sizeof(struct ehci_qh_hw));
diff --git a/port/fsdev/README.md b/port/fsdev/README.md
index 95e9ac2e..f7a876f8 100644
--- a/port/fsdev/README.md
+++ b/port/fsdev/README.md
@@ -28,3 +28,11 @@
## CH32
- CH32F10x、CH32V10x
+
+## N32
+
+- N32H473、N32H474、N32H475、N32H481、N32H482、N32H480、N32H487、N32H488
+
+- N32H49X
+
+
diff --git a/port/fsdev/usb_dc_fsdev.c b/port/fsdev/usb_dc_fsdev.c
index 3e1fa22b..eb8dde24 100644
--- a/port/fsdev/usb_dc_fsdev.c
+++ b/port/fsdev/usb_dc_fsdev.c
@@ -60,10 +60,30 @@ __WEAK void usb_dc_low_level_deinit(void)
{
}
+/**
+ * @brief Initialize the USB device controller.
+ * @param busid USB bus index (fsdev supports a single instance, bus 0).
+ * @retval 0 on success.
+ */
int usb_dc_init(uint8_t busid)
{
usb_dc_low_level_init();
+#if defined(N32H4X_FSDEV)
+ /* N32H4x power-on sequence: the USB_CTRL reset value is 0x0300
+ * -- PD(bit9, power-down) and FRST(bit8, force-reset) are both set.
+ * 1. Clear PD, wait for the internal analog voltage reference to settle.
+ * 2. Clear FRST.
+ * 3. Clear the STS register to drop pending interrupts. */
+ USB->CNTR &= (uint32_t)~USB_CNTR_PDWN; /* exit power-down */
+
+ /* Wait for the internal reference voltage to stabilise. */
+ for (volatile uint32_t i = 0U; i < 1000U; i++) {
+ }
+
+ USB->CNTR &= (uint32_t)~USB_CNTR_FRES; /* clear force-reset flag */
+ USB->ISTR = 0U; /* clear pending interrupts */
+#else
/* Init Device */
/* CNTR_FRES = 1 */
USB->CNTR = (uint16_t)USB_CNTR_FRES;
@@ -73,6 +93,7 @@ int usb_dc_init(uint8_t busid)
/* Clear pending interrupts */
USB->ISTR = 0U;
+#endif
/*Set Btable Address*/
USB->BTABLE = BTABLE_ADDRESS;
@@ -91,8 +112,13 @@ int usb_dc_init(uint8_t busid)
/* Set interrupt mask */
USB->CNTR = (uint16_t)winterruptmask;
+#if defined(N32H4X_FSDEV)
+ /* Enabling DP Pull-UP through USB_CTRL.PU */
+ USB->CNTR |= (uint32_t)USB_CNTR_PU;
+#else
/* Enabling DP Pull-UP bit to Connect internal PU resistor on USB DP line */
USB->BCDR |= (uint16_t)USB_BCDR_DPPU;
+#endif
return 0;
}
@@ -325,6 +351,10 @@ int usbd_ep_start_read(uint8_t busid, const uint8_t ep, uint8_t *data, uint32_t
return 0;
}
+/**
+ * @brief USB FS device interrupt handler: dispatches ISTR events.
+ * @param busid USB bus index (fsdev supports a single instance, bus 0).
+ */
void USBD_IRQHandler(uint8_t busid)
{
uint16_t wIstr, wEPVal;
@@ -339,7 +369,13 @@ void USBD_IRQHandler(uint8_t busid)
wIstr = USB->ISTR;
/* extract highest priority endpoint number */
+#if defined(N32H4X_FSDEV)
+ /* EP_ID is at STS bits [11:8] */
+ ep_idx = (uint8_t)((wIstr & USB_ISTR_EP_ID) >> USB_ISTR_EP_ID_Pos);
+#else
+ /* ST: EP_ID is at ISTR bits [3:0] */
ep_idx = (uint8_t)(wIstr & USB_ISTR_EP_ID);
+#endif
if (ep_idx == 0U) {
if ((wIstr & USB_ISTR_DIR) == 0U) {
diff --git a/port/fsdev/usb_fsdev_reg.h b/port/fsdev/usb_fsdev_reg.h
index c67cf573..22e657f1 100644
--- a/port/fsdev/usb_fsdev_reg.h
+++ b/port/fsdev/usb_fsdev_reg.h
@@ -11,6 +11,40 @@
* @brief Universal Serial Bus Full Speed Device
*/
+/* N32H4x family detection (N32H47x_48x + N32H49x). */
+#if defined(N32H473) || defined(N32H474) || defined(N32H475) || \
+ defined(N32H481) || defined(N32H482) || defined(N32H480) || \
+ defined(N32H487) || defined(N32H488) || defined(N32H49X)
+#define N32H4X_FSDEV 1
+#endif
+
+#if defined(N32H4X_FSDEV)
+/* N32H4x re-arranged the Synopsys USBFS registers:
+ * - USB_EPn is a 32-bit register (no reserved words), EPn at 0x00 + n*4
+ * - CTRL@0x40 (named CNTR here to keep usb_dc_fsdev.c unchanged)
+ * - STS @0x44 (named ISTR here), BUFTAB@0x48 (named BTABLE), FN@0x4C, ADDR@0x50
+ * - No BCDR register: the DP pull-up moved to CTRL.PU (bit13)
+ * The buffer descriptor table layout (n*16 per endpoint) matches the ST macro
+ * addressing with PMA_ACCESS=2, so PCD_* btable macros are reused as-is.
+ */
+typedef struct
+{
+ __IO uint32_t EP0R; /*!< USB_EP0, Address offset: 0x00 */
+ __IO uint32_t EP1R; /*!< USB_EP1, Address offset: 0x04 */
+ __IO uint32_t EP2R; /*!< USB_EP2, Address offset: 0x08 */
+ __IO uint32_t EP3R; /*!< USB_EP3, Address offset: 0x0C */
+ __IO uint32_t EP4R; /*!< USB_EP4, Address offset: 0x10 */
+ __IO uint32_t EP5R; /*!< USB_EP5, Address offset: 0x14 */
+ __IO uint32_t EP6R; /*!< USB_EP6, Address offset: 0x18 */
+ __IO uint32_t EP7R; /*!< USB_EP7, Address offset: 0x1C */
+ __IO uint32_t RESERVED0[8]; /*!< Reserved Address offset: 0x20 .. 0x3C */
+ __IO uint32_t CNTR; /*!< USB_CTRL, Address offset: 0x40 */
+ __IO uint32_t ISTR; /*!< USB_STS, Address offset: 0x44 */
+ __IO uint32_t BTABLE; /*!< USB_BUFTAB, Address offset: 0x48 */
+ __IO uint32_t FN; /*!< USB_FN, Address offset: 0x4C */
+ __IO uint32_t DADDR; /*!< USB_ADDR, Address offset: 0x50 */
+} USB_TypeDef;
+#else
typedef struct
{
__IO uint16_t EP0R; /*!< USB Endpoint 0 register, Address offset: 0x00 */
@@ -44,6 +78,7 @@ typedef struct
__IO uint16_t BCDR; /*!< Battery Charging detector register, Address offset: 0x58 */
__IO uint16_t RESERVEDE; /*!< Reserved */
} USB_TypeDef;
+#endif
/******************************************************************************/
/* */
@@ -52,6 +87,7 @@ typedef struct
/******************************************************************************/
/*!< Endpoint-specific registers */
+#if !defined(N32H4X_FSDEV)
#define USB_EP0R USB_BASE /*!< Endpoint 0 register address */
#define USB_EP1R (USB_BASE + 0x00000004) /*!< Endpoint 1 register address */
#define USB_EP2R (USB_BASE + 0x00000008) /*!< Endpoint 2 register address */
@@ -1133,9 +1169,316 @@ typedef struct
#define USB_COUNT7_RX_1_BLSIZE_1 0x80000000U /*!< BLock SIZE (high) */
-/**
- * @}
- */
+#endif /* !(N32H4X_FSDEV) */
+
+#if defined(N32H4X_FSDEV)
+/* ===========================================================================
+ * N32H4x (N32H47x_48x / N32H49x) USBFS register bit re-mapping.
+ *
+ * Nations kept the Synopsys USBFS core but re-arranged every non-reserved
+ * bit field. This block re-defines the common macros used by
+ * usb_dc_fsdev.c with the N32H4x layout.
+ *
+ * USB_EPn register bits:
+ * STS_TX[15:14] | DATTOG_TX[13] | CTRS_TX[12] | STS_RX[11:10] | DATTOG_RX[9]
+ * CTRS_RX[8] | EP_TYPE[7:6] | EP_KIND[5] | SETUP[4] | EPADDR[3:0]
+ * ===========================================================================
+ */
+#undef USB_EP_CTR_RX_Pos
+#undef USB_EP_CTR_RX_Msk
+#undef USB_EP_CTR_RX
+#define USB_EP_CTR_RX_Pos (8U) /* CTRS_RX */
+#define USB_EP_CTR_RX_Msk (0x1UL << USB_EP_CTR_RX_Pos)
+#define USB_EP_CTR_RX USB_EP_CTR_RX_Msk
+#undef USB_EP_DTOG_RX_Pos
+#undef USB_EP_DTOG_RX_Msk
+#undef USB_EP_DTOG_RX
+#define USB_EP_DTOG_RX_Pos (9U) /* DATTOG_RX */
+#define USB_EP_DTOG_RX_Msk (0x1UL << USB_EP_DTOG_RX_Pos)
+#define USB_EP_DTOG_RX USB_EP_DTOG_RX_Msk
+#undef USB_EPRX_STAT_Pos
+#undef USB_EPRX_STAT_Msk
+#undef USB_EPRX_STAT
+#define USB_EPRX_STAT_Pos (10U) /* STS_RX[1:0] */
+#define USB_EPRX_STAT_Msk (0x3UL << USB_EPRX_STAT_Pos)
+#define USB_EPRX_STAT USB_EPRX_STAT_Msk
+#undef USB_EP_SETUP_Pos
+#undef USB_EP_SETUP_Msk
+#undef USB_EP_SETUP
+#define USB_EP_SETUP_Pos (4U) /* SETUP */
+#define USB_EP_SETUP_Msk (0x1UL << USB_EP_SETUP_Pos)
+#define USB_EP_SETUP USB_EP_SETUP_Msk
+#undef USB_EP_T_FIELD_Pos
+#undef USB_EP_T_FIELD_Msk
+#undef USB_EP_T_FIELD
+#define USB_EP_T_FIELD_Pos (6U) /* EP_TYPE[1:0] */
+#define USB_EP_T_FIELD_Msk (0x3UL << USB_EP_T_FIELD_Pos)
+#define USB_EP_T_FIELD USB_EP_T_FIELD_Msk
+#undef USB_EP_KIND_Pos
+#undef USB_EP_KIND_Msk
+#undef USB_EP_KIND
+#define USB_EP_KIND_Pos (5U) /* EP_KIND */
+#define USB_EP_KIND_Msk (0x1UL << USB_EP_KIND_Pos)
+#define USB_EP_KIND USB_EP_KIND_Msk
+#undef USB_EP_CTR_TX_Pos
+#undef USB_EP_CTR_TX_Msk
+#undef USB_EP_CTR_TX
+#define USB_EP_CTR_TX_Pos (12U) /* CTRS_TX */
+#define USB_EP_CTR_TX_Msk (0x1UL << USB_EP_CTR_TX_Pos)
+#define USB_EP_CTR_TX USB_EP_CTR_TX_Msk
+#undef USB_EP_DTOG_TX_Pos
+#undef USB_EP_DTOG_TX_Msk
+#undef USB_EP_DTOG_TX
+#define USB_EP_DTOG_TX_Pos (13U) /* DATTOG_TX */
+#define USB_EP_DTOG_TX_Msk (0x1UL << USB_EP_DTOG_TX_Pos)
+#define USB_EP_DTOG_TX USB_EP_DTOG_TX_Msk
+#undef USB_EPTX_STAT_Pos
+#undef USB_EPTX_STAT_Msk
+#undef USB_EPTX_STAT
+#define USB_EPTX_STAT_Pos (14U) /* STS_TX[1:0] */
+#define USB_EPTX_STAT_Msk (0x3UL << USB_EPTX_STAT_Pos)
+#define USB_EPTX_STAT USB_EPTX_STAT_Msk
+/* EPADDR[3:0] stays at bit 0 */
+
+/* EndPoint REGister MASK (no toggle/status fields) */
+#undef USB_EPREG_MASK
+#define USB_EPREG_MASK (USB_EP_CTR_RX|USB_EP_SETUP|USB_EP_T_FIELD|USB_EP_KIND|USB_EP_CTR_TX|USB_EPADDR_FIELD)
+
+#undef USB_EP_TYPE_MASK_Pos
+#undef USB_EP_TYPE_MASK_Msk
+#undef USB_EP_TYPE_MASK
+#define USB_EP_TYPE_MASK_Pos (6U)
+#define USB_EP_TYPE_MASK_Msk (0x3UL << USB_EP_TYPE_MASK_Pos)
+#define USB_EP_TYPE_MASK USB_EP_TYPE_MASK_Msk
+#undef USB_EP_BULK
+#undef USB_EP_CONTROL
+#undef USB_EP_ISOCHRONOUS
+#undef USB_EP_INTERRUPT
+#define USB_EP_BULK (0UL << USB_EP_T_FIELD_Pos) /* 00 */
+#define USB_EP_CONTROL (1UL << USB_EP_T_FIELD_Pos) /* 01 */
+#define USB_EP_ISOCHRONOUS (2UL << USB_EP_T_FIELD_Pos) /* 10 */
+#define USB_EP_INTERRUPT (3UL << USB_EP_T_FIELD_Pos) /* 11 */
+#undef USB_EP_T_MASK
+#define USB_EP_T_MASK (~USB_EP_T_FIELD & USB_EPREG_MASK)
+#undef USB_EPKIND_MASK
+#define USB_EPKIND_MASK (~USB_EP_KIND & USB_EPREG_MASK)
+
+/* STS_TX[1:0] bit15:14: 00 DIS / 01 STALL / 10 NAK / 11 VALID */
+#undef USB_EP_TX_DIS
+#undef USB_EP_TX_STALL
+#undef USB_EP_TX_NAK
+#undef USB_EP_TX_VALID
+#undef USB_EPTX_DTOG1
+#undef USB_EPTX_DTOG2
+#undef USB_EPTX_DTOGMASK
+#define USB_EP_TX_DIS (0UL << USB_EPTX_STAT_Pos)
+#define USB_EP_TX_STALL (1UL << USB_EPTX_STAT_Pos)
+#define USB_EP_TX_NAK (2UL << USB_EPTX_STAT_Pos)
+#define USB_EP_TX_VALID (3UL << USB_EPTX_STAT_Pos)
+#define USB_EPTX_DTOG1 (1UL << USB_EPTX_STAT_Pos)
+#define USB_EPTX_DTOG2 (2UL << USB_EPTX_STAT_Pos)
+#define USB_EPTX_DTOGMASK (USB_EPTX_STAT|USB_EPREG_MASK)
+
+/* STS_RX[1:0] bit11:10: 00 DIS / 01 STALL / 10 NAK / 11 VALID */
+#undef USB_EP_RX_DIS
+#undef USB_EP_RX_STALL
+#undef USB_EP_RX_NAK
+#undef USB_EP_RX_VALID
+#undef USB_EPRX_DTOG1
+#undef USB_EPRX_DTOG2
+#undef USB_EPRX_DTOGMASK
+#define USB_EP_RX_DIS (0UL << USB_EPRX_STAT_Pos)
+#define USB_EP_RX_STALL (1UL << USB_EPRX_STAT_Pos)
+#define USB_EP_RX_NAK (2UL << USB_EPRX_STAT_Pos)
+#define USB_EP_RX_VALID (3UL << USB_EPRX_STAT_Pos)
+#define USB_EPRX_DTOG1 (1UL << USB_EPRX_STAT_Pos)
+#define USB_EPRX_DTOG2 (2UL << USB_EPRX_STAT_Pos)
+#define USB_EPRX_DTOGMASK (USB_EPRX_STAT|USB_EPREG_MASK)
+
+/* USB_CTRL register bits: PU[13] RESUM[12] FSUSPD[11] LP_MODE[10] PD[9] FRST[8]
+ * PMAOM[7] CTRSM[6] ERRORM[5] WKUPM[4] SUSPDM[3] RSTM[2] SOFM[1] ESOFM[0]
+ * Default value 0x0300 -> PD and FRST are set after reset.
+ */
+#undef USB_CNTR_FRES_Pos
+#undef USB_CNTR_FRES_Msk
+#undef USB_CNTR_FRES
+#define USB_CNTR_FRES_Pos (8U) /* FRST */
+#define USB_CNTR_FRES_Msk (0x1UL << USB_CNTR_FRES_Pos)
+#define USB_CNTR_FRES USB_CNTR_FRES_Msk
+#undef USB_CNTR_PDWN_Pos
+#undef USB_CNTR_PDWN_Msk
+#undef USB_CNTR_PDWN
+#define USB_CNTR_PDWN_Pos (9U) /* PD */
+#define USB_CNTR_PDWN_Msk (0x1UL << USB_CNTR_PDWN_Pos)
+#define USB_CNTR_PDWN USB_CNTR_PDWN_Msk
+#undef USB_CNTR_LP_MODE_Pos
+#undef USB_CNTR_LP_MODE_Msk
+#undef USB_CNTR_LP_MODE
+#define USB_CNTR_LP_MODE_Pos (10U) /* LP_MODE */
+#define USB_CNTR_LP_MODE_Msk (0x1UL << USB_CNTR_LP_MODE_Pos)
+#define USB_CNTR_LP_MODE USB_CNTR_LP_MODE_Msk
+#undef USB_CNTR_FSUSP_Pos
+#undef USB_CNTR_FSUSP_Msk
+#undef USB_CNTR_FSUSP
+#define USB_CNTR_FSUSP_Pos (11U) /* FSUSPD */
+#define USB_CNTR_FSUSP_Msk (0x1UL << USB_CNTR_FSUSP_Pos)
+#define USB_CNTR_FSUSP USB_CNTR_FSUSP_Msk
+#undef USB_CNTR_RESUME_Pos
+#undef USB_CNTR_RESUME_Msk
+#undef USB_CNTR_RESUME
+#define USB_CNTR_RESUME_Pos (12U) /* RESUM */
+#define USB_CNTR_RESUME_Msk (0x1UL << USB_CNTR_RESUME_Pos)
+#define USB_CNTR_RESUME USB_CNTR_RESUME_Msk
+#define USB_CNTR_PU_Pos (13U) /* PU (DP pull-up, replaces ST BCDR.DPPU) */
+#define USB_CNTR_PU_Msk (0x1UL << USB_CNTR_PU_Pos)
+#define USB_CNTR_PU USB_CNTR_PU_Msk
+#undef USB_CNTR_ESOFM_Pos
+#undef USB_CNTR_ESOFM_Msk
+#undef USB_CNTR_ESOFM
+#define USB_CNTR_ESOFM_Pos (0U) /* ESOFM */
+#define USB_CNTR_ESOFM_Msk (0x1UL << USB_CNTR_ESOFM_Pos)
+#define USB_CNTR_ESOFM USB_CNTR_ESOFM_Msk
+#undef USB_CNTR_SOFM_Pos
+#undef USB_CNTR_SOFM_Msk
+#undef USB_CNTR_SOFM
+#define USB_CNTR_SOFM_Pos (1U) /* SOFM */
+#define USB_CNTR_SOFM_Msk (0x1UL << USB_CNTR_SOFM_Pos)
+#define USB_CNTR_SOFM USB_CNTR_SOFM_Msk
+#undef USB_CNTR_RESETM_Pos
+#undef USB_CNTR_RESETM_Msk
+#undef USB_CNTR_RESETM
+#define USB_CNTR_RESETM_Pos (2U) /* RSTM */
+#define USB_CNTR_RESETM_Msk (0x1UL << USB_CNTR_RESETM_Pos)
+#define USB_CNTR_RESETM USB_CNTR_RESETM_Msk
+#undef USB_CNTR_SUSPM_Pos
+#undef USB_CNTR_SUSPM_Msk
+#undef USB_CNTR_SUSPM
+#define USB_CNTR_SUSPM_Pos (3U) /* SUSPDM */
+#define USB_CNTR_SUSPM_Msk (0x1UL << USB_CNTR_SUSPM_Pos)
+#define USB_CNTR_SUSPM USB_CNTR_SUSPM_Msk
+#undef USB_CNTR_WKUPM_Pos
+#undef USB_CNTR_WKUPM_Msk
+#undef USB_CNTR_WKUPM
+#define USB_CNTR_WKUPM_Pos (4U) /* WKUPM */
+#define USB_CNTR_WKUPM_Msk (0x1UL << USB_CNTR_WKUPM_Pos)
+#define USB_CNTR_WKUPM USB_CNTR_WKUPM_Msk
+#undef USB_CNTR_ERRM_Pos
+#undef USB_CNTR_ERRM_Msk
+#undef USB_CNTR_ERRM
+#define USB_CNTR_ERRM_Pos (5U) /* ERRORM */
+#define USB_CNTR_ERRM_Msk (0x1UL << USB_CNTR_ERRM_Pos)
+#define USB_CNTR_ERRM USB_CNTR_ERRM_Msk
+#undef USB_CNTR_PMAOVRM_Pos
+#undef USB_CNTR_PMAOVRM_Msk
+#undef USB_CNTR_PMAOVRM
+#define USB_CNTR_PMAOVRM_Pos (7U) /* PMAOM */
+#define USB_CNTR_PMAOVRM_Msk (0x1UL << USB_CNTR_PMAOVRM_Pos)
+#define USB_CNTR_PMAOVRM USB_CNTR_PMAOVRM_Msk
+#undef USB_CNTR_CTRM_Pos
+#undef USB_CNTR_CTRM_Msk
+#undef USB_CNTR_CTRM
+#define USB_CNTR_CTRM_Pos (6U) /* CTRSM */
+#define USB_CNTR_CTRM_Msk (0x1UL << USB_CNTR_CTRM_Pos)
+#define USB_CNTR_CTRM USB_CNTR_CTRM_Msk
+
+/* USB_STS register bits: DIR[12] EP_ID[11:8] PMAO[7] CTRS[6] ERROR[5] WKUP[4]
+ * SUSPD[3] RST[2] SOF[1] ESOF[0]
+ */
+#undef USB_ISTR_EP_ID_Pos
+#undef USB_ISTR_EP_ID_Msk
+#undef USB_ISTR_EP_ID
+#define USB_ISTR_EP_ID_Pos (8U)
+#define USB_ISTR_EP_ID_Msk (0xFUL << USB_ISTR_EP_ID_Pos)
+#define USB_ISTR_EP_ID USB_ISTR_EP_ID_Msk
+#undef USB_ISTR_DIR_Pos
+#undef USB_ISTR_DIR_Msk
+#undef USB_ISTR_DIR
+#define USB_ISTR_DIR_Pos (12U)
+#define USB_ISTR_DIR_Msk (0x1UL << USB_ISTR_DIR_Pos)
+#define USB_ISTR_DIR USB_ISTR_DIR_Msk
+#undef USB_ISTR_ESOF_Pos
+#undef USB_ISTR_ESOF_Msk
+#undef USB_ISTR_ESOF
+#define USB_ISTR_ESOF_Pos (0U)
+#define USB_ISTR_ESOF_Msk (0x1UL << USB_ISTR_ESOF_Pos)
+#define USB_ISTR_ESOF USB_ISTR_ESOF_Msk
+#undef USB_ISTR_SOF_Pos
+#undef USB_ISTR_SOF_Msk
+#undef USB_ISTR_SOF
+#define USB_ISTR_SOF_Pos (1U)
+#define USB_ISTR_SOF_Msk (0x1UL << USB_ISTR_SOF_Pos)
+#define USB_ISTR_SOF USB_ISTR_SOF_Msk
+#undef USB_ISTR_RESET_Pos
+#undef USB_ISTR_RESET_Msk
+#undef USB_ISTR_RESET
+#define USB_ISTR_RESET_Pos (2U)
+#define USB_ISTR_RESET_Msk (0x1UL << USB_ISTR_RESET_Pos)
+#define USB_ISTR_RESET USB_ISTR_RESET_Msk
+#undef USB_ISTR_SUSP_Pos
+#undef USB_ISTR_SUSP_Msk
+#undef USB_ISTR_SUSP
+#define USB_ISTR_SUSP_Pos (3U)
+#define USB_ISTR_SUSP_Msk (0x1UL << USB_ISTR_SUSP_Pos)
+#define USB_ISTR_SUSP USB_ISTR_SUSP_Msk
+#undef USB_ISTR_WKUP_Pos
+#undef USB_ISTR_WKUP_Msk
+#undef USB_ISTR_WKUP
+#define USB_ISTR_WKUP_Pos (4U)
+#define USB_ISTR_WKUP_Msk (0x1UL << USB_ISTR_WKUP_Pos)
+#define USB_ISTR_WKUP USB_ISTR_WKUP_Msk
+#undef USB_ISTR_ERR_Pos
+#undef USB_ISTR_ERR_Msk
+#undef USB_ISTR_ERR
+#define USB_ISTR_ERR_Pos (5U)
+#define USB_ISTR_ERR_Msk (0x1UL << USB_ISTR_ERR_Pos)
+#define USB_ISTR_ERR USB_ISTR_ERR_Msk
+#undef USB_ISTR_PMAOVR_Pos
+#undef USB_ISTR_PMAOVR_Msk
+#undef USB_ISTR_PMAOVR
+#define USB_ISTR_PMAOVR_Pos (7U)
+#define USB_ISTR_PMAOVR_Msk (0x1UL << USB_ISTR_PMAOVR_Pos)
+#define USB_ISTR_PMAOVR USB_ISTR_PMAOVR_Msk
+#undef USB_ISTR_CTR_Pos
+#undef USB_ISTR_CTR_Msk
+#undef USB_ISTR_CTR
+#define USB_ISTR_CTR_Pos (6U)
+#define USB_ISTR_CTR_Msk (0x1UL << USB_ISTR_CTR_Pos)
+#define USB_ISTR_CTR USB_ISTR_CTR_Msk
+
+/* The ST bank above is skipped under N32, so re-provide the macros whose bit
+ * positions are identical on N32 (so the later PCD_* macros still resolve):
+
+ * USB_EPADDR[3:0] -- same as ST (endpoint address). */
+#undef USB_EPADDR_FIELD_Pos
+#undef USB_EPADDR_FIELD_Msk
+#undef USB_EPADDR_FIELD
+#define USB_EPADDR_FIELD_Pos (0U)
+#define USB_EPADDR_FIELD_Msk (0xFUL << USB_EPADDR_FIELD_Pos)
+#define USB_EPADDR_FIELD USB_EPADDR_FIELD_Msk
+
+/* USB_ADDR (device address): EFUC[7] ADDR[6:0], same as ST USB_DADDR. */
+#undef USB_DADDR_ADD_Pos
+#undef USB_DADDR_ADD_Msk
+#undef USB_DADDR_ADD
+#define USB_DADDR_ADD_Pos (0U)
+#define USB_DADDR_ADD_Msk (0x7FUL << USB_DADDR_ADD_Pos)
+#define USB_DADDR_ADD USB_DADDR_ADD_Msk
+#undef USB_DADDR_EF_Pos
+#undef USB_DADDR_EF_Msk
+#undef USB_DADDR_EF
+#define USB_DADDR_EF_Pos (7U) /* EFUC */
+#define USB_DADDR_EF_Msk (0x1UL << USB_DADDR_EF_Pos)
+#define USB_DADDR_EF USB_DADDR_EF_Msk
+
+/* USB_BUFTAB: BUFTAB[15:3] (8-byte aligned), same as ST USB_BTABLE. */
+#undef USB_BTABLE_BTABLE_Pos
+#undef USB_BTABLE_BTABLE_Msk
+#undef USB_BTABLE_BTABLE
+#define USB_BTABLE_BTABLE_Pos (3U)
+#define USB_BTABLE_BTABLE_Msk (0x1FFFUL << USB_BTABLE_BTABLE_Pos)
+#define USB_BTABLE_BTABLE USB_BTABLE_BTABLE_Msk
+
+#endif /* N32H4X_FSDEV */
#define BTABLE_ADDRESS 0x000U
@@ -1146,11 +1489,22 @@ typedef struct
#define USB_CNTRX_NBLK_MSK (0x1FU << 10)
#define USB_CNTRX_BLSIZE (0x1U << 15)
+#if defined(N32H4X_FSDEV)
+/* N32H4x: USB_EPn are 32-bit registers at 0x00 + n*4 (no reserved words),
+ * so each endpoint is a single 32-bit step. */
+
+/* SetENDPOINT */
+#define PCD_SET_ENDPOINT(USBx, bEpNum, wRegValue) (*(__IO uint32_t *)(&(USBx)->EP0R + ((bEpNum) * 1U)) = (uint32_t)(wRegValue))
+
+/* GetENDPOINT */
+#define PCD_GET_ENDPOINT(USBx, bEpNum) (*(__IO uint32_t *)(&(USBx)->EP0R + ((bEpNum) * 1U)))
+#else
/* SetENDPOINT */
#define PCD_SET_ENDPOINT(USBx, bEpNum, wRegValue) (*(__IO uint16_t *)(&(USBx)->EP0R + ((bEpNum) * 2U)) = (uint16_t)(wRegValue))
/* GetENDPOINT */
#define PCD_GET_ENDPOINT(USBx, bEpNum) (*(__IO uint16_t *)(&(USBx)->EP0R + ((bEpNum) * 2U)))
+#endif
/**
* @brief sets the type in the endpoint register(bits EP_TYPE[1:0])
@@ -1353,6 +1707,26 @@ typedef struct
* @param bEpNum Endpoint Number.
* @retval None
*/
+#if defined(N32H4X_FSDEV)
+/* N32H4x: CTRS_RX is bit8, CTRS_TX is bit12 (rc_w0, write-0 to clear). */
+#define PCD_CLEAR_RX_EP_CTR(USBx, bEpNum) \
+ do { \
+ uint16_t _wRegVal; \
+ \
+ _wRegVal = PCD_GET_ENDPOINT((USBx), (bEpNum)) & (0xFEFFU & USB_EPREG_MASK); \
+ \
+ PCD_SET_ENDPOINT((USBx), (bEpNum), (_wRegVal | USB_EP_CTR_TX)); \
+ } while(0) /* PCD_CLEAR_RX_EP_CTR */
+
+#define PCD_CLEAR_TX_EP_CTR(USBx, bEpNum) \
+ do { \
+ uint16_t _wRegVal; \
+ \
+ _wRegVal = PCD_GET_ENDPOINT((USBx), (bEpNum)) & (0xEFFFU & USB_EPREG_MASK); \
+ \
+ PCD_SET_ENDPOINT((USBx), (bEpNum), (_wRegVal | USB_EP_CTR_RX)); \
+ } while(0) /* PCD_CLEAR_TX_EP_CTR */
+#else
#define PCD_CLEAR_RX_EP_CTR(USBx, bEpNum) \
do { \
uint16_t _wRegVal; \
@@ -1370,6 +1744,7 @@ typedef struct
\
PCD_SET_ENDPOINT((USBx), (bEpNum), (_wRegVal | USB_EP_CTR_RX)); \
} while(0) /* PCD_CLEAR_TX_EP_CTR */
+#endif
/**
* @brief Toggles DTOG_RX / DTOG_TX bit in the endpoint register.
diff --git a/port/fsdev/usb_glue_nation.c b/port/fsdev/usb_glue_nation.c
new file mode 100644
index 00000000..0ab539a2
--- /dev/null
+++ b/port/fsdev/usb_glue_nation.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2025, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+
+/* N32H4x family detection (N32H47x_48x + N32H49x): same part-macro list as
+ * the SDKs (n32h47x_48x.h typedef enum IRQn, n32h49x.h) and as
+ * usb_fsdev_reg.h. Defined here locally so this file never needs to include
+ * usb_fsdev_reg.h next to the SDK headers (which would re-define e.g.
+ * USB_EP_SETUP). */
+#if defined(N32H473) || defined(N32H474) || defined(N32H475) || \
+ defined(N32H481) || defined(N32H482) || defined(N32H480) || \
+ defined(N32H487) || defined(N32H488) || defined(N32H49X)
+#define N32H4X_FSDEV 1
+#endif
+
+#if defined(N32H4X_FSDEV)
+
+#if defined(N32H473) || defined(N32H474) || defined(N32H475) || \
+ defined(N32H481) || defined(N32H482) || defined(N32H480) || \
+ defined(N32H487) || defined(N32H488)
+#include "n32h47x_48x_rcc.h"
+#include "n32h47x_48x_gpio.h"
+#include "misc.h"
+
+#define USBFS_REG_BASE 0x40004800UL
+#define USBFS_SRAM_BASE 0x40004C00UL
+#define USBFS_APB1_CLK RCC_APB1_PERIPH_USBFS
+
+#elif defined(N32H49X)
+#include "n32h49x_rcc.h"
+#include "n32h49x_gpio.h"
+#include "misc.h"
+
+#define USBFS_REG_BASE 0x40004800UL
+#define USBFS_SRAM_BASE 0x40004C00UL
+#define USBFS_APB1_CLK RCC_APB1_PERIPHEN_USBFS
+
+#else
+#error "unsupported N32H4x part: define a N32H47x_48x part macro (N32H473/474/475/481/482/480/487/488) or N32H49X"
+#endif /* N32H473..N32H488 (48x SDK) / N32H49X (49x SDK) */
+
+/*
+ * N32H4x (N32H47x_48x / N32H49x) USB Full-Speed Device (USB_FS_Device).
+ *
+ * The USBFS core is the same Synopsys USBFS as STM32, but Nations re-arranged
+ * every register/bit field. The re-map lives in usb_fsdev_reg.h; this file
+ * only does the chip-level bring-up (clock, GPIO, NVIC, interrupt dispatch)
+ * and mirrors the Nations USBFSD examples (HID_CDC_Composite): hw_config.c
+ * (USBFS_IO_Configure / Set_USBClock / USB_Config) and n32h47x_48x_it.c /
+ * n32h49x_it.c (USB_FS_LP_IRQHandler).
+ *
+ * USBFS:
+ * - register base : 0x40004800 (USB_BASE)
+ * - packet buffer : 0x40004C00 (USB_SRAM_BASE, 512 bytes)
+ * - APB1 peripheral; USBFS needs a precise 48 MHz USB clock derived from
+ * PLLCLK (48/96/144/192/240 MHz -> DIV1/2/3/4/5).
+ * - IRQ : USB_FS_LP_IRQn (20, low priority, used here) ; USB_FS_HP_IRQn
+ * (19, high priority, iso/double-buffer only)
+ */
+
+/**
+ * @brief Configure the USBFS 48 MHz clock from the actual PLLCLK.
+ *
+ * The prescaler source is derived from SystemCoreClock at runtime, so the
+ * divider always matches the frequency the board is actually running at
+ * (no per-part compile-time table to keep in sync with the PLL setup).
+ * Supported PLLCLK values: 48/96/144/192/240 MHz -> DIV1/2/3/4/5.
+ * Unsupported frequencies leave the prescaler untouched and must be fixed
+ * in the board clock configuration (48 MHz USB clock is then not guaranteed).
+ */
+static void Set_USBClock(void)
+{
+ switch (SystemCoreClock) {
+ case 48000000: /* SYSCLK_VALUE_48MHz -> DIV1 */
+ RCC_ConfigUSBPLLPresClk(RCC_USBPLLCLK_SRC_PLL, RCC_USBPLLCLK_DIV1);
+ RCC->CFG3 &= ~RCC_CFG3_USBFSTM;
+ break;
+
+ case 96000000: /* SYSCLK_VALUE_96MHz -> DIV2 */
+ RCC_ConfigUSBPLLPresClk(RCC_USBPLLCLK_SRC_PLL, RCC_USBPLLCLK_DIV2);
+ RCC->CFG3 &= ~RCC_CFG3_USBFSTM;
+ break;
+
+ case 144000000: /* SYSCLK_VALUE_144MHz -> DIV3 */
+ RCC_ConfigUSBPLLPresClk(RCC_USBPLLCLK_SRC_PLL, RCC_USBPLLCLK_DIV3);
+ RCC->CFG3 |= RCC_CFG3_USBFSTM;
+ break;
+
+ case 192000000: /* SYSCLK_VALUE_192MHz -> DIV4 */
+ RCC_ConfigUSBPLLPresClk(RCC_USBPLLCLK_SRC_PLL, RCC_USBPLLCLK_DIV4);
+ RCC->CFG3 |= RCC_CFG3_USBFSTM;
+ break;
+
+ case 240000000: /* SYSCLK_VALUE_240MHz -> DIV5 */
+ RCC_ConfigUSBPLLPresClk(RCC_USBPLLCLK_SRC_PLL, RCC_USBPLLCLK_DIV5);
+ RCC->CFG3 |= RCC_CFG3_USBFSTM;
+ break;
+
+ default:
+ USB_LOG_ERR("Unsupported system clock %u Hz for USBFS 48 MHz clock (supported: 48/96/144/192/240 MHz)\r\n",
+ (unsigned int)SystemCoreClock);
+ break;
+ }
+}
+
+/**
+ * @brief Configure the USB DM/DP pins.
+ *
+ * Defaults to the EVAL board mapping (PA11 = DM, PA12 = DP, AF10).
+ * Marked __WEAK so the board layer can override it for other pin maps.
+ */
+__WEAK void n32h4xx_usbfs_gpio_init(void)
+{
+ RCC_EnableAHB1PeriphClk(RCC_AHB_PERIPHEN_GPIOA, ENABLE);
+
+ GPIO_InitType GPIO_InitStructure;
+ GPIO_InitStruct(&GPIO_InitStructure);
+
+ GPIO_InitStructure.Pin = GPIO_PIN_11;
+ GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStructure.GPIO_Alternate = GPIO_AF10;
+ GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);
+
+ GPIO_InitStructure.Pin = GPIO_PIN_12;
+ GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStructure.GPIO_Alternate = GPIO_AF10;
+ GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);
+}
+
+/**
+ * @brief Low-level USBFS initialization: clock, GPIO and interrupt channel.
+ *
+ * Signature must match the __WEAK stub in usb_dc_fsdev.c (no parameter),
+ * so the fsdev port always talks to bus 0.
+ */
+void usb_dc_low_level_init(void)
+{
+ if (g_usbdev_bus[0].reg_base != USBFS_REG_BASE) {
+ return;
+ }
+
+ /* 1. USBFS 48 MHz clock: source select, PLL prescaler, APB1 enable. */
+ RCC_ConfigUSBFSClk(RCC_USBFS_CLKSRC_PLLPRES);
+ Set_USBClock();
+ RCC_EnableAPB1PeriphClk(USBFS_APB1_CLK, ENABLE);
+
+ /* 2. USB DM/DP pins (weak hook, override in the board layer). */
+ n32h4xx_usbfs_gpio_init();
+
+ /* 3. Low-priority USB FS interrupt (all FS device events, including the
+ * USB_STS.WKUP/SUSPD bits handled by usb_dc_fsdev.c). Priority-group
+ * selection stays with the board/application startup code. */
+ NVIC_InitType NVIC_InitStructure;
+
+ NVIC_InitStructure.NVIC_IRQChannel = USB_FS_LP_IRQn;
+ NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
+ NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
+ NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
+ NVIC_Init(&NVIC_InitStructure);
+}
+
+/**
+ * @brief Low-level USBFS deinitialization: disable interrupt and APB1 clock.
+ */
+void usb_dc_low_level_deinit(void)
+{
+ if (g_usbdev_bus[0].reg_base != USBFS_REG_BASE) {
+ return;
+ }
+
+ NVIC_InitType NVIC_InitStructure;
+
+ NVIC_InitStructure.NVIC_IRQChannel = USB_FS_LP_IRQn;
+ NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
+ NVIC_Init(&NVIC_InitStructure);
+
+ RCC_EnableAPB1PeriphClk(USBFS_APB1_CLK, DISABLE);
+}
+
+/**
+ * @brief Low-priority USB FS interrupt: dispatches all FS device events.
+ */
+void USB_FS_LP_IRQHandler(void)
+{
+ USBD_IRQHandler(0);
+}
+
+#endif /* N32H4X_FSDEV */
diff --git a/port/hpmicro/usb_dc_hpm.c b/port/hpmicro/usb_dc_hpm.c
index 60af1187..8ece72a3 100644
--- a/port/hpmicro/usb_dc_hpm.c
+++ b/port/hpmicro/usb_dc_hpm.c
@@ -7,11 +7,6 @@
#include "usbd_core.h"
#include "hpm_usb_device.h"
#include "usb_glue_hpm.h"
-#include "hpm_sdk_version.h"
-
-#if SDK_VERSION_NUMBER < 0x10C00
-#error "Please use SDK version 1.12.0 or later because of USB api modification"
-#endif
#define USB_NUM_BIDIR_ENDPOINTS USB_SOC_DCD_MAX_ENDPOINT_COUNT
diff --git a/port/musb/usb_hc_musb.c b/port/musb/usb_hc_musb.c
index daa0f056..784243e3 100644
--- a/port/musb/usb_hc_musb.c
+++ b/port/musb/usb_hc_musb.c
@@ -576,6 +576,7 @@ int usb_hc_init(struct usbh_bus *bus)
for (uint8_t i = 0; i < CONFIG_USB_MUSB_PIPE_NUM; i++) {
g_musb_hcd[bus->hcd.hcd_id].pipe_pool[i].waitsem = usb_osal_sem_create(0);
+ USB_ASSERT(g_musb_hcd[bus->hcd.hcd_id].pipe_pool[i].waitsem != NULL);
}
cfg_num = usbh_get_musb_fifo_cfg(&cfg);
diff --git a/port/ohci/usb_hc_ohci.c b/port/ohci/usb_hc_ohci.c
index 2bec2a03..8ed10165 100644
--- a/port/ohci/usb_hc_ohci.c
+++ b/port/ohci/usb_hc_ohci.c
@@ -50,6 +50,7 @@ int ohci_init(struct usbh_bus *bus)
for (uint8_t index = 0; index < CONFIG_USB_OHCI_ED_NUM; index++) {
ed = &g_ohci_ed_pool[bus->hcd.hcd_id][index];
ed->waitsem = usb_osal_sem_create(0);
+ USB_ASSERT(ed->waitsem != NULL);
}
USB_LOG_INFO("OHCI hcrevision:0x%02x\r\n", (unsigned int)OHCI_HCOR->hcrevision);
diff --git a/port/rp2040/usb_hc_rp2040.c b/port/rp2040/usb_hc_rp2040.c
index e9751b88..87c88d09 100644
--- a/port/rp2040/usb_hc_rp2040.c
+++ b/port/rp2040/usb_hc_rp2040.c
@@ -292,10 +292,7 @@ int usb_hc_init(struct usbh_bus *bus)
for (uint8_t i = 0; i <= USB_HOST_INTERRUPT_ENDPOINTS; i++) {
g_rp2040_hcd[bus->hcd.hcd_id].pipe_pool[i].waitsem = usb_osal_sem_create(0);
- if (g_rp2040_hcd[bus->hcd.hcd_id].pipe_pool[i].waitsem == NULL) {
- USB_LOG_ERR("Failed to create waitsem\r\n");
- return -USB_ERR_NOMEM;
- }
+ USB_ASSERT(g_rp2040_hcd[bus->hcd.hcd_id].pipe_pool[i].waitsem != NULL);
}
g_rp2040_hcd[bus->hcd.hcd_id].ep0_mutex = usb_osal_mutex_create();