From 39693f47020e0f8ceadab71a959e38d64cc8731f Mon Sep 17 00:00:00 2001 From: sakumisu <1203593632@qq.com> Date: Sun, 10 Mar 2024 19:53:33 +0800 Subject: [PATCH] support cdc ncm host --- class/cdc/usb_cdc.h | 132 ++++++++++++- class/cdc/usbd_cdc_ecm.c | 2 +- class/cdc/usbh_cdc_ecm.c | 4 +- class/cdc/usbh_cdc_ecm.h | 4 +- class/cdc/usbh_cdc_ncm.c | 410 +++++++++++++++++++++++++++++++++++++++ class/cdc/usbh_cdc_ncm.h | 57 ++++++ demo/usb_host.c | 117 ++++++++++- 7 files changed, 716 insertions(+), 10 deletions(-) create mode 100644 class/cdc/usbh_cdc_ncm.c create mode 100644 class/cdc/usbh_cdc_ncm.h diff --git a/class/cdc/usb_cdc.h b/class/cdc/usb_cdc.h index de0e794f..b8a544f3 100644 --- a/class/cdc/usb_cdc.h +++ b/class/cdc/usb_cdc.h @@ -117,6 +117,7 @@ #define CDC_FUNC_DESC_COMMAND_SET_DETAIL 0x17 #define CDC_FUNC_DESC_TELEPHONE_CONTROL_MODEL 0x18 #define CDC_FUNC_DESC_OBEX_SERVICE_IDENTIFIER 0x19 +#define CDC_FUNC_DESC_NCM 0x1A /* CDC class-specific request codes */ /* (usbcdc11.pdf, 6.2, Table 46) */ @@ -156,6 +157,17 @@ #define CDC_REQUEST_GET_ATM_DEVICE_STATISTICS 0x51 #define CDC_REQUEST_SET_ATM_DEFAULT_VC 0x52 #define CDC_REQUEST_GET_ATM_VC_STATISTICS 0x53 +#define CDC_REQUEST_GET_NTB_PARAMETERS 0x80 +#define CDC_REQUEST_GET_NET_ADDRESS 0x81 +#define CDC_REQUEST_SET_NET_ADDRESS 0x82 +#define CDC_REQUEST_GET_NTB_FORMAT 0x83 +#define CDC_REQUEST_SET_NTB_FORMAT 0x84 +#define CDC_REQUEST_GET_NTB_INPUT_SIZE 0x85 +#define CDC_REQUEST_SET_NTB_INPUT_SIZE 0x86 +#define CDC_REQUEST_GET_MAX_DATAGRAM_SIZE 0x87 +#define CDC_REQUEST_SET_MAX_DATAGRAM_SIZE 0x88 +#define CDC_REQUEST_GET_CRC_MODE 0x89 +#define CDC_REQUEST_SET_CRC_MODE 0x90 /* Communication feature selector codes */ /* (usbcdc11.pdf, 6.2.2..6.2.4, Table 47) */ @@ -263,6 +275,10 @@ #define CDC_ECM_NOTIFY_CODE_RESPONSE_AVAILABLE 0x01 #define CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE 0x2A +#define CDC_NCM_NTH16_SIGNATURE 0x484D434E +#define CDC_NCM_NDP16_SIGNATURE_NCM0 0x304D434E +#define CDC_NCM_NDP16_SIGNATURE_NCM1 0x314D434E + /*------------------------------------------------------------------------------ * Structures based on usbcdc11.pdf (www.usb.org) *----------------------------------------------------------------------------*/ @@ -339,7 +355,7 @@ struct cdc_acm_notification { } __PACKED; /** Ethernet Networking Functional Descriptor */ -struct cdc_ecm_descriptor { +struct cdc_eth_descriptor { uint8_t bFunctionLength; uint8_t bDescriptorType; uint8_t bDescriptorSubtype; @@ -350,7 +366,7 @@ struct cdc_ecm_descriptor { uint8_t bNumberPowerFilters; } __PACKED; -struct cdc_ecm_notification { +struct cdc_eth_notification { uint8_t bmRequestType; uint8_t bNotificationType; uint16_t wValue; @@ -359,6 +375,41 @@ struct cdc_ecm_notification { uint8_t data[8]; } __PACKED; +struct cdc_ncm_ntb_parameters { + uint16_t wLength; + uint16_t bmNtbFormatsSupported; + uint32_t dwNtbInMaxSize; + uint16_t wNdbInDivisor; + uint16_t wNdbInPayloadRemainder; + uint16_t wNdbInAlignment; + uint16_t wReserved; + uint32_t dwNtbOutMaxSize; + uint16_t wNdbOutDivisor; + uint16_t wNdbOutPayloadRemainder; + uint16_t wNdbOutAlignment; + uint16_t wNtbOutMaxDatagrams; +}; + +struct cdc_ncm_nth16 { + uint32_t dwSignature; + uint16_t wHeaderLength; + uint16_t wSequence; + uint16_t wBlockLength; + uint16_t wNdpIndex; +}; + +struct cdc_ncm_ndp16_datagram { + uint16_t wDatagramIndex; + uint16_t wDatagramLength; +}; + +struct cdc_ncm_ndp16 { + uint32_t dwSignature; + uint16_t wLength; + uint16_t wNextNdpIndex; + struct cdc_ncm_ndp16_datagram datagram[]; +}; + /*Length of template descriptor: 66 bytes*/ #define CDC_ACM_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 7 + 7) // clang-format off @@ -499,7 +550,7 @@ struct cdc_ecm_notification { #define DBVAL_BE(x) ((x >> 24) & 0xFF), ((x >> 16) & 0xFF), ((x >> 8) & 0xFF), (x & 0xFF) -/*Length of template descriptor: 66 bytes*/ +/*Length of template descriptor: 71 bytes*/ #define CDC_ECM_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 13 + 7 + 9 + 7 + 7) // clang-format off #define CDC_ECM_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, wMaxPacketSize, \ @@ -569,4 +620,79 @@ eth_statistics, wMaxSegmentSize, wNumberMCFilters, bNumberPowerFilters, str_idx) 0x00 /* bInterval */ // clang-format on +/*Length of template descriptor: 77 bytes*/ +#define CDC_NCM_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 13 + 6 + 7 + 9 + 7 + 7) +// clang-format off +#define CDC_NCM_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, wMaxPacketSize, \ +eth_statistics, wMaxSegmentSize, wNumberMCFilters, bNumberPowerFilters, str_idx) \ + /* Interface Associate */ \ + 0x08, /* bLength */ \ + USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, /* bDescriptorType */ \ + bFirstInterface, /* bFirstInterface */ \ + 0x02, /* bInterfaceCount */ \ + USB_DEVICE_CLASS_CDC, /* bFunctionClass */ \ + CDC_NETWORK_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_NETWORK_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_UNION, /* bDescriptorSubtype */ \ + bFirstInterface, /* bMasterInterface */ \ + (uint8_t)(bFirstInterface + 1), /* bSlaveInterface0 */ \ + /* CDC ETH Functional Descriptor */ \ + 0x0D, /* bFunctionLength */\ + CDC_CS_INTERFACE, /* bDescriptorType: CS_INTERFACE */\ + CDC_FUNC_DESC_ETHERNET_NETWORKING, /* Ethernet Networking functional descriptor subtype */\ + str_idx, /* Device's MAC string index */\ + DBVAL_BE(eth_statistics), /* Ethernet statistics (bitmap) */\ + WBVAL(wMaxPacketSize),/* wMaxSegmentSize: Ethernet Maximum Segment size, typically 1514 bytes */\ + WBVAL(wNumberMCFilters), /* wNumberMCFilters: the number of multicast filters */\ + bNumberPowerFilters, /* bNumberPowerFilters: the number of wakeup power filters */\ + 0x06, \ + CDC_CS_INTERFACE, \ + CDC_FUNC_DESC_NCM, \ + 0x00, 0x01, \ + 0x23, \ + 0x07, /* bLength */ \ + USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \ + int_ep, /* bEndpointAddress */ \ + 0x03, /* bmAttributes */ \ + 0x10, 0x00, /* wMaxPacketSize */ \ + 0x10, /* bInterval */ \ + 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 */ \ + 0x07, /* bLength */ \ + USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \ + in_ep, /* bEndpointAddress */ \ + 0x02, /* bmAttributes */ \ + WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \ + 0x00 /* bInterval */ +// clang-format on + #endif /* USB_CDC_H */ diff --git a/class/cdc/usbd_cdc_ecm.c b/class/cdc/usbd_cdc_ecm.c index 70b5365c..267b1b4d 100644 --- a/class/cdc/usbd_cdc_ecm.c +++ b/class/cdc/usbd_cdc_ecm.c @@ -35,7 +35,7 @@ static uint32_t g_connect_speed_table[2] = { CDC_ECM_CONNECT_SPEED_UPSTREAM, void usbd_cdc_ecm_send_notify(uint8_t notifycode, uint8_t value, uint32_t *speed) { - struct cdc_ecm_notification *notify = (struct cdc_ecm_notification *)g_cdc_ecm_notify_buf; + struct cdc_eth_notification *notify = (struct cdc_eth_notification *)g_cdc_ecm_notify_buf; uint8_t bytes2send = 0; notify->bmRequestType = CDC_ECM_BMREQUEST_TYPE_ECM; diff --git a/class/cdc/usbh_cdc_ecm.c b/class/cdc/usbh_cdc_ecm.c index 54bc7df6..7e63e654 100644 --- a/class/cdc/usbh_cdc_ecm.c +++ b/class/cdc/usbh_cdc_ecm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, sakumisu + * Copyright (c) 2024, sakumisu * * SPDX-License-Identifier: Apache-2.0 */ @@ -95,7 +95,7 @@ static int usbh_cdc_ecm_connect(struct usbh_hubport *hport, uint8_t intf) break; case CDC_CS_INTERFACE: if ((cur_iface == cdc_ecm_class->ctrl_intf) && p[DESC_bDescriptorSubType] == CDC_FUNC_DESC_ETHERNET_NETWORKING) { - struct cdc_ecm_descriptor *desc = (struct cdc_ecm_descriptor *)p; + struct cdc_eth_descriptor *desc = (struct cdc_eth_descriptor *)p; mac_str_idx = desc->iMACAddress; cdc_ecm_class->max_segment_size = desc->wMaxSegmentSize; goto get_mac; diff --git a/class/cdc/usbh_cdc_ecm.h b/class/cdc/usbh_cdc_ecm.h index 9fd94088..ab81794e 100644 --- a/class/cdc/usbh_cdc_ecm.h +++ b/class/cdc/usbh_cdc_ecm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, sakumisu + * Copyright (c) 2024, sakumisu * * SPDX-License-Identifier: Apache-2.0 */ @@ -50,4 +50,4 @@ void usbh_cdc_ecm_rx_thread(void *argument); } #endif -#endif /* USBH_CDC_ACM_H */ +#endif /* USBH_CDC_ECM_H */ diff --git a/class/cdc/usbh_cdc_ncm.c b/class/cdc/usbh_cdc_ncm.c new file mode 100644 index 00000000..3495e158 --- /dev/null +++ b/class/cdc/usbh_cdc_ncm.c @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "usbh_core.h" +#include "usbh_cdc_ncm.h" + +#undef USB_DBG_TAG +#define USB_DBG_TAG "usbh_cdc_ncm" +#include "usb_log.h" + +#define DEV_FORMAT "/dev/cdc_ncm" + +/* general descriptor field offsets */ +#define DESC_bLength 0 /** Length offset */ +#define DESC_bDescriptorType 1 /** Descriptor type offset */ +#define DESC_bDescriptorSubType 2 /** Descriptor subtype offset */ + +/* interface descriptor field offsets */ +#define INTF_DESC_bInterfaceNumber 2 /** Interface number offset */ +#define INTF_DESC_bAlternateSetting 3 /** Alternate setting offset */ + +#define CONFIG_USBHOST_CDC_NCM_ETH_MAX_SEGSZE 1514U + +static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_rx_buffer[2048]; +static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_tx_buffer[2048]; +static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_inttx_buffer[16]; + +USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ncm_buf[32]; + +static struct usbh_cdc_ncm g_cdc_ncm_class; + +static int usbh_cdc_ncm_get_ntb_parameters(struct usbh_cdc_ncm *cdc_ncm_class, struct cdc_ncm_ntb_parameters *param) +{ + struct usb_setup_packet *setup = cdc_ncm_class->hport->setup; + int ret; + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE; + setup->bRequest = CDC_REQUEST_GET_NTB_PARAMETERS; + setup->wValue = 0; + setup->wIndex = cdc_ncm_class->ctrl_intf; + setup->wLength = 28; + + ret = usbh_control_transfer(cdc_ncm_class->hport, setup, g_cdc_ncm_buf); + if (ret < 0) { + return ret; + } + + memcpy((uint8_t *)param, g_cdc_ncm_buf, ret - 8); + return 0; +} + +static void print_ntb_parameters(struct cdc_ncm_ntb_parameters *param) +{ + USB_LOG_RAW("CDC NCM ntb parameters:\r\n"); + USB_LOG_RAW("wLength: 0x%02x \r\n", param->wLength); + USB_LOG_RAW("bmNtbFormatsSupported: %s \r\n", param->bmNtbFormatsSupported ? "NTB16" : "NTB32"); + + USB_LOG_RAW("dwNtbInMaxSize: 0x%04x \r\n", param->dwNtbInMaxSize); + USB_LOG_RAW("wNdbInDivisor: 0x%02x \r\n", param->wNdbInDivisor); + USB_LOG_RAW("wNdbInPayloadRemainder: 0x%02x \r\n", param->wNdbInPayloadRemainder); + USB_LOG_RAW("wNdbInAlignment: 0x%02x \r\n", param->wNdbInAlignment); + + USB_LOG_RAW("dwNtbOutMaxSize: 0x%04x \r\n", param->dwNtbOutMaxSize); + USB_LOG_RAW("wNdbOutDivisor: 0x%02x \r\n", param->wNdbOutDivisor); + USB_LOG_RAW("wNdbOutPayloadRemainder: 0x%02x \r\n", param->wNdbOutPayloadRemainder); + USB_LOG_RAW("wNdbOutAlignment: 0x%02x \r\n", param->wNdbOutAlignment); + + USB_LOG_RAW("wNtbOutMaxDatagrams: 0x%02x \r\n", param->wNtbOutMaxDatagrams); +} + +int usbh_cdc_ncm_get_connect_status(struct usbh_cdc_ncm *cdc_ncm_class) +{ + int ret; + + usbh_int_urb_fill(&cdc_ncm_class->intin_urb, cdc_ncm_class->hport, cdc_ncm_class->intin, g_cdc_ncm_inttx_buffer, 16, USB_OSAL_WAITING_FOREVER, NULL, NULL); + ret = usbh_submit_urb(&cdc_ncm_class->intin_urb); + if (ret < 0) { + return ret; + } + + if (g_cdc_ncm_inttx_buffer[1] == CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION) { + if (g_cdc_ncm_inttx_buffer[2] == CDC_ECM_NET_CONNECTED) { + cdc_ncm_class->connect_status = true; + } else { + cdc_ncm_class->connect_status = false; + } + } else if (g_cdc_ncm_inttx_buffer[1] == CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE) { + memcpy(cdc_ncm_class->speed, &g_cdc_ncm_inttx_buffer[8], 8); + } + return 0; +} + +static int usbh_cdc_ncm_connect(struct usbh_hubport *hport, uint8_t intf) +{ + struct usb_endpoint_descriptor *ep_desc; + int ret; + uint8_t altsetting = 0; + char mac_buffer[12]; + uint8_t *p; + uint8_t cur_iface = 0xff; + uint8_t mac_str_idx = 0xff; + + struct usbh_cdc_ncm *cdc_ncm_class = &g_cdc_ncm_class; + + memset(cdc_ncm_class, 0, sizeof(struct usbh_cdc_ncm)); + + cdc_ncm_class->hport = hport; + cdc_ncm_class->ctrl_intf = intf; + cdc_ncm_class->data_intf = intf + 1; + + hport->config.intf[intf].priv = cdc_ncm_class; + hport->config.intf[intf + 1].priv = NULL; + + p = hport->raw_config_desc; + while (p[DESC_bLength]) { + switch (p[DESC_bDescriptorType]) { + case USB_DESCRIPTOR_TYPE_INTERFACE: + cur_iface = p[INTF_DESC_bInterfaceNumber]; + //cur_alt_setting = p[INTF_DESC_bAlternateSetting]; + break; + case CDC_CS_INTERFACE: + if ((cur_iface == cdc_ncm_class->ctrl_intf) && p[DESC_bDescriptorSubType] == CDC_FUNC_DESC_ETHERNET_NETWORKING) { + struct cdc_eth_descriptor *desc = (struct cdc_eth_descriptor *)p; + mac_str_idx = desc->iMACAddress; + cdc_ncm_class->max_segment_size = desc->wMaxSegmentSize; + goto get_mac; + } + break; + + default: + break; + } + /* skip to next descriptor */ + p += p[DESC_bLength]; + } + +get_mac: + if (mac_str_idx == 0xff) { + USB_LOG_ERR("Do not find cdc ncm mac string\r\n"); + return -1; + } + + memset(mac_buffer, 0, 12); + ret = usbh_get_string_desc(cdc_ncm_class->hport, mac_str_idx, (uint8_t *)mac_buffer); + if (ret < 0) { + return ret; + } + + for (int i = 0, j = 0; i < 12; i += 2, j++) { + char byte_str[3]; + byte_str[0] = mac_buffer[i]; + byte_str[1] = mac_buffer[i + 1]; + byte_str[2] = '\0'; + + uint32_t byte = strtoul(byte_str, NULL, 16); + cdc_ncm_class->mac[j] = (unsigned char)byte; + } + + USB_LOG_INFO("CDC NCM MAC address %02x:%02x:%02x:%02x:%02x:%02x\r\n", + cdc_ncm_class->mac[0], + cdc_ncm_class->mac[1], + cdc_ncm_class->mac[2], + cdc_ncm_class->mac[3], + cdc_ncm_class->mac[4], + cdc_ncm_class->mac[5]); + + if (cdc_ncm_class->max_segment_size > CONFIG_USBHOST_CDC_NCM_ETH_MAX_SEGSZE) { + USB_LOG_ERR("CDC NCM Max Segment Size is overflow, default is %u, but now %u\r\n", CONFIG_USBHOST_CDC_NCM_ETH_MAX_SEGSZE, cdc_ncm_class->max_segment_size); + } else { + USB_LOG_INFO("CDC NCM Max Segment Size:%u\r\n", cdc_ncm_class->max_segment_size); + } + + usbh_cdc_ncm_get_ntb_parameters(cdc_ncm_class, &cdc_ncm_class->ntb_param); + print_ntb_parameters(&cdc_ncm_class->ntb_param); + + /* enable int ep */ + ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc; + USBH_EP_INIT(cdc_ncm_class->intin, ep_desc); + + if (hport->config.intf[intf + 1].altsetting_num > 1) { + altsetting = hport->config.intf[intf + 1].altsetting_num - 1; + + for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[altsetting].intf_desc.bNumEndpoints; i++) { + ep_desc = &hport->config.intf[intf + 1].altsetting[altsetting].ep[i].ep_desc; + + if (ep_desc->bEndpointAddress & 0x80) { + USBH_EP_INIT(cdc_ncm_class->bulkin, ep_desc); + } else { + USBH_EP_INIT(cdc_ncm_class->bulkout, ep_desc); + } + } + + USB_LOG_INFO("Select cdc ncm altsetting: %d\r\n", altsetting); + usbh_set_interface(cdc_ncm_class->hport, cdc_ncm_class->data_intf, altsetting); + } else { + for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[0].intf_desc.bNumEndpoints; i++) { + ep_desc = &hport->config.intf[intf + 1].altsetting[0].ep[i].ep_desc; + + if (ep_desc->bEndpointAddress & 0x80) { + USBH_EP_INIT(cdc_ncm_class->bulkin, ep_desc); + } else { + USBH_EP_INIT(cdc_ncm_class->bulkout, ep_desc); + } + } + } + + memcpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN); + + USB_LOG_INFO("Register CDC NCM Class:%s\r\n", hport->config.intf[intf].devname); + + usbh_cdc_ncm_run(cdc_ncm_class); + return ret; +} + +static int usbh_cdc_ncm_disconnect(struct usbh_hubport *hport, uint8_t intf) +{ + int ret = 0; + + struct usbh_cdc_ncm *cdc_ncm_class = (struct usbh_cdc_ncm *)hport->config.intf[intf].priv; + + if (cdc_ncm_class) { + if (cdc_ncm_class->bulkin) { + usbh_kill_urb(&cdc_ncm_class->bulkin_urb); + } + + if (cdc_ncm_class->bulkout) { + usbh_kill_urb(&cdc_ncm_class->bulkout_urb); + } + + if (cdc_ncm_class->intin) { + usbh_kill_urb(&cdc_ncm_class->intin_urb); + } + + if (hport->config.intf[intf].devname[0] != '\0') { + USB_LOG_INFO("Unregister CDC NCM Class:%s\r\n", hport->config.intf[intf].devname); + usbh_cdc_ncm_stop(cdc_ncm_class); + } + + memset(cdc_ncm_class, 0, sizeof(struct usbh_cdc_ncm)); + } + + return ret; +} + +void usbh_cdc_ncm_rx_thread(void *argument) +{ + uint32_t g_cdc_ncm_rx_length; + int ret; + err_t err; + struct pbuf *p; + struct netif *netif = (struct netif *)argument; + + 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; + } + + 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; + } + } + + 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], USB_GET_MAXPACKETSIZE(g_cdc_ncm_class.bulkin->wMaxPacketSize), USB_OSAL_WAITING_FOREVER, NULL, NULL); + ret = usbh_submit_urb(&g_cdc_ncm_class.bulkin_urb); + if (ret < 0) { + goto find_class; + } + + g_cdc_ncm_rx_length += g_cdc_ncm_class.bulkin_urb.actual_length; + + if (g_cdc_ncm_class.bulkin_urb.actual_length != USB_GET_MAXPACKETSIZE(g_cdc_ncm_class.bulkin->wMaxPacketSize)) { + USB_LOG_DBG("rxlen:%d\r\n", g_cdc_ncm_rx_length); + + struct cdc_ncm_nth16 *nth16 = &g_cdc_ncm_rx_buffer[0]; + if ((nth16->dwSignature != CDC_NCM_NTH16_SIGNATURE) || + (nth16->wHeaderLength != 12) || + (nth16->wBlockLength != g_cdc_ncm_rx_length)) { + USB_LOG_ERR("invalid rx nth16\r\n"); + g_cdc_ncm_rx_length = 0; + continue; + } + + struct cdc_ncm_ndp16 *ndp16 = &g_cdc_ncm_rx_buffer[nth16->wNdpIndex]; + if ((ndp16->dwSignature != CDC_NCM_NDP16_SIGNATURE_NCM0) && (ndp16->dwSignature != CDC_NCM_NDP16_SIGNATURE_NCM1)) { + USB_LOG_ERR("invalid rx ndp16\r\n"); + g_cdc_ncm_rx_length = 0; + continue; + } + + uint16_t datagram_num = (ndp16->wLength - 8) / 4; + + USB_LOG_DBG("datagram num:%02x\r\n", datagram_num); + for (uint16_t i = 0; i < datagram_num; i++) { + struct cdc_ncm_ndp16_datagram *ndp16_datagram = &g_cdc_ncm_rx_buffer[nth16->wNdpIndex + 8 + 4 * i]; + if (ndp16_datagram->wDatagramIndex && ndp16_datagram->wDatagramLength) { + USB_LOG_DBG("ndp16_datagram index:%02x, length:%02x\r\n", ndp16_datagram->wDatagramIndex, ndp16_datagram->wDatagramLength); + + p = pbuf_alloc(PBUF_RAW, ndp16_datagram->wDatagramLength, PBUF_POOL); + if (p != NULL) { + memcpy(p->payload, (uint8_t *)&g_cdc_ncm_rx_buffer[ndp16_datagram->wDatagramIndex], ndp16_datagram->wDatagramLength); + + err = netif->input(p, netif); + if (err != ERR_OK) { + pbuf_free(p); + } + } else { + USB_LOG_ERR("No memory to alloc pbuf for cdc ncm rx\r\n"); + } + } + } + + g_cdc_ncm_rx_length = 0; + + } else { + } + } + // clang-format off +delete: + USB_LOG_INFO("Delete cdc ncm rx thread\r\n"); + usb_osal_thread_delete(NULL); + // clang-format on +} + +#define USB_ALIGN_UP(size, align) (((size) + (align)-1) & ~((align)-1)) + +err_t usbh_cdc_ncm_linkoutput(struct netif *netif, struct pbuf *p) +{ + int ret; + struct pbuf *q; + uint8_t *buffer; + struct cdc_ncm_ndp16_datagram *ndp16_datagram; + + if (g_cdc_ncm_class.connect_status == false) { + return ERR_BUF; + } + + struct cdc_ncm_nth16 *nth16 = &g_cdc_ncm_tx_buffer[0]; + + nth16->dwSignature = CDC_NCM_NTH16_SIGNATURE; + nth16->wHeaderLength = 12; + nth16->wSequence = g_cdc_ncm_class.bulkout_sequence++; + nth16->wBlockLength = 16 + 16 + USB_ALIGN_UP(p->tot_len, 4); + nth16->wNdpIndex = 16 + USB_ALIGN_UP(p->tot_len, 4); + + struct cdc_ncm_ndp16 *ndp16 = &g_cdc_ncm_tx_buffer[nth16->wNdpIndex]; + + ndp16->dwSignature = CDC_NCM_NDP16_SIGNATURE_NCM0; + ndp16->wLength = 16; + ndp16->wNextNdpIndex = 0; + + ndp16_datagram = &g_cdc_ncm_tx_buffer[nth16->wNdpIndex + 8 + 4 * 0]; + ndp16_datagram->wDatagramIndex = 16; + ndp16_datagram->wDatagramLength = p->tot_len; + + ndp16_datagram = &g_cdc_ncm_tx_buffer[nth16->wNdpIndex + 8 + 4 * 1]; + ndp16_datagram->wDatagramIndex = 0; + ndp16_datagram->wDatagramLength = 0; + + buffer = &g_cdc_ncm_tx_buffer[16]; + + for (q = p; q != NULL; q = q->next) { + memcpy(buffer, q->payload, q->len); + buffer += q->len; + } + + USB_LOG_DBG("txlen:%d\r\n", nth16->wBlockLength); + + usbh_bulk_urb_fill(&g_cdc_ncm_class.bulkout_urb, g_cdc_ncm_class.hport, g_cdc_ncm_class.bulkout, g_cdc_ncm_tx_buffer, nth16->wBlockLength, USB_OSAL_WAITING_FOREVER, NULL, NULL); + ret = usbh_submit_urb(&g_cdc_ncm_class.bulkout_urb); + if (ret < 0) { + return ERR_BUF; + } + + return ERR_OK; +} + +__WEAK void usbh_cdc_ncm_run(struct usbh_cdc_ncm *cdc_ncm_class) +{ +} + +__WEAK void usbh_cdc_ncm_stop(struct usbh_cdc_ncm *cdc_ncm_class) +{ +} + +const struct usbh_class_driver cdc_ncm_class_driver = { + .driver_name = "cdc_ncm", + .connect = usbh_cdc_ncm_connect, + .disconnect = usbh_cdc_ncm_disconnect +}; + +CLASS_INFO_DEFINE const struct usbh_class_info cdc_ncm_class_info = { + .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL, + .class = USB_DEVICE_CLASS_CDC, + .subclass = CDC_NETWORK_CONTROL_MODEL, + .protocol = CDC_COMMON_PROTOCOL_NONE, + .vid = 0x00, + .pid = 0x00, + .class_driver = &cdc_ncm_class_driver +}; diff --git a/class/cdc/usbh_cdc_ncm.h b/class/cdc/usbh_cdc_ncm.h new file mode 100644 index 00000000..d8012550 --- /dev/null +++ b/class/cdc/usbh_cdc_ncm.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024, sakumisu + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef USBH_CDC_NCM_H +#define USBH_CDC_NCM_H + +#include "usb_cdc.h" + +#include "lwip/netif.h" +#include "lwip/pbuf.h" + +struct usbh_cdc_ncm { + struct usbh_hubport *hport; + struct usb_endpoint_descriptor *bulkin; /* Bulk IN endpoint */ + struct usb_endpoint_descriptor *bulkout; /* Bulk OUT endpoint */ + struct usb_endpoint_descriptor *intin; /* Interrupt IN endpoint */ + struct usbh_urb bulkout_urb; /* Bulk out endpoint */ + struct usbh_urb bulkin_urb; /* Bulk IN endpoint */ + struct usbh_urb intin_urb; /* Interrupt IN endpoint */ + + uint8_t ctrl_intf; /* Control interface number */ + uint8_t data_intf; /* Data interface number */ + uint8_t minor; + + struct cdc_ncm_ntb_parameters ntb_param; + uint16_t bulkin_sequence; + uint16_t bulkout_sequence; + + uint8_t mac[6]; + bool connect_status; + uint16_t max_segment_size; + uint32_t speed[2]; + + ip_addr_t ipaddr; + ip_addr_t netmask; + ip_addr_t gateway; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int usbh_cdc_ncm_get_connect_status(struct usbh_cdc_ncm *cdc_ncm_class); + +void usbh_cdc_ncm_run(struct usbh_cdc_ncm *cdc_ncm_class); +void usbh_cdc_ncm_stop(struct usbh_cdc_ncm *cdc_ncm_class); + +err_t usbh_cdc_ncm_linkoutput(struct netif *netif, struct pbuf *p); +void usbh_cdc_ncm_rx_thread(void *argument); + +#ifdef __cplusplus +} +#endif + +#endif /* USBH_CDC_NCM_H */ diff --git a/demo/usb_host.c b/demo/usb_host.c index b8926362..0ce95730 100644 --- a/demo/usb_host.c +++ b/demo/usb_host.c @@ -12,6 +12,7 @@ #define TEST_USBH_AUDIO 0 #define TEST_USBH_VIDEO 0 #define TEST_USBH_CDC_ECM 0 +#define TEST_USBH_CDC_NCM 0 #define TEST_USBH_RNDIS 0 #define TEST_USBH_ASIX 0 @@ -344,7 +345,7 @@ void usbh_videostreaming_parse_yuyv2(struct usbh_urb *urb, struct usbh_videostre } #endif -#if TEST_USBH_CDC_ECM || TEST_USBH_RNDIS || TEST_USBH_ASIX +#if TEST_USBH_CDC_ECM || || TEST_USBH_CDC_NCM || TEST_USBH_RNDIS || TEST_USBH_ASIX #include "netif/etharp.h" #include "lwip/netif.h" #include "lwip/pbuf.h" @@ -506,6 +507,118 @@ void usbh_cdc_ecm_stop(struct usbh_cdc_ecm *cdc_ecm_class) } #endif +#if TEST_USBH_CDC_NCM +#include "usbh_cdc_ncm.h" + +struct netif g_cdc_ncm_netif; + +#ifdef __RTTHREAD__ +static struct eth_device cdc_ncm_dev; + +static rt_err_t rt_usbh_cdc_ncm_control(rt_device_t dev, int cmd, void *args) +{ + struct usbh_cdc_ncm *cdc_ncm_class = (struct usbh_cdc_ncm *)dev->user_data; + + switch (cmd) { + case NIOCTL_GADDR: + + /* get mac address */ + if (args) + rt_memcpy(args, cdc_ncm_class->mac, 6); + else + return -RT_ERROR; + + break; + + default: + break; + } + + return RT_EOK; +} + +static rt_err_t rt_usbh_cdc_ncm_eth_tx(rt_device_t dev, struct pbuf *p) +{ + return usbh_cdc_ncm_linkoutput(NULL, p); +} +#endif + +static err_t usbh_cdc_ncm_if_init(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + + netif->mtu = 1500; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP; + netif->state = NULL; + netif->name[0] = 'E'; + netif->name[1] = 'X'; + netif->output = etharp_output; + netif->linkoutput = usbh_cdc_ncm_linkoutput; + return ERR_OK; +} + +void usbh_cdc_ncm_run(struct usbh_cdc_ncm *cdc_ncm_class) +{ +#ifdef __RTTHREAD__ + struct netdev *netdev; + + memset(&cdc_ncm_dev, 0, sizeof(struct eth_device)); + + cdc_ncm_dev.parent.control = rt_usbh_cdc_ncm_control; + cdc_ncm_dev.eth_rx = NULL; + cdc_ncm_dev.eth_tx = rt_usbh_cdc_ncm_eth_tx; + cdc_ncm_dev.parent.user_data = cdc_ncm_class; + + eth_device_init(&cdc_ncm_dev, "u0"); + eth_device_linkchange(&cdc_ncm_dev, RT_TRUE); + + usb_osal_thread_create("usbh_cdc_ncm_rx", 2048, CONFIG_USBHOST_PSC_PRIO + 1, usbh_cdc_ncm_rx_thread, cdc_ncm_dev.netif); +#else + struct netif *netif = &g_cdc_ncm_netif; + + netif->hwaddr_len = 6; + memcpy(netif->hwaddr, cdc_ncm_class->mac, 6); + + IP4_ADDR(&cdc_ncm_class->ipaddr, 0, 0, 0, 0); + IP4_ADDR(&cdc_ncm_class->netmask, 0, 0, 0, 0); + IP4_ADDR(&cdc_ncm_class->gateway, 0, 0, 0, 0); + + netif = netif_add(netif, &cdc_ncm_class->ipaddr, &cdc_ncm_class->netmask, &cdc_ncm_class->gateway, NULL, usbh_cdc_ncm_if_init, tcpip_input); + netif_set_default(netif); + while (!netif_is_up(netif)) { + } + + dhcp_handle1 = xTimerCreate((const char *)"dhcp1", (TickType_t)200, (UBaseType_t)pdTRUE, (void *const)netif, (TimerCallbackFunction_t)dhcp_timeout); + if (dhcp_handle1 == NULL) { + USB_LOG_ERR("timer creation failed! \r\n"); + while (1) { + } + } + + usb_osal_thread_create("usbh_cdc_ncm_rx", 2048, CONFIG_USBHOST_PSC_PRIO + 1, usbh_cdc_ncm_rx_thread, netif); +#if LWIP_DHCP + dhcp_start(netif); + xTimerStart(dhcp_handle1, 0); +#endif +#endif +} + +void usbh_cdc_ncm_stop(struct usbh_cdc_ncm *cdc_ncm_class) +{ +#ifdef __RTTHREAD__ + eth_device_deinit(&cdc_ncm_dev); +#else + struct netif *netif = &g_cdc_ncm_netif; +#if LWIP_DHCP + dhcp_stop(netif); + dhcp_cleanup(netif); +#endif + netif_set_down(netif); + netif_remove(netif); +#endif +} +#endif + #if TEST_USBH_RNDIS #include "usbh_rndis.h" @@ -844,7 +957,7 @@ void usbh_class_test(void) #ifdef __RTTHREAD__ /* do nothing */ #else -#if TEST_USBH_CDC_ECM || TEST_USBH_RNDIS || TEST_USBH_ASIX +#if TEST_USBH_CDC_ECM || TEST_USBH_CDC_NCM || TEST_USBH_RNDIS || TEST_USBH_ASIX /* Initialize the LwIP stack */ tcpip_init(NULL, NULL); #endif