summaryrefslogtreecommitdiff
path: root/class
diff options
context:
space:
mode:
Diffstat (limited to 'class')
-rw-r--r--class/adb/usbd_adb.c616
-rw-r--r--class/adb/usbd_adb.h68
-rw-r--r--class/adb/usbd_adb_cherrysh.c355
-rw-r--r--class/adb/usbd_adb_sync.c932
-rw-r--r--class/aoa/usbh_aoa.c2
-rw-r--r--class/audio/usb_audio.h40
-rw-r--r--class/audio/usbd_audio.c146
-rw-r--r--class/audio/usbd_audio.h5
-rw-r--r--class/audio/usbh_audio.c33
-rw-r--r--class/cdc/usb_cdc.h86
-rw-r--r--class/cdc/usbd_cdc_ecm.c33
-rw-r--r--class/cdc/usbd_cdc_ncm.c545
-rw-r--r--class/cdc/usbd_cdc_ncm.h41
-rw-r--r--class/cdc/usbh_cdc_ecm.c22
-rw-r--r--class/cdc/usbh_cdc_ncm.c46
-rw-r--r--class/gs_usb/gs_usb.h319
-rw-r--r--class/gs_usb/usbd_gs_usb.c113
-rw-r--r--class/gs_usb/usbd_gs_usb.h18
-rw-r--r--class/hid/usbh_hid.c12
-rw-r--r--class/hub/usbh_hub.c24
-rw-r--r--class/msc/usbd_msc.c58
-rw-r--r--class/msc/usbh_msc.c15
-rw-r--r--class/mtp/usbd_mtp.h76
-rw-r--r--class/serial/usbh_cdc_acm.c2
-rw-r--r--class/serial/usbh_ch34x.c2
-rw-r--r--class/serial/usbh_cp210x.c2
-rw-r--r--class/serial/usbh_ftdi.c4
-rw-r--r--class/serial/usbh_pl2303.c8
-rw-r--r--class/serial/usbh_serial.c85
-rw-r--r--class/serial/usbh_serial.h13
-rw-r--r--class/tmc/usb_tmc.h149
-rw-r--r--class/tmc/usbd_tmc.c258
-rw-r--r--class/tmc/usbd_tmc.h27
-rw-r--r--class/vendor/net/usbh_asix.c46
-rw-r--r--class/vendor/net/usbh_rtl8152.c46
-rw-r--r--class/video/usbh_video.c2
-rw-r--r--class/wireless/usbd_rndis.c57
-rw-r--r--class/wireless/usbh_rndis.c283
38 files changed, 3894 insertions, 695 deletions
diff --git a/class/adb/usbd_adb.c b/class/adb/usbd_adb.c
index dc664309..b82ab998 100644
--- a/class/adb/usbd_adb.c
+++ b/class/adb/usbd_adb.c
@@ -1,203 +1,367 @@
/*
- * Copyright (c) 2024, sakumisu
+ * Copyright (c) 2024 ~ 2026, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbd_core.h"
#include "usbd_adb.h"
-#define ADB_OUT_EP_IDX 0
-#define ADB_IN_EP_IDX 1
+#undef USB_DBG_TAG
+#define USB_DBG_TAG "usbd_adb"
+#include "usb_log.h"
-#define ADB_STATE_READ_MSG 0
-#define ADB_STATE_READ_DATA 1
-#define ADB_STATE_WRITE_MSG 2
-#define ADB_STATE_WRITE_DATA 3
-#define ADB_STATE_AWRITE_MSG 4
-#define ADB_STATE_AWRITE_DATA 5
+#define ADB_OUT_EP_IDX 0u
+#define ADB_IN_EP_IDX 1u
-#define MAX_PAYLOAD_V1 (4 * 1024)
-#define MAX_PAYLOAD_V2 (256 * 1024)
-#define MAX_PAYLOAD MAX_PAYLOAD_V1
-#define A_VERSION 0x01000000
+#define ADB_RX_STATE_READ_MSG 0u /* armed, waiting for the 24-byte header */
+#define ADB_RX_STATE_READ_DATA 1u /* armed, waiting for the payload */
+#define ADB_RX_STATE_COMPLETE 2u /* full packet ready, ISR already notified */
-#define A_SYNC 0x434e5953
-#define A_CNXN 0x4e584e43
-#define A_OPEN 0x4e45504f
-#define A_OKAY 0x59414b4f
-#define A_CLSE 0x45534c43
-#define A_WRTE 0x45545257
-#define A_AUTH 0x48545541
+#define ADB_TX_STATE_MSG 0u /* 24-byte header in flight */
+#define ADB_TX_STATE_DATA 1u /* payload in flight */
+#define ADB_TX_STATE_COMPLETE 2u /* transfer finished, the next send may start */
-struct adb_msg {
- uint32_t command; /* command identifier constant (A_CNXN, ...) */
- uint32_t arg0; /* first argument */
- uint32_t arg1; /* second argument */
- uint32_t data_length; /* length of payload (0 is allowed) */
- uint32_t data_crc32; /* crc32 of data payload */
- uint32_t magic; /* command ^ 0xffffffff */
-};
+#define ADB_DAEMON_THREAD_STACK 4096u
+#define ADB_DAEMON_THREAD_PRIO 10u
-struct adb_packet {
- USB_MEM_ALIGNX struct adb_msg msg;
- USB_MEM_ALIGNX uint8_t payload[USB_ALIGN_UP(MAX_PAYLOAD, CONFIG_USB_ALIGN_SIZE)];
-};
+struct usbd_adb_priv {
+ struct usbd_endpoint ep_data[2];
+ uint8_t busid;
-struct usbd_adb {
- uint8_t state;
- uint8_t common_state;
- uint8_t write_state;
- bool writable;
- uint32_t localid;
- uint32_t shell_remoteid;
- uint32_t file_remoteid;
-} adb_client;
+ const struct adb_service *svc_tab[ADB_MAX_SERVICE];
+ uint32_t remote_id[ADB_MAX_SERVICE];
+ bool svc_opened[ADB_MAX_SERVICE];
-static struct usbd_endpoint adb_ep_data[2];
+ volatile uint8_t rx_state;
+ volatile uint8_t tx_state;
-USB_NOCACHE_RAM_SECTION struct adb_packet tx_packet;
-USB_NOCACHE_RAM_SECTION struct adb_packet rx_packet;
+ usb_osal_sem_t rx_sem;
+ usb_osal_sem_t tx_sem;
+ usb_osal_mutex_t tx_lock;
+ usb_osal_thread_t rx_thread;
+} g_usbd_adb;
-static inline uint32_t adb_packet_checksum(struct adb_packet *packet)
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX struct adb_packet adb_tx_packet;
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX struct adb_packet adb_rx_packet;
+
+static uint32_t adb_packet_checksum(const struct adb_packet *packet)
{
uint32_t sum = 0;
uint32_t i;
- for (i = 0; i < packet->msg.data_length; ++i) {
- sum += (uint32_t)(packet->payload[i]);
+ for (i = 0; i < packet->msg.data_length; i++) {
+ sum += (uint32_t)packet->payload[i];
}
return sum;
}
-static uint32_t usbd_adb_get_remoteid(uint32_t localid)
+static int adb_svc_slot(uint32_t localid)
{
- if (localid == ADB_SHELL_LOALID) {
- return adb_client.shell_remoteid;
- } else {
- return adb_client.file_remoteid;
+ uint32_t i;
+
+ for (i = 0; i < ADB_MAX_SERVICE; i++) {
+ if (g_usbd_adb.svc_tab[i] && (g_usbd_adb.svc_tab[i]->localid == localid)) {
+ return (int)i;
+ }
}
+
+ return -1;
}
-static void adb_send_msg(struct adb_packet *packet)
+int usbd_adb_service_register(const struct adb_service *svc)
{
- adb_client.common_state = ADB_STATE_WRITE_MSG;
+ uint32_t i;
+
+ if (!svc || !svc->name || (svc->localid == 0u)) {
+ return -1;
+ }
- packet->msg.data_crc32 = adb_packet_checksum(packet);
- packet->msg.magic = packet->msg.command ^ 0xffffffff;
+ if (adb_svc_slot(svc->localid) >= 0) {
+ return -1; /* local id already taken */
+ }
- usbd_ep_start_write(0, adb_ep_data[ADB_IN_EP_IDX].ep_addr, (uint8_t *)&packet->msg, sizeof(struct adb_msg));
+ for (i = 0; i < ADB_MAX_SERVICE; i++) {
+ if (g_usbd_adb.svc_tab[i] == NULL) {
+ g_usbd_adb.svc_tab[i] = svc;
+ g_usbd_adb.remote_id[i] = 0;
+ g_usbd_adb.svc_opened[i] = false;
+ return 0;
+ }
+ }
+
+ return -1; /* table full */
}
-static void adb_send_okay(struct adb_packet *packet, uint32_t localid)
+static uint32_t usbd_adb_service_get_remoteid(uint32_t localid)
{
- packet->msg.command = A_OKAY;
- packet->msg.arg0 = localid;
- packet->msg.arg1 = usbd_adb_get_remoteid(localid);
- packet->msg.data_length = 0;
+ int slot = adb_svc_slot(localid);
- adb_send_msg(&tx_packet);
+ return (slot >= 0) ? g_usbd_adb.remote_id[slot] : 0u;
}
-static void adb_send_close(struct adb_packet *packet, uint32_t localid, uint32_t remoteid)
+static bool usbd_adb_service_is_opened(uint32_t localid)
{
- packet->msg.command = A_CLSE;
- packet->msg.arg0 = localid;
- packet->msg.arg1 = remoteid;
- packet->msg.data_length = 0;
+ int slot = adb_svc_slot(localid);
- adb_send_msg(&tx_packet);
+ return (slot >= 0) ? g_usbd_adb.svc_opened[slot] : false;
}
-void usbd_adb_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
+static int usbd_adb_send_internal(uint32_t command,
+ uint32_t arg0,
+ uint32_t arg1,
+ const uint8_t *data,
+ uint32_t len)
{
- (void)ep;
+ int ret;
- 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));
+ if (len && !data) {
+ return -1;
+ }
- USB_LOG_DBG("command:%x arg0:%x arg1:%x len:%d\r\n",
- rx_packet.msg.command,
- rx_packet.msg.arg0,
- rx_packet.msg.arg1,
- rx_packet.msg.data_length);
+ /* one transmitter at a time */
+ usb_osal_mutex_take(g_usbd_adb.tx_lock);
- if (rx_packet.msg.data_length) {
- /* setup next out ep read transfer */
- adb_client.common_state = ADB_STATE_READ_DATA;
- usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr, rx_packet.payload, rx_packet.msg.data_length);
- } else {
- if (rx_packet.msg.command == A_CLSE) {
- adb_client.writable = false;
- usbd_adb_notify_write_done();
- USB_LOG_INFO("Close remoteid:%x\r\n", rx_packet.msg.arg0);
- }
- adb_client.common_state = ADB_STATE_READ_MSG;
- /* setup first out ep read transfer */
- usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr, (uint8_t *)&rx_packet.msg, sizeof(struct adb_msg));
+ adb_tx_packet.msg.command = command;
+ adb_tx_packet.msg.arg0 = arg0;
+ adb_tx_packet.msg.arg1 = arg1;
+ adb_tx_packet.msg.data_length = len;
+ if (len) {
+ memcpy(adb_tx_packet.payload, data, len);
+ }
+ adb_tx_packet.msg.data_crc32 = adb_packet_checksum(&adb_tx_packet);
+ adb_tx_packet.msg.magic = command ^ 0xffffffffu;
+
+ /* drop a stale completion left behind by a previous timeout */
+ usb_osal_sem_reset(g_usbd_adb.tx_sem);
+
+ g_usbd_adb.tx_state = ADB_TX_STATE_MSG;
+ usbd_ep_start_write(g_usbd_adb.busid, g_usbd_adb.ep_data[ADB_IN_EP_IDX].ep_addr,
+ (uint8_t *)&adb_tx_packet.msg, sizeof(struct adb_msg));
+
+ ret = usb_osal_sem_take(g_usbd_adb.tx_sem, ADB_TX_TIMEOUT_MS);
+ if (ret != 0) {
+ USB_LOG_ERR("tx timeout, cmd %08x len %u\r\n", (unsigned)command, (unsigned)len);
+ g_usbd_adb.tx_state = ADB_TX_STATE_COMPLETE;
+ ret = -2;
+ } else {
+ ret = 0;
+ }
+
+ usb_osal_mutex_give(g_usbd_adb.tx_lock);
+
+ return ret;
+}
+
+static void adb_reply_okay(uint32_t localid, uint32_t remoteid)
+{
+ usbd_adb_send_internal(A_OKAY, localid, remoteid, NULL, 0);
+}
+
+static void adb_reply_close(uint32_t localid, uint32_t remoteid)
+{
+ usbd_adb_send_internal(A_CLSE, localid, remoteid, NULL, 0);
+}
+
+static void adb_handle_cnxn(struct adb_packet *pkt)
+{
+ static const char support_feature[] =
+ "device::"
+ "ro.product.name=cherryadb;"
+ "ro.product.model=cherrysh;"
+ "ro.product.device=cherryadb;"
+ "features=cmd,shell_v1";
+
+ (void)pkt;
+
+ usbd_adb_send_internal(A_CNXN, ADB_VERSION, ADB_MAX_PAYLOAD,
+ (const uint8_t *)support_feature, strlen(support_feature));
+}
+
+static void adb_handle_open(struct adb_packet *pkt)
+{
+ uint32_t remoteid = pkt->msg.arg0;
+ char *dest = (char *)pkt->payload;
+ uint32_t i;
+
+ if ((pkt->msg.data_length == 0) || (pkt->msg.data_length >= ADB_MAX_PAYLOAD)) {
+ adb_reply_close(0, remoteid);
+ return;
+ }
+
+ /* the destination string is not guaranteed to be NUL terminated */
+ dest[pkt->msg.data_length] = '\0';
+
+ for (i = 0; i < ADB_MAX_SERVICE; i++) {
+ const struct adb_service *svc = g_usbd_adb.svc_tab[i];
+ size_t name_len;
+
+ if (!svc) {
+ continue;
+ }
+
+ name_len = strlen(svc->name);
+ if (strncmp(dest, svc->name, name_len) != 0) {
+ continue;
}
- } else if (adb_client.common_state == ADB_STATE_READ_DATA) {
- switch (rx_packet.msg.command) {
- case A_SYNC:
- break;
- case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */
- {
- char *support_feature = "device::"
- "ro.product.name=cherryadb;"
- "ro.product.model=cherrysh;"
- "ro.product.device=cherryadb;"
- "features=cmd,shell_v1";
+ g_usbd_adb.remote_id[i] = remoteid;
+ g_usbd_adb.svc_opened[i] = true;
+
+ if (svc->on_open) {
+ svc->on_open(remoteid);
+ }
+
+ adb_reply_okay(svc->localid, remoteid);
+ return;
+ }
+
+ USB_LOG_WRN("unsupported destination '%s'\r\n", dest);
+ adb_reply_close(0, remoteid);
+}
+
+static void adb_handle_write(struct adb_packet *pkt)
+{
+ uint32_t remoteid = pkt->msg.arg0;
+ uint32_t localid = pkt->msg.arg1;
+ int slot = adb_svc_slot(localid);
+
+ if ((slot < 0) || !g_usbd_adb.svc_opened[slot] || (g_usbd_adb.remote_id[slot] != remoteid)) {
+ adb_reply_close(0, remoteid);
+ return;
+ }
+
+ if (g_usbd_adb.svc_tab[slot]->on_write) {
+ g_usbd_adb.svc_tab[slot]->on_write(remoteid, pkt->payload, pkt->msg.data_length);
+ }
+
+ adb_reply_okay(localid, remoteid);
+}
+
+static void adb_handle_close(struct adb_packet *pkt)
+{
+ uint32_t remoteid = pkt->msg.arg0;
+ uint32_t localid = pkt->msg.arg1;
+ int slot = adb_svc_slot(localid);
+
+ if (slot < 0) {
+ return;
+ }
+
+ g_usbd_adb.svc_opened[slot] = false;
+
+ if (g_usbd_adb.svc_tab[slot]->on_close) {
+ g_usbd_adb.svc_tab[slot]->on_close(remoteid);
+ }
+
+ g_usbd_adb.remote_id[slot] = 0;
+}
+
+static void adb_dispatch(struct adb_packet *pkt)
+{
+ switch (pkt->msg.command) {
+ case A_CNXN:
+ adb_handle_cnxn(pkt);
+ break;
+ case A_OPEN:
+ adb_handle_open(pkt);
+ break;
+ case A_WRTE:
+ adb_handle_write(pkt);
+ break;
+ case A_CLSE:
+ adb_handle_close(pkt);
+ break;
+ case A_OKAY:
+ /* the host acknowledged one of our writes, nothing to answer */
+ break;
+ case A_SYNC:
+ case A_AUTH:
+ break;
+ default:
+ USB_LOG_WRN("unknown command 0x%08x\r\n", (unsigned)pkt->msg.command);
+ break;
+ }
+}
+
+static void adb_handle_packet(struct adb_packet *pkt)
+{
+ if (pkt->msg.magic != (pkt->msg.command ^ 0xffffffffu)) {
+ USB_LOG_ERR("bad magic 0x%08x for command 0x%08x\r\n",
+ (unsigned)pkt->msg.magic, (unsigned)pkt->msg.command);
+ return;
+ }
+
+ adb_dispatch(pkt);
+}
+
+static void adb_rx_thread_entry(CONFIG_USB_OSAL_THREAD_SET_ARGV)
+{
+ (void)argument;
- tx_packet.msg.command = A_CNXN;
- tx_packet.msg.arg0 = A_VERSION;
- tx_packet.msg.arg1 = MAX_PAYLOAD;
- tx_packet.msg.data_length = strlen(support_feature);
- memcpy(tx_packet.payload, support_feature, strlen(support_feature));
+ while (1) {
+ if (usb_osal_sem_take(g_usbd_adb.rx_sem, USB_OSAL_WAITING_FOREVER) != 0) {
+ continue;
+ }
- adb_send_msg(&tx_packet);
+ adb_handle_packet(&adb_rx_packet);
- adb_client.writable = false;
- } break;
- case A_OPEN: /* OPEN(local-id, 0, "destination") */
- rx_packet.payload[rx_packet.msg.data_length] = '\0';
+ /* the packet has been consumed, arm the OUT endpoint again */
+ g_usbd_adb.rx_state = ADB_RX_STATE_READ_MSG;
+ usbd_ep_start_read(g_usbd_adb.busid, g_usbd_adb.ep_data[ADB_OUT_EP_IDX].ep_addr,
+ (uint8_t *)&adb_rx_packet.msg, sizeof(struct adb_msg));
+ }
+}
- if (strncmp((const char *)rx_packet.payload, "shell:", 6) == 0) {
- adb_client.localid = ADB_SHELL_LOALID;
- adb_client.shell_remoteid = rx_packet.msg.arg0;
- adb_send_okay(&tx_packet, ADB_SHELL_LOALID);
+void usbd_adb_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
+{
+ (void)ep;
- USB_LOG_INFO("Open shell service, remoteid:%x\r\n", rx_packet.msg.arg0);
- } else if (strncmp((const char *)rx_packet.payload, "sync:", 5) == 0) {
- adb_client.localid = ADB_FILE_LOALID;
- adb_client.file_remoteid = rx_packet.msg.arg0;
- adb_send_okay(&tx_packet, ADB_FILE_LOALID);
- USB_LOG_INFO("Open file service, remoteid:%x\r\n", rx_packet.msg.arg0);
- }
- break;
- case A_OKAY:
+ if (nbytes == 0) {
+ g_usbd_adb.rx_state = ADB_RX_STATE_READ_MSG;
+ usbd_ep_start_read(busid, g_usbd_adb.ep_data[ADB_OUT_EP_IDX].ep_addr, (uint8_t *)&adb_rx_packet.msg,
+ sizeof(struct adb_msg));
+ return;
+ }
- break;
- case A_CLSE:
+ if (g_usbd_adb.rx_state == ADB_RX_STATE_READ_MSG) {
+ if (nbytes != sizeof(struct adb_msg)) {
+ USB_LOG_ERR("short header %u bytes\r\n", (unsigned)nbytes);
+ g_usbd_adb.rx_state = ADB_RX_STATE_READ_MSG;
+ usbd_ep_start_read(busid, g_usbd_adb.ep_data[ADB_OUT_EP_IDX].ep_addr, (uint8_t *)&adb_rx_packet.msg,
+ sizeof(struct adb_msg));
+ return;
+ }
- break;
- case A_WRTE: /* WRITE(local-id, remote-id, "data") */
- if ((rx_packet.msg.arg0 == adb_client.shell_remoteid) && (rx_packet.msg.arg1 == ADB_SHELL_LOALID)) {
- adb_send_okay(&tx_packet, rx_packet.msg.arg1);
- } else if ((rx_packet.msg.arg0 == adb_client.file_remoteid) && (rx_packet.msg.arg1 == ADB_FILE_LOALID)) {
- adb_send_okay(&tx_packet, rx_packet.msg.arg1);
- } else {
- adb_send_close(&tx_packet, 0, rx_packet.msg.arg0);
- }
- break;
- case A_AUTH:
+ if (adb_rx_packet.msg.data_length > ADB_MAX_PAYLOAD) {
+ USB_LOG_ERR("header length %u too large\r\n",
+ (unsigned)adb_rx_packet.msg.data_length);
+ g_usbd_adb.rx_state = ADB_RX_STATE_READ_MSG;
+ usbd_ep_start_read(busid, g_usbd_adb.ep_data[ADB_OUT_EP_IDX].ep_addr, (uint8_t *)&adb_rx_packet.msg,
+ sizeof(struct adb_msg));
+ return;
+ }
- break;
+ if (adb_rx_packet.msg.data_length) {
+ USB_LOG_DBG("rx cmd 0x%08x arg0 0x%x arg1 0x%x len %u\r\n",
+ (unsigned)adb_rx_packet.msg.command,
+ (unsigned)adb_rx_packet.msg.arg0,
+ (unsigned)adb_rx_packet.msg.arg1,
+ (unsigned)adb_rx_packet.msg.data_length);
- default:
- break;
+ g_usbd_adb.rx_state = ADB_RX_STATE_READ_DATA;
+ usbd_ep_start_read(busid, g_usbd_adb.ep_data[ADB_OUT_EP_IDX].ep_addr, adb_rx_packet.payload,
+ adb_rx_packet.msg.data_length);
+ } else {
+ /* no payload -> the packet is already complete */
+ g_usbd_adb.rx_state = ADB_RX_STATE_COMPLETE;
+ usb_osal_sem_give(g_usbd_adb.rx_sem);
}
+ } else if (g_usbd_adb.rx_state == ADB_RX_STATE_READ_DATA) {
+ /* payload complete -> the packet is complete */
+ g_usbd_adb.rx_state = ADB_RX_STATE_COMPLETE;
+ usb_osal_sem_give(g_usbd_adb.rx_sem);
+ } else {
}
}
@@ -206,104 +370,152 @@ void usbd_adb_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
(void)ep;
(void)nbytes;
- if (adb_client.common_state == ADB_STATE_WRITE_MSG) {
- if (tx_packet.msg.data_length) {
- adb_client.common_state = ADB_STATE_WRITE_DATA;
- usbd_ep_start_write(busid, adb_ep_data[ADB_IN_EP_IDX].ep_addr, tx_packet.payload, tx_packet.msg.data_length);
- } else {
- if (rx_packet.msg.command == A_WRTE) {
- adb_client.writable = true;
- if (adb_client.localid == ADB_SHELL_LOALID) {
- usbd_adb_notify_shell_read(rx_packet.payload, rx_packet.msg.data_length);
- } else {
- }
- }
- adb_client.common_state = ADB_STATE_READ_MSG;
- /* setup first out ep read transfer */
- usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr, (uint8_t *)&rx_packet.msg, sizeof(struct adb_msg));
- }
- } else if (adb_client.common_state == ADB_STATE_WRITE_DATA) {
- adb_client.common_state = ADB_STATE_READ_MSG;
- /* setup first out ep read transfer */
- usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr, (uint8_t *)&rx_packet.msg, sizeof(struct adb_msg));
- } else if (adb_client.write_state == ADB_STATE_AWRITE_MSG) {
- if (tx_packet.msg.data_length) {
- adb_client.write_state = ADB_STATE_AWRITE_DATA;
- usbd_ep_start_write(busid, adb_ep_data[ADB_IN_EP_IDX].ep_addr, tx_packet.payload, tx_packet.msg.data_length);
+ if ((nbytes % usbd_get_ep_mps(busid, ep)) == 0 && nbytes) {
+ /* send zlp */
+ usbd_ep_start_write(busid, g_usbd_adb.ep_data[ADB_IN_EP_IDX].ep_addr, NULL, 0);
+ return;
+ }
+
+ if (g_usbd_adb.tx_state == ADB_TX_STATE_MSG) {
+ if (adb_tx_packet.msg.data_length) {
+ g_usbd_adb.tx_state = ADB_TX_STATE_DATA;
+ usbd_ep_start_write(busid, g_usbd_adb.ep_data[ADB_IN_EP_IDX].ep_addr,
+ adb_tx_packet.payload, adb_tx_packet.msg.data_length);
} else {
+ g_usbd_adb.tx_state = ADB_TX_STATE_COMPLETE;
+ usb_osal_sem_give(g_usbd_adb.tx_sem);
}
- } else if (adb_client.write_state == ADB_STATE_AWRITE_DATA) {
- usbd_adb_notify_write_done();
+ } else if (g_usbd_adb.tx_state == ADB_TX_STATE_DATA) {
+ g_usbd_adb.tx_state = ADB_TX_STATE_COMPLETE;
+ usb_osal_sem_give(g_usbd_adb.tx_sem);
}
}
-void adb_notify_handler(uint8_t busid, uint8_t event, void *arg)
+static void adb_notify_handler(uint8_t busid, uint8_t event, void *arg)
{
(void)arg;
switch (event) {
- case USBD_EVENT_INIT:
- break;
- case USBD_EVENT_DEINIT:
+ case USBD_EVENT_CONFIGURED:
+ g_usbd_adb.rx_state = ADB_RX_STATE_READ_MSG;
+ g_usbd_adb.tx_state = ADB_TX_STATE_COMPLETE;
+ usbd_ep_start_read(busid, g_usbd_adb.ep_data[ADB_OUT_EP_IDX].ep_addr,
+ (uint8_t *)&adb_rx_packet.msg, sizeof(struct adb_msg));
break;
+
case USBD_EVENT_RESET:
- break;
- case USBD_EVENT_CONFIGURED:
- adb_client.common_state = ADB_STATE_READ_MSG;
- /* setup first out ep read transfer */
- usbd_ep_start_read(busid, adb_ep_data[ADB_OUT_EP_IDX].ep_addr, (uint8_t *)&rx_packet.msg, sizeof(struct adb_msg));
+ for (uint32_t i = 0; i < ADB_MAX_SERVICE; i++) {
+ g_usbd_adb.svc_opened[i] = false;
+ g_usbd_adb.remote_id[i] = 0;
+ }
break;
+ case USBD_EVENT_INIT:
+ break;
+ case USBD_EVENT_DEINIT:
+ break;
default:
break;
}
}
-struct usbd_interface *usbd_adb_init_intf(uint8_t busid, struct usbd_interface *intf, uint8_t in_ep, uint8_t out_ep)
+/* -------------------------------------------------------------------------
+ * initialisation
+ * ---------------------------------------------------------------------- */
+struct usbd_interface *usbd_adb_init_intf(uint8_t busid, struct usbd_interface *intf,
+ uint8_t in_ep, uint8_t out_ep)
{
- (void)busid;
+ g_usbd_adb.busid = busid;
+
+ g_usbd_adb.ep_data[ADB_OUT_EP_IDX].ep_addr = out_ep;
+ g_usbd_adb.ep_data[ADB_OUT_EP_IDX].ep_cb = usbd_adb_bulk_out;
+ g_usbd_adb.ep_data[ADB_IN_EP_IDX].ep_addr = in_ep;
+ g_usbd_adb.ep_data[ADB_IN_EP_IDX].ep_cb = usbd_adb_bulk_in;
+
+ usbd_add_endpoint(busid, &g_usbd_adb.ep_data[ADB_OUT_EP_IDX]);
+ usbd_add_endpoint(busid, &g_usbd_adb.ep_data[ADB_IN_EP_IDX]);
intf->class_interface_handler = NULL;
intf->class_endpoint_handler = NULL;
intf->vendor_handler = NULL;
intf->notify_handler = adb_notify_handler;
- adb_ep_data[ADB_OUT_EP_IDX].ep_addr = out_ep;
- adb_ep_data[ADB_OUT_EP_IDX].ep_cb = usbd_adb_bulk_out;
- adb_ep_data[ADB_IN_EP_IDX].ep_addr = in_ep;
- adb_ep_data[ADB_IN_EP_IDX].ep_cb = usbd_adb_bulk_in;
+ g_usbd_adb.rx_sem = usb_osal_sem_create(0);
+ USB_ASSERT_MSG(g_usbd_adb.rx_sem, "adb rx_sem fail");
+ g_usbd_adb.tx_sem = usb_osal_sem_create(0);
+ USB_ASSERT_MSG(g_usbd_adb.tx_sem, "adb tx_sem fail");
+ g_usbd_adb.tx_lock = usb_osal_mutex_create();
+ USB_ASSERT_MSG(g_usbd_adb.tx_lock, "adb tx_lock fail");
- usbd_add_endpoint(busid, &adb_ep_data[ADB_OUT_EP_IDX]);
- usbd_add_endpoint(busid, &adb_ep_data[ADB_IN_EP_IDX]);
+ g_usbd_adb.rx_thread = usb_osal_thread_create("usbd_adb", ADB_DAEMON_THREAD_STACK,
+ ADB_DAEMON_THREAD_PRIO, adb_rx_thread_entry, NULL);
+ USB_ASSERT_MSG(g_usbd_adb.rx_thread, "adb rx_thread fail");
+ usbd_adb_shell_init();
+ usbd_adb_sync_init();
return intf;
}
-bool usbd_adb_can_write(void)
+int usbd_adb_write(uint32_t localid,
+ const uint8_t *data,
+ uint32_t len)
{
- return adb_client.writable;
-}
+ uint32_t sent = 0;
-int usbd_abd_write(uint32_t localid, const uint8_t *data, uint32_t len)
-{
- struct adb_packet *packet;
+ if (usbd_adb_service_is_opened(localid) == false) {
+ return -1;
+ }
- packet = &tx_packet;
- packet->msg.command = A_WRTE;
- packet->msg.arg0 = localid;
- packet->msg.arg1 = usbd_adb_get_remoteid(localid);
- packet->msg.data_length = len;
- memcpy(packet->payload, data, len);
+ uint32_t remoteid = usbd_adb_service_get_remoteid(localid);
+ if (!remoteid) {
+ return -1;
+ }
- packet->msg.data_crc32 = adb_packet_checksum(packet);
- packet->msg.magic = packet->msg.command ^ 0xffffffff;
+ if (!len) {
+ return 0;
+ }
+
+ while (sent < len) {
+ uint32_t chunk = len - sent;
+ int ret;
+
+ if (chunk > ADB_MAX_PAYLOAD) {
+ chunk = ADB_MAX_PAYLOAD;
+ }
+
+ ret = usbd_adb_send_internal(A_WRTE,
+ localid,
+ remoteid,
+ data + sent,
+ chunk);
+ if (ret != 0) {
+ return ret;
+ }
+
+ sent += chunk;
+ }
- adb_client.write_state = ADB_STATE_AWRITE_MSG;
- usbd_ep_start_write(0, adb_ep_data[ADB_IN_EP_IDX].ep_addr, (uint8_t *)&packet->msg, sizeof(struct adb_msg));
return 0;
}
void usbd_adb_close(uint32_t localid)
{
- adb_send_close(&tx_packet, 0, usbd_adb_get_remoteid(localid));
+ if (usbd_adb_service_is_opened(localid) == false) {
+ return;
+ }
+
+ uint32_t remoteid = usbd_adb_service_get_remoteid(localid);
+ if (!remoteid) {
+ return;
+ }
+
+ usbd_adb_send_internal(A_CLSE, localid, remoteid, NULL, 0);
+}
+
+__WEAK void usbd_adb_shell_init(void)
+{
+}
+
+__WEAK void usbd_adb_sync_init(void)
+{
} \ No newline at end of file
diff --git a/class/adb/usbd_adb.h b/class/adb/usbd_adb.h
index 98ec19ba..dae33236 100644
--- a/class/adb/usbd_adb.h
+++ b/class/adb/usbd_adb.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, sakumisu
+ * Copyright (c) 2024 ~ 2026, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -7,14 +7,59 @@
#define USBD_ADB_H
#include <stdint.h>
+#include "usb_vfs.h"
-#define ADB_SHELL_LOALID 0x01
-#define ADB_FILE_LOALID 0x02
+#define ADB_VERSION 0x01000000u
+#define ADB_MAX_PAYLOAD (4u * 1024u)
+#define ADB_TX_TIMEOUT_MS 5000u
+#define ADB_MAX_SERVICE 4u
+
+#define ADB_LOCALID_SHELL 0x01u
+#define ADB_LOCALID_SYNC 0x02u
+
+/* -------------------------------------------------------------------------
+ * adb packet commands
+ *
+ * The 4cc is stored as a little endian integer, exactly like the official
+ * adb implementation does (A_CNXN == 0x4e584e43 == "CNXN" on the wire).
+ * ---------------------------------------------------------------------- */
+#define ADB_FOURCC(c0, c1, c2, c3) ((uint32_t)(c0) | ((uint32_t)(c1) << 8) | \
+ ((uint32_t)(c2) << 16) | ((uint32_t)(c3) << 24))
+
+#define A_SYNC ADB_FOURCC('S', 'Y', 'N', 'C')
+#define A_CNXN ADB_FOURCC('C', 'N', 'X', 'N')
+#define A_OPEN ADB_FOURCC('O', 'P', 'E', 'N')
+#define A_OKAY ADB_FOURCC('O', 'K', 'A', 'Y')
+#define A_CLSE ADB_FOURCC('C', 'L', 'S', 'E')
+#define A_WRTE ADB_FOURCC('W', 'R', 'T', 'E')
+#define A_AUTH ADB_FOURCC('A', 'U', 'T', 'H')
+
+struct adb_msg {
+ uint32_t command; /* A_CNXN / A_OPEN / A_OKAY / A_CLSE / A_WRTE */
+ uint32_t arg0; /* first argument */
+ uint32_t arg1; /* second argument */
+ uint32_t data_length; /* length of the payload that follows */
+ uint32_t data_crc32; /* checksum of the payload (plain 32-bit sum) */
+ uint32_t magic; /* command ^ 0xffffffff */
+};
+
+struct adb_packet {
+ USB_MEM_ALIGNX struct adb_msg msg;
+ USB_MEM_ALIGNX uint8_t payload[ADB_MAX_PAYLOAD];
+};
+
+struct adb_service {
+ const char *name; /* destination prefix, e.g. "shell:" / "sync:" */
+ uint32_t localid; /* ADB_LOCALID_SHELL / ADB_LOCALID_FILE / ... */
+ void (*on_open)(uint32_t remoteid);
+ void (*on_close)(uint32_t remoteid);
+ void (*on_write)(uint32_t remoteid, const uint8_t *data, uint32_t len);
+};
// clang-format off
-#define ADB_DESCRIPTOR_INIT(bFirstInterface, in_ep, out_ep, wMaxPacketSize) \
+#define ADB_DESCRIPTOR_INIT(bFirstInterface, in_ep, out_ep, wMaxPacketSize) \
USB_INTERFACE_DESCRIPTOR_INIT(bFirstInterface, 0x00, 0x02, 0xff, 0x42, 0x01, 0x02), \
- USB_ENDPOINT_DESCRIPTOR_INIT(in_ep, 0x02, wMaxPacketSize, 0x00), \
+ USB_ENDPOINT_DESCRIPTOR_INIT(in_ep, 0x02, wMaxPacketSize, 0x00), \
USB_ENDPOINT_DESCRIPTOR_INIT(out_ep, 0x02, wMaxPacketSize, 0x00)
// clang-format on
@@ -23,14 +68,15 @@ extern "C" {
#endif
struct usbd_interface *usbd_adb_init_intf(uint8_t busid, struct usbd_interface *intf, uint8_t in_ep, uint8_t out_ep);
-
-void usbd_adb_notify_shell_read(uint8_t *data, uint32_t len);
-void usbd_adb_notify_file_read(uint8_t *data, uint32_t len);
-void usbd_adb_notify_write_done(void);
-bool usbd_adb_can_write(void);
-int usbd_abd_write(uint32_t localid, const uint8_t *data, uint32_t len);
+int usbd_adb_write(uint32_t localid,
+ const uint8_t *data,
+ uint32_t len);
void usbd_adb_close(uint32_t localid);
+int usbd_adb_service_register(const struct adb_service *svc);
+void usbd_adb_shell_init(void);
+void usbd_adb_sync_init(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/class/adb/usbd_adb_cherrysh.c b/class/adb/usbd_adb_cherrysh.c
new file mode 100644
index 00000000..61ed3b73
--- /dev/null
+++ b/class/adb/usbd_adb_cherrysh.c
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2024 ~ 2026, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+#include "usbd_adb.h"
+#include "csh.h"
+
+#include "FreeRTOS.h"
+#include "task.h"
+#include "event_groups.h"
+
+#ifndef CHERRYSH_TASK_REPL_PRIORITY
+#define CHERRYSH_TASK_REPL_PRIORITY (2)
+#endif
+
+#ifndef CHERRYSH_TASK_EXEC_PRIORITY
+#define CHERRYSH_TASK_EXEC_PRIORITY (1)
+#endif
+
+#ifndef CHERRYSH_TASK_REPL_STACK_SIZE
+#define CHERRYSH_TASK_REPL_STACK_SIZE 1024U
+#endif
+
+#ifndef CHERRYSH_TASK_EXEC_STACK_SIZE
+#define CHERRYSH_TASK_EXEC_STACK_SIZE 1024U
+#endif
+
+static chry_shell_t csh;
+static volatile bool login = false;
+static usb_ringbuffer_t shell_rb;
+static uint8_t mempool[1024];
+
+static TaskHandle_t task_hdl_repl = NULL;
+static TaskHandle_t task_hdl_exec = NULL;
+
+static EventGroupHandle_t event_hdl;
+static StaticEventGroup_t event_grp;
+
+static int shell_init(bool need_login);
+
+static void adb_shell_on_open(uint32_t remoteid)
+{
+ USB_LOG_INFO("adb shell open, remoteid:%u\r\n", (unsigned)remoteid);
+}
+
+static void adb_shell_on_close(uint32_t remoteid)
+{
+ USB_LOG_INFO("adb shell close, remoteid:%u\r\n", (unsigned)remoteid);
+}
+
+static void adb_shell_on_write(uint32_t remoteid, const uint8_t *data, uint32_t len)
+{
+ usb_ringbuffer_write(&shell_rb, (uint8_t *)data, len);
+ xEventGroupSetBits(event_hdl, 0x10);
+}
+
+static const struct adb_service adb_shell_service = {
+ .name = "shell:",
+ .localid = ADB_LOCALID_SHELL,
+ .on_open = adb_shell_on_open,
+ .on_close = adb_shell_on_close,
+ .on_write = adb_shell_on_write,
+};
+
+void usbd_adb_shell_init(void)
+{
+ if (usbd_adb_service_register(&adb_shell_service) != 0) {
+ USB_LOG_ERR("adb shell: register failed\r\n");
+ }
+ shell_init(false);
+}
+
+static uint16_t csh_sput_cb(chry_readline_t *rl, const void *data, uint16_t size)
+{
+ (void)rl;
+
+ usbd_adb_write(ADB_LOCALID_SHELL, data, size);
+
+ return size;
+}
+
+static uint16_t csh_sget_cb(chry_readline_t *rl, void *data, uint16_t size)
+{
+ (void)rl;
+ return usb_ringbuffer_read(&shell_rb, data, size);
+}
+
+static void wait_char(void)
+{
+ EventBits_t event;
+wait:
+ /* In order to lock the log from being disrupted , wait for REPL task execution to complete */
+ event = xEventGroupWaitBits(event_hdl, (0x10 | 0x01 | 0x04), pdTRUE, pdFALSE, portMAX_DELAY);
+ if ((event & 0x10) == 0) {
+ if (event & 0x01) {
+ chry_readline_erase_line(&csh.rl);
+ xEventGroupSetBits(event_hdl, 0x02);
+ }
+ if (event & 0x04) {
+ chry_readline_edit_refresh(&csh.rl);
+ xEventGroupSetBits(event_hdl, 0x08);
+ }
+
+ goto wait;
+ }
+}
+
+static void task_repl(void *param)
+{
+ (void)param;
+ int ret;
+ volatile uint8_t *pexec = (void *)&csh.exec;
+
+ for (;;) {
+ restart:
+ if (login) {
+ goto repl;
+ } else {
+ }
+
+ ret = csh_login(&csh);
+ if (ret == 0) {
+ login = true;
+ } else if (ret == 1) {
+ /*!< no enough char */
+ wait_char();
+ continue;
+ } else {
+ continue;
+ }
+
+ repl:
+ ret = chry_shell_task_repl(&csh);
+
+ if (ret == -1) {
+ /*!< error */
+ goto restart;
+ } else if (ret == 1) {
+ /*!< no enough char */
+ wait_char();
+ } else {
+ /*!< restart */
+ }
+
+ /*!< check flag */
+ if (*pexec == CSH_STATUS_EXEC_DONE) {
+ *pexec = CSH_STATUS_EXEC_IDLE;
+ chry_readline_auto_refresh(&csh.rl, true);
+ chry_readline_ignore(&csh.rl, false);
+ chry_readline_edit_refresh(&csh.rl);
+ }
+
+ if (login == false) {
+ chry_readline_erase_line(&csh.rl);
+ csh.rl.noblock = false;
+ }
+ }
+}
+
+static void task_exec(void *param)
+{
+ (void)param;
+
+ /*!< execute shell command */
+ chry_shell_task_exec(&csh);
+
+ /*!< notify REPL task execute done */
+ xEventGroupSetBits(event_hdl, 0x10);
+
+ /*!< wait for REPL task delete */
+ vTaskSuspend(NULL);
+}
+
+int chry_shell_port_create_context(chry_shell_t *csh, int argc, const char **argv)
+{
+ volatile TaskHandle_t *p_task_hdl_exec = (void *)&task_hdl_exec;
+ (void)csh;
+ (void)argc;
+ (void)argv;
+
+ if (*p_task_hdl_exec != NULL) {
+ vTaskDelete(*p_task_hdl_exec);
+ }
+
+ *p_task_hdl_exec = NULL;
+ if (xTaskCreate(task_exec, "task_exec", CHERRYSH_TASK_EXEC_STACK_SIZE, NULL, CHERRYSH_TASK_EXEC_PRIORITY, (TaskHandle_t *)p_task_hdl_exec) != pdPASS) {
+ return -1;
+ }
+ return 0;
+}
+
+void chry_shell_port_default_handler(chry_shell_t *csh, int sig)
+{
+ volatile uint8_t *pexec = (void *)&csh->exec;
+ volatile TaskHandle_t *p_task_hdl_exec = (void *)&task_hdl_exec;
+
+ switch (sig) {
+ case CSH_SIGINT:
+ case CSH_SIGQUIT:
+ case CSH_SIGKILL:
+ case CSH_SIGTERM:
+ break;
+ default:
+ return;
+ }
+
+ /*!< force delete task */
+ if (*p_task_hdl_exec != NULL) {
+ vTaskDelete(task_hdl_exec);
+ *p_task_hdl_exec = NULL;
+ }
+
+ switch (sig) {
+ case CSH_SIGINT:
+ csh->rl.sput(&csh->rl, "^SIGINT" CONFIG_CSH_NEWLINE, sizeof("^SIGINT" CONFIG_CSH_NEWLINE) - 1);
+ break;
+ case CSH_SIGQUIT:
+ csh->rl.sput(&csh->rl, "^SIGQUIT" CONFIG_CSH_NEWLINE, sizeof("^SIGQUIT" CONFIG_CSH_NEWLINE) - 1);
+ break;
+ case CSH_SIGKILL:
+ csh->rl.sput(&csh->rl, "^SIGKILL" CONFIG_CSH_NEWLINE, sizeof("^SIGKILL" CONFIG_CSH_NEWLINE) - 1);
+ break;
+ case CSH_SIGTERM:
+ csh->rl.sput(&csh->rl, "^SIGTERM" CONFIG_CSH_NEWLINE, sizeof("^SIGTERM" CONFIG_CSH_NEWLINE) - 1);
+ break;
+ default:
+ return;
+ }
+
+ *pexec = CSH_STATUS_EXEC_IDLE;
+ chry_readline_auto_refresh(&csh->rl, true);
+ chry_readline_ignore(&csh->rl, false);
+ chry_readline_edit_refresh(&csh->rl);
+}
+
+static int shell_init(bool need_login)
+{
+ chry_shell_init_t csh_init = {0};
+
+ if (usb_ringbuffer_init(&shell_rb, mempool, sizeof(mempool))) {
+ return -1;
+ }
+
+ if (need_login) {
+ login = false;
+ } else {
+ login = true;
+ }
+
+ /*!< I/O callback */
+ csh_init.sput = csh_sput_cb;
+ csh_init.sget = csh_sget_cb;
+
+#if defined(CONFIG_CSH_SYMTAB) && CONFIG_CSH_SYMTAB
+#if defined(__GNUC__)
+ extern const int __fsymtab_start;
+ extern const int __fsymtab_end;
+ extern const int __vsymtab_start;
+ extern const int __vsymtab_end;
+
+ /*!< get table from ld symbol */
+ csh_init.command_table_beg = &__fsymtab_start;
+ csh_init.command_table_end = &__fsymtab_end;
+ csh_init.variable_table_beg = &__vsymtab_start;
+ csh_init.variable_table_end = &__vsymtab_end;
+#elif defined(__ICCARM__) || defined(__ICCRX__) || defined(__ICCRISCV__)
+#pragma section="FSymTab"
+#pragma section="VSymTab"
+ csh_init.command_table_beg = __section_begin("FSymTab");
+ csh_init.command_table_end = __section_end("FSymTab");
+ csh_init.variable_table_beg = __section_begin("VSymTab");
+ csh_init.variable_table_end = __section_end("VSymTab");
+#endif
+#endif
+
+#if defined(CONFIG_CSH_PROMPTEDIT) && CONFIG_CSH_PROMPTEDIT
+ static char csh_prompt_buffer[128];
+
+ /*!< set prompt buffer */
+ csh_init.prompt_buffer = csh_prompt_buffer;
+ csh_init.prompt_buffer_size = sizeof(csh_prompt_buffer);
+#endif
+
+#if defined(CONFIG_CSH_HISTORY) && CONFIG_CSH_HISTORY
+ static char csh_history_buffer[128];
+
+ /*!< set history buffer */
+ csh_init.history_buffer = csh_history_buffer;
+ csh_init.history_buffer_size = sizeof(csh_history_buffer);
+#endif
+
+#if defined(CONFIG_CSH_LNBUFF_STATIC) && CONFIG_CSH_LNBUFF_STATIC
+ static char csh_line_buffer[128];
+
+ /*!< set linebuffer */
+ csh_init.line_buffer = csh_line_buffer;
+ csh_init.line_buffer_size = sizeof(csh_line_buffer);
+#endif
+
+ csh_init.uid = 0;
+ csh_init.user[0] = "cherry";
+
+ /*!< The port hash function is required,
+ and the strcmp attribute is used weakly by default,
+ int chry_shell_port_hash_strcmp(const char *hash, const char *str); */
+ csh_init.hash[0] = "12345678"; /*!< If there is no password, set to NULL */
+ csh_init.host = BOARD_NAME;
+ csh_init.user_data = NULL;
+
+ int ret = chry_shell_init(&csh, &csh_init);
+ if (ret) {
+ return -1;
+ }
+
+ task_hdl_exec = NULL;
+ event_hdl = xEventGroupCreateStatic(&event_grp);
+ task_hdl_repl = NULL;
+ if (xTaskCreate(task_repl, "task_repl", CHERRYSH_TASK_REPL_STACK_SIZE, NULL, CHERRYSH_TASK_REPL_PRIORITY, (TaskHandle_t *)&task_hdl_repl) != pdPASS) {
+ return -1;
+ }
+
+ return 0;
+}
+
+void shell_lock(void)
+{
+ xEventGroupSetBits(event_hdl, 0x01);
+ xEventGroupWaitBits(event_hdl, 0x02, pdTRUE, pdTRUE, portMAX_DELAY);
+}
+
+void shell_unlock(void)
+{
+ xEventGroupSetBits(event_hdl, 0x04);
+ xEventGroupWaitBits(event_hdl, 0x08, pdTRUE, pdTRUE, portMAX_DELAY);
+}
+
+static int csh_exit(int argc, char **argv)
+{
+ (void)argc;
+ (void)argv;
+
+ usbd_adb_close(ADB_LOCALID_SHELL);
+ return 0;
+}
+CSH_SCMD_EXPORT_ALIAS(csh_exit, exit, );
+
+#define __ENV_PATH "/sbin:/bin"
+const char ENV_PATH[] = __ENV_PATH;
+CSH_RVAR_EXPORT(ENV_PATH, PATH, sizeof(__ENV_PATH));
+
+#define __ENV_ZERO ""
+const char ENV_ZERO[] = __ENV_ZERO;
+CSH_RVAR_EXPORT(ENV_ZERO, ZERO, sizeof(__ENV_ZERO));
diff --git a/class/adb/usbd_adb_sync.c b/class/adb/usbd_adb_sync.c
new file mode 100644
index 00000000..b18e2b09
--- /dev/null
+++ b/class/adb/usbd_adb_sync.c
@@ -0,0 +1,932 @@
+/*
+ * Copyright (c) 2024 ~ 2026, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+#include "usbd_adb.h"
+
+#include "stdarg.h"
+
+#define ID_STAT ADB_FOURCC('S', 'T', 'A', 'T') /* "lstat_v1": the v1 stat */
+#define ID_LIST ADB_FOURCC('L', 'I', 'S', 'T')
+#define ID_DENT ADB_FOURCC('D', 'E', 'N', 'T')
+#define ID_SEND ADB_FOURCC('S', 'E', 'N', 'D')
+#define ID_RECV ADB_FOURCC('R', 'E', 'C', 'V')
+#define ID_DONE ADB_FOURCC('D', 'O', 'N', 'E')
+#define ID_DATA ADB_FOURCC('D', 'A', 'T', 'A')
+#define ID_OKAY ADB_FOURCC('O', 'K', 'A', 'Y')
+#define ID_FAIL ADB_FOURCC('F', 'A', 'I', 'L')
+#define ID_QUIT ADB_FOURCC('Q', 'U', 'I', 'T')
+
+struct adb_sync_wire_hdr {
+ uint32_t id;
+ uint32_t len;
+};
+
+struct adb_sync_wire_stat {
+ uint32_t id;
+ uint32_t mode;
+ uint32_t size;
+ uint32_t mtime;
+};
+
+struct adb_sync_wire_dent {
+ uint32_t id;
+ uint32_t mode;
+ uint32_t size;
+ uint32_t mtime;
+ uint32_t namelen;
+};
+
+/* The one size a request has: the client refuses to send anything whose path is
+ * longer than this (SendRequest(), and the same check on the "path,mode" of a
+ * SEND), so a body arriving here cannot be longer, and a body is all a request
+ * carries. Every path this file keeps is held with one byte over, for its
+ * terminator. */
+#define ADB_SYNC_PATH_MAX 1024u
+/* How much is read from the card and sent in one DATA record. The client sends
+ * up to 64KiB at a time and this side reassembles whatever arrives in whatever
+ * pieces it arrives in, so this only decides how big the records are - and it is
+ * what sizes the payload of the one record buffer, see struct adb_sync_packet. */
+#define ADB_SYNC_DATA_MAX 2048u
+#define ADB_SYNC_NAME_MAX VFS_MAX_PATHNAME
+/* Room for the longest reason this file builds, which is a path and an errno. */
+#define ADB_SYNC_MSG_MAX 160u
+
+#define SYNC_RX_STATE_HEADER 0u
+#define SYNC_RX_STATE_BODY 1u
+#define SYNC_RX_STATE_DATA 2u
+#define SYNC_RX_STATE_SKIP 3u
+
+struct adb_sync_packet {
+ struct adb_sync_wire_hdr hdr;
+ uint8_t payload[ADB_SYNC_DATA_MAX];
+};
+
+struct adb_sync_priv {
+ uint32_t req;
+
+ uint8_t rx_state;
+ uint32_t hdr_len;
+ uint32_t body_len;
+ uint32_t rx_remain;
+
+ bool push_failed;
+ bool push_isdir;
+ int fd;
+
+ bool have_target;
+ bool target_isdir;
+ char target[ADB_SYNC_PATH_MAX + 1u];
+
+ char path[ADB_SYNC_PATH_MAX + 1u];
+
+ struct adb_sync_packet packet;
+} g_adb_sync;
+
+static void adb_sync_send(const void *data, uint32_t len)
+{
+ if (len) {
+ usbd_adb_write(ADB_LOCALID_SYNC, (const uint8_t *)data, (uint32_t)len);
+ }
+}
+
+static void adb_sync_send_record(uint32_t id, const void *payload, uint32_t len)
+{
+ g_adb_sync.packet.hdr.id = id;
+ g_adb_sync.packet.hdr.len = len;
+
+ if (len) {
+ memcpy(g_adb_sync.packet.payload, payload, len);
+ }
+
+ adb_sync_send(&g_adb_sync.packet, sizeof(g_adb_sync.packet.hdr) + len);
+}
+
+static void adb_sync_send_data_record(uint32_t len)
+{
+ g_adb_sync.packet.hdr.id = ID_DATA;
+ g_adb_sync.packet.hdr.len = len;
+
+ adb_sync_send(&g_adb_sync.packet, sizeof(g_adb_sync.packet.hdr) + len);
+}
+
+static void adb_sync_send_stat(uint32_t mode, uint32_t size, uint32_t mtime)
+{
+ struct adb_sync_wire_stat *st = (struct adb_sync_wire_stat *)&g_adb_sync.packet;
+
+ st->id = ID_STAT;
+ st->mode = mode;
+ st->size = size;
+ st->mtime = mtime;
+
+ adb_sync_send(st, (uint32_t)sizeof(*st));
+}
+
+static void adb_sync_send_dent(uint32_t mode, uint32_t size, uint32_t mtime,
+ const char *name, uint32_t namelen)
+{
+ struct adb_sync_wire_dent *dent = (struct adb_sync_wire_dent *)&g_adb_sync.packet;
+
+ dent->id = ID_DENT;
+ dent->mode = mode;
+ dent->size = size;
+ dent->mtime = mtime;
+ dent->namelen = namelen;
+
+ memcpy((uint8_t *)dent + sizeof(*dent), name, namelen);
+
+ adb_sync_send(dent, (uint32_t)sizeof(*dent) + namelen);
+}
+
+static void adb_sync_send_list_end(void)
+{
+ struct adb_sync_wire_dent *dent = (struct adb_sync_wire_dent *)&g_adb_sync.packet;
+
+ memset(dent, 0, sizeof(*dent));
+ dent->id = ID_DONE;
+
+ adb_sync_send(dent, (uint32_t)sizeof(*dent));
+}
+
+static void adb_sync_send_recv_end(void)
+{
+ adb_sync_send_record(ID_DONE, NULL, 0u);
+}
+
+static void adb_sync_fail(const char *fmt, ...)
+{
+ char msg[ADB_SYNC_MSG_MAX];
+ va_list ap;
+ int n;
+
+ va_start(ap, fmt);
+ n = vsnprintf(msg, sizeof(msg), fmt, ap);
+ va_end(ap);
+
+ if ((n < 0) || ((size_t)n >= sizeof(msg))) {
+ n = (int)sizeof(msg) - 1; /* the reason was long: it got cut off */
+ }
+ msg[n] = '\0';
+
+ USB_LOG_ERR("adb sync: %s\r\n", msg);
+
+ switch (g_adb_sync.req) {
+ /* A SEND has its payload and the DONE behind it still on their way, and
+ * a pushed file is answered exactly once: the rest of it is read and
+ * dropped, and only this FAIL goes back. adbd does the same, for the
+ * same reason. Only a SEND and a pull can read a FAIL at all. */
+ case ID_SEND:
+ case ID_RECV:
+ if (g_adb_sync.req == ID_SEND) {
+ g_adb_sync.push_failed = true;
+ g_adb_sync.push_isdir = false;
+ }
+
+ adb_sync_send_record(ID_FAIL, msg, (uint32_t)n);
+ break;
+
+ case ID_STAT:
+ /* A stat is a query and is answered with the record it asked for,
+ * with nothing in it: it is a valid answer, and the client reads a
+ * FAIL in its place as a protocol fault. */
+ adb_sync_send_stat(0u, 0u, 0u);
+ break;
+
+ case ID_LIST:
+ adb_sync_send_list_end();
+ break;
+
+ default:
+ break; /* no request behind it, so there is nothing to answer */
+ }
+}
+
+static bool adb_sync_path_is_root(const char *path)
+{
+ return strcmp(path, vfs_get_fs_root_path()) == 0;
+}
+
+static bool adb_sync_name_is_dir(const char *path)
+{
+ size_t i = strlen(path);
+
+ while ((i > 0u) && (path[i - 1u] != '/')) {
+ if (path[i - 1u] == '.') {
+ return false; /* it has an extension, so it names a file */
+ }
+ i--;
+ }
+
+ return true;
+}
+
+/* Where a pushed directory lands is decided by the client, and it says so in
+ * the path it sends: "src" at an existing "dst" arrives as "dst/Basename(src)/",
+ * while "src/." arrives as "dst/./" - whose "." is a component like any other
+ * here, so folding it away is exactly "the contents of src land in dst".
+ * Both spellings therefore already arrive as the path they mean, and this side
+ * never has to guess which of the two was asked for.
+ *
+ * A Windows host spells its local source with backslashes and the client takes
+ * that basename verbatim, so "adb push .\output /sdc" arrives as
+ * "/sdc/.\output/...". FatFs takes both separators alike, so the backslashes
+ * are folded into forward ones before any component is looked at - otherwise
+ * ".\output" stays a single, unmakable name and the push dies on the first
+ * file. The checks above run first, so this never turns a relative path into
+ * something that looks absolute. */
+static bool adb_sync_build_full_path(const char *root, char *path, bool *wants_dir)
+{
+ char *dst0;
+ char *dst;
+ char *src;
+ size_t len;
+ size_t root_len;
+ size_t i;
+
+ *wants_dir = false;
+
+ root_len = strlen(root);
+ len = strlen(path);
+
+ if ((root_len == 0u) || (len < root_len) || (strncmp(path, root, root_len) != 0)) {
+ return false;
+ }
+
+ if ((len > root_len) && (root[root_len - 1u] != '/') && (path[root_len] != '/')) {
+ return false;
+ }
+
+ for (i = 0u; i < len; i++) {
+ if (path[i] == '\\') {
+ path[i] = '/';
+ }
+ }
+
+ *wants_dir = (len > 1u) && (path[len - 1u] == '/');
+
+ dst0 = path;
+ dst = path;
+ src = path;
+
+ for (;;) {
+ char c = *src;
+
+ if (c == '.') {
+ if (src[1] == '\0') {
+ src++; /* "." and the path ends there */
+ } else if (src[1] == '/') {
+ src += 2; /* "./" */
+ while (*src == '/') {
+ src++;
+ }
+ continue;
+ } else if ((src[1] == '.') && ((src[2] == '\0') || (src[2] == '/'))) {
+ src += (src[2] == '/') ? 3 : 2;
+ while (*src == '/') {
+ src++;
+ }
+ goto up_one; /* ".." */
+ }
+ }
+
+ while (((c = *src++) != '\0') && (c != '/')) {
+ *dst++ = c;
+ }
+
+ if (c != '/') {
+ break; /* the path ends here */
+ }
+
+ *dst++ = '/';
+ while ((c = *src++) == '/') {
+ /* an empty component, "//" - nothing to copy */
+ }
+ src--;
+
+ continue;
+
+ up_one:
+ /* keep the topmost root directory: there is nothing above it */
+ if (((dst - dst0) != 1) || (dst[-1] != '/')) {
+ if (dst > dst0) {
+ dst--;
+ }
+ }
+ while ((dst0 < dst) && (dst[-1] != '/')) {
+ dst--;
+ }
+ }
+
+ *dst = '\0';
+
+ /* the separator that said "directory" is not part of the path */
+ if (((dst - dst0) > 1) && (dst[-1] == '/')) {
+ dst[-1] = '\0';
+ }
+
+ return true;
+}
+
+static bool adb_sync_path_join(const char *dir, const char *name, char *out, size_t out_size)
+{
+ size_t dir_len = strlen(dir);
+ int n;
+
+ if ((dir_len == 0u) || (dir[dir_len - 1u] == '/')) {
+ n = snprintf(out, out_size, "%s%s", dir, name);
+ } else {
+ n = snprintf(out, out_size, "%s/%s", dir, name);
+ }
+
+ return (n > 0) && ((size_t)n < out_size);
+}
+
+static int adb_sync_path_type(const char *path, struct vfs_stat *info)
+{
+ if (adb_sync_path_is_root(path)) {
+ memset(info, 0, sizeof(*info));
+ info->st_mode = VFS_S_IFDIR | VFS_S_IRWXU | VFS_S_IRWXG | VFS_S_IRWXO;
+ return 0;
+ }
+
+ return vfs_stat(path, info);
+}
+
+static int adb_sync_mkdir_one(const char *path)
+{
+ struct vfs_stat info;
+ bool there = (adb_sync_path_type(path, &info) == 0);
+ int ret;
+
+ if (there && ((info.st_mode & VFS_S_IFMT) == VFS_S_IFDIR)) {
+ return 0; /* already a directory: nothing to make, nothing to do */
+ }
+
+ if (there && (adb_sync_name_is_dir(path) == false)) {
+ return VFS_ERR_NOTDIR; /* it is a file, and it is meant to be one */
+ }
+
+ if (there) {
+ if (vfs_unlink(path) != 0) {
+ return VFS_ERR_NOTDIR; /* still in the way, and it could not be taken away */
+ }
+ }
+
+ ret = vfs_mkdir(path);
+ if (ret != 0) {
+ return ret; /* the port layer's own reason is the one worth passing on */
+ }
+
+ /* It says it worked, so say so too: which directories a push had to make is
+ * the first thing worth knowing when a tree lands one level off. */
+ USB_LOG_INFO("adb sync: mkdir '%s'\r\n", path);
+
+ if ((adb_sync_path_type(path, &info) != 0) ||
+ ((info.st_mode & VFS_S_IFMT) != VFS_S_IFDIR)) {
+ /* It says it worked and the directory cannot be found: believe neither,
+ * and let the caller report a failure rather than write into nothing. */
+ return VFS_ERR_NOENT;
+ }
+
+ return 0;
+}
+
+static int adb_sync_mkdirs(char *path, bool last_is_dir)
+{
+ size_t end = strlen(path);
+ size_t cut;
+ size_t i;
+ char saved;
+ int ret;
+
+ if (adb_sync_path_is_root(path)) {
+ return 0; /* the root of the volume is always there */
+ }
+
+ if (last_is_dir == false) {
+ for (cut = end; cut > 0u; cut--) {
+ if (path[cut - 1u] == '/') {
+ break;
+ }
+ }
+ if (cut <= 1u) {
+ return 0; /* the parent is the root, which always exists */
+ }
+ end = cut - 1u;
+ }
+
+ for (i = 1u; i < end; i++) {
+ if (path[i] != '/') {
+ continue;
+ }
+
+ path[i] = '\0';
+ ret = adb_sync_mkdir_one(path);
+ path[i] = '/';
+ if (ret != 0) {
+ return ret;
+ }
+ }
+
+ saved = path[end];
+ path[end] = '\0'; /* in front of the file name, or of the trailing slash */
+ ret = adb_sync_mkdir_one(path);
+ path[end] = saved;
+
+ return ret;
+}
+
+static int adb_sync_write_err(const char *path, int err)
+{
+ struct vfs_stat info;
+
+ if ((err == VFS_ERR_ROFS) && (adb_sync_path_type(path, &info) == 0) &&
+ ((info.st_mode & VFS_S_IFMT) == VFS_S_IFDIR)) {
+ return VFS_ERR_ISDIR;
+ }
+
+ return err;
+}
+
+static void adb_sync_fail_write(char *path, int err)
+{
+ struct vfs_stat info;
+ char saved;
+ size_t cut;
+ size_t len = strlen(path);
+
+ if (err == VFS_ERR_NOENT) {
+ for (cut = 1u; cut < len; cut++) {
+ if (path[cut] != '/') {
+ continue;
+ }
+
+ saved = path[cut];
+ path[cut] = '\0';
+
+ if (adb_sync_path_type(path, &info) != 0) {
+ adb_sync_fail("cannot write into '%s': that directory is not there", path);
+ path[cut] = saved;
+ return;
+ }
+
+ if ((info.st_mode & VFS_S_IFMT) != VFS_S_IFDIR) {
+ adb_sync_fail("cannot write into '%s': a file is where a directory has to be", path);
+ path[cut] = saved;
+ return;
+ }
+
+ path[cut] = saved; /* an ancestor, and a directory as it should be */
+ }
+ }
+
+ adb_sync_fail("cannot write '%s': %s", path, vfs_strerror(adb_sync_write_err(path, err)));
+}
+
+static void adb_sync_remember_target(const char *path, bool is_dir)
+{
+ size_t len = strlen(path);
+
+ if (g_adb_sync.have_target) {
+ return;
+ }
+
+ if (len >= sizeof(g_adb_sync.target)) {
+ return;
+ }
+
+ memcpy(g_adb_sync.target, path, len);
+ g_adb_sync.target[len] = '\0';
+ g_adb_sync.target_isdir = is_dir;
+ g_adb_sync.have_target = true;
+}
+
+static void adb_sync_do_stat(const char *path, bool wants_dir)
+{
+ struct vfs_stat info;
+ bool as_dir = (wants_dir || adb_sync_name_is_dir(path));
+ bool is_dir;
+ int ret;
+
+ USB_LOG_INFO("adb sync: stat '%s'\r\n", path);
+
+ ret = adb_sync_path_type(path, &info);
+ is_dir = (ret == 0) && ((info.st_mode & VFS_S_IFMT) == VFS_S_IFDIR);
+
+ if (is_dir) {
+ adb_sync_remember_target(path, true);
+ adb_sync_send_stat(info.st_mode, (uint32_t)info.st_size, info.st_mtime);
+ } else if (as_dir) {
+ adb_sync_remember_target(path, true);
+ adb_sync_send_stat(VFS_S_IFDIR | VFS_S_IRWXU | VFS_S_IRWXG | VFS_S_IRWXO, 0u, 0u);
+ } else if (ret == 0) {
+ adb_sync_remember_target(path, false);
+ adb_sync_send_stat(info.st_mode, (uint32_t)info.st_size, info.st_mtime);
+ } else {
+ adb_sync_remember_target(path, false);
+ adb_sync_fail("'%s' does not exist", path);
+ }
+}
+
+static void adb_sync_do_list(char *path)
+{
+ struct vfs_dirent *de;
+ struct vfs_stat info;
+ VFS_DIR *dir;
+ uint32_t namelen;
+ char *entry = (char *)g_adb_sync.packet.payload;
+
+ USB_LOG_INFO("adb sync: listing '%s'\r\n", path);
+
+ dir = vfs_opendir(path);
+ if (!dir) {
+ adb_sync_fail("cannot read '%s' as a directory", path);
+ return;
+ }
+
+ while ((de = vfs_readdir(dir)) != NULL) {
+ namelen = de->d_namlen;
+
+ /* "." and ".." are never part of a listing */
+ if ((namelen == 0u) || (namelen > ADB_SYNC_NAME_MAX)) {
+ continue;
+ }
+ if ((strcmp(de->d_name, ".") == 0) || (strcmp(de->d_name, "..") == 0)) {
+ continue;
+ }
+
+ if (adb_sync_path_join(path, de->d_name, entry,
+ sizeof(g_adb_sync.packet.payload)) == false) {
+ continue;
+ }
+
+ if (vfs_stat(entry, &info) == 0) {
+ adb_sync_send_dent(info.st_mode, (uint32_t)info.st_size, info.st_mtime,
+ de->d_name, namelen);
+ }
+ }
+
+ vfs_closedir(dir);
+
+ adb_sync_send_list_end();
+}
+
+static void adb_sync_do_recv(const char *path)
+{
+ int fd;
+ int n;
+
+ USB_LOG_INFO("adb sync: reading '%s'\r\n", path);
+
+ fd = vfs_open(path, VFS_O_RDONLY);
+ if (fd < 0) {
+ adb_sync_fail("cannot open '%s' for reading: %s", path, vfs_strerror(fd));
+ return;
+ }
+
+ for (;;) {
+ n = vfs_read(fd, g_adb_sync.packet.payload, ADB_SYNC_DATA_MAX);
+ if (n < 0) {
+ vfs_close(fd);
+ adb_sync_fail("cannot read '%s': %s", path, vfs_strerror(n));
+ return; /* the FAIL is the answer; the client reads it as failure */
+ }
+
+ if (n == 0) {
+ break;
+ }
+
+ adb_sync_send_data_record((uint32_t)n);
+ }
+
+ vfs_close(fd);
+
+ adb_sync_send_recv_end();
+}
+
+static void adb_sync_do_send(char *path, bool wants_dir, uint32_t mode)
+{
+ int ret;
+
+ if ((mode & VFS_S_IFMT) == VFS_S_IFDIR) {
+ ret = adb_sync_mkdirs(path, true);
+ if (ret != 0) {
+ adb_sync_fail("cannot make '%s' a directory: %s", path, vfs_strerror(ret));
+ return;
+ }
+
+ g_adb_sync.push_isdir = true;
+ g_adb_sync.fd = -1;
+ return;
+ }
+
+ if (g_adb_sync.have_target && g_adb_sync.target_isdir &&
+ (strcmp(path, g_adb_sync.target) == 0)) {
+ ret = adb_sync_mkdirs(path, true);
+ if (ret != 0) {
+ adb_sync_fail("cannot make '%s' a directory: %s", path, vfs_strerror(ret));
+ } else {
+ adb_sync_fail("'%s' is a directory, so no file can be written at it", path);
+ }
+ return;
+ }
+
+ if (wants_dir) {
+ ret = adb_sync_mkdirs(path, true);
+ if (ret != 0) {
+ adb_sync_fail("cannot make '%s' a directory: %s", path, vfs_strerror(ret));
+ } else {
+ adb_sync_fail("'%s' is a directory, so no file can be written at it", path);
+ }
+ return;
+ }
+
+ USB_LOG_INFO("adb sync: writing '%s'\r\n", path);
+
+ g_adb_sync.fd = vfs_open(path, VFS_O_WRONLY);
+ if (g_adb_sync.fd == VFS_ERR_NOENT) {
+ ret = adb_sync_mkdirs(path, false);
+ if (ret != 0) {
+ adb_sync_fail_write(path, ret);
+ return;
+ }
+ g_adb_sync.fd = vfs_open(path, VFS_O_WRONLY);
+ }
+
+ if (g_adb_sync.fd < 0) {
+ ret = g_adb_sync.fd;
+ g_adb_sync.fd = -1;
+ adb_sync_fail_write(path, ret);
+ return;
+ }
+}
+
+static void adb_sync_do_send_done(void)
+{
+ if (g_adb_sync.fd >= 0) {
+ vfs_close(g_adb_sync.fd);
+ g_adb_sync.fd = -1;
+ }
+
+ if (g_adb_sync.push_isdir) {
+ g_adb_sync.push_isdir = false;
+ adb_sync_send_record(ID_OKAY, NULL, 0u);
+ return;
+ }
+
+ if (g_adb_sync.push_failed) {
+ g_adb_sync.push_failed = false;
+ return;
+ }
+
+ adb_sync_send_record(ID_OKAY, NULL, 0u);
+}
+
+static void adb_sync_data_write(const uint8_t *data, uint32_t len)
+{
+ int n;
+
+ if (g_adb_sync.fd < 0) {
+ return; /* refused, or a directory: the bytes are dropped */
+ }
+
+ n = vfs_write(g_adb_sync.fd, data, len);
+ if (n != (int)len) {
+ int err = (n < 0) ? n : VFS_ERR_IO; /* a short write counts as I/O */
+
+ vfs_close(g_adb_sync.fd);
+ g_adb_sync.fd = -1;
+
+ adb_sync_fail("cannot write '%s': %s", g_adb_sync.path, vfs_strerror(err));
+ }
+}
+
+static void adb_sync_reset(void)
+{
+ if (g_adb_sync.fd >= 0) {
+ vfs_close(g_adb_sync.fd);
+ g_adb_sync.fd = -1;
+ }
+
+ g_adb_sync.rx_state = SYNC_RX_STATE_HEADER;
+ g_adb_sync.hdr_len = 0;
+ g_adb_sync.body_len = 0;
+ g_adb_sync.rx_remain = 0;
+ g_adb_sync.req = 0;
+ g_adb_sync.push_failed = false;
+ g_adb_sync.push_isdir = false;
+ g_adb_sync.have_target = false;
+ g_adb_sync.target_isdir = false;
+}
+
+static void adb_sync_dispatch(uint32_t id, char *body)
+{
+ bool wants_dir = false;
+ uint32_t mode = 0u;
+ size_t len;
+
+ g_adb_sync.req = id;
+
+ if (id == ID_QUIT) {
+ adb_sync_reset();
+ return;
+ }
+
+ if ((id != ID_STAT) && (id != ID_LIST) && (id != ID_RECV) && (id != ID_SEND)) {
+ adb_sync_fail("unsupported request 0x%08x", (unsigned)id);
+ return;
+ }
+
+ if (id == ID_SEND) {
+ char *comma = strrchr(body, ',');
+
+ if (comma == NULL) {
+ adb_sync_fail("the SEND request came without a mode behind its path");
+ return;
+ }
+
+ mode = (uint32_t)strtoul(comma + 1, NULL, 0);
+ *comma = '\0';
+ }
+
+ len = strlen(body);
+ memcpy(g_adb_sync.path, body, len + 1u);
+
+ if (adb_sync_build_full_path(vfs_get_fs_root_path(), g_adb_sync.path, &wants_dir) == false) {
+ adb_sync_fail("the path has to be absolute, starting with '%s', not '%s'",
+ vfs_get_fs_root_path(), g_adb_sync.path);
+ return;
+ }
+
+ switch (id) {
+ case ID_STAT:
+ adb_sync_do_stat(g_adb_sync.path, wants_dir);
+ break;
+ case ID_LIST:
+ adb_sync_do_list(g_adb_sync.path);
+ break;
+ case ID_RECV:
+ adb_sync_do_recv(g_adb_sync.path);
+ break;
+ case ID_SEND:
+ adb_sync_do_send(g_adb_sync.path, wants_dir, mode);
+ break;
+ default:
+ break; /* not reachable: the four above are the only ones left */
+ }
+}
+
+static void adb_sync_header_ready(void)
+{
+ uint32_t id = g_adb_sync.packet.hdr.id;
+ uint32_t len = g_adb_sync.packet.hdr.len;
+
+ if (id == ID_DONE) {
+ adb_sync_do_send_done();
+ g_adb_sync.rx_state = SYNC_RX_STATE_HEADER;
+ return;
+ }
+
+ if (id == ID_DATA) {
+ g_adb_sync.rx_remain = len;
+ g_adb_sync.rx_state = len ? SYNC_RX_STATE_DATA : SYNC_RX_STATE_HEADER;
+ return;
+ }
+
+ if ((id == ID_STAT) || (id == ID_LIST) || (id == ID_RECV) || (id == ID_SEND)) {
+ if (len > ADB_SYNC_PATH_MAX) {
+ g_adb_sync.req = id;
+ g_adb_sync.rx_remain = len;
+ g_adb_sync.rx_state = SYNC_RX_STATE_SKIP; /* read it and drop it */
+ adb_sync_fail("a request of %u bytes is longer than the %u bytes a path may be",
+ (unsigned)len, (unsigned)ADB_SYNC_PATH_MAX);
+ return;
+ }
+
+ g_adb_sync.body_len = 0;
+ g_adb_sync.rx_remain = len;
+
+ if (len == 0) {
+ g_adb_sync.packet.payload[0] = '\0';
+ adb_sync_dispatch(id, (char *)g_adb_sync.packet.payload);
+ g_adb_sync.rx_state = SYNC_RX_STATE_HEADER;
+ } else {
+ g_adb_sync.rx_state = SYNC_RX_STATE_BODY;
+ }
+ return;
+ }
+
+ g_adb_sync.rx_remain = len;
+ g_adb_sync.rx_state = len ? SYNC_RX_STATE_SKIP : SYNC_RX_STATE_HEADER;
+ adb_sync_dispatch(id, (char *)g_adb_sync.packet.payload);
+}
+
+static void adb_sync_consume(const uint8_t *data, uint32_t len)
+{
+ uint8_t *body = g_adb_sync.packet.payload;
+ uint32_t take;
+
+ while (len) {
+ switch (g_adb_sync.rx_state) {
+ case SYNC_RX_STATE_HEADER:
+ take = sizeof(struct adb_sync_wire_hdr) - g_adb_sync.hdr_len;
+ if (take > len) {
+ take = len;
+ }
+ memcpy((uint8_t *)&g_adb_sync.packet.hdr + g_adb_sync.hdr_len, data, take);
+ g_adb_sync.hdr_len += take;
+ data += take;
+ len -= take;
+
+ if (g_adb_sync.hdr_len == sizeof(struct adb_sync_wire_hdr)) {
+ g_adb_sync.hdr_len = 0;
+ adb_sync_header_ready();
+ }
+ break;
+
+ case SYNC_RX_STATE_BODY:
+ take = (g_adb_sync.rx_remain < len) ? g_adb_sync.rx_remain : len;
+ memcpy(body + g_adb_sync.body_len, data, take);
+ g_adb_sync.body_len += take;
+ g_adb_sync.rx_remain -= take;
+ data += take;
+ len -= take;
+
+ if (g_adb_sync.rx_remain == 0) {
+ body[g_adb_sync.body_len] = '\0';
+ adb_sync_dispatch(g_adb_sync.packet.hdr.id, (char *)body);
+ g_adb_sync.rx_state = SYNC_RX_STATE_HEADER;
+ }
+ break;
+
+ case SYNC_RX_STATE_DATA:
+ take = (g_adb_sync.rx_remain < len) ? g_adb_sync.rx_remain : len;
+ adb_sync_data_write(data, take);
+ g_adb_sync.rx_remain -= take;
+ data += take;
+ len -= take;
+
+ if (g_adb_sync.rx_remain == 0) {
+ g_adb_sync.rx_state = SYNC_RX_STATE_HEADER;
+ }
+ break;
+
+ case SYNC_RX_STATE_SKIP:
+ default:
+ take = (g_adb_sync.rx_remain < len) ? g_adb_sync.rx_remain : len;
+ g_adb_sync.rx_remain -= take;
+ data += take;
+ len -= take;
+
+ if (g_adb_sync.rx_remain == 0) {
+ g_adb_sync.rx_state = SYNC_RX_STATE_HEADER;
+ }
+ break;
+ }
+ }
+}
+
+static void usbd_adb_sync_on_open(uint32_t remoteid)
+{
+ adb_sync_reset();
+ USB_LOG_INFO("adb sync open, remoteid:%u\r\n", (unsigned)remoteid);
+}
+
+static void usbd_adb_sync_on_close(uint32_t remoteid)
+{
+ (void)remoteid;
+
+ adb_sync_reset();
+ USB_LOG_INFO("adb sync close, remoteid:%u\r\n", (unsigned)remoteid);
+}
+
+static void usbd_adb_sync_on_write(uint32_t remoteid, const uint8_t *data, uint32_t len)
+{
+ (void)remoteid;
+
+ if (!data || !len) {
+ return;
+ }
+
+ adb_sync_consume(data, len);
+}
+
+static const struct adb_service adb_sync_service = {
+ .name = "sync:",
+ .localid = ADB_LOCALID_SYNC,
+ .on_open = usbd_adb_sync_on_open,
+ .on_close = usbd_adb_sync_on_close,
+ .on_write = usbd_adb_sync_on_write,
+};
+
+void usbd_adb_sync_init(void)
+{
+ g_adb_sync.fd = -1;
+ adb_sync_reset();
+
+ if (usbd_adb_service_register(&adb_sync_service) != 0) {
+ USB_LOG_ERR("adb sync: registering the service failed\r\n");
+ }
+}
diff --git a/class/aoa/usbh_aoa.c b/class/aoa/usbh_aoa.c
index 02d9fab5..22426dad 100644
--- a/class/aoa/usbh_aoa.c
+++ b/class/aoa/usbh_aoa.c
@@ -36,7 +36,7 @@ int usbh_aoa_switch(struct usbh_hubport *hport, struct aoa_string_info *info)
setup->wLength = 2;
ret = usbh_control_transfer(hport, setup, g_aoa_buffer);
- if (ret < 0) {
+ if (ret < 2) {
return ret;
}
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/usbd_audio.c b/class/audio/usbd_audio.c
index 9e4e2ac0..446ecdf0 100644
--- a/class/audio/usbd_audio.c
+++ b/class/audio/usbd_audio.c
@@ -6,19 +6,53 @@
#include "usbd_core.h"
#include "usbd_audio.h"
-struct audio_entity_param {
- uint32_t wCur;
- uint32_t wMin;
- uint32_t wMax;
- uint32_t wRes;
-};
-
struct usbd_audio_priv {
struct audio_entity_info *table;
uint8_t num;
uint16_t uac_version;
} g_usbd_audio[CONFIG_USBDEV_MAX_BUS];
+static float volume_hex2float(int16_t volume_hex)
+{
+ return (float)volume_hex / 256.0f;
+}
+
+static int16_t volume_float2hex(float volume_db)
+{
+ return (int16_t)(volume_db * 256);
+}
+
+static float volume_roundf(float x) {
+ if (x >= 0.0f) {
+ int intpart = (int)x;
+ float frac = x - (float)intpart;
+
+ if (frac >= 0.5f) {
+ intpart++;
+ }
+ return (float)intpart;
+ } else {
+ int intpart = (int)x;
+ float frac = x - (float)intpart;
+ if (frac <= -0.5f) {
+ intpart--;
+ }
+ return (float)intpart;
+ }
+}
+
+static float volume_align_to_res(int16_t volume_hex, int16_t res_hex)
+{
+ float val = volume_hex2float(volume_hex);
+ float res = volume_hex2float(res_hex);
+
+ if (res == 0.0f) {
+ return val;
+ }
+
+ return volume_roundf(val / res) * res;
+}
+
static int audio_class_endpoint_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
{
uint8_t control_selector;
@@ -66,8 +100,14 @@ static int audio_class_interface_request_handler(uint8_t busid, struct usb_setup
uint8_t control_selector;
uint8_t ch;
uint8_t mute;
- uint16_t volume;
- int volume_db = 0;
+ int16_t volume_hex_curr;
+ int16_t volume_hex_min;
+ int16_t volume_hex_max;
+ int16_t volume_hex_res;
+ float volume_db_curr = 0.0f;
+ float volume_db_min = 0.0f;
+ float volume_db_max = 0.0f;
+ float volume_db_res = 0.0f;
uint32_t sampling_freq = 0;
const char *mute_string[2] = { "off", "on" };
@@ -93,6 +133,11 @@ static int audio_class_interface_request_handler(uint8_t busid, struct usb_setup
USB_LOG_DBG("Audio entity_id:%02x, subtype:%02x, cs:%02x\r\n", entity_id, subtype, control_selector);
+ usbd_audio_get_volume_range(busid, ep, ch, &volume_db_min, &volume_db_max, &volume_db_res);
+ volume_hex_res = volume_float2hex(volume_db_res);
+ volume_hex_min = volume_float2hex(volume_db_min);
+ volume_hex_max = volume_float2hex(volume_db_max);
+
switch (subtype) {
case AUDIO_CONTROL_FEATURE_UNIT:
switch (control_selector) {
@@ -130,39 +175,29 @@ static int audio_class_interface_request_handler(uint8_t busid, struct usb_setup
if (g_usbd_audio[busid].uac_version < 0x0200) {
switch (setup->bRequest) {
case AUDIO_REQUEST_SET_CUR:
- memcpy(&volume, *data, 2);
- if (volume < 0x8000) {
- volume_db = volume / 256;
- } else {
- volume_db = (volume - 0x10000) / 256;
- }
- USB_LOG_DBG("Set ep:0x%02x ch:%d vol_hex:0x%04x, vol_db:%d dB\r\n", ep, ch, volume, volume_db);
- usbd_audio_set_volume(busid, ep, ch, volume_db);
+ memcpy(&volume_hex_curr, *data, 2);
+ volume_db_curr = volume_align_to_res(volume_hex_curr, volume_hex_res);
+ volume_db_curr = MIN(MAX(volume_db_curr, volume_db_min), volume_db_max);
+ USB_LOG_DBG("Set ep:0x%02x ch:%d vol_hex:0x%04x, vol_db:%.2f dB\r\n", ep, ch, volume_hex_curr, volume_db_curr);
+ usbd_audio_set_volume(busid, ep, ch, volume_db_curr);
break;
case AUDIO_REQUEST_GET_CUR:
- volume_db = usbd_audio_get_volume(busid, ep, ch);
- if (volume_db >= 0) {
- volume = volume_db * 256;
- } else {
- volume = volume_db * 256 + 0x10000;
- }
- USB_LOG_DBG("Get ep:0x%02x ch:%d vol_hex:0x%04x, vol_db:%d dB\r\n", ep, ch, volume, volume_db);
- memcpy(*data, &volume, 2);
+ volume_db_curr = usbd_audio_get_volume(busid, ep, ch);
+ volume_hex_curr = volume_float2hex(volume_db_curr);
+ USB_LOG_DBG("Get ep:0x%02x ch:%d vol_hex:0x%04x, vol_db:%.2f dB\r\n", ep, ch, volume_hex_curr, volume_db_curr);
+ memcpy(*data, &volume_hex_curr, 2);
*len = 2;
break;
case AUDIO_REQUEST_GET_MIN:
- (*data)[0] = 0x00; /* -100 dB */
- (*data)[1] = 0x9c;
+ memcpy(*data, &volume_hex_min, 2);
*len = 2;
break;
case AUDIO_REQUEST_GET_MAX:
- (*data)[0] = 0x00; /* 0 dB */
- (*data)[1] = 0x00;
+ memcpy(*data, &volume_hex_max, 2);
*len = 2;
break;
case AUDIO_REQUEST_GET_RES:
- (*data)[0] = 0x00; /* 1 dB */
- (*data)[1] = 0x01;
+ memcpy(*data, &volume_hex_res, 2);
*len = 2;
break;
default:
@@ -172,32 +207,26 @@ static int audio_class_interface_request_handler(uint8_t busid, struct usb_setup
switch (setup->bRequest) {
case AUDIO_REQUEST_CUR:
if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
- volume_db = usbd_audio_get_volume(busid, ep, ch);
- if (volume_db >= 0) {
- volume = volume_db * 256;
- } else {
- volume = volume_db * 256 + 0x10000;
- }
- USB_LOG_DBG("Get ep:0x%02x ch:%d vol_hex:0x%04x, vol_db:%d dB\r\n", ep, ch, volume, volume_db);
- memcpy(*data, &volume, 2);
+ volume_db_curr = usbd_audio_get_volume(busid, ep, ch);
+ volume_hex_curr = volume_float2hex(volume_db_curr);
+ USB_LOG_DBG("Get ep:0x%02x ch:%d vol_hex:0x%04x, vol_db:%d dB\r\n", ep, ch, volume_hex_curr, volume_db_curr);
+ memcpy(*data, &volume_hex_curr, 2);
*len = 2;
} else {
- memcpy(&volume, *data, 2);
- if (volume < 0x8000) {
- volume_db = volume / 256;
- } else {
- volume_db = (volume - 0x10000) / 256;
- }
- USB_LOG_DBG("Set ep:0x%02x ch:%d vol_hex:0x%04x, vol_db:%d dB\r\n", ep, ch, volume, volume_db);
- usbd_audio_set_volume(busid, ep, ch, volume_db);
+ memcpy(&volume_hex_curr, *data, 2);
+ volume_db_curr = volume_align_to_res(volume_hex_curr, volume_hex_res);
+ volume_db_curr = MIN(MAX(volume_db_curr, volume_db_min), volume_db_max);
+ USB_LOG_DBG("Set ep:0x%02x ch:%d vol_hex:0x%04x, vol_db:%d dB\r\n", ep, ch, volume_hex_curr, volume_db_curr);
+ usbd_audio_set_volume(busid, ep, ch, volume_db_curr);
}
break;
case AUDIO_REQUEST_RANGE:
if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
*((uint16_t *)(*data + 0)) = 1;
- *((uint16_t *)(*data + 2)) = 0x9c00; /* MIN -100 dB */
- *((uint16_t *)(*data + 4)) = 0x0000; /* MAX 0 dB */
- *((uint16_t *)(*data + 6)) = 0x100; /* RES 1 dB */
+
+ *((uint16_t *)(*data + 2)) = volume_hex_min;
+ *((uint16_t *)(*data + 4)) = volume_hex_max;
+ *((uint16_t *)(*data + 6)) = volume_hex_res;
*len = 8;
} else {
}
@@ -312,7 +341,7 @@ struct usbd_interface *usbd_audio_init_intf(uint8_t busid,
return intf;
}
-__WEAK void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int volume_db)
+__WEAK void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, float volume_db)
{
(void)busid;
(void)ep;
@@ -320,13 +349,24 @@ __WEAK void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int vol
(void)volume_db;
}
-__WEAK int usbd_audio_get_volume(uint8_t busid, uint8_t ep, uint8_t ch)
+__WEAK float usbd_audio_get_volume(uint8_t busid, uint8_t ep, uint8_t ch)
{
(void)busid;
(void)ep;
(void)ch;
- return 0;
+ return 0.0f;
+}
+
+__WEAK void usbd_audio_get_volume_range(uint8_t busid, uint8_t ep, uint8_t ch, float *min, float *max, float *res)
+{
+ (void)busid;
+ (void)ep;
+ (void)ch;
+
+ *min = -100.0f;
+ *max = 0.0f;
+ *res = 1.0f;
}
__WEAK void usbd_audio_set_mute(uint8_t busid, uint8_t ep, uint8_t ch, bool mute)
diff --git a/class/audio/usbd_audio.h b/class/audio/usbd_audio.h
index 75a0b17d..c74f54a4 100644
--- a/class/audio/usbd_audio.h
+++ b/class/audio/usbd_audio.h
@@ -27,8 +27,9 @@ struct usbd_interface *usbd_audio_init_intf(uint8_t busid, struct usbd_interface
void usbd_audio_open(uint8_t busid, uint8_t intf);
void usbd_audio_close(uint8_t busid, uint8_t intf);
-void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int volume_db);
-int usbd_audio_get_volume(uint8_t busid, uint8_t ep, uint8_t ch);
+void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, float volume_db);
+float usbd_audio_get_volume(uint8_t busid, uint8_t ep, uint8_t ch);
+void usbd_audio_get_volume_range(uint8_t busid, uint8_t ep, uint8_t ch, float *min, float *max, float *res);
void usbd_audio_set_mute(uint8_t busid, uint8_t ep, uint8_t ch, bool mute);
bool usbd_audio_get_mute(uint8_t busid, uint8_t ep, uint8_t ch);
void usbd_audio_set_sampling_freq(uint8_t busid, uint8_t ep, uint32_t sampling_freq);
diff --git a/class/audio/usbh_audio.c b/class/audio/usbh_audio.c
index ee0a3349..5ef08c90 100644
--- a/class/audio/usbh_audio.c
+++ b/class/audio/usbh_audio.c
@@ -37,6 +37,25 @@ static int16_t volume_float2hex(float volume_db)
return (int16_t)(volume_db * 256);
}
+static float volume_roundf(float x) {
+ if (x >= 0.0f) {
+ int intpart = (int)x;
+ float frac = x - (float)intpart;
+
+ if (frac >= 0.5f) {
+ intpart++;
+ }
+ return (float)intpart;
+ } else {
+ int intpart = (int)x;
+ float frac = x - (float)intpart;
+ if (frac <= -0.5f) {
+ intpart--;
+ }
+ return (float)intpart;
+ }
+}
+
static struct usbh_audio *usbh_audio_class_alloc(void)
{
uint8_t devno;
@@ -385,7 +404,7 @@ feature_found:
setup->wLength = 2;
ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf);
- if (ret < 0) {
+ if (ret < 2) {
return ret;
}
@@ -400,7 +419,7 @@ feature_found:
setup->wLength = 2;
ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf);
- if (ret < 0) {
+ if (ret < 2) {
return ret;
}
@@ -415,7 +434,7 @@ feature_found:
setup->wLength = 2;
ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf);
- if (ret < 0) {
+ if (ret < 2) {
return ret;
}
memcpy(volume_max, g_audio_buf, 2);
@@ -429,7 +448,7 @@ feature_found:
setup->wLength = 2;
ret = usbh_control_transfer(audio_class->hport, setup, g_audio_buf);
- if (ret < 0) {
+ if (ret < 2) {
return ret;
}
memcpy(volume_res, g_audio_buf, 2);
@@ -486,7 +505,7 @@ feature_found:
/* Calculate target volume in float to avoid int16_t calculation overflow. */
volume_db = volume_hex2float(volume_min) + (volume_hex2float(volume_max) - volume_hex2float(volume_min)) * (float)volume / 100.0f;
/* Round to the nearest float value based on volume_res. */
- volume_db = roundf(volume_db / volume_hex2float(volume_res)) * volume_hex2float(volume_res);
+ volume_db = volume_roundf(volume_db / volume_hex2float(volume_res)) * volume_hex2float(volume_res);
volume_hex = volume_float2hex(volume_db);
USB_LOG_INFO("ch %d volume info: (%.2f dB ~ %.2f dB, step: %.2f dB)\r\n",
@@ -694,12 +713,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..be718301 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
@@ -477,6 +480,89 @@ struct cdc_ncm_ndp16 {
0x02, /* bmAttributes */ \
WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
0x00 /* bInterval */
+
+/*Length of template descriptor: 84 bytes*/
+#define USB3_CDC_ACM_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 4 + 5 + 7 + 6 + 9 + 7 + 6 + 7 + 6)
+
+#define USB3_CDC_ACM_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, wMaxPacketSize, bMaxBurst, maxstream, str_idx) \
+ /* Interface Associate */ \
+ 0x08, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, /* bDescriptorType */ \
+ bFirstInterface, /* bFirstInterface */ \
+ 0x02, /* bInterfaceCount */ \
+ USB_DEVICE_CLASS_CDC, /* bFunctionClass */ \
+ CDC_ABSTRACT_CONTROL_MODEL, /* bFunctionSubClass */ \
+ CDC_COMMON_PROTOCOL_NONE, /* bFunctionProtocol */ \
+ 0x00, /* iFunction */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bFirstInterface, /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x01, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_CDC, /* bInterfaceClass */ \
+ CDC_ABSTRACT_CONTROL_MODEL, /* bInterfaceSubClass */ \
+ CDC_COMMON_PROTOCOL_NONE, /* bInterfaceProtocol */ \
+ str_idx, /* iInterface */ \
+ 0x05, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_HEADER, /* bDescriptorSubtype */ \
+ WBVAL(CDC_V1_10), /* bcdCDC */ \
+ 0x05, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_CALL_MANAGEMENT, /* bDescriptorSubtype */ \
+ 0x00, /* bmCapabilities */ \
+ (uint8_t)(bFirstInterface + 1), /* bDataInterface */ \
+ 0x04, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, /* bDescriptorSubtype */ \
+ 0x02, /* bmCapabilities */ \
+ 0x05, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_UNION, /* bDescriptorSubtype */ \
+ bFirstInterface, /* bMasterInterface */ \
+ (uint8_t)(bFirstInterface + 1), /* bSlaveInterface0 */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ int_ep, /* bEndpointAddress */ \
+ 0x03, /* bmAttributes */ \
+ 0x08, 0x00, /* wMaxPacketSize */ \
+ 0x0a, /* bInterval */ \
+ 0x06, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT_COMPANION, /* bDescriptorType */ \
+ 0x00, /* bMaxBurst */ \
+ 0x00, /* bmAttributes */ \
+ WBVAL(0x08), /* wBytesPerInterval */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ (uint8_t)(bFirstInterface + 1), /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x02, /* bNumEndpoints */ \
+ CDC_DATA_INTERFACE_CLASS, /* bInterfaceClass */ \
+ 0x00, /* bInterfaceSubClass */ \
+ 0x00, /* bInterfaceProtocol */ \
+ 0x00, /* iInterface */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ out_ep, /* bEndpointAddress */ \
+ 0x02, /* bmAttributes */ \
+ WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
+ 0x00, /* bInterval */ \
+ 0x06, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT_COMPANION, /* bDescriptorType */ \
+ bMaxBurst, /* bMaxBurst */ \
+ maxstream, /* bmAttributes */ \
+ WBVAL(0), /* wBytesPerInterval */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ in_ep, /* bEndpointAddress */ \
+ 0x02, /* bmAttributes */ \
+ WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
+ 0x00, /* bInterval */ \
+ 0x06, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT_COMPANION, /* bDescriptorType */ \
+ bMaxBurst, /* bMaxBurst */ \
+ maxstream, /* bmAttributes */ \
+ WBVAL(0) /* wBytesPerInterval */ \
// clang-format on
/*Length of template descriptor: 66 bytes*/
diff --git a/class/cdc/usbd_cdc_ecm.c b/class/cdc/usbd_cdc_ecm.c
index dde40907..bb3071ee 100644
--- a/class/cdc/usbd_cdc_ecm.c
+++ b/class/cdc/usbd_cdc_ecm.c
@@ -27,6 +27,7 @@ volatile uint32_t g_cdc_ecm_tx_data_length = 0;
static volatile uint8_t g_current_net_status = 0;
static volatile uint8_t g_cmd_intf = 0;
+static volatile bool g_cdc_ecm_opened = false;
static uint32_t g_connect_speed_table[2] = { CDC_ECM_CONNECT_SPEED_UPSTREAM,
CDC_ECM_CONNECT_SPEED_DOWNSTREAM };
@@ -122,12 +123,23 @@ void cdc_ecm_notify_handler(uint8_t busid, uint8_t event, void *arg)
g_current_net_status = 0;
g_cdc_ecm_rx_data_length = 0;
g_cdc_ecm_tx_data_length = 0;
+ g_cdc_ecm_opened = false;
break;
case USBD_EVENT_CONFIGURED:
+ break;
+ case USBD_EVENT_SET_INTERFACE: {
+ struct usb_interface_descriptor *intf = (struct usb_interface_descriptor *)arg;
+ if (intf->bAlternateSetting) {
+ g_cdc_ecm_opened = true;
#ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
- usbd_cdc_ecm_start_read(g_cdc_ecm_rx_buffer, CONFIG_CDC_ECM_ETH_MAX_SEGSZE);
+ usbd_cdc_ecm_start_read(g_cdc_ecm_rx_buffer, CONFIG_CDC_ECM_ETH_MAX_SEGSZE);
#endif
- break;
+ } else {
+ g_cdc_ecm_rx_data_length = 0;
+ g_cdc_ecm_tx_data_length = 0;
+ g_cdc_ecm_opened = false;
+ }
+ } break;
default:
break;
@@ -171,6 +183,8 @@ void cdc_ecm_int_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
int usbd_cdc_ecm_start_write(uint8_t *buf, uint32_t len)
{
+ int ret;
+
if (!usb_device_is_configured(0)) {
return -USB_ERR_NOTCONN;
}
@@ -179,10 +193,19 @@ int usbd_cdc_ecm_start_write(uint8_t *buf, uint32_t len)
return -USB_ERR_BUSY;
}
+ if (!g_cdc_ecm_opened) {
+ return -USB_ERR_NOTCONN;
+ }
+
g_cdc_ecm_tx_data_length = len;
USB_LOG_DBG("txlen:%d\r\n", g_cdc_ecm_tx_data_length);
- return usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr, buf, len);
+ ret = usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr, buf, len);
+ if (ret < 0) {
+ g_cdc_ecm_tx_data_length = 0;
+ return ret;
+ }
+ return 0;
}
int usbd_cdc_ecm_start_read(uint8_t *buf, uint32_t len)
@@ -191,6 +214,10 @@ int usbd_cdc_ecm_start_read(uint8_t *buf, uint32_t len)
return -USB_ERR_NOTCONN;
}
+ if (!g_cdc_ecm_opened) {
+ return -USB_ERR_NOTCONN;
+ }
+
g_cdc_ecm_rx_data_length = 0;
return usbd_ep_start_read(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, buf, len);
}
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/cdc/usbh_cdc_ecm.c b/class/cdc/usbh_cdc_ecm.c
index 6fef9b20..35b92e2a 100644
--- a/class/cdc/usbh_cdc_ecm.c
+++ b/class/cdc/usbh_cdc_ecm.c
@@ -245,9 +245,7 @@ void usbh_cdc_ecm_rx_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV)
(void)CONFIG_USB_OSAL_THREAD_GET_ARGV;
USB_LOG_INFO("Create cdc ecm rx thread\r\n");
- // clang-format off
-find_class:
- // clang-format on
+
g_cdc_ecm_class.connect_status = false;
if (usbh_find_class_instance("/dev/cdc_ether") == NULL) {
goto delete;
@@ -256,8 +254,7 @@ find_class:
while (g_cdc_ecm_class.connect_status == false) {
ret = usbh_cdc_ecm_get_connect_status(&g_cdc_ecm_class);
if (ret < 0) {
- usb_osal_msleep(100);
- goto find_class;
+ goto delete;
}
usb_osal_msleep(128);
}
@@ -267,25 +264,22 @@ find_class:
usbh_bulk_urb_fill(&g_cdc_ecm_class.bulkin_urb, g_cdc_ecm_class.hport, g_cdc_ecm_class.bulkin, g_cdc_ecm_rx_buffer, CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE, USB_OSAL_WAITING_FOREVER, NULL, NULL);
ret = usbh_submit_urb(&g_cdc_ecm_class.bulkin_urb);
if (ret < 0) {
- goto find_class;
+ goto delete;
}
g_cdc_ecm_rx_length = g_cdc_ecm_class.bulkin_urb.actual_length;
+ if (g_cdc_ecm_rx_length == 0) {
+ continue;
+ }
- /* A transfer is complete because last packet is a short packet.
- * Short packet is not zero, match g_cdc_ecm_rx_length % USB_GET_MAXPACKETSIZE(g_cdc_ecm_class.bulkin->wMaxPacketSize).
- * Short packet is zero, check if g_cdc_ecm_class.bulkin_urb.actual_length < transfer_size, for example transfer is complete with size is 512 < 1514.
- * This case is always true
- */
- if (g_cdc_ecm_rx_length % USB_GET_MAXPACKETSIZE(g_cdc_ecm_class.bulkin->wMaxPacketSize) ||
- (g_cdc_ecm_class.bulkin_urb.actual_length < CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE)) {
+ if (g_cdc_ecm_rx_length < CONFIG_USBHOST_CDC_ECM_ETH_MAX_SIZE) {
USB_LOG_DBG("rxlen:%d\r\n", g_cdc_ecm_rx_length);
usbh_cdc_ecm_eth_input(g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_length);
g_cdc_ecm_rx_length = 0;
} else {
- /* There's no way to run here. */
+ USB_LOG_ERR("cdc ecm rx packet overflow\r\n");
}
}
// clang-format off
diff --git a/class/cdc/usbh_cdc_ncm.c b/class/cdc/usbh_cdc_ncm.c
index 225a7fcd..83310e09 100644
--- a/class/cdc/usbh_cdc_ncm.c
+++ b/class/cdc/usbh_cdc_ncm.c
@@ -23,6 +23,10 @@
#define CONFIG_USBHOST_CDC_NCM_ETH_MAX_SEGSZE 1514U
+#if CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE > (16 * 1024)
+#error "CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE must be less than 16K"
+#endif
+
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_rx_buffer[CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_tx_buffer[CONFIG_USBHOST_CDC_NCM_ETH_MAX_TX_SIZE];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_inttx_buffer[USB_ALIGN_UP(16, CONFIG_USB_ALIGN_SIZE)];
@@ -48,11 +52,11 @@ static int usbh_cdc_ncm_get_ntb_parameters(struct usbh_cdc_ncm *cdc_ncm_class, s
setup->wLength = 28;
ret = usbh_control_transfer(cdc_ncm_class->hport, setup, g_cdc_ncm_buf);
- if (ret < 8) {
+ if (ret < sizeof(struct cdc_ncm_ntb_parameters)) {
return ret;
}
- memcpy((uint8_t *)param, g_cdc_ncm_buf, MIN(ret - 8, sizeof(struct cdc_ncm_ntb_parameters)));
+ memcpy((uint8_t *)param, g_cdc_ncm_buf, sizeof(struct cdc_ncm_ntb_parameters));
return 0;
}
@@ -260,17 +264,10 @@ void usbh_cdc_ncm_rx_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV)
{
uint32_t g_cdc_ncm_rx_length;
int ret;
-#if CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE <= (16 * 1024)
- uint32_t transfer_size = CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE;
-#else
- uint32_t transfer_size = (16 * 1024);
-#endif
(void)CONFIG_USB_OSAL_THREAD_GET_ARGV;
USB_LOG_INFO("Create cdc ncm rx thread\r\n");
- // clang-format off
-find_class:
- // clang-format on
+
g_cdc_ncm_class.connect_status = false;
if (usbh_find_class_instance("/dev/cdc_ncm") == NULL) {
goto delete;
@@ -279,27 +276,24 @@ find_class:
while (g_cdc_ncm_class.connect_status == false) {
ret = usbh_cdc_ncm_get_connect_status(&g_cdc_ncm_class);
if (ret < 0) {
- usb_osal_msleep(100);
- goto find_class;
+ goto delete;
}
}
g_cdc_ncm_rx_length = 0;
while (1) {
- usbh_bulk_urb_fill(&g_cdc_ncm_class.bulkin_urb, g_cdc_ncm_class.hport, g_cdc_ncm_class.bulkin, &g_cdc_ncm_rx_buffer[g_cdc_ncm_rx_length], transfer_size, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ usbh_bulk_urb_fill(&g_cdc_ncm_class.bulkin_urb, g_cdc_ncm_class.hport, g_cdc_ncm_class.bulkin, g_cdc_ncm_rx_buffer, CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE, USB_OSAL_WAITING_FOREVER, NULL, NULL);
ret = usbh_submit_urb(&g_cdc_ncm_class.bulkin_urb);
if (ret < 0) {
- goto find_class;
+ goto delete;
}
- g_cdc_ncm_rx_length += g_cdc_ncm_class.bulkin_urb.actual_length;
+ g_cdc_ncm_rx_length = g_cdc_ncm_class.bulkin_urb.actual_length;
+ if(g_cdc_ncm_rx_length == 0) {
+ continue;
+ }
- /* A transfer is complete because last packet is a short packet.
- * Short packet is not zero, match g_cdc_ncm_rx_length % USB_GET_MAXPACKETSIZE(g_cdc_ncm_class.bulkin->wMaxPacketSize).
- * Short packet is zero, check if g_cdc_ncm_class.bulkin_urb.actual_length < transfer_size, for example transfer is complete with size is 1024 < 2048.
- */
- if ((g_cdc_ncm_rx_length % USB_GET_MAXPACKETSIZE(g_cdc_ncm_class.bulkin->wMaxPacketSize)) ||
- (g_cdc_ncm_class.bulkin_urb.actual_length < transfer_size)) {
+ if (g_cdc_ncm_rx_length < CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE) {
USB_LOG_DBG("rxlen:%d\r\n", g_cdc_ncm_rx_length);
struct cdc_ncm_nth16 *nth16 = (struct cdc_ncm_nth16 *)&g_cdc_ncm_rx_buffer[0];
@@ -333,15 +327,7 @@ find_class:
g_cdc_ncm_rx_length = 0;
} else {
-#if CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE <= (16 * 1024)
- if (g_cdc_ncm_rx_length == CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE) {
-#else
- if ((g_cdc_ncm_rx_length + (16 * 1024)) > CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE) {
-#endif
- USB_LOG_ERR("Rx packet is overflow, please reduce tcp window size or increase CONFIG_USBHOST_CDC_NCM_ETH_MAX_RX_SIZE\r\n");
- while (1) {
- }
- }
+ USB_LOG_ERR("cdc ncm rx packet overflow\r\n");
}
}
// clang-format off
diff --git a/class/gs_usb/gs_usb.h b/class/gs_usb/gs_usb.h
new file mode 100644
index 00000000..7869831e
--- /dev/null
+++ b/class/gs_usb/gs_usb.h
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2016 Hubert Denkmair
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef GS_USB_H
+#define GS_USB_H
+
+/*
+ * gs_usb wire protocol definitions.
+ *
+ * Names and layout follow Linux drivers/net/can/usb/gs_usb.c
+ */
+
+enum gs_usb_breq {
+ GS_USB_BREQ_HOST_FORMAT = 0,
+ GS_USB_BREQ_BITTIMING,
+ GS_USB_BREQ_MODE,
+ GS_USB_BREQ_BERR,
+ GS_USB_BREQ_BT_CONST,
+ GS_USB_BREQ_DEVICE_CONFIG,
+ GS_USB_BREQ_TIMESTAMP,
+ GS_USB_BREQ_IDENTIFY,
+ GS_USB_BREQ_GET_USER_ID,
+ GS_USB_BREQ_QUIRK_CANTACT_PRO_DATA_BITTIMING = GS_USB_BREQ_GET_USER_ID,
+ GS_USB_BREQ_SET_USER_ID,
+ GS_USB_BREQ_DATA_BITTIMING,
+ GS_USB_BREQ_BT_CONST_EXT,
+ GS_USB_BREQ_SET_TERMINATION,
+ GS_USB_BREQ_GET_TERMINATION,
+ GS_USB_BREQ_GET_STATE,
+};
+
+enum gs_can_mode {
+ /* reset a channel. turns it off */
+ GS_CAN_MODE_RESET = 0,
+ /* starts a channel */
+ GS_CAN_MODE_START
+};
+
+enum gs_can_state {
+ GS_CAN_STATE_ERROR_ACTIVE = 0,
+ GS_CAN_STATE_ERROR_WARNING,
+ GS_CAN_STATE_ERROR_PASSIVE,
+ GS_CAN_STATE_BUS_OFF,
+ GS_CAN_STATE_STOPPED,
+ GS_CAN_STATE_SLEEPING
+};
+
+enum gs_can_identify_mode {
+ GS_CAN_IDENTIFY_OFF = 0,
+ GS_CAN_IDENTIFY_ON
+};
+
+enum gs_can_termination_state {
+ GS_CAN_TERMINATION_STATE_OFF = 0,
+ GS_CAN_TERMINATION_STATE_ON
+};
+
+#define GS_CAN_MODE_NORMAL 0
+#define GS_CAN_MODE_LISTEN_ONLY (1U << 0)
+#define GS_CAN_MODE_LOOP_BACK (1U << 1)
+#define GS_CAN_MODE_TRIPLE_SAMPLE (1U << 2)
+#define GS_CAN_MODE_ONE_SHOT (1U << 3)
+#define GS_CAN_MODE_HW_TIMESTAMP (1U << 4)
+/* GS_CAN_FEATURE_IDENTIFY BIT(5) */
+/* GS_CAN_FEATURE_USER_ID BIT(6) */
+#define GS_CAN_MODE_PAD_PKTS_TO_MAX_PKT_SIZE (1U << 7)
+#define GS_CAN_MODE_FD (1U << 8)
+/* GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX BIT(9) */
+/* GS_CAN_FEATURE_BT_CONST_EXT BIT(10) */
+/* GS_CAN_FEATURE_TERMINATION BIT(11) */
+#define GS_CAN_MODE_BERR_REPORTING (1U << 12)
+/* GS_CAN_FEATURE_GET_STATE BIT(13) */
+
+#define GS_CAN_FEATURE_LISTEN_ONLY (1U << 0)
+#define GS_CAN_FEATURE_LOOP_BACK (1U << 1)
+#define GS_CAN_FEATURE_TRIPLE_SAMPLE (1U << 2)
+#define GS_CAN_FEATURE_ONE_SHOT (1U << 3)
+#define GS_CAN_FEATURE_HW_TIMESTAMP (1U << 4)
+#define GS_CAN_FEATURE_IDENTIFY (1U << 5)
+#define GS_CAN_FEATURE_USER_ID (1U << 6)
+#define GS_CAN_FEATURE_PAD_PKTS_TO_MAX_PKT_SIZE (1U << 7)
+#define GS_CAN_FEATURE_FD (1U << 8)
+#define GS_CAN_FEATURE_REQ_USB_QUIRK_LPC546XX (1U << 9)
+#define GS_CAN_FEATURE_BT_CONST_EXT (1U << 10)
+#define GS_CAN_FEATURE_TERMINATION (1U << 11)
+#define GS_CAN_FEATURE_BERR_REPORTING (1U << 12)
+#define GS_CAN_FEATURE_GET_STATE (1U << 13)
+#define GS_CAN_FEATURE_MASK ((1U << 14) - 1)
+
+/* internal quirks - keep in GS_CAN_FEATURE space for now */
+
+/* CANtact Pro original firmware:
+ * BREQ DATA_BITTIMING overlaps with GET_USER_ID
+ */
+#define GS_CAN_FEATURE_QUIRK_BREQ_CANTACT_PRO (1U << 31)
+
+#define GS_CAN_FLAG_OVERFLOW (1U << 0)
+#define GS_CAN_FLAG_FD (1U << 1)
+#define GS_CAN_FLAG_BRS (1U << 2)
+#define GS_CAN_FLAG_ESI (1U << 3)
+
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error message frame */
+
+#define CAN_ERR_DLC 8 /* dlc for error message frames */
+
+/* error class (mask) in can_id */
+#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
+#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
+#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
+#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
+#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
+#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
+#define CAN_ERR_BUSOFF 0x00000040U /* bus off */
+#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
+#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
+#define CAN_ERR_CNT 0x00000200U /* TX error counter / data[6] */
+ /* RX error counter / data[7] */
+
+/* arbitration lost in bit ... / data[0] */
+#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */
+/* else bit number in bitstream */
+
+/* error status of CAN-controller / data[1] */
+#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
+#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
+#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
+#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
+#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
+#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
+/* (at least one error counter exceeds */
+/* the protocol-defined level of 127) */
+#define CAN_ERR_CRTL_ACTIVE 0x40 /* recovered to error active state */
+
+/* error in CAN protocol (type) / data[2] */
+#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_BIT 0x01 /* single bit error */
+#define CAN_ERR_PROT_FORM 0x02 /* frame format error */
+#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */
+#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */
+#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */
+#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */
+#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */
+#define CAN_ERR_PROT_TX 0x80 /* error occurred on transmission */
+
+/* error in CAN protocol (location) / data[3] */
+#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */
+#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */
+#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */
+#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/
+#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */
+#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */
+#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */
+#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */
+#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */
+#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */
+#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */
+#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */
+#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */
+#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */
+#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */
+#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */
+#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */
+#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */
+#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */
+#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */
+
+/* error status of CAN-transceiver / data[4] */
+/* CANH CANL */
+#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */
+#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */
+#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
+
+struct gs_host_config {
+ uint32_t byte_order;
+} __PACKED;
+
+struct gs_device_config {
+ uint8_t reserved1;
+ uint8_t reserved2;
+ uint8_t reserved3;
+ uint8_t icount;
+ uint32_t sw_version;
+ uint32_t hw_version;
+} __PACKED;
+
+struct gs_device_mode {
+ uint32_t mode;
+ uint32_t flags;
+} __PACKED;
+
+struct gs_device_state {
+ uint32_t state;
+ uint32_t rxerr;
+ uint32_t txerr;
+} __PACKED;
+
+struct gs_device_bittiming {
+ uint32_t prop_seg;
+ uint32_t phase_seg1;
+ uint32_t phase_seg2;
+ uint32_t sjw;
+ uint32_t brp;
+} __PACKED;
+
+struct gs_identify_mode {
+ uint32_t mode;
+} __PACKED;
+
+struct gs_device_termination_state {
+ uint32_t state;
+} __PACKED;
+
+/* Hardware bit-timing ranges. Linux stores these fields inline in gs_device_bt_const. */
+struct can_bt_const {
+ uint32_t tseg1_min; /* Minimum of (prop_seg + phase_seg1) */
+ uint32_t tseg1_max;
+ uint32_t tseg2_min;
+ uint32_t tseg2_max;
+ uint32_t sjw_max;
+ uint32_t brp_min;
+ uint32_t brp_max;
+ uint32_t brp_inc; /* Prescaler step (1 or 2 depending on hardware) */
+} __PACKED;
+
+struct gs_device_bt_const {
+ uint32_t feature; /* GS_CAN_FEATURE_* bit mask */
+ uint32_t fclk_can; /* CAN clock in Hz, used with brp to form bit time */
+ struct can_bt_const bt_const; /* Classic / arbitration timing limits */
+} __PACKED;
+
+/* Layout must begin with the same three members as gs_device_bt_const (feature,
+ * fclk_can, bt_const), in the same order/size, so GET_BT_CONST can reuse the
+ * leading bytes of this extended struct.
+ */
+struct gs_device_bt_const_extended {
+ uint32_t feature;
+ uint32_t fclk_can;
+ struct can_bt_const bt_const; /* Classic / arbitration timing limits */
+ struct can_bt_const dbt_const; /* CAN FD data-phase timing limits */
+} __PACKED;
+
+struct classic_can {
+ uint8_t data[8];
+} __PACKED;
+
+struct classic_can_ts {
+ uint8_t data[8];
+ uint32_t timestamp_us;
+} __PACKED;
+
+struct classic_can_quirk {
+ uint8_t data[8];
+ uint8_t quirk;
+} __PACKED;
+
+struct canfd {
+ uint8_t data[64];
+} __PACKED;
+
+struct canfd_ts {
+ uint8_t data[64];
+ uint32_t timestamp_us;
+} __PACKED;
+
+struct canfd_quirk {
+ uint8_t data[64];
+ uint8_t quirk;
+} __PACKED;
+
+/* struct gs_host_frame::echo_id == GS_HOST_FRAME_ECHO_ID_RX indicates
+ * a regular RX'ed CAN frame
+ */
+#define GS_HOST_FRAME_ECHO_ID_RX 0xffffffff
+
+#define DECLARE_FLEX_ARRAY(_t, _n) \
+ struct { \
+ struct { \
+ } __dummy_##_n; \
+ _t _n[0]; \
+ }
+
+struct gs_host_frame {
+ uint32_t echo_id;
+ uint32_t can_id;
+
+ uint8_t can_dlc;
+ uint8_t channel;
+ uint8_t flags;
+ uint8_t reserved;
+
+ union {
+ DECLARE_FLEX_ARRAY(struct classic_can, classic_can);
+ DECLARE_FLEX_ARRAY(struct classic_can_ts, classic_can_ts);
+ DECLARE_FLEX_ARRAY(struct classic_can_quirk, classic_can_quirk);
+ DECLARE_FLEX_ARRAY(struct canfd, canfd);
+ DECLARE_FLEX_ARRAY(struct canfd_ts, canfd_ts);
+ DECLARE_FLEX_ARRAY(struct canfd_quirk, canfd_quirk);
+ };
+} __PACKED;
+
+#define struct_size(ptr, field, num) \
+ (offsetof(__typeof(*(ptr)), field) + sizeof((ptr)->field[0]) * (num))
+
+#define GS_HOST_CANFD_FRAME_SIZE struct_size((struct gs_host_frame *)NULL, canfd_ts, 1)
+#define GS_HOST_CAN_FRAME_SIZE struct_size((struct gs_host_frame *)NULL, classic_can, 1)
+
+#endif // GS_USB_H \ No newline at end of file
diff --git a/class/gs_usb/usbd_gs_usb.c b/class/gs_usb/usbd_gs_usb.c
new file mode 100644
index 00000000..77d970d2
--- /dev/null
+++ b/class/gs_usb/usbd_gs_usb.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2026, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+#include "usbd_gs_usb.h"
+
+static int gs_usb_vendor_class_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USB_LOG_DBG("GS USB Class request: "
+ "bRequest 0x%02x\r\n",
+ setup->bRequest);
+
+ uint16_t can_channel = setup->wValue;
+
+ switch (setup->bRequest) {
+ case GS_USB_BREQ_BT_CONST:
+ *len = sizeof(struct gs_device_bt_const);
+ return usbd_gs_usb_can_get(can_channel, setup->bRequest, *data, *len);
+ case GS_USB_BREQ_DEVICE_CONFIG:
+ *len = sizeof(struct gs_device_config);
+ return usbd_gs_usb_can_get(can_channel, setup->bRequest, *data, *len);
+ case GS_USB_BREQ_TIMESTAMP:
+ *len = sizeof(uint32_t);
+ return usbd_gs_usb_can_get(can_channel, setup->bRequest, *data, *len);
+ case GS_USB_BREQ_BT_CONST_EXT:
+ *len = sizeof(struct gs_device_bt_const_extended);
+ return usbd_gs_usb_can_get(can_channel, setup->bRequest, *data, *len);
+ case GS_USB_BREQ_GET_TERMINATION:
+ *len = sizeof(struct gs_device_termination_state);
+ return usbd_gs_usb_can_get(can_channel, setup->bRequest, *data, *len);
+ case GS_USB_BREQ_GET_STATE:
+ *len = sizeof(struct gs_device_state);
+ return usbd_gs_usb_can_get(can_channel, setup->bRequest, *data, *len);
+ case GS_USB_BREQ_HOST_FORMAT:
+ case GS_USB_BREQ_BITTIMING:
+ case GS_USB_BREQ_MODE:
+ case GS_USB_BREQ_IDENTIFY:
+ case GS_USB_BREQ_DATA_BITTIMING:
+ case GS_USB_BREQ_SET_TERMINATION:
+ return usbd_gs_usb_can_set(can_channel, setup->bRequest, *data, *len);
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+struct usbd_interface *usbd_gs_usb_init_intf(struct usbd_interface *intf)
+{
+ intf->class_interface_handler = NULL;
+ intf->class_endpoint_handler = NULL;
+ intf->vendor_handler = gs_usb_vendor_class_request_handler;
+ intf->notify_handler = NULL;
+
+ return intf;
+}
+
+__WEAK int usbd_gs_usb_can_get(uint16_t can_channel, uint8_t bRequest, uint8_t *data, uint32_t len)
+{
+ (void)can_channel;
+ (void)bRequest;
+ (void)data;
+ (void)len;
+
+ switch (bRequest) {
+ case GS_USB_BREQ_BT_CONST:
+ break;
+ case GS_USB_BREQ_DEVICE_CONFIG:
+ break;
+ case GS_USB_BREQ_TIMESTAMP:
+ break;
+ case GS_USB_BREQ_BT_CONST_EXT:
+ break;
+ case GS_USB_BREQ_GET_TERMINATION:
+ break;
+ case GS_USB_BREQ_GET_STATE:
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+__WEAK int usbd_gs_usb_can_set(uint16_t can_channel, uint8_t bRequest, uint8_t *data, uint32_t len)
+{
+ (void)can_channel;
+ (void)bRequest;
+ (void)data;
+ (void)len;
+
+ switch (bRequest) {
+ case GS_USB_BREQ_HOST_FORMAT:
+ break;
+ case GS_USB_BREQ_BITTIMING:
+ break;
+ case GS_USB_BREQ_MODE:
+ break;
+ case GS_USB_BREQ_IDENTIFY:
+ break;
+ case GS_USB_BREQ_DATA_BITTIMING:
+ break;
+ case GS_USB_BREQ_SET_TERMINATION:
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/class/gs_usb/usbd_gs_usb.h b/class/gs_usb/usbd_gs_usb.h
new file mode 100644
index 00000000..7e03d3f4
--- /dev/null
+++ b/class/gs_usb/usbd_gs_usb.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2026, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBD_GS_USB_H
+#define USBD_GS_USB_H
+
+#include "gs_usb.h"
+
+/* Init gs usb interface driver */
+struct usbd_interface *usbd_gs_usb_init_intf(struct usbd_interface *intf);
+
+/* Setup request command callback api */
+int usbd_gs_usb_can_get(uint16_t can_channel, uint8_t bRequest, uint8_t *data, uint32_t len);
+int usbd_gs_usb_can_set(uint16_t can_channel, uint8_t bRequest, uint8_t *data, uint32_t len);
+
+#endif // USBD_GS_USB_H \ No newline at end of file
diff --git a/class/hid/usbh_hid.c b/class/hid/usbh_hid.c
index 3039f5aa..f8b89786 100644
--- a/class/hid/usbh_hid.c
+++ b/class/hid/usbh_hid.c
@@ -103,10 +103,10 @@ int usbh_hid_get_idle(struct usbh_hid *hid_class, uint8_t *buffer)
setup->wLength = 1;
ret = usbh_control_transfer(hid_class->hport, setup, g_hid_buf[hid_class->minor]);
- if (ret < 8) {
+ if (ret < 1) {
return ret;
}
- memcpy(buffer, g_hid_buf[hid_class->minor], MIN((uint32_t)ret - 8, 1));
+ *buffer = g_hid_buf[hid_class->minor][0];
return ret;
}
@@ -145,10 +145,10 @@ int usbh_hid_get_protocol(struct usbh_hid *hid_class, uint8_t *protocol)
setup->wLength = 1;
ret = usbh_control_transfer(hid_class->hport, setup, g_hid_buf[hid_class->minor]);
- if (ret < 8) {
+ if (ret < 1) {
return ret;
}
- memcpy(protocol, g_hid_buf[hid_class->minor], MIN((uint32_t)ret - 8, 1));
+ *protocol = g_hid_buf[hid_class->minor][0];
return ret;
}
@@ -324,7 +324,7 @@ int usbh_hid_parse_report_descriptor(const uint8_t *report_data, uint32_t report
if (itemsize == 3) /* HID spec: 6.2.2.2 - Short Items */
itemsize = 4;
- if((itemsize + i + 1) > report_size) {
+ if ((itemsize + i + 1) > report_size) {
goto err;
}
@@ -450,7 +450,7 @@ int usbh_hid_report_convert(struct usbh_hid_report_item *item, const uint8_t *re
const uint8_t *src;
uint32_t bits_len = item->attribute.report_size * item->attribute.report_count;
- if(bits_len == 0) {
+ if (bits_len == 0) {
return -1;
}
diff --git a/class/hub/usbh_hub.c b/class/hub/usbh_hub.c
index 5538de86..98e9f0e2 100644
--- a/class/hub/usbh_hub.c
+++ b/class/hub/usbh_hub.c
@@ -71,7 +71,7 @@ static int _usbh_hub_get_hub_descriptor(struct usbh_hub *hub, uint8_t *buffer)
setup->wLength = USB_SIZEOF_HUB_DESC;
ret = usbh_control_transfer(hub->parent, setup, g_hub_buf[hub->bus->busid]);
- if (ret < 0) {
+ if (ret < USB_SIZEOF_HUB_DESC) {
return ret;
}
memcpy(buffer, g_hub_buf[hub->bus->busid], USB_SIZEOF_HUB_DESC);
@@ -93,7 +93,7 @@ static int _usbh_hub_get_hub_ss_descriptor(struct usbh_hub *hub, uint8_t *buffer
setup->wLength = USB_SIZEOF_HUB_SS_DESC;
ret = usbh_control_transfer(hub->parent, setup, g_hub_buf[hub->bus->busid]);
- if (ret < 0) {
+ if (ret < USB_SIZEOF_HUB_SS_DESC) {
return ret;
}
memcpy(buffer, g_hub_buf[hub->bus->busid], USB_SIZEOF_HUB_SS_DESC);
@@ -115,7 +115,7 @@ static int _usbh_hub_get_portstatus(struct usbh_hub *hub, uint8_t port, struct h
setup->wLength = 4;
ret = usbh_control_transfer(hub->parent, setup, g_hub_buf[hub->bus->busid]);
- if (ret < 0) {
+ if (ret < 4) {
return ret;
}
memcpy(port_status, g_hub_buf[hub->bus->busid], 4);
@@ -647,15 +647,9 @@ static void usbh_hub_events(struct usbh_hub *hub)
USB_LOG_INFO("New %s device on Bus %u, Hub %u, Port %u connected\r\n", speed_table[speed], hub->bus->busid, hub->index, port + 1);
if (usbh_enumerate(child) < 0) {
- /** release child sources */
- usbh_hubport_release(child);
USB_LOG_ERR("Port %u enumerate fail\r\n", child->port);
}
} else {
- child = &hub->child[port];
- /** release child sources */
- usbh_hubport_release(child);
-
/** some USB 3.0 ip may failed to enable USB 2.0 port for USB 3.0 device */
USB_LOG_WRN("Failed to enable port %u\r\n", port + 1);
@@ -731,21 +725,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;
}
diff --git a/class/msc/usbd_msc.c b/class/msc/usbd_msc.c
index 6523ffb9..d21051f0 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;
@@ -151,8 +152,8 @@ static void usbd_msc_send_csw(uint8_t busid, uint8_t CSW_Status)
g_usbd_msc[busid].csw.bStatus = CSW_Status;
/* updating the State Machine , so that we wait CSW when this
- * transfer is complete, ie when we get a bulk in callback
- */
+ * transfer is complete, ie when we get a bulk in callback
+ */
g_usbd_msc[busid].stage = MSC_WAIT_CSW;
USB_LOG_DBG("Send csw\r\n");
@@ -164,8 +165,8 @@ static void usbd_msc_send_info(uint8_t busid, uint8_t *buffer, uint8_t size)
size = MIN(size, g_usbd_msc[busid].cbw.dDataLength);
/* updating the State Machine , so that we send CSW when this
- * transfer is complete, ie when we get a bulk in callback
- */
+ * transfer is complete, ie when we get a bulk in callback
+ */
g_usbd_msc[busid].stage = MSC_SEND_CSW;
usbd_ep_start_write(busid, mass_ep_data[busid][MSD_IN_EP_IDX].ep_addr, buffer, size);
@@ -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;
@@ -657,10 +691,15 @@ static bool SCSI_processRead(uint8_t busid)
static bool SCSI_processWrite(uint8_t busid, uint32_t nbytes)
{
uint32_t data_len = 0;
+ uint8_t csw_status = CSW_STATUS_CMD_PASSED;
USB_LOG_DBG("write lba:%d\r\n", g_usbd_msc[busid].start_sector);
- if (usbd_msc_sector_write(busid, g_usbd_msc[busid].cbw.bLUN, g_usbd_msc[busid].start_sector, g_usbd_msc[busid].block_buffer, nbytes) != 0) {
+ if (g_usbd_msc[busid].readonly) {
+ /* Discard data when read-only */
+ SCSI_SetSenseData(busid, SCSI_KCQWP_COMMANDNOTALLOWED);
+ csw_status = CSW_STATUS_CMD_FAILED;
+ } else if (usbd_msc_sector_write(busid, g_usbd_msc[busid].cbw.bLUN, g_usbd_msc[busid].start_sector, g_usbd_msc[busid].block_buffer, nbytes) != 0) {
SCSI_SetSenseData(busid, SCSI_KCQHE_WRITEFAULT);
return false;
}
@@ -670,7 +709,7 @@ static bool SCSI_processWrite(uint8_t busid, uint32_t nbytes)
g_usbd_msc[busid].csw.dDataResidue -= nbytes;
if (g_usbd_msc[busid].nsectors == 0) {
- usbd_msc_send_csw(busid, CSW_STATUS_CMD_PASSED);
+ usbd_msc_send_csw(busid, csw_status);
} else {
data_len = MIN(g_usbd_msc[busid].nsectors * g_usbd_msc[busid].scsi_blk_size[g_usbd_msc[busid].cbw.bLUN], CONFIG_USBDEV_MSC_MAX_BUFSIZE);
usbd_ep_start_read(busid, mass_ep_data[busid][MSD_OUT_EP_IDX].ep_addr, g_usbd_msc[busid].block_buffer, data_len);
@@ -727,6 +766,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/msc/usbh_msc.c b/class/msc/usbh_msc.c
index cec9eb0c..1a93af6a 100644
--- a/class/msc/usbh_msc.c
+++ b/class/msc/usbh_msc.c
@@ -52,6 +52,7 @@ static void usbh_msc_class_free(struct usbh_msc *msc_class)
static int usbh_msc_get_maxlun(struct usbh_msc *msc_class, uint8_t *buffer)
{
struct usb_setup_packet *setup;
+ int ret;
if (!msc_class || !msc_class->hport) {
return -USB_ERR_INVAL;
@@ -64,7 +65,12 @@ static int usbh_msc_get_maxlun(struct usbh_msc *msc_class, uint8_t *buffer)
setup->wIndex = msc_class->intf;
setup->wLength = 1;
- return usbh_control_transfer(msc_class->hport, setup, buffer);
+ ret = usbh_control_transfer(msc_class->hport, setup, buffer);
+ if (ret < 1) {
+ return ret;
+ }
+
+ return ret;
}
static void usbh_msc_cbw_dump(struct CBW *cbw)
@@ -383,11 +389,14 @@ int usbh_msc_scsi_init(struct usbh_msc *msc_class)
return ret;
}
- if (msc_class->blocksize > 0) {
+ if (msc_class->blocksize == 512 ||
+ msc_class->blocksize == 1024 ||
+ msc_class->blocksize == 2048 ||
+ msc_class->blocksize == 4096) {
USB_LOG_INFO("Capacity info:\r\n");
USB_LOG_INFO("Block num:%d,block size:%d\r\n", (unsigned int)msc_class->blocknum, (unsigned int)msc_class->blocksize);
} else {
- USB_LOG_ERR("Invalid block size\r\n");
+ USB_LOG_ERR("Invalid block size %u\r\n", (unsigned int)msc_class->blocksize);
return -USB_ERR_RANGE;
}
diff --git a/class/mtp/usbd_mtp.h b/class/mtp/usbd_mtp.h
index 5db8cf97..6f9209cf 100644
--- a/class/mtp/usbd_mtp.h
+++ b/class/mtp/usbd_mtp.h
@@ -8,63 +8,6 @@
#include "usb_mtp.h"
-#define MTP_O_RDONLY 0 /* +1 == FREAD */
-#define MTP_O_WRONLY 1 /* +1 == FWRITE */
-#define MTP_O_RDWR 2 /* +1 == FREAD|FWRITE */
-
-#define MTP_S_IFMT 0170000 /* type of file */
-#define MTP_S_IFDIR 0040000 /* directory */
-#define MTP_S_IFCHR 0020000 /* character special */
-#define MTP_S_IFBLK 0060000 /* block special */
-#define MTP_S_IFREG 0100000 /* regular */
-#define MTP_S_IFLNK 0120000 /* symbolic link */
-#define MTP_S_IFSOCK 0140000 /* socket */
-#define MTP_S_IFIFO 0010000 /* fifo */
-#define MTP_S_IREAD 0000400 /* read permission, owner */
-#define MTP_S_IWRITE 0000200 /* write permission, owner */
-#define MTP_S_IEXEC 0000100 /* execute/search permission, owner */
-#define MTP_S_ENFMT 0002000 /* enforcement-mode locking */
-
-#define MTP_S_IRWXU (MTP_S_IRUSR | MTP_S_IWUSR | MTP_S_IXUSR)
-#define MTP_S_IRUSR 0000400 /* read permission, owner */
-#define MTP_S_IWUSR 0000200 /* write permission, owner */
-#define MTP_S_IXUSR 0000100 /* execute/search permission, owner */
-#define MTP_S_IRWXG (MTP_S_IRGRP | MTP_S_IWGRP | MTP_S_IXGRP)
-#define MTP_S_IRGRP 0000040 /* read permission, group */
-#define MTP_S_IWGRP 0000020 /* write permission, grougroup */
-#define MTP_S_IXGRP 0000010 /* execute/search permission, group */
-#define MTP_S_IRWXO (MTP_S_IROTH | MTP_S_IWOTH | MTP_S_IXOTH)
-#define MTP_S_IROTH 0000004 /* read permission, other */
-#define MTP_S_IWOTH 0000002 /* write permission, other */
-#define MTP_S_IXOTH 0000001 /* execute/search permission, other */
-
-typedef void MTP_DIR;
-
-struct mtp_statfs {
- size_t f_bsize; /* block size */
- size_t f_blocks; /* total data blocks in file system */
- size_t f_bfree; /* free blocks in file system */
-};
-
-struct mtp_stat {
- uint32_t st_dev;
- uint32_t st_ino;
- uint32_t st_mode;
- uint32_t st_nlink;
- uint32_t st_uid;
- uint32_t st_gid;
- size_t st_size;
- size_t st_blksize;
- size_t st_blocks;
-};
-
-struct mtp_dirent {
- uint8_t d_type; /* The type of the file */
- uint8_t d_namlen; /* The length of the not including the terminating null file name */
- uint16_t d_reclen; /* length of this record */
- char d_name[CONFIG_USBDEV_MTP_MAX_PATHNAME]; /* The null-terminated file name */
-};
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -77,25 +20,6 @@ struct usbd_interface *usbd_mtp_init_intf(struct usbd_interface *intf,
int usbd_mtp_notify_object_add(const char *path);
int usbd_mtp_notify_object_remove(const char *path);
-const char *usbd_mtp_fs_root_path(void);
-const char *usbd_mtp_fs_description(void);
-
-int usbd_mtp_mkdir(const char *path);
-int usbd_mtp_rmdir(const char *path);
-MTP_DIR *usbd_mtp_opendir(const char *name);
-int usbd_mtp_closedir(MTP_DIR *d);
-struct mtp_dirent *usbd_mtp_readdir(MTP_DIR *d);
-
-int usbd_mtp_statfs(const char *path, struct mtp_statfs *buf);
-int usbd_mtp_stat(const char *file, struct mtp_stat *buf);
-
-int usbd_mtp_open(const char *path, uint8_t mode);
-int usbd_mtp_close(int fd);
-int usbd_mtp_read(int fd, void *buf, size_t len);
-int usbd_mtp_write(int fd, const void *buf, size_t len);
-
-int usbd_mtp_unlink(const char *path);
-
#ifdef __cplusplus
}
#endif
diff --git a/class/serial/usbh_cdc_acm.c b/class/serial/usbh_cdc_acm.c
index cf5363ef..48643cb3 100644
--- a/class/serial/usbh_cdc_acm.c
+++ b/class/serial/usbh_cdc_acm.c
@@ -108,7 +108,7 @@ static int usbh_cdc_acm_get_line_coding(struct usbh_serial *serial, struct cdc_l
setup->wLength = 7;
ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
- if (ret < 0) {
+ if (ret < sizeof(struct cdc_line_coding)) {
return ret;
}
memcpy(line_coding, serial->iobuffer, sizeof(struct cdc_line_coding));
diff --git a/class/serial/usbh_ch34x.c b/class/serial/usbh_ch34x.c
index 4595bcae..a5fcd334 100644
--- a/class/serial/usbh_ch34x.c
+++ b/class/serial/usbh_ch34x.c
@@ -103,7 +103,7 @@ static int usbh_ch34x_control_in(struct usbh_serial *serial, uint8_t bRequest, u
setup->wLength = size;
ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
- if (ret < 0) {
+ if (ret < size) {
return ret;
}
memcpy(data, serial->iobuffer, size);
diff --git a/class/serial/usbh_cp210x.c b/class/serial/usbh_cp210x.c
index 7b240e2c..0f645a17 100644
--- a/class/serial/usbh_cp210x.c
+++ b/class/serial/usbh_cp210x.c
@@ -181,7 +181,7 @@ static int usbh_cp210x_control_in(struct usbh_serial *serial, uint8_t bRequest,
setup->wLength = size;
ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
- if (ret < 0) {
+ if (ret < size) {
return ret;
}
memcpy(data, serial->iobuffer, size);
diff --git a/class/serial/usbh_ftdi.c b/class/serial/usbh_ftdi.c
index c2d3a77b..19f00110 100644
--- a/class/serial/usbh_ftdi.c
+++ b/class/serial/usbh_ftdi.c
@@ -340,8 +340,8 @@ static int usbh_ftdi_get_modem_status(struct usbh_serial *serial)
setup->wLength = 2;
ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
- if (ret < 0) {
- return 0;
+ if (ret < 2) {
+ return ret;
}
status = (serial->iobuffer[0] & FTDI_SIO_DSR_MASK ? USBH_SERIAL_TIOCM_DSR : 0) |
diff --git a/class/serial/usbh_pl2303.c b/class/serial/usbh_pl2303.c
index 062f8128..e3b6a307 100644
--- a/class/serial/usbh_pl2303.c
+++ b/class/serial/usbh_pl2303.c
@@ -242,10 +242,10 @@ static int pl2303_vendor_read(struct usbh_serial *serial, uint16_t wValue, uint8
setup->wLength = 1;
ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
- if (ret < 0) {
+ if (ret < 1) {
return ret;
}
- memcpy(data, serial->iobuffer, 1);
+ *data = serial->iobuffer[0];
return ret;
}
@@ -280,7 +280,7 @@ static bool pl2303_is_hxd_clone(struct usbh_serial *serial)
setup->wLength = 7;
ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
- if (ret < 0) {
+ if (ret < 7) {
return false;
}
return true;
@@ -566,7 +566,7 @@ static int usbh_pl2303_get_line_coding(struct usbh_serial *serial, struct cdc_li
setup->wLength = 7;
ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
- if (ret < 0) {
+ if (ret < sizeof(struct cdc_line_coding)) {
return ret;
}
memcpy(line_coding, serial->iobuffer, sizeof(struct cdc_line_coding));
diff --git a/class/serial/usbh_serial.c b/class/serial/usbh_serial.c
index 66aa51fc..65bf63f6 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,19 +74,30 @@ 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);
- }
+ USB_LOG_ERR("serial transfer error: %d\n", nbytes);
serial->rx_errorcode = nbytes;
usb_osal_sem_give(serial->rx_complete_sem);
return;
@@ -93,30 +111,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 +392,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,20 +494,27 @@ int usbh_serial_read(struct usbh_serial *serial, void *buffer, uint32_t buflen)
return -USB_ERR_NODEV;
}
- if (serial->open_flags & USBH_SERIAL_O_NONBLOCK) {
- return usb_ringbuffer_read(&serial->rx_rb, buffer, buflen);
- } else {
+ 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)) {
if (usb_ringbuffer_get_used(&serial->rx_rb) == 0) {
ret = usb_osal_sem_take(serial->rx_complete_sem, serial->rx_timeout_ms == 0 ? USB_OSAL_WAITING_FOREVER : serial->rx_timeout_ms);
if (ret < 0) {
return ret;
}
- if (serial->rx_errorcode < 0) {
- return serial->rx_errorcode;
- }
}
- return usb_ringbuffer_read(&serial->rx_rb, buffer, buflen);
}
+
+ if (serial->rx_errorcode < 0) {
+ return serial->rx_errorcode;
+ }
+ return usb_ringbuffer_read(&serial->rx_rb, buffer, buflen);
}
int usbh_serial_cdc_write_async(struct usbh_serial *serial, uint8_t *buffer, uint32_t buflen, usbh_complete_callback_t complete, void *arg)
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/class/tmc/usb_tmc.h b/class/tmc/usb_tmc.h
new file mode 100644
index 00000000..de03d297
--- /dev/null
+++ b/class/tmc/usb_tmc.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USB_TMC_H
+#define USB_TMC_H
+
+#define TMC_VERSION 0x0100
+
+/* Table 43: TMC Class Code */
+#define TMC_CLASS_APPLICATION 0xfe
+#define TMC_APPLICATION_SUBCLASS_TMC 0x03
+
+/* Table 44 */
+#define TMC_PROTOCOL_NONE 0
+#define TMC_PROTOCOL_USB488 1
+
+/* USB TMC Class Specific Requests, Table 15 sec 4.2.1 */
+#define TMC_REQUEST_INITIATE_ABORT_BULK_OUT 1
+#define TMC_REQUEST_CHECK_ABORT_BULK_OUT_STATUS 2
+#define TMC_REQUEST_INITIATE_ABORT_BULK_IN 3
+#define TMC_REQUEST_CHECK_ABORT_BULK_IN_STATUS 4
+#define TMC_REQUEST_INITIATE_CLEAR 5
+#define TMC_REQUEST_CHECK_CLEAR_STATUS 6
+#define TMC_REQUEST_GET_CAPABILITIES 7
+#define TMC_REQUEST_INDICATOR_PULSE 64 //optional
+
+/* USB TMC status values Table 16 */
+#define TMC_STATUS_SUCCESS 1
+#define TMC_STATUS_PENDING 2
+#define TMC_STATUS_FAILED 0x80
+#define TMC_STATUS_TRANSFER_NOT_IN_PROGRESS 0x81
+#define TMC_STATUS_SPLIT_NOT_IN_PROGRESS 0x82
+#define TMC_STATUS_SPLIT_IN_PROGRESS 0x83
+
+struct tmc_bulk_header {
+ uint8_t MsgID;
+ uint8_t bTag;
+ uint8_t bTagInverse;
+ uint8_t reserved;
+
+ union {
+ struct _dev_dep_msg_out {
+ uint32_t transferSize;
+ uint8_t bmTransferAttributes;
+ uint8_t reserved[3];
+ } dev_dep_msg_out;
+
+ struct _req_dev_dep_msg_in {
+ uint32_t transferSize;
+ uint8_t bmTransferAttributes;
+ uint8_t TermChar;
+ uint8_t reserved[2];
+ } req_dev_dep_msg_in;
+
+ struct _dev_dep_msg_in {
+ uint32_t transferSize;
+ uint8_t bmTransferAttributes;
+ uint8_t reserved[3];
+ } dev_dep_msg_in;
+
+ struct _vendor_specific_out {
+ uint32_t transferSize;
+ uint8_t reserved[4];
+ } vendor_specific_out;
+
+ struct _req_vendor_specific_in {
+ uint32_t transferSize;
+ uint8_t reserved[4];
+ } req_vendor_specific_in;
+
+ struct _vendor_specific_in {
+ uint32_t transferSize;
+ uint8_t reserved[4];
+ } vendor_specific_in;
+
+ uint8_t data[8];
+ } msg_specific;
+} __attribute__((packed));
+
+/* Table 2, MsgId values */
+#define TMC_MSGID_OUT_DEV_DEP_MSG_OUT 1
+#define TMC_MSGID_OUT_REQUEST_DEV_DEP_MSG_IN 2
+#define TMC_MSGID_IN_DEV_DEP_MSG_IN 2
+/* 3-125 Reserved for USBTMC */
+#define TMC_MSGID_OUT_VENDOR_SPECIFIC_OUT 126
+#define TMC_MSGID_OUT_REQUEST_VENDOR_SPECIFIC_IN 127
+#define TMC_MSGID_IN_VENDOR_SPECIFIC_IN 127
+/* 128-255 Reserved for USBTMC subclass and VISA */
+
+/** The last USBTMC message data byte in the transfer is the last byte of the
+ * USBTMC message. */
+#define TMC_BULK_HEADER_TRANSFER_ATTR_EOM (1 << 0)
+/** The Bulk-IN transfer must terminate on the specified TermChar. The Host may
+ * only set this bit if the USBTMC interface indicates it supports TermChar in
+ * the GET_CAPABILITIES response packet */
+#define TMC_BULK_HEADER_TRANSFER_ATTR_TERMCHAR (1 << 1)
+
+#define TMC_INTERFACE_CAPABILITY_INDICATOR_PULSE (1 << 2)
+#define TMC_INTERFACE_CAPABILITY_TALK_ONLY (1 << 1)
+#define TMC_INTERFACE_CAPABILITY_LISTEN_ONLY (1 << 0)
+#define TMC_DEVICE_CAPABILITY_TERMCHAR (1 << 0)
+
+struct tmc_capabilities_response {
+ uint8_t USBTMC_status;
+ uint8_t reserved0;
+ uint16_t bcdUSBTMC;
+ uint8_t InterfaceCapabilities;
+ uint8_t DeviceCapabilities;
+ uint8_t reserved1[6];
+ uint16_t bcdUSB488;
+ uint8_t InterfaceCapabilities488;
+ uint8_t DeviceCapabilities488;
+ uint8_t reserved_subclass[8];
+} __attribute__((packed));
+
+struct tmc_initiate_abort_response {
+ uint8_t USBTMC_status;
+ uint8_t bTag;
+} __attribute__((packed));
+
+struct tmc_check_abort_bulk_response {
+ uint8_t USBTMC_status;
+ uint8_t bmAbortBulkIn; // D0: BulkInFifoBytes/BulkOutFifoBytes
+ uint8_t reserved[2];
+ uint32_t NBYTES_RXD_TXD;
+} __attribute__((packed));
+
+struct tmc_check_clear_status_response {
+ uint8_t USBTMC_status;
+ uint8_t bmClear; // D0: BulkInFifoBytes/BulkOutFifoBytes
+} __attribute__((packed));
+
+// clang-format off
+#define TMC_DESCRIPTOR_INIT(bInterfaceNumber, out_ep, in_ep, wMaxPacketSize, str_idx) \
+ USB_INTERFACE_DESCRIPTOR_INIT(bInterfaceNumber, 0x00, 0x02, 0xFE, 0x03, 0x00, str_idx), \
+ USB_ENDPOINT_DESCRIPTOR_INIT(out_ep, USB_ENDPOINT_TYPE_BULK, wMaxPacketSize, 0x00), \
+ USB_ENDPOINT_DESCRIPTOR_INIT(in_ep, USB_ENDPOINT_TYPE_BULK, wMaxPacketSize, 0x00)
+// clang-format on
+
+// clang-format off
+#define TMC488_DESCRIPTOR_INIT(bInterfaceNumber, out_ep, in_ep, wMaxPacketSize, str_idx) \
+ USB_INTERFACE_DESCRIPTOR_INIT(bInterfaceNumber, 0x00, 0x02, 0xFE, 0x03, 0x01, str_idx), \
+ USB_ENDPOINT_DESCRIPTOR_INIT(out_ep, USB_ENDPOINT_TYPE_BULK, wMaxPacketSize, 0x00), \
+ USB_ENDPOINT_DESCRIPTOR_INIT(in_ep, USB_ENDPOINT_TYPE_BULK, wMaxPacketSize, 0x00)
+// clang-format on
+
+#endif /* USB_TMC_H */ \ No newline at end of file
diff --git a/class/tmc/usbd_tmc.c b/class/tmc/usbd_tmc.c
new file mode 100644
index 00000000..7f351e99
--- /dev/null
+++ b/class/tmc/usbd_tmc.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+#include "usbd_tmc.h"
+
+#define TMC_OUT_EP_IDX 0
+#define TMC_IN_EP_IDX 1
+
+/* Describe EndPoints configuration */
+static struct usbd_endpoint tmc_ep_data[2];
+
+#ifndef CONFIG_USBDEV_TMC_MAX_BUFSIZE
+#define CONFIG_USBDEV_TMC_MAX_BUFSIZE 1024
+#endif
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t tmc_read_buffer[CONFIG_USBDEV_TMC_MAX_BUFSIZE];
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t tmc_write_buffer[CONFIG_USBDEV_TMC_MAX_BUFSIZE];
+
+static volatile uint32_t g_tmc_read_offset = 0;
+static volatile uint32_t g_tmc_read_total = 0;
+
+static scpi_t scpi_context;
+
+static char scpi_input_buffer[CONFIG_USBDEV_TMC_MAX_BUFSIZE];
+
+#define SCPI_ERROR_QUEUE_SIZE 4
+static scpi_error_t scpi_error_queue_data[SCPI_ERROR_QUEUE_SIZE];
+
+static size_t SCPI_Write(scpi_t *context, const char *data, size_t len)
+{
+ (void)context;
+
+ struct tmc_bulk_header *header = (struct tmc_bulk_header *)tmc_write_buffer;
+
+ USB_ASSERT_MSG((header->msg_specific.dev_dep_msg_in.transferSize + len) <= (CONFIG_USBDEV_TMC_MAX_BUFSIZE - sizeof(struct tmc_bulk_header)), "SCPI_Write: buffer overflow\r\n");
+
+ memcpy(tmc_write_buffer + sizeof(struct tmc_bulk_header) + header->msg_specific.dev_dep_msg_in.transferSize, data, len);
+ header->msg_specific.dev_dep_msg_in.transferSize += len;
+
+ return len;
+}
+
+static scpi_result_t SCPI_Flush(scpi_t *context)
+{
+ (void)context;
+
+ return SCPI_RES_OK;
+}
+
+static int SCPI_Error(scpi_t *context, int_fast16_t err)
+{
+ (void)context;
+
+ return SCPI_RES_OK;
+}
+
+static scpi_result_t SCPI_Control(scpi_t *context, scpi_ctrl_name_t ctrl, scpi_reg_val_t val)
+{
+ (void)context;
+
+ return SCPI_RES_OK;
+}
+
+static scpi_result_t SCPI_Reset(scpi_t *context)
+{
+ (void)context;
+
+ return SCPI_RES_OK;
+}
+
+static scpi_interface_t scpi_interface = {
+ .error = SCPI_Error,
+ .write = SCPI_Write,
+ .control = SCPI_Control,
+ .flush = SCPI_Flush,
+ .reset = SCPI_Reset,
+};
+
+static int tmc_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USB_LOG_DBG("TMC Class request: "
+ "bRequest 0x%02x\r\n",
+ setup->bRequest);
+
+ struct tmc_capabilities_response s_tmc_capabilities = {
+ .USBTMC_status = TMC_STATUS_SUCCESS,
+ .bcdUSBTMC = 0x0100,
+ .reserved0 = 0,
+ .reserved1 = { 0 },
+ .reserved_subclass = { 0 },
+ .InterfaceCapabilities = 0,
+ .DeviceCapabilities = 0
+ };
+
+ switch (setup->bRequest) {
+ case TMC_REQUEST_INITIATE_ABORT_BULK_OUT:
+ (*data)[0] = TMC_STATUS_SUCCESS;
+ *len = 2;
+ break;
+ case TMC_REQUEST_CHECK_ABORT_BULK_OUT_STATUS:
+ (*data)[0] = TMC_STATUS_SUCCESS;
+ (*data)[1] = 0; // bmAbortBulkIn
+ (*data)[2] = 0; // reserved
+ (*data)[3] = 0; // reserved
+ *len = 4;
+ break;
+ case TMC_REQUEST_INITIATE_ABORT_BULK_IN:
+ (*data)[0] = TMC_STATUS_SUCCESS;
+ *len = 2;
+ break;
+ case TMC_REQUEST_CHECK_ABORT_BULK_IN_STATUS:
+ (*data)[0] = TMC_STATUS_SUCCESS;
+ (*data)[1] = 0; // bmAbortBulkIn
+ (*data)[2] = 0; // reserved
+ (*data)[3] = 0; // reserved
+ *len = 4;
+ break;
+ case TMC_REQUEST_INITIATE_CLEAR:
+ (*data)[0] = TMC_STATUS_SUCCESS;
+ *len = 1;
+ break;
+ case TMC_REQUEST_CHECK_CLEAR_STATUS:
+ (*data)[0] = TMC_STATUS_SUCCESS;
+ (*data)[1] = 0;
+ *len = 2;
+ break;
+ case TMC_REQUEST_GET_CAPABILITIES:
+ memcpy(*data, &s_tmc_capabilities, sizeof(s_tmc_capabilities));
+ *len = sizeof(s_tmc_capabilities);
+ break;
+ case TMC_REQUEST_INDICATOR_PULSE:
+ (*data)[0] = TMC_STATUS_SUCCESS;
+ *len = 1;
+ break;
+ default:
+ USB_LOG_WRN("Unhandled TMC Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+
+ return 0;
+}
+
+void tmc_notify_handler(uint8_t busid, uint8_t event, void *arg)
+{
+ (void)arg;
+
+ switch (event) {
+ case USBD_EVENT_RESET:
+ break;
+ case USBD_EVENT_CONFIGURED:
+ g_tmc_read_offset = 0;
+ g_tmc_read_total = 0;
+ usbd_ep_start_read(busid, tmc_ep_data[TMC_OUT_EP_IDX].ep_addr, tmc_read_buffer + g_tmc_read_offset, usbd_get_ep_mps(busid, tmc_ep_data[TMC_OUT_EP_IDX].ep_addr));
+ break;
+
+ default:
+ break;
+ }
+}
+
+void tmc_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
+{
+ struct tmc_bulk_header *out_header = (struct tmc_bulk_header *)tmc_read_buffer;
+ struct tmc_bulk_header *in_header = (struct tmc_bulk_header *)tmc_write_buffer;
+
+ g_tmc_read_offset += nbytes;
+
+ if (g_tmc_read_total == 0) {
+ USB_ASSERT_MSG(nbytes >= sizeof(struct tmc_bulk_header), "tmc header size error\r\n");
+
+ switch (out_header->MsgID) {
+ case TMC_MSGID_OUT_DEV_DEP_MSG_OUT:
+ g_tmc_read_total = ((out_header->msg_specific.dev_dep_msg_out.transferSize + 3u) & ~3u) + sizeof(struct tmc_bulk_header);
+ break;
+ case TMC_MSGID_OUT_REQUEST_DEV_DEP_MSG_IN:
+ g_tmc_read_total = sizeof(struct tmc_bulk_header);
+ break;
+ default:
+ USB_LOG_WRN("Unhandled TMC MsgID 0x%02x\r\n", out_header->MsgID);
+ break;
+ }
+ USB_ASSERT_MSG(g_tmc_read_total <= CONFIG_USBDEV_TMC_MAX_BUFSIZE, "tmc read buffer overflow\r\n");
+ }
+
+ if (g_tmc_read_offset < g_tmc_read_total) {
+ usbd_ep_start_read(busid, tmc_ep_data[TMC_OUT_EP_IDX].ep_addr, tmc_read_buffer + g_tmc_read_offset, usbd_get_ep_mps(busid, tmc_ep_data[TMC_OUT_EP_IDX].ep_addr));
+ return;
+ }
+
+ switch (out_header->MsgID) {
+ case TMC_MSGID_OUT_DEV_DEP_MSG_OUT:
+ SCPI_Input(&scpi_context,
+ (const char *)(tmc_read_buffer + sizeof(struct tmc_bulk_header)),
+ g_tmc_read_total);
+ break;
+ case TMC_MSGID_OUT_REQUEST_DEV_DEP_MSG_IN:
+
+ in_header->MsgID = TMC_MSGID_IN_DEV_DEP_MSG_IN;
+ in_header->bTag = out_header->bTag;
+ in_header->bTagInverse = out_header->bTagInverse;
+ in_header->reserved = 0;
+ in_header->msg_specific.dev_dep_msg_in.bmTransferAttributes = TMC_BULK_HEADER_TRANSFER_ATTR_TERMCHAR;
+
+ usbd_ep_start_write(busid, tmc_ep_data[TMC_IN_EP_IDX].ep_addr, tmc_write_buffer, sizeof(struct tmc_bulk_header) + in_header->msg_specific.dev_dep_msg_in.transferSize);
+ break;
+ default:
+ USB_LOG_WRN("Unhandled TMC MsgID 0x%02x\r\n", out_header->MsgID);
+ break;
+ }
+
+ g_tmc_read_offset = 0;
+ g_tmc_read_total = 0;
+ usbd_ep_start_read(busid, tmc_ep_data[TMC_OUT_EP_IDX].ep_addr, tmc_read_buffer + g_tmc_read_offset, usbd_get_ep_mps(busid, tmc_ep_data[TMC_OUT_EP_IDX].ep_addr));
+}
+
+void tmc_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
+{
+ if ((nbytes % usbd_get_ep_mps(busid, ep)) == 0 && nbytes) {
+ /* send zlp */
+ usbd_ep_start_write(busid, ep, NULL, 0);
+ } else {
+ }
+}
+
+struct usbd_interface *usbd_tmc_init_intf(uint8_t busid,
+ struct usbd_interface *intf,
+ const uint8_t out_ep, const uint8_t in_ep,
+ const scpi_command_t *scpi_commands,
+ const char **idn)
+{
+ (void)busid;
+
+ intf->class_interface_handler = tmc_class_interface_request_handler;
+ intf->class_endpoint_handler = NULL;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = tmc_notify_handler;
+
+ tmc_ep_data[TMC_OUT_EP_IDX].ep_addr = out_ep;
+ tmc_ep_data[TMC_OUT_EP_IDX].ep_cb = tmc_bulk_out;
+ tmc_ep_data[TMC_IN_EP_IDX].ep_addr = in_ep;
+ tmc_ep_data[TMC_IN_EP_IDX].ep_cb = tmc_bulk_in;
+
+ usbd_add_endpoint(busid, &tmc_ep_data[TMC_OUT_EP_IDX]);
+ usbd_add_endpoint(busid, &tmc_ep_data[TMC_IN_EP_IDX]);
+
+ SCPI_Init(&scpi_context,
+ scpi_commands,
+ &scpi_interface,
+ scpi_units_def,
+ idn[0], idn[1], idn[2], idn[3],
+ scpi_input_buffer, CONFIG_USBDEV_TMC_MAX_BUFSIZE,
+ scpi_error_queue_data, SCPI_ERROR_QUEUE_SIZE);
+
+ return intf;
+} \ No newline at end of file
diff --git a/class/tmc/usbd_tmc.h b/class/tmc/usbd_tmc.h
new file mode 100644
index 00000000..ca17d0d6
--- /dev/null
+++ b/class/tmc/usbd_tmc.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2024, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBD_TMC_H
+#define USBD_TMC_H
+
+#include "usb_tmc.h"
+#include "scpi/scpi.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Init tmc interface driver */
+struct usbd_interface *usbd_tmc_init_intf(uint8_t busid,
+ struct usbd_interface *intf,
+ const uint8_t out_ep, const uint8_t in_ep,
+ const scpi_command_t *scpi_commands,
+ const char **idn);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBD_TMC_H */
diff --git a/class/vendor/net/usbh_asix.c b/class/vendor/net/usbh_asix.c
index 06a0d0ba..e63b274b 100644
--- a/class/vendor/net/usbh_asix.c
+++ b/class/vendor/net/usbh_asix.c
@@ -15,6 +15,10 @@
static struct usbh_asix g_asix_class;
+#if CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE > (16 * 1024)
+#error "CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE must be less than 16K"
+#endif
+
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_asix_rx_buffer[USB_ALIGN_UP(CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE, CONFIG_USB_ALIGN_SIZE)];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_asix_tx_buffer[USB_ALIGN_UP(CONFIG_USBHOST_ASIX_ETH_MAX_TX_SIZE, CONFIG_USB_ALIGN_SIZE)];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_asix_inttx_buffer[USB_ALIGN_UP(16, CONFIG_USB_ALIGN_SIZE)];
@@ -71,10 +75,10 @@ static int usbh_asix_read_cmd(struct usbh_asix *asix_class,
setup->wLength = size;
ret = usbh_control_transfer(asix_class->hport, setup, g_asix_buf);
- if (ret < 8) {
+ if (ret < size) {
return ret;
}
- memcpy(data, g_asix_buf, MIN(ret - 8, size));
+ memcpy(data, g_asix_buf, size);
return ret;
}
@@ -679,17 +683,10 @@ void usbh_asix_rx_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV)
uint16_t len;
uint16_t len_crc;
uint32_t data_offset;
-#if CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE <= (16 * 1024)
- uint32_t transfer_size = CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE;
-#else
- uint32_t transfer_size = (16 * 1024);
-#endif
(void)CONFIG_USB_OSAL_THREAD_GET_ARGV;
USB_LOG_INFO("Create asix rx thread\r\n");
- // clang-format off
-find_class:
- // clang-format on
+
g_asix_class.connect_status = false;
if (usbh_find_class_instance("/dev/asix") == NULL) {
goto delete;
@@ -698,28 +695,25 @@ find_class:
while (g_asix_class.connect_status == false) {
ret = usbh_asix_get_connect_status(&g_asix_class);
if (ret < 0) {
- usb_osal_msleep(100);
- goto find_class;
+ goto delete;
}
usb_osal_msleep(128);
}
g_asix_rx_length = 0;
while (1) {
- usbh_bulk_urb_fill(&g_asix_class.bulkin_urb, g_asix_class.hport, g_asix_class.bulkin, &g_asix_rx_buffer[g_asix_rx_length], transfer_size, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ usbh_bulk_urb_fill(&g_asix_class.bulkin_urb, g_asix_class.hport, g_asix_class.bulkin, g_asix_rx_buffer, CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE, USB_OSAL_WAITING_FOREVER, NULL, NULL);
ret = usbh_submit_urb(&g_asix_class.bulkin_urb);
if (ret < 0) {
- goto find_class;
+ goto delete;
}
- g_asix_rx_length += g_asix_class.bulkin_urb.actual_length;
+ g_asix_rx_length = g_asix_class.bulkin_urb.actual_length;
+ if (g_asix_rx_length == 0) {
+ continue;
+ }
- /* A transfer is complete because last packet is a short packet.
- * Short packet is not zero, match g_asix_rx_length % USB_GET_MAXPACKETSIZE(g_asix_class.bulkin->wMaxPacketSize).
- * Short packet is zero, check if g_asix_class.bulkin_urb.actual_length < transfer_size, for example transfer is complete with size is 1024 < 2048.
- */
- if (g_asix_rx_length % USB_GET_MAXPACKETSIZE(g_asix_class.bulkin->wMaxPacketSize) ||
- (g_asix_class.bulkin_urb.actual_length < transfer_size)) {
+ if (g_asix_rx_length < CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE) {
USB_LOG_DBG("rxlen:%d\r\n", g_asix_rx_length);
data_offset = 0;
@@ -743,15 +737,7 @@ find_class:
}
}
} else {
-#if CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE <= (16 * 1024)
- if (g_asix_rx_length == CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE) {
-#else
- if ((g_asix_rx_length + (16 * 1024)) > CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE) {
-#endif
- USB_LOG_ERR("Rx packet is overflow, please reduce tcp window size or increase CONFIG_USBHOST_ASIX_ETH_MAX_RX_SIZE\r\n");
- while (1) {
- }
- }
+ USB_LOG_ERR("asix packet overflow\r\n");
}
}
// clang-format off
diff --git a/class/vendor/net/usbh_rtl8152.c b/class/vendor/net/usbh_rtl8152.c
index 50269f26..16393ab1 100644
--- a/class/vendor/net/usbh_rtl8152.c
+++ b/class/vendor/net/usbh_rtl8152.c
@@ -12,6 +12,10 @@
#define DEV_FORMAT "/dev/rtl8152"
+#if CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE > (16 * 1024)
+#error "CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE must be less than 16K"
+#endif
+
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rtl8152_rx_buffer[USB_ALIGN_UP(CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE, CONFIG_USB_ALIGN_SIZE)];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rtl8152_tx_buffer[USB_ALIGN_UP(CONFIG_USBHOST_RTL8152_ETH_MAX_TX_SIZE, CONFIG_USB_ALIGN_SIZE)];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rtl8152_inttx_buffer[USB_ALIGN_UP(2, CONFIG_USB_ALIGN_SIZE)];
@@ -962,10 +966,10 @@ static int usbh_rtl8152_read_regs(struct usbh_rtl8152 *rtl8152_class,
setup->wLength = size;
ret = usbh_control_transfer(rtl8152_class->hport, setup, g_rtl8152_buf);
- if (ret < 8) {
+ if (ret < size) {
return ret;
}
- memcpy(data, g_rtl8152_buf, MIN(ret - 8, size));
+ memcpy(data, g_rtl8152_buf, size);
return ret;
}
@@ -2138,17 +2142,10 @@ void usbh_rtl8152_rx_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV)
int ret;
uint16_t len;
uint16_t data_offset;
-#if CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE <= (16 * 1024)
- uint32_t transfer_size = CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE;
-#else
- uint32_t transfer_size = (16 * 1024);
-#endif
(void)CONFIG_USB_OSAL_THREAD_GET_ARGV;
USB_LOG_INFO("Create rtl8152 rx thread\r\n");
- // clang-format off
-find_class:
- // clang-format on
+
g_rtl8152_class.connect_status = false;
if (usbh_find_class_instance("/dev/rtl8152") == NULL) {
goto delete;
@@ -2157,8 +2154,7 @@ find_class:
while (g_rtl8152_class.connect_status == false) {
ret = usbh_rtl8152_get_connect_status(&g_rtl8152_class);
if (ret < 0) {
- usb_osal_msleep(100);
- goto find_class;
+ goto delete;
}
usb_osal_msleep(128);
}
@@ -2174,20 +2170,18 @@ find_class:
g_rtl8152_rx_length = 0;
while (1) {
- usbh_bulk_urb_fill(&g_rtl8152_class.bulkin_urb, g_rtl8152_class.hport, g_rtl8152_class.bulkin, &g_rtl8152_rx_buffer[g_rtl8152_rx_length], transfer_size, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ usbh_bulk_urb_fill(&g_rtl8152_class.bulkin_urb, g_rtl8152_class.hport, g_rtl8152_class.bulkin, g_rtl8152_rx_buffer, CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE, USB_OSAL_WAITING_FOREVER, NULL, NULL);
ret = usbh_submit_urb(&g_rtl8152_class.bulkin_urb);
if (ret < 0) {
- goto find_class;
+ goto delete;
}
- g_rtl8152_rx_length += g_rtl8152_class.bulkin_urb.actual_length;
+ g_rtl8152_rx_length = g_rtl8152_class.bulkin_urb.actual_length;
+ if (g_rtl8152_rx_length == 0) {
+ continue;
+ }
- /* A transfer is complete because last packet is a short packet.
- * Short packet is not zero, match g_rtl8152_rx_length % USB_GET_MAXPACKETSIZE(g_rtl8152_class.bulkin->wMaxPacketSize).
- * Short packet is zero, check if g_rtl8152_class.bulkin_urb.actual_length < transfer_size, for example transfer is complete with size is 1024 < 2048.
- */
- if (g_rtl8152_rx_length % USB_GET_MAXPACKETSIZE(g_rtl8152_class.bulkin->wMaxPacketSize) ||
- (g_rtl8152_class.bulkin_urb.actual_length < transfer_size)) {
+ if (g_rtl8152_rx_length < CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE) {
data_offset = 0;
USB_LOG_DBG("rxlen:%d\r\n", g_rtl8152_rx_length);
@@ -2210,15 +2204,7 @@ find_class:
}
}
} else {
-#if CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE <= (16 * 1024)
- if (g_rtl8152_rx_length == CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE) {
-#else
- if ((g_rtl8152_rx_length + (16 * 1024)) > CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE) {
-#endif
- USB_LOG_ERR("Rx packet is overflow, please reduce tcp window size or increase CONFIG_USBHOST_RTL8152_ETH_MAX_RX_SIZE\r\n");
- while (1) {
- }
- }
+ USB_LOG_ERR("rtl8152 packet overflow\r\n");
}
}
// clang-format off
diff --git a/class/video/usbh_video.c b/class/video/usbh_video.c
index 8ecd252d..8e782e20 100644
--- a/class/video/usbh_video.c
+++ b/class/video/usbh_video.c
@@ -77,7 +77,7 @@ int usbh_video_get(struct usbh_video *video_class, uint8_t request, uint8_t intf
retry = 0;
while (1) {
ret = usbh_control_transfer(video_class->hport, setup, g_video_buf);
- if (ret > 0) {
+ if (ret >= len) {
break;
}
retry++;
diff --git a/class/wireless/usbd_rndis.c b/class/wireless/usbd_rndis.c
index 5fceb98f..ea62c50e 100644
--- a/class/wireless/usbd_rndis.c
+++ b/class/wireless/usbd_rndis.c
@@ -28,7 +28,7 @@ struct usbd_rndis_priv {
uint8_t mac[6];
} g_usbd_rndis;
-#if CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE < 140
+#if CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE < 156
#undef CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE
#define CONFIG_USBDEV_RNDIS_RESP_BUFFER_SIZE 156
#endif
@@ -112,8 +112,7 @@ static int rndis_class_interface_request_handler(uint8_t busid, struct usb_setup
case CDC_REQUEST_SEND_ENCAPSULATED_COMMAND:
g_usbd_rndis.set_rsp_get = true;
- rndis_encapsulated_cmd_handler(*data, setup->wLength);
- break;
+ return rndis_encapsulated_cmd_handler(*data, setup->wLength);
case CDC_REQUEST_GET_ENCAPSULATED_RESPONSE:
g_usbd_rndis.set_rsp_get = false;
@@ -162,7 +161,9 @@ static int rndis_init_cmd_handler(uint8_t *data, uint32_t len)
rndis_initialize_msg_t *cmd = (rndis_initialize_msg_t *)data;
rndis_initialize_cmplt_t *resp;
- (void)len;
+ if (len < sizeof(rndis_initialize_msg_t)) {
+ return -1;
+ }
resp = ((rndis_initialize_cmplt_t *)rndis_encapsulated_resp_buffer);
resp->RequestId = cmd->RequestId;
@@ -190,7 +191,10 @@ static int rndis_halt_cmd_handler(uint8_t *data, uint32_t len)
rndis_halt_msg_t *resp;
(void)data;
- (void)len;
+
+ if (len < sizeof(rndis_halt_msg_t)) {
+ return -1;
+ }
resp = ((rndis_halt_msg_t *)rndis_encapsulated_resp_buffer);
resp->MessageLength = 0;
@@ -207,7 +211,9 @@ static int rndis_query_cmd_handler(uint8_t *data, uint32_t len)
uint8_t *infomation_buffer;
uint32_t infomation_len = 0;
- (void)len;
+ if (len < sizeof(rndis_query_msg_t)) {
+ return -1;
+ }
resp = ((rndis_query_cmplt_t *)rndis_encapsulated_resp_buffer);
resp->MessageType = REMOTE_NDIS_QUERY_CMPLT;
@@ -354,8 +360,11 @@ static int rndis_set_cmd_handler(uint8_t *data, uint32_t len)
rndis_set_msg_t *cmd = (rndis_set_msg_t *)data;
rndis_set_cmplt_t *resp;
rndis_config_parameter_t *param;
+ uint32_t *filter;
- (void)len;
+ if (len < sizeof(rndis_set_msg_t)) {
+ return -1;
+ }
resp = ((rndis_set_cmplt_t *)rndis_encapsulated_resp_buffer);
resp->RequestId = cmd->RequestId;
@@ -367,21 +376,12 @@ static int rndis_set_cmd_handler(uint8_t *data, uint32_t len)
case OID_GEN_RNDIS_CONFIG_PARAMETER:
break;
case OID_GEN_CURRENT_PACKET_FILTER:
- if (cmd->InformationBufferLength < sizeof(g_usbd_rndis.net_filter)) {
- resp->Status = RNDIS_STATUS_INVALID_DATA;
- } else {
- uint32_t *filter;
- /* Parameter starts at offset buf_offset of the req_id field */
- filter = (uint32_t *)((uint8_t *)&(cmd->RequestId) + cmd->InformationBufferOffset);
-
- //g_usbd_rndis.net_filter = param->ParameterNameOffset;
- g_usbd_rndis.net_filter = *(uint32_t *)filter;
- if (g_usbd_rndis.net_filter) {
- g_usbd_rndis.init_state = rndis_data_initialized;
- } else {
- g_usbd_rndis.init_state = rndis_initialized;
- }
+ if (len < (sizeof(rndis_set_msg_t) + cmd->InformationBufferLength)) {
+ return -1;
}
+
+ filter = (uint32_t *)(data + sizeof(rndis_set_msg_t));
+ g_usbd_rndis.net_filter = *filter;
break;
case OID_GEN_CURRENT_LOOKAHEAD:
break;
@@ -409,7 +409,10 @@ static int rndis_reset_cmd_handler(uint8_t *data, uint32_t len)
rndis_reset_cmplt_t *resp;
(void)data;
- (void)len;
+
+ if (len < sizeof(rndis_reset_cmplt_t)) {
+ return -1;
+ }
resp = ((rndis_reset_cmplt_t *)rndis_encapsulated_resp_buffer);
resp->MessageType = REMOTE_NDIS_RESET_CMPLT;
@@ -429,7 +432,9 @@ static int rndis_keepalive_cmd_handler(uint8_t *data, uint32_t len)
rndis_keepalive_msg_t *cmd = (rndis_keepalive_msg_t *)data;
rndis_keepalive_cmplt_t *resp;
- (void)len;
+ if (len < sizeof(rndis_keepalive_msg_t)) {
+ return -1;
+ }
resp = ((rndis_keepalive_cmplt_t *)rndis_encapsulated_resp_buffer);
resp->RequestId = cmd->RequestId;
@@ -512,7 +517,7 @@ int usbd_rndis_start_write(uint8_t *buf, uint32_t len)
return -USB_ERR_BUSY;
}
- if((len % usbd_get_ep_mps(0, rndis_ep_data[RNDIS_IN_EP_IDX].ep_addr)) == 0) {
+ if ((len % usbd_get_ep_mps(0, rndis_ep_data[RNDIS_IN_EP_IDX].ep_addr)) == 0) {
/* If the data length is a multiple of the endpoint max packet size, add one byte to indicate the end of the transfer. */
len += 1;
}
@@ -628,7 +633,7 @@ int usbd_rndis_set_connect(bool connect)
return -USB_ERR_NOTCONN;
}
- if(g_usbd_rndis.set_rsp_get)
+ if (g_usbd_rndis.set_rsp_get)
return -USB_ERR_BUSY;
rndis_indicate_status_t *resp;
@@ -636,7 +641,7 @@ int usbd_rndis_set_connect(bool connect)
resp = ((rndis_indicate_status_t *)rndis_encapsulated_resp_buffer);
resp->MessageType = REMOTE_NDIS_INDICATE_STATUS_MSG;
resp->MessageLength = sizeof(rndis_indicate_status_t);
- if(connect) {
+ if (connect) {
resp->Status = RNDIS_STATUS_MEDIA_CONNECT;
g_usbd_rndis.link_status = NDIS_MEDIA_STATE_CONNECTED;
} else {
diff --git a/class/wireless/usbh_rndis.c b/class/wireless/usbh_rndis.c
index 709630cb..22f0e369 100644
--- a/class/wireless/usbh_rndis.c
+++ b/class/wireless/usbh_rndis.c
@@ -13,10 +13,11 @@
#define DEV_FORMAT "/dev/rndis"
-USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_buf[512];
+#if CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE > (16 * 1024)
+#error "CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE must be less than 16K"
+#endif
-#define CONFIG_USBHOST_RNDIS_ETH_MAX_FRAME_SIZE 1514
-#define CONFIG_USBHOST_RNDIS_ETH_MSG_SIZE (CONFIG_USBHOST_RNDIS_ETH_MAX_FRAME_SIZE + 44)
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_buf[512];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_rx_buffer[USB_ALIGN_UP(CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE, CONFIG_USB_ALIGN_SIZE)];
static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_rndis_tx_buffer[USB_ALIGN_UP(CONFIG_USBHOST_RNDIS_ETH_MAX_TX_SIZE, CONFIG_USB_ALIGN_SIZE)];
@@ -39,12 +40,96 @@ static int usbh_rndis_get_notification(struct usbh_rndis *rndis_class)
return 0;
}
+static int usbh_rndis_parse_resp(struct usbh_rndis *rndis_class)
+{
+ struct usb_setup_packet *setup;
+ rndis_generic_msg_t *resp_generic;
+ int ret;
+
+ if (!rndis_class || !rndis_class->hport) {
+ return -USB_ERR_INVAL;
+ }
+ setup = rndis_class->hport->setup;
+
+ usbh_rndis_get_notification(rndis_class);
+
+ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE;
+ setup->wValue = 0;
+ setup->wIndex = 0;
+ setup->wLength = sizeof(g_rndis_buf);
+
+ ret = usbh_control_transfer(rndis_class->hport, setup, g_rndis_buf);
+ if (ret < sizeof(rndis_generic_msg_t)) {
+ USB_LOG_ERR("resp recv error, ret: %d\r\n", ret);
+ return ret;
+ }
+
+ resp_generic = (rndis_generic_msg_t *)g_rndis_buf;
+ if (resp_generic->MessageLength != ret) {
+ USB_LOG_ERR("resp length error, expect: %d, actual: %d\r\n", resp_generic->MessageLength, ret);
+ return -USB_ERR_RANGE;
+ }
+
+ switch (resp_generic->MessageType) {
+ case REMOTE_NDIS_INITIALIZE_CMPLT:
+ if (ret != sizeof(rndis_initialize_cmplt_t)) {
+ return -USB_ERR_RANGE;
+ }
+ rndis_initialize_cmplt_t *resp_init;
+
+ resp_init = (rndis_initialize_cmplt_t *)g_rndis_buf;
+
+ rndis_class->max_transfer_pkts = resp_init->MaxPacketsPerTransfer;
+ rndis_class->max_transfer_size = resp_init->MaxTransferSize;
+ USB_LOG_INFO("MaxPacketsPerTransfer: %u\r\n", (unsigned int)resp_init->MaxPacketsPerTransfer);
+ USB_LOG_INFO("MaxTransferSize: %u\r\n", (unsigned int)resp_init->MaxTransferSize);
+ break;
+ case REMOTE_NDIS_QUERY_CMPLT:
+ if (ret < sizeof(rndis_query_cmplt_t)) {
+ return -USB_ERR_RANGE;
+ }
+
+ rndis_query_cmplt_t *resp_query;
+ resp_query = (rndis_query_cmplt_t *)g_rndis_buf;
+
+ if (ret != (sizeof(rndis_query_cmplt_t) + resp_query->InformationBufferLength)) {
+ return -USB_ERR_RANGE;
+ }
+ if (resp_query->InformationBufferOffset != (sizeof(rndis_query_cmplt_t) - sizeof(rndis_generic_msg_t))) {
+ return -USB_ERR_RANGE;
+ }
+
+ break;
+ case REMOTE_NDIS_SET_CMPLT:
+ break;
+ case REMOTE_NDIS_RESET_CMPLT:
+ break;
+ case REMOTE_NDIS_KEEPALIVE_CMPLT:
+ break;
+ case REMOTE_NDIS_INDICATE_STATUS_MSG:
+ if (ret != sizeof(rndis_indicate_status_t)) {
+ return -USB_ERR_RANGE;
+ }
+
+ rndis_indicate_status_t *resp_indicate;
+ resp_indicate = (rndis_indicate_status_t *)g_rndis_buf;
+
+ rndis_class->connect_status = (resp_indicate->Status == RNDIS_STATUS_MEDIA_CONNECT) ? true : false;
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
static int usbh_rndis_init_msg_transfer(struct usbh_rndis *rndis_class)
{
struct usb_setup_packet *setup;
- int ret = 0;
+ int ret;
rndis_initialize_msg_t *cmd;
- rndis_initialize_cmplt_t *resp;
if (!rndis_class || !rndis_class->hport) {
return -USB_ERR_INVAL;
@@ -68,46 +153,36 @@ static int usbh_rndis_init_msg_transfer(struct usbh_rndis *rndis_class)
ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)cmd);
if (ret < 0) {
- USB_LOG_ERR("init send error, ret: %d\r\n", ret);
+ USB_LOG_ERR("init msg send error, ret: %d\r\n", ret);
return ret;
}
- usbh_rndis_get_notification(rndis_class);
-
- resp = (rndis_initialize_cmplt_t *)g_rndis_buf;
-
- setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
- setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE;
- setup->wValue = 0;
- setup->wIndex = 0;
- setup->wLength = sizeof(g_rndis_buf);
-
- ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)resp);
- if (ret < 0) {
- USB_LOG_ERR("init recv error, ret: %d\r\n", ret);
- return ret;
- }
-
- rndis_class->max_transfer_pkts = resp->MaxPacketsPerTransfer;
- rndis_class->max_transfer_size = resp->MaxTransferSize;
- USB_LOG_INFO("MaxPacketsPerTransfer: %u\r\n", (unsigned int)resp->MaxPacketsPerTransfer);
- USB_LOG_INFO("MaxTransferSize: %u\r\n", (unsigned int)resp->MaxTransferSize);
-
- return ret;
+ return usbh_rndis_parse_resp(rndis_class);
}
-int usbh_rndis_query_msg_transfer(struct usbh_rndis *rndis_class, uint32_t oid, uint32_t query_len, uint8_t *info, uint32_t *info_len)
+static int usbh_rndis_query_msg_transfer(struct usbh_rndis *rndis_class,
+ uint32_t oid,
+ uint32_t query_len,
+ uint8_t *info,
+ uint32_t info_max_len,
+ uint32_t *info_len)
{
struct usb_setup_packet *setup;
- int ret = 0;
+ int ret;
rndis_query_msg_t *cmd;
rndis_query_cmplt_t *resp;
if (!rndis_class || !rndis_class->hport) {
return -USB_ERR_INVAL;
}
+
+ if(query_len > (sizeof(g_rndis_buf) - sizeof(rndis_query_msg_t))) {
+ return -USB_ERR_NOMEM;
+ }
+
setup = rndis_class->hport->setup;
+
cmd = (rndis_query_msg_t *)g_rndis_buf;
cmd->MessageType = REMOTE_NDIS_QUERY_MSG;
@@ -126,26 +201,29 @@ int usbh_rndis_query_msg_transfer(struct usbh_rndis *rndis_class, uint32_t oid,
ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)cmd);
if (ret < 0) {
- USB_LOG_ERR("oid:%08x send error, ret: %d\r\n", (unsigned int)oid, ret);
+ USB_LOG_ERR("query oid:%08x msg send error, ret: %d\r\n", (unsigned int)oid, ret);
return ret;
}
- usbh_rndis_get_notification(rndis_class);
+ ret = usbh_rndis_parse_resp(rndis_class);
+ if (ret < 0) {
+ return ret;
+ }
resp = (rndis_query_cmplt_t *)g_rndis_buf;
- setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
- setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE;
- setup->wValue = 0;
- setup->wIndex = 0;
- setup->wLength = sizeof(g_rndis_buf);
-
- ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)resp);
- if (ret < 0) {
- USB_LOG_ERR("oid:%08x recv error, ret: %d\r\n", (unsigned int)oid, ret);
+ if(resp->MessageType != REMOTE_NDIS_QUERY_CMPLT) {
return ret;
}
+ if (query_len != 0 && resp->InformationBufferLength != query_len) {
+ return -USB_ERR_RANGE;
+ }
+
+ if (resp->InformationBufferLength > info_max_len) {
+ return -USB_ERR_NOMEM;
+ }
+
memcpy(info, ((uint8_t *)resp + sizeof(rndis_query_cmplt_t)), resp->InformationBufferLength);
*info_len = resp->InformationBufferLength;
@@ -155,13 +233,17 @@ int usbh_rndis_query_msg_transfer(struct usbh_rndis *rndis_class, uint32_t oid,
static int usbh_rndis_set_msg_transfer(struct usbh_rndis *rndis_class, uint32_t oid, uint8_t *info, uint32_t info_len)
{
struct usb_setup_packet *setup;
- int ret = 0;
+ int ret;
rndis_set_msg_t *cmd;
- rndis_set_cmplt_t *resp;
if (!rndis_class || !rndis_class->hport) {
return -USB_ERR_INVAL;
}
+
+ if(info_len > (sizeof(g_rndis_buf) - sizeof(rndis_set_msg_t))) {
+ return -USB_ERR_NOMEM;
+ }
+
setup = rndis_class->hport->setup;
cmd = (rndis_set_msg_t *)g_rndis_buf;
@@ -187,35 +269,24 @@ static int usbh_rndis_set_msg_transfer(struct usbh_rndis *rndis_class, uint32_t
return ret;
}
- usbh_rndis_get_notification(rndis_class);
-
- resp = (rndis_set_cmplt_t *)g_rndis_buf;
-
- setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
- setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE;
- setup->wValue = 0;
- setup->wIndex = 0;
- setup->wLength = sizeof(g_rndis_buf);
-
- ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)resp);
- if (ret < 0) {
- USB_LOG_ERR("oid:%08x recv error, ret: %d\r\n", (unsigned int)oid, ret);
- return ret;
- }
-
- return ret;
+ return usbh_rndis_parse_resp(rndis_class);
}
int usbh_rndis_get_connect_status(struct usbh_rndis *rndis_class)
{
int ret;
- uint8_t data[32];
+ uint8_t data[4];
uint32_t data_len;
- ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_MEDIA_CONNECT_STATUS, 4, data, &data_len);
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_MEDIA_CONNECT_STATUS, 4, data, sizeof(data), &data_len);
if (ret < 0) {
return ret;
}
+
+ if (ret == sizeof(rndis_indicate_status_t)) {
+ return 0;
+ }
+
if (NDIS_MEDIA_STATE_CONNECTED == data[0]) {
rndis_class->connect_status = true;
} else {
@@ -229,7 +300,6 @@ int usbh_rndis_keepalive(struct usbh_rndis *rndis_class)
struct usb_setup_packet *setup;
int ret = 0;
rndis_keepalive_msg_t *cmd;
- rndis_keepalive_cmplt_t *resp;
if (!rndis_class || !rndis_class->hport) {
return -USB_ERR_INVAL;
@@ -254,23 +324,7 @@ int usbh_rndis_keepalive(struct usbh_rndis *rndis_class)
return ret;
}
- usbh_rndis_get_notification(rndis_class);
-
- resp = (rndis_keepalive_cmplt_t *)g_rndis_buf;
-
- setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
- setup->bRequest = CDC_REQUEST_GET_ENCAPSULATED_RESPONSE;
- setup->wValue = 0;
- setup->wIndex = 0;
- setup->wLength = sizeof(g_rndis_buf);
-
- ret = usbh_control_transfer(rndis_class->hport, setup, (uint8_t *)resp);
- if (ret < 0) {
- USB_LOG_ERR("keepalive recv error, ret: %d\r\n", ret);
- return ret;
- }
-
- return ret;
+ return usbh_rndis_parse_resp(rndis_class);
}
static int usbh_rndis_connect(struct usbh_hubport *hport, uint8_t intf)
@@ -282,7 +336,7 @@ static int usbh_rndis_connect(struct usbh_hubport *hport, uint8_t intf)
uint32_t oid_num = 0;
uint32_t data_len;
uint8_t tmp_buffer[512];
- uint8_t data[32];
+ uint8_t data[6];
struct usbh_rndis *rndis_class = &g_rndis_class;
@@ -313,7 +367,7 @@ static int usbh_rndis_connect(struct usbh_hubport *hport, uint8_t intf)
return ret;
}
- ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_SUPPORTED_LIST, 0, tmp_buffer, &data_len);
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_SUPPORTED_LIST, 0, tmp_buffer, sizeof(tmp_buffer), &data_len);
if (ret < 0) {
return ret;
}
@@ -326,19 +380,19 @@ static int usbh_rndis_connect(struct usbh_hubport *hport, uint8_t intf)
oid = oid_support_list[i];
switch (oid) {
case OID_GEN_PHYSICAL_MEDIUM:
- ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_PHYSICAL_MEDIUM, 4, data, &data_len);
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_PHYSICAL_MEDIUM, 4, data, sizeof(data), &data_len);
if (ret < 0) {
goto query_errorout;
}
break;
case OID_GEN_MAXIMUM_FRAME_SIZE:
- ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_MAXIMUM_FRAME_SIZE, 4, data, &data_len);
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_MAXIMUM_FRAME_SIZE, 4, data, sizeof(data), &data_len);
if (ret < 0) {
goto query_errorout;
}
break;
case OID_GEN_LINK_SPEED:
- ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_LINK_SPEED, 4, data, &data_len);
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_LINK_SPEED, 4, data, sizeof(data), &data_len);
if (ret < 0) {
goto query_errorout;
}
@@ -346,7 +400,7 @@ static int usbh_rndis_connect(struct usbh_hubport *hport, uint8_t intf)
memcpy(&rndis_class->link_speed, data, 4);
break;
case OID_GEN_MEDIA_CONNECT_STATUS:
- ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_MEDIA_CONNECT_STATUS, 4, data, &data_len);
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_GEN_MEDIA_CONNECT_STATUS, 4, data, sizeof(data), &data_len);
if (ret < 0) {
goto query_errorout;
}
@@ -357,13 +411,13 @@ static int usbh_rndis_connect(struct usbh_hubport *hport, uint8_t intf)
}
break;
case OID_802_3_MAXIMUM_LIST_SIZE:
- ret = usbh_rndis_query_msg_transfer(rndis_class, OID_802_3_MAXIMUM_LIST_SIZE, 4, data, &data_len);
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_802_3_MAXIMUM_LIST_SIZE, 4, data, sizeof(data), &data_len);
if (ret < 0) {
goto query_errorout;
}
break;
case OID_802_3_CURRENT_ADDRESS:
- ret = usbh_rndis_query_msg_transfer(rndis_class, OID_802_3_CURRENT_ADDRESS, 6, data, &data_len);
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_802_3_CURRENT_ADDRESS, 6, data, sizeof(data), &data_len);
if (ret < 0) {
goto query_errorout;
}
@@ -373,7 +427,7 @@ static int usbh_rndis_connect(struct usbh_hubport *hport, uint8_t intf)
}
break;
case OID_802_3_PERMANENT_ADDRESS:
- ret = usbh_rndis_query_msg_transfer(rndis_class, OID_802_3_PERMANENT_ADDRESS, 6, data, &data_len);
+ ret = usbh_rndis_query_msg_transfer(rndis_class, OID_802_3_PERMANENT_ADDRESS, 6, data, sizeof(data), &data_len);
if (ret < 0) {
goto query_errorout;
}
@@ -451,18 +505,11 @@ void usbh_rndis_rx_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV)
uint32_t pmg_offset;
rndis_data_packet_t *pmsg;
rndis_data_packet_t temp;
-#if CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE <= (16 * 1024)
- uint32_t transfer_size = CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE;
-#else
- uint32_t transfer_size = (16 * 1024);
-#endif
(void)CONFIG_USB_OSAL_THREAD_GET_ARGV;
USB_LOG_INFO("Create rndis rx thread\r\n");
- // clang-format off
-find_class:
- // clang-format on
+
g_rndis_class.connect_status = false;
if (usbh_find_class_instance("/dev/rndis") == NULL) {
goto delete;
@@ -471,30 +518,28 @@ find_class:
while (g_rndis_class.connect_status == false) {
ret = usbh_rndis_get_connect_status(&g_rndis_class);
if (ret < 0) {
- usb_osal_msleep(100);
- goto find_class;
+ goto delete;
}
usb_osal_msleep(128);
}
g_rndis_rx_length = 0;
while (1) {
- usbh_bulk_urb_fill(&g_rndis_class.bulkin_urb, g_rndis_class.hport, g_rndis_class.bulkin, &g_rndis_rx_buffer[g_rndis_rx_length], transfer_size, USB_OSAL_WAITING_FOREVER, NULL, NULL);
+ usbh_bulk_urb_fill(&g_rndis_class.bulkin_urb, g_rndis_class.hport, g_rndis_class.bulkin, g_rndis_rx_buffer, CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE, USB_OSAL_WAITING_FOREVER, NULL, NULL);
ret = usbh_submit_urb(&g_rndis_class.bulkin_urb);
if (ret < 0) {
- break;
+ goto delete;
}
- g_rndis_rx_length += g_rndis_class.bulkin_urb.actual_length;
+ g_rndis_rx_length = g_rndis_class.bulkin_urb.actual_length;
+ if (g_rndis_rx_length == 0) {
+ USB_LOG_WRN("rndis rx length is 0 but no zero required in spec\r\n");
+ continue;
+ }
- /* A transfer is complete because last packet is a short packet.
- * Short packet is not zero, match g_rndis_rx_length % USB_GET_MAXPACKETSIZE(g_rndis_class.bulkin->wMaxPacketSize).
- * Short packet cannot be zero.
- */
if (g_rndis_rx_length % USB_GET_MAXPACKETSIZE(g_rndis_class.bulkin->wMaxPacketSize)) {
pmg_offset = 0;
-
- uint32_t total_len = g_rndis_rx_length;
+ USB_LOG_DBG("rxlen:%d\r\n", g_rndis_rx_length);
while (g_rndis_rx_length > 0) {
USB_LOG_DBG("rxlen:%u\r\n", (unsigned int)g_rndis_rx_length);
@@ -519,21 +564,12 @@ find_class:
g_rndis_rx_length = 0;
}
} else {
- USB_LOG_ERR("offset:%u,remain:%u,total:%u\r\n", (unsigned int)pmg_offset, (unsigned int)g_rndis_rx_length, (unsigned int)total_len);
+ USB_LOG_ERR("Error rndis packet message, offset:%u, remain:%u\r\n", (unsigned int)pmg_offset, (unsigned int)g_rndis_rx_length);
g_rndis_rx_length = 0;
- USB_LOG_ERR("Error rndis packet message\r\n");
}
}
} else {
-#if CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE <= (16 * 1024)
- if (g_rndis_rx_length == CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE) {
-#else
- if ((g_rndis_rx_length + (16 * 1024)) > CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE) {
-#endif
- USB_LOG_ERR("Rx packet is overflow, please reduce tcp window size or increase CONFIG_USBHOST_RNDIS_ETH_MAX_RX_SIZE\r\n");
- while (1) {
- }
- }
+ USB_LOG_ERR("rndis packet overflow\r\n");
}
}
@@ -601,3 +637,12 @@ CLASS_INFO_DEFINE const struct usbh_class_info rndis_cdcacm_class_info = {
.id_table = NULL,
.class_driver = &rndis_class_driver
};
+
+CLASS_INFO_DEFINE const struct usbh_class_info rndis_android_class_info = {
+ .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
+ .bInterfaceClass = USB_DEVICE_CLASS_MISC,
+ .bInterfaceSubClass = 0x04,
+ .bInterfaceProtocol = 0x01,
+ .id_table = NULL,
+ .class_driver = &rndis_class_driver
+};