summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsakumisu <[email protected]>2023-11-04 17:16:24 +0800
committersakumisu <[email protected]>2023-11-04 17:16:24 +0800
commite79319cac92722bec99cedd117d890332874f2f8 (patch)
treeeab52f9d47ff907cc48b1ce1954d0868e619796a
parent7acb667e20c43d5a61c79827e51e5a1f8fd8a455 (diff)
add cdc ecm host
-rw-r--r--class/cdc/usbh_cdc_ecm.c247
-rw-r--r--class/cdc/usbh_cdc_ecm.h36
2 files changed, 283 insertions, 0 deletions
diff --git a/class/cdc/usbh_cdc_ecm.c b/class/cdc/usbh_cdc_ecm.c
new file mode 100644
index 00000000..f7321037
--- /dev/null
+++ b/class/cdc/usbh_cdc_ecm.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "usbh_core.h"
+#include "usbh_cdc_ecm.h"
+
+#define DEV_FORMAT "/dev/cdc_ether"
+
+/* 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_MAX_CDC_ECM_CLASS 1
+static struct usbh_cdc_ecm g_cdc_ecm_class[CONFIG_USBHOST_MAX_CDC_ECM_CLASS];
+static uint32_t g_devinuse = 0;
+
+static struct usbh_cdc_ecm *usbh_cdc_ecm_class_alloc(void)
+{
+ int devno;
+
+ for (devno = 0; devno < CONFIG_USBHOST_MAX_CDC_ECM_CLASS; devno++) {
+ if ((g_devinuse & (1 << devno)) == 0) {
+ g_devinuse |= (1 << devno);
+ memset(&g_cdc_ecm_class[devno], 0, sizeof(struct usbh_cdc_ecm));
+ g_cdc_ecm_class[devno].minor = devno;
+ return &g_cdc_ecm_class[devno];
+ }
+ }
+ return NULL;
+}
+
+static void usbh_cdc_ecm_class_free(struct usbh_cdc_ecm *cdc_ecm_class)
+{
+ int devno = cdc_ecm_class->minor;
+
+ if (devno >= 0 && devno < 32) {
+ g_devinuse &= ~(1 << devno);
+ }
+ memset(cdc_ecm_class, 0, sizeof(struct usbh_cdc_ecm));
+}
+
+int usbh_cdc_ecm_set_eth_packet_filter(struct usbh_cdc_ecm *cdc_ecm_class, uint16_t filter_value)
+{
+ struct usb_setup_packet *setup = cdc_ecm_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = CDC_REQUEST_SET_ETHERNET_PACKET_FILTER;
+ setup->wValue = filter_value;
+ setup->wIndex = cdc_ecm_class->ctrl_intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(cdc_ecm_class->hport->ep0, setup, NULL);
+}
+
+int usbh_cdc_ecm_select_altsetting(struct usbh_cdc_ecm *cdc_ecm_class, uint16_t alt_setting)
+{
+ struct usb_setup_packet *setup = cdc_ecm_class->hport->setup;
+
+ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_INTERFACE;
+ setup->bRequest = USB_REQUEST_SET_INTERFACE;
+ setup->wValue = alt_setting;
+ setup->wIndex = cdc_ecm_class->data_intf;
+ setup->wLength = 0;
+
+ return usbh_control_transfer(cdc_ecm_class->hport->ep0, setup, NULL);
+}
+
+static int usbh_cdc_ecm_connect(struct usbh_hubport *hport, uint8_t intf)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ int ret;
+ uint8_t altsetting = 0;
+ uint8_t mac_buffer[32];
+ uint8_t *p;
+ uint8_t cur_iface = 0xff;
+ uint8_t mac_str_idx = 0xff;
+
+ struct usbh_cdc_ecm *cdc_ecm_class = usbh_cdc_ecm_class_alloc();
+ if (cdc_ecm_class == NULL) {
+ USB_LOG_ERR("Fail to alloc cdc_ecm_class\r\n");
+ return -ENOMEM;
+ }
+
+ cdc_ecm_class->hport = hport;
+ cdc_ecm_class->ctrl_intf = intf;
+ cdc_ecm_class->data_intf = intf + 1;
+
+ hport->config.intf[intf].priv = cdc_ecm_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_ecm_class->ctrl_intf) && p[DESC_bDescriptorSubType] == CDC_FUNC_DESC_ETHERNET_NETWORKING) {
+ mac_str_idx = p[3];
+ 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 ecm mac string\r\n");
+ return -1;
+ }
+
+ memset(mac_buffer, 0, 32);
+ ret = usbh_get_string_desc(cdc_ecm_class->hport, mac_str_idx, mac_buffer);
+ if (ret < 0) {
+ return ret;
+ }
+
+ uint8_t len = strlen(mac_buffer);
+
+ for (int i = 0, j = 0; i < len; 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_ecm_class->mac[j] = (unsigned char)byte;
+ }
+
+ USB_LOG_INFO("CDC ECM mac address %02x: %02x: %02x: %02x: %02x: %02x\r\n",
+ cdc_ecm_class->mac[0],
+ cdc_ecm_class->mac[1],
+ cdc_ecm_class->mac[2],
+ cdc_ecm_class->mac[3],
+ cdc_ecm_class->mac[4],
+ cdc_ecm_class->mac[5]);
+
+ /* enable int ep */
+ ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc;
+ usbh_hport_activate_epx(&cdc_ecm_class->intin, hport, 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_hport_activate_epx(&cdc_ecm_class->bulkin, hport, ep_desc);
+ } else {
+ usbh_hport_activate_epx(&cdc_ecm_class->bulkout, hport, ep_desc);
+ }
+ }
+
+ USB_LOG_INFO("Select cdc ecm altsetting: %d\r\n", altsetting);
+ usbh_cdc_ecm_select_altsetting(cdc_ecm_class, 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_hport_activate_epx(&cdc_ecm_class->bulkin, hport, ep_desc);
+ } else {
+ usbh_hport_activate_epx(&cdc_ecm_class->bulkout, hport, ep_desc);
+ }
+ }
+ }
+
+ // ret = usbh_cdc_ecm_set_eth_packet_filter(cdc_ecm_class, 0x000c);
+ // if (ret < 0) {
+ // return ret;
+ // }
+
+ snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, cdc_ecm_class->minor);
+
+ USB_LOG_INFO("Register CDC ECM Class:%s\r\n", hport->config.intf[intf].devname);
+
+ usbh_cdc_ecm_run(cdc_ecm_class);
+ return ret;
+}
+
+static int usbh_cdc_ecm_disconnect(struct usbh_hubport *hport, uint8_t intf)
+{
+ int ret = 0;
+
+ struct usbh_cdc_ecm *cdc_ecm_class = (struct usbh_cdc_ecm *)hport->config.intf[intf].priv;
+
+ if (cdc_ecm_class) {
+ if (cdc_ecm_class->bulkin) {
+ usbh_pipe_free(cdc_ecm_class->bulkin);
+ }
+
+ if (cdc_ecm_class->bulkout) {
+ usbh_pipe_free(cdc_ecm_class->bulkout);
+ }
+
+ if (cdc_ecm_class->intin) {
+ usbh_pipe_free(cdc_ecm_class->intin);
+ }
+
+ if (hport->config.intf[intf].devname[0] != '\0') {
+ USB_LOG_INFO("Unregister CDC ECM Class:%s\r\n", hport->config.intf[intf].devname);
+ usbh_cdc_ecm_stop(cdc_ecm_class);
+ }
+
+ usbh_cdc_ecm_class_free(cdc_ecm_class);
+ }
+
+ return ret;
+}
+
+__WEAK void usbh_cdc_ecm_run(struct usbh_cdc_ecm *cdc_ecm_class)
+{
+}
+
+__WEAK void usbh_cdc_ecm_stop(struct usbh_cdc_ecm *cdc_ecm_class)
+{
+}
+
+const struct usbh_class_driver cdc_ecm_class_driver = {
+ .driver_name = "cdc_ecm",
+ .connect = usbh_cdc_ecm_connect,
+ .disconnect = usbh_cdc_ecm_disconnect
+};
+
+CLASS_INFO_DEFINE const struct usbh_class_info cdc_ecm_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_ETHERNET_NETWORKING_CONTROL_MODEL,
+ .protocol = CDC_COMMON_PROTOCOL_NONE,
+ .vid = 0x00,
+ .pid = 0x00,
+ .class_driver = &cdc_ecm_class_driver
+}; \ No newline at end of file
diff --git a/class/cdc/usbh_cdc_ecm.h b/class/cdc/usbh_cdc_ecm.h
new file mode 100644
index 00000000..1b3be821
--- /dev/null
+++ b/class/cdc/usbh_cdc_ecm.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2022, sakumisu
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef USBH_CDC_ECM_H
+#define USBH_CDC_ECM_H
+
+#include "usb_cdc.h"
+
+struct usbh_cdc_ecm {
+ struct usbh_hubport *hport;
+
+ uint8_t ctrl_intf; /* Control interface number */
+ uint8_t data_intf; /* Data interface number */
+ uint8_t minor;
+ uint8_t mac[6];
+ usbh_pipe_t bulkin; /* Bulk IN endpoint */
+ usbh_pipe_t bulkout; /* Bulk OUT endpoint */
+ usbh_pipe_t intin; /* Interrupt IN endpoint */
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int usbh_cdc_ecm_set_eth_packet_filter(struct usbh_cdc_ecm *cdc_ecm_class, uint16_t filter_value);
+
+void usbh_cdc_ecm_run(struct usbh_cdc_ecm *cdc_ecm_class);
+void usbh_cdc_ecm_stop(struct usbh_cdc_ecm *cdc_ecm_class);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USBH_CDC_ACM_H */