diff options
Diffstat (limited to 'class')
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 +}; |
