summaryrefslogtreecommitdiff
path: root/class
diff options
context:
space:
mode:
Diffstat (limited to 'class')
-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
10 files changed, 720 insertions, 50 deletions
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 */