summaryrefslogtreecommitdiff
path: root/class
diff options
context:
space:
mode:
authorsakumisu <[email protected]>2023-11-04 16:44:02 +0800
committersakumisu <[email protected]>2023-11-04 16:44:02 +0800
commita04b1fa55122f03ad3ebbd27f089776da2f263e8 (patch)
tree8f3a395ac7311c390356ded6a4e55fd1c7111aa4 /class
parentc37c60c7c7b07d3356a5b55a06640437662e5ef2 (diff)
add cdc ecm device
Diffstat (limited to 'class')
-rw-r--r--class/cdc/usb_cdc.h132
-rw-r--r--class/cdc/usbd_cdc_ecm.c237
-rw-r--r--class/cdc/usbd_cdc_ecm.h36
3 files changed, 405 insertions, 0 deletions
diff --git a/class/cdc/usb_cdc.h b/class/cdc/usb_cdc.h
index 027e08d6..de0e794f 100644
--- a/class/cdc/usb_cdc.h
+++ b/class/cdc/usb_cdc.h
@@ -212,6 +212,57 @@
#define CDC_SERIAL_STATE_RX_CARRIER_Pos (0)
#define CDC_SERIAL_STATE_RX_CARRIER_Msk (1 << CDC_SERIAL_STATE_RX_CARRIER_Pos)
+#define CDC_ECM_XMIT_OK (1 << 0)
+#define CDC_ECM_RVC_OK (1 << 1)
+#define CDC_ECM_XMIT_ERROR (1 << 2)
+#define CDC_ECM_RCV_ERROR (1 << 3)
+#define CDC_ECM_RCV_NO_BUFFER (1 << 4)
+#define CDC_ECM_DIRECTED_BYTES_XMIT (1 << 5)
+#define CDC_ECM_DIRECTED_FRAMES_XMIT (1 << 6)
+#define CDC_ECM_MULTICAST_BYTES_XMIT (1 << 7)
+#define CDC_ECM_MULTICAST_FRAMES_XMIT (1 << 8)
+#define CDC_ECM_BROADCAST_BYTES_XMIT (1 << 9)
+#define CDC_ECM_BROADCAST_FRAMES_XMIT (1 << 10)
+#define CDC_ECM_DIRECTED_BYTES_RCV (1 << 11)
+#define CDC_ECM_DIRECTED_FRAMES_RCV (1 << 12)
+#define CDC_ECM_MULTICAST_BYTES_RCV (1 << 13)
+#define CDC_ECM_MULTICAST_FRAMES_RCV (1 << 14)
+#define CDC_ECM_BROADCAST_BYTES_RCV (1 << 15)
+#define CDC_ECM_BROADCAST_FRAMES_RCV (1 << 16)
+#define CDC_ECM_RCV_CRC_ERROR (1 << 17)
+#define CDC_ECM_TRANSMIT_QUEUE_LENGTH (1 << 18)
+#define CDC_ECM_RCV_ERROR_ALIGNMENT (1 << 19)
+#define CDC_ECM_XMIT_ONE_COLLISION (1 << 20)
+#define CDC_ECM_XMIT_MORE_COLLISIONS (1 << 21)
+#define CDC_ECM_XMIT_DEFERRED (1 << 22)
+#define CDC_ECM_XMIT_MAX_COLLISIONS (1 << 23)
+#define CDC_ECM_RCV_OVERRUN (1 << 24)
+#define CDC_ECM_XMIT_UNDERRUN (1 << 25)
+#define CDC_ECM_XMIT_HEARTBEAT_FAILURE (1 << 26)
+#define CDC_ECM_XMIT_TIMES_CRS_LOST (1 << 27)
+#define CDC_ECM_XMIT_LATE_COLLISIONS (1 << 28)
+
+#define CDC_ECM_MAC_STR_DESC (uint8_t *)"010202030000"
+#define CDC_ECM_MAC_ADDR0 0x00U /* 01 */
+#define CDC_ECM_MAC_ADDR1 0x02U /* 02 */
+#define CDC_ECM_MAC_ADDR2 0x02U /* 03 */
+#define CDC_ECM_MAC_ADDR3 0x03U /* 00 */
+#define CDC_ECM_MAC_ADDR4 0x00U /* 00 */
+#define CDC_ECM_MAC_ADDR5 0x00U /* 00 */
+
+#define CDC_ECM_NET_DISCONNECTED 0x00U
+#define CDC_ECM_NET_CONNECTED 0x01U
+
+#define CDC_ECM_ETH_STATS_RESERVED 0xE0U
+#define CDC_ECM_BMREQUEST_TYPE_ECM 0xA1U
+
+#define CDC_ECM_CONNECT_SPEED_UPSTREAM 0x004C4B40U /* 5Mbps */
+#define CDC_ECM_CONNECT_SPEED_DOWNSTREAM 0x004C4B40U /* 5Mbps */
+
+#define CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION 0x00
+#define CDC_ECM_NOTIFY_CODE_RESPONSE_AVAILABLE 0x01
+#define CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE 0x2A
+
/*------------------------------------------------------------------------------
* Structures based on usbcdc11.pdf (www.usb.org)
*----------------------------------------------------------------------------*/
@@ -299,6 +350,15 @@ struct cdc_ecm_descriptor {
uint8_t bNumberPowerFilters;
} __PACKED;
+struct cdc_ecm_notification {
+ uint8_t bmRequestType;
+ uint8_t bNotificationType;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+ uint8_t data[8];
+} __PACKED;
+
/*Length of template descriptor: 66 bytes*/
#define CDC_ACM_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 7 + 7)
// clang-format off
@@ -437,4 +497,76 @@ struct cdc_ecm_descriptor {
0x00 /* bInterval */
// clang-format on
+#define DBVAL_BE(x) ((x >> 24) & 0xFF), ((x >> 16) & 0xFF), ((x >> 8) & 0xFF), (x & 0xFF)
+
+/*Length of template descriptor: 66 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, \
+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_ETHERNET_NETWORKING_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_ETHERNET_NETWORKING_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_ECM 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 */\
+ 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
new file mode 100644
index 00000000..1ebdecf6
--- /dev/null
+++ b/class/cdc/usbd_cdc_ecm.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2023, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbd_core.h"
+#include "usbd_cdc_ecm.h"
+
+#define CDC_ECM_OUT_EP_IDX 0
+#define CDC_ECM_IN_EP_IDX 1
+#define CDC_ECM_INT_EP_IDX 2
+
+/* Describe EndPoints configuration */
+static struct usbd_endpoint cdc_ecm_ep_data[3];
+
+#ifdef CONFIG_USB_HS
+#define CDC_ECM_MAX_PACKET_SIZE 512
+#else
+#define CDC_ECM_MAX_PACKET_SIZE 64
+#endif
+
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_rx_buffer[CONFIG_CDC_ECM_ETH_MAX_SEGSZE];
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_tx_buffer[CONFIG_CDC_ECM_ETH_MAX_SEGSZE];
+USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_notify_buf[16];
+
+volatile uint8_t *g_cdc_ecm_rx_data_buffer = NULL;
+volatile uint32_t g_cdc_ecm_rx_data_length = 0;
+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 uint32_t g_connect_speed_table[2] = { CDC_ECM_CONNECT_SPEED_UPSTREAM,
+ CDC_ECM_CONNECT_SPEED_DOWNSTREAM };
+
+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;
+ uint8_t bytes2send = 0;
+
+ notify->bmRequestType = CDC_ECM_BMREQUEST_TYPE_ECM;
+ notify->bNotificationType = notifycode;
+
+ switch (notifycode) {
+ case CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION:
+ notify->wValue = value;
+ notify->wIndex = g_cmd_intf;
+ notify->wLength = 0U;
+
+ for (uint8_t i = 0U; i < 8U; i++) {
+ notify->data[i] = 0U;
+ }
+ bytes2send = 8U;
+ break;
+ case CDC_ECM_NOTIFY_CODE_RESPONSE_AVAILABLE:
+ notify->wValue = 0U;
+ notify->wIndex = g_cmd_intf;
+ notify->wLength = 0U;
+ for (uint8_t i = 0U; i < 8U; i++) {
+ notify->data[i] = 0U;
+ }
+ bytes2send = 8U;
+ break;
+ case CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE:
+ notify->wValue = 0U;
+ notify->wIndex = g_cmd_intf;
+ notify->wLength = 0x0008U;
+ bytes2send = 16U;
+
+ memcpy(notify->data, speed, 8);
+ break;
+
+ default:
+ break;
+ }
+
+ if (bytes2send) {
+ usbd_ep_start_write(cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_addr, g_cdc_ecm_notify_buf, bytes2send);
+ }
+}
+
+static int cdc_ecm_class_interface_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USB_LOG_DBG("CDC ECM Class request: "
+ "bRequest 0x%02x\r\n",
+ setup->bRequest);
+
+ g_cmd_intf = LO_BYTE(setup->wIndex);
+
+ switch (setup->bRequest) {
+ case CDC_REQUEST_SET_ETHERNET_PACKET_FILTER:
+ /* bit0 Promiscuous
+ * bit1 ALL Multicast
+ * bit2 Directed
+ * bit3 Broadcast
+ * bit4 Multicast
+ */
+ if (g_current_net_status == 0) {
+ g_current_net_status = 1;
+ usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, NULL);
+ }
+
+ break;
+ default:
+ USB_LOG_WRN("Unhandled CDC ECM Class bRequest 0x%02x\r\n", setup->bRequest);
+ return -1;
+ }
+
+ return 0;
+}
+
+void cdc_ecm_notify_handler(uint8_t event, void *arg)
+{
+ switch (event) {
+ case USBD_EVENT_RESET:
+ g_current_net_status = 0;
+ g_cdc_ecm_rx_data_length = 0;
+ g_cdc_ecm_tx_data_length = 0;
+ g_cdc_ecm_rx_data_buffer = NULL;
+ break;
+ case USBD_EVENT_CONFIGURED:
+ usbd_ep_start_read(cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, &g_cdc_ecm_rx_buffer[g_cdc_ecm_rx_data_length], CDC_ECM_MAX_PACKET_SIZE);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void cdc_ecm_bulk_out(uint8_t ep, uint32_t nbytes)
+{
+ g_cdc_ecm_rx_data_length += nbytes;
+
+ if (nbytes < CDC_ECM_MAX_PACKET_SIZE) {
+ g_cdc_ecm_rx_data_buffer = g_cdc_ecm_rx_buffer;
+ usbd_cdc_ecm_data_recv_done();
+ } else {
+ usbd_ep_start_read(ep, &g_cdc_ecm_rx_buffer[g_cdc_ecm_rx_data_length], CDC_ECM_MAX_PACKET_SIZE);
+ }
+}
+
+void cdc_ecm_bulk_in(uint8_t ep, uint32_t nbytes)
+{
+ if ((nbytes % CDC_ECM_MAX_PACKET_SIZE) == 0 && nbytes) {
+ /* send zlp */
+ usbd_ep_start_write(ep, NULL, 0);
+ } else {
+ g_cdc_ecm_tx_data_length = 0;
+ }
+}
+
+void cdc_ecm_int_in(uint8_t ep, uint32_t nbytes)
+{
+ if (g_current_net_status == 1) {
+ g_current_net_status = 2;
+ usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, g_connect_speed_table);
+ }
+}
+
+#ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
+struct pbuf *usbd_cdc_ecm_eth_rx(void)
+{
+ struct pbuf *p;
+
+ if (g_cdc_ecm_rx_data_buffer == NULL) {
+ return NULL;
+ }
+ p = pbuf_alloc(PBUF_RAW, g_cdc_ecm_rx_data_length, PBUF_POOL);
+ if (p == NULL) {
+ return NULL;
+ }
+ memcpy(p->payload, (uint8_t *)g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_data_length);
+ p->len = g_cdc_ecm_rx_data_length;
+
+ USB_LOG_DBG("rxlen:%d\r\n", g_cdc_ecm_rx_data_length);
+ g_cdc_ecm_rx_data_length = 0;
+ g_cdc_ecm_rx_data_buffer = NULL;
+ usbd_ep_start_read(cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, g_cdc_ecm_rx_buffer, CDC_ECM_MAX_PACKET_SIZE);
+
+ return p;
+}
+
+int usbd_cdc_ecm_eth_tx(struct pbuf *p)
+{
+ struct pbuf *q;
+ uint8_t *buffer;
+
+ if (g_cdc_ecm_tx_data_length > 0) {
+ return -EBUSY;
+ }
+
+ if (p->tot_len > sizeof(g_cdc_ecm_tx_buffer)) {
+ p->tot_len = sizeof(g_cdc_ecm_tx_buffer);
+ }
+
+ buffer = g_cdc_ecm_tx_buffer;
+ for (q = p; q != NULL; q = q->next) {
+ memcpy(buffer, q->payload, q->len);
+ buffer += q->len;
+ }
+
+ g_cdc_ecm_tx_data_length = p->tot_len;
+
+ USB_LOG_DBG("txlen:%d\r\n", g_cdc_ecm_tx_data_length);
+ return usbd_ep_start_write(cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr, g_cdc_ecm_tx_buffer, g_cdc_ecm_tx_data_length);
+}
+#endif
+
+struct usbd_interface *usbd_cdc_ecm_init_intf(struct usbd_interface *intf, const uint8_t int_ep, const uint8_t out_ep, const uint8_t in_ep)
+{
+ intf->class_interface_handler = cdc_ecm_class_interface_request_handler;
+ intf->class_endpoint_handler = NULL;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = cdc_ecm_notify_handler;
+
+ cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr = out_ep;
+ cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_cb = cdc_ecm_bulk_out;
+ cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr = in_ep;
+ cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_cb = cdc_ecm_bulk_in;
+ cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_addr = int_ep;
+ cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_cb = cdc_ecm_int_in;
+
+ usbd_add_endpoint(&cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX]);
+ usbd_add_endpoint(&cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX]);
+ usbd_add_endpoint(&cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX]);
+
+ return intf;
+}
+
+void usbd_cdc_ecm_set_connect_speed(uint32_t speed[2])
+{
+ memcpy(g_connect_speed_table, speed, 8);
+}
+
+__WEAK void usbd_cdc_ecm_data_recv_done(void)
+{
+} \ No newline at end of file
diff --git a/class/cdc/usbd_cdc_ecm.h b/class/cdc/usbd_cdc_ecm.h
new file mode 100644
index 00000000..8381b1a5
--- /dev/null
+++ b/class/cdc/usbd_cdc_ecm.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBD_CDC_ECM_H
+#define USBD_CDC_ECM_H
+
+#include "usb_cdc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Ethernet Maximum Segment size, typically 1514 bytes */
+#define CONFIG_CDC_ECM_ETH_MAX_SEGSZE 1514U
+#define CONFIG_USBDEV_CDC_ECM_USING_LWIP
+
+/* Init cdc ecm interface driver */
+struct usbd_interface *usbd_cdc_ecm_init_intf(struct usbd_interface *intf, const uint8_t int_ep, const uint8_t out_ep, const uint8_t in_ep);
+
+/* Setup request command callback api */
+void usbd_cdc_ecm_set_connect_speed(uint32_t speed[2]);
+void usbd_cdc_ecm_data_recv_done(void);
+
+#ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
+#include <lwip/pbuf.h>
+struct pbuf *usbd_cdc_ecm_eth_rx(void);
+int usbd_cdc_ecm_eth_tx(struct pbuf *p);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBD_CDC_ECM_H */