/* * Copyright (c) 2017 Intel Corporation * Copyright (c) 2023 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT zephyr_cdc_ecm_ethernet #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(cdc_ecm, CONFIG_USBD_CDC_ECM_LOG_LEVEL); #define CDC_ECM_EP_MPS_INT 16 #define CDC_ECM_INTERVAL_DEFAULT 10000UL #define CDC_ECM_FS_INT_EP_INTERVAL USB_FS_INT_EP_INTERVAL(10000U) #define CDC_ECM_HS_INT_EP_INTERVAL USB_HS_INT_EP_INTERVAL(10000U) enum { CDC_ECM_IFACE_UP, CDC_ECM_DATA_IFACE_ENABLED, CDC_ECM_CLASS_SUSPENDED, CDC_ECM_OUT_ENGAGED, }; /* * Transfers through two endpoints proceed in a synchronous manner, * with maximum block of NET_ETH_MAX_FRAME_SIZE. */ UDC_BUF_POOL_DEFINE(cdc_ecm_ep_pool, DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) * 2, NET_ETH_MAX_FRAME_SIZE, sizeof(struct udc_buf_info), NULL); struct cdc_ecm_notification { union { uint8_t bmRequestType; struct usb_req_type_field RequestType; }; uint8_t bNotificationType; uint16_t wValue; uint16_t wIndex; uint16_t wLength; } __packed; /* * Collection of descriptors used to assemble specific function descriptors. * This structure is used by CDC ECM implementation to update and fetch * properties at runtime. We currently support full and high speed. */ struct usbd_cdc_ecm_desc { struct usb_association_descriptor iad; struct usb_if_descriptor if0; struct cdc_header_descriptor if0_header; struct cdc_union_descriptor if0_union; struct cdc_ecm_descriptor if0_ecm; struct usb_ep_descriptor if0_int_ep; struct usb_ep_descriptor if0_hs_int_ep; struct usb_if_descriptor if1_0; struct usb_if_descriptor if1_1; struct usb_ep_descriptor if1_1_in_ep; struct usb_ep_descriptor if1_1_out_ep; struct usb_ep_descriptor if1_1_hs_in_ep; struct usb_ep_descriptor if1_1_hs_out_ep; struct usb_desc_header nil_desc; }; struct cdc_ecm_eth_data { struct usbd_class_data *c_data; struct usbd_desc_node *const mac_desc_data; struct usbd_cdc_ecm_desc *const desc; const struct usb_desc_header **const fs_desc; const struct usb_desc_header **const hs_desc; struct net_if *iface; uint8_t mac_addr[6]; struct k_sem sync_sem; atomic_t state; }; static uint8_t cdc_ecm_get_ctrl_if(struct cdc_ecm_eth_data *const data) { struct usbd_cdc_ecm_desc *desc = data->desc; return desc->if0.bInterfaceNumber; } static uint8_t cdc_ecm_get_int_in(struct usbd_class_data *const c_data) { struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data); const struct device *dev = usbd_class_get_private(c_data); struct cdc_ecm_eth_data *data = dev->data; struct usbd_cdc_ecm_desc *desc = data->desc; if (usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) { return desc->if0_hs_int_ep.bEndpointAddress; } return desc->if0_int_ep.bEndpointAddress; } static uint8_t cdc_ecm_get_bulk_in(struct usbd_class_data *const c_data) { struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data); const struct device *dev = usbd_class_get_private(c_data); struct cdc_ecm_eth_data *data = dev->data; struct usbd_cdc_ecm_desc *desc = data->desc; if (usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) { return desc->if1_1_hs_in_ep.bEndpointAddress; } return desc->if1_1_in_ep.bEndpointAddress; } static uint16_t cdc_ecm_get_bulk_in_mps(struct usbd_class_data *const c_data) { struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data); if (usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) { return 512U; } return 64U; } static uint8_t cdc_ecm_get_bulk_out(struct usbd_class_data *const c_data) { struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data); const struct device *dev = usbd_class_get_private(c_data); struct cdc_ecm_eth_data *data = dev->data; struct usbd_cdc_ecm_desc *desc = data->desc; if (usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) { return desc->if1_1_hs_out_ep.bEndpointAddress; } return desc->if1_1_out_ep.bEndpointAddress; } static struct net_buf *cdc_ecm_buf_alloc(const uint8_t ep) { struct net_buf *buf = NULL; struct udc_buf_info *bi; buf = net_buf_alloc(&cdc_ecm_ep_pool, K_NO_WAIT); if (!buf) { return NULL; } bi = udc_get_buf_info(buf); bi->ep = ep; return buf; } /* Retrieve expected pkt size from ethernet/ip header */ static size_t ecm_eth_size(void *const ecm_pkt, const size_t len) { uint8_t *ip_data = (uint8_t *)ecm_pkt + sizeof(struct net_eth_hdr); struct net_eth_hdr *hdr = (void *)ecm_pkt; uint16_t ip_len; if (len < NET_IPV6H_LEN + sizeof(struct net_eth_hdr)) { /* Too short */ return 0; } switch (ntohs(hdr->type)) { case NET_ETH_PTYPE_IP: __fallthrough; case NET_ETH_PTYPE_ARP: ip_len = ntohs(((struct net_ipv4_hdr *)ip_data)->len); break; case NET_ETH_PTYPE_IPV6: ip_len = ntohs(((struct net_ipv6_hdr *)ip_data)->len); break; default: LOG_DBG("Unknown hdr type 0x%04x", hdr->type); return 0; } return sizeof(struct net_eth_hdr) + ip_len; } static int cdc_ecm_out_start(struct usbd_class_data *const c_data) { const struct device *dev = usbd_class_get_private(c_data); struct cdc_ecm_eth_data *data = dev->data; struct net_buf *buf; uint8_t ep; int ret; if (atomic_test_and_set_bit(&data->state, CDC_ECM_OUT_ENGAGED)) { return -EBUSY; } ep = cdc_ecm_get_bulk_out(c_data); buf = cdc_ecm_buf_alloc(ep); if (buf == NULL) { return -ENOMEM; } ret = usbd_ep_enqueue(c_data, buf); if (ret) { LOG_ERR("Failed to enqueue net_buf for 0x%02x", ep); net_buf_unref(buf); } return ret; } static int cdc_ecm_acl_out_cb(struct usbd_class_data *const c_data, struct net_buf *const buf, const int err) { const struct device *dev = usbd_class_get_private(c_data); struct cdc_ecm_eth_data *data = dev->data; struct net_pkt *pkt; if (err || buf->len == 0) { if (err != -ECONNABORTED) { LOG_ERR("Bulk OUT transfer error (%d) or zero length", err); } goto restart_out_transfer; } /* Linux considers by default that network usb device controllers are * not able to handle Zero Length Packet (ZLP) and then generates * a short packet containing a null byte. Handle by checking the IP * header length and dropping the extra byte. */ if (buf->data[buf->len - 1] == 0U) { /* Last byte is null */ if (ecm_eth_size(buf->data, buf->len) == (buf->len - 1)) { /* last byte has been appended as delimiter, drop it */ net_buf_remove_u8(buf); } } pkt = net_pkt_rx_alloc_with_buffer(data->iface, buf->len, AF_UNSPEC, 0, K_FOREVER); if (!pkt) { LOG_ERR("No memory for net_pkt"); goto restart_out_transfer; } if (net_pkt_write(pkt, buf->data, buf->len)) { LOG_ERR("Unable to write into pkt"); net_pkt_unref(pkt); goto restart_out_transfer; } LOG_DBG("Received packet len %zu", net_pkt_get_len(pkt)); if (net_recv_data(data->iface, pkt) < 0) { LOG_ERR("Packet %p dropped by network stack", pkt); net_pkt_unref(pkt); } restart_out_transfer: net_buf_unref(buf); atomic_clear_bit(&data->state, CDC_ECM_OUT_ENGAGED); if (atomic_test_bit(&data->state, CDC_ECM_DATA_IFACE_ENABLED)) { return cdc_ecm_out_start(c_data); } return 0; } static int usbd_cdc_ecm_request(struct usbd_class_data *const c_data, struct net_buf *buf, int err) { struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data); const struct device *dev = usbd_class_get_private(c_data); struct cdc_ecm_eth_data *data = dev->data; struct udc_buf_info *bi; bi = udc_get_buf_info(buf); if (bi->ep == cdc_ecm_get_bulk_out(c_data)) { return cdc_ecm_acl_out_cb(c_data, buf, err); } if (bi->ep == cdc_ecm_get_bulk_in(c_data)) { k_sem_give(&data->sync_sem); return 0; } if (bi->ep == cdc_ecm_get_int_in(c_data)) { LOG_INF("Notification %s", err ? "cancelled or failed" : "sent"); } return usbd_ep_buf_free(uds_ctx, buf); } static int cdc_ecm_send_notification(const struct device *dev, const bool connected) { struct cdc_ecm_eth_data *data = dev->data; struct usbd_class_data *c_data = data->c_data; struct cdc_ecm_notification notification = { .RequestType = { .direction = USB_REQTYPE_DIR_TO_HOST, .type = USB_REQTYPE_TYPE_CLASS, .recipient = USB_REQTYPE_RECIPIENT_INTERFACE, }, .bNotificationType = USB_CDC_NETWORK_CONNECTION, .wValue = sys_cpu_to_le16((uint16_t)connected), .wIndex = sys_cpu_to_le16(cdc_ecm_get_ctrl_if(data)), .wLength = 0, }; struct net_buf *buf; uint8_t ep; int ret; if (!atomic_test_bit(&data->state, CDC_ECM_DATA_IFACE_ENABLED)) { LOG_INF("USB configuration is not enabled"); return 0; } if (atomic_test_bit(&data->state, CDC_ECM_CLASS_SUSPENDED)) { LOG_INF("USB device is suspended (FIXME)"); return 0; } ep = cdc_ecm_get_int_in(c_data); buf = usbd_ep_buf_alloc(c_data, ep, sizeof(struct cdc_ecm_notification)); if (buf == NULL) { return -ENOMEM; } net_buf_add_mem(buf, ¬ification, sizeof(struct cdc_ecm_notification)); ret = usbd_ep_enqueue(c_data, buf); if (ret) { LOG_ERR("Failed to enqueue net_buf for 0x%02x", ep); net_buf_unref(buf); return ret; } return 0; } static void usbd_cdc_ecm_update(struct usbd_class_data *const c_data, const uint8_t iface, const uint8_t alternate) { const struct device *dev = usbd_class_get_private(c_data); struct cdc_ecm_eth_data *data = dev->data; struct usbd_cdc_ecm_desc *desc = data->desc; const uint8_t data_iface = desc->if1_1.bInterfaceNumber; LOG_INF("New configuration, interface %u alternate %u", iface, alternate); if (data_iface == iface && alternate == 0) { atomic_clear_bit(&data->state, CDC_ECM_DATA_IFACE_ENABLED); net_if_carrier_off(data->iface); } if (data_iface == iface && alternate == 1) { atomic_set_bit(&data->state, CDC_ECM_DATA_IFACE_ENABLED); if (atomic_test_bit(&data->state, CDC_ECM_IFACE_UP)) { net_if_carrier_on(data->iface); if (cdc_ecm_send_notification(dev, true)) { LOG_ERR("Failed to send connected notification"); } } if (cdc_ecm_out_start(c_data)) { LOG_ERR("Failed to start OUT transfer"); } } } static void usbd_cdc_ecm_enable(struct usbd_class_data *const c_data) { LOG_INF("Enabled %s", c_data->name); } static void usbd_cdc_ecm_disable(struct usbd_class_data *const c_data) { const struct device *dev = usbd_class_get_private(c_data); struct cdc_ecm_eth_data *data = dev->data; atomic_clear_bit(&data->state, CDC_ECM_CLASS_SUSPENDED); LOG_INF("Disabled %s", c_data->name); } static void usbd_cdc_ecm_suspended(struct usbd_class_data *const c_data) { const struct device *dev = usbd_class_get_private(c_data); struct cdc_ecm_eth_data *data = dev->data; atomic_set_bit(&data->state, CDC_ECM_CLASS_SUSPENDED); } static void usbd_cdc_ecm_resumed(struct usbd_class_data *const c_data) { const struct device *dev = usbd_class_get_private(c_data); struct cdc_ecm_eth_data *data = dev->data; atomic_clear_bit(&data->state, CDC_ECM_CLASS_SUSPENDED); } static int usbd_cdc_ecm_ctd(struct usbd_class_data *const c_data, const struct usb_setup_packet *const setup, const struct net_buf *const buf) { if (setup->RequestType.recipient == USB_REQTYPE_RECIPIENT_INTERFACE && setup->bRequest == SET_ETHERNET_PACKET_FILTER) { LOG_INF("bRequest 0x%02x (SetPacketFilter) not implemented", setup->bRequest); return 0; } LOG_DBG("bmRequestType 0x%02x bRequest 0x%02x unsupported", setup->bmRequestType, setup->bRequest); errno = -ENOTSUP; return 0; } static int usbd_cdc_ecm_init(struct usbd_class_data *const c_data) { struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data); const struct device *dev = usbd_class_get_private(c_data); struct cdc_ecm_eth_data *const data = dev->data; struct usbd_cdc_ecm_desc *desc = data->desc; const uint8_t if_num = desc->if0.bInterfaceNumber; /* Update relevant b*Interface fields */ desc->if0_union.bControlInterface = if_num; desc->if0_union.bSubordinateInterface0 = if_num + 1; LOG_DBG("CDC ECM class initialized"); if (usbd_add_descriptor(uds_ctx, data->mac_desc_data)) { LOG_ERR("Failed to add iMACAddress string descriptor"); } else { desc->if0_ecm.iMACAddress = usbd_str_desc_get_idx(data->mac_desc_data); } return 0; } static void usbd_cdc_ecm_shutdown(struct usbd_class_data *const c_data) { const struct device *dev = usbd_class_get_private(c_data); struct cdc_ecm_eth_data *const data = dev->data; struct usbd_cdc_ecm_desc *desc = data->desc; desc->if0_ecm.iMACAddress = 0; sys_dlist_remove(&data->mac_desc_data->node); } static void *usbd_cdc_ecm_get_desc(struct usbd_class_data *const c_data, const enum usbd_speed speed) { const struct device *dev = usbd_class_get_private(c_data); struct cdc_ecm_eth_data *const data = dev->data; if (speed == USBD_SPEED_HS) { return data->hs_desc; } return data->fs_desc; } static int cdc_ecm_send(const struct device *dev, struct net_pkt *const pkt) { struct cdc_ecm_eth_data *const data = dev->data; struct usbd_class_data *c_data = data->c_data; size_t len = net_pkt_get_len(pkt); struct net_buf *buf; if (len > NET_ETH_MAX_FRAME_SIZE) { LOG_WRN("Trying to send too large packet, drop"); return -ENOMEM; } if (!atomic_test_bit(&data->state, CDC_ECM_DATA_IFACE_ENABLED) || !atomic_test_bit(&data->state, CDC_ECM_IFACE_UP)) { LOG_INF("Configuration is not enabled or interface not ready"); return -EACCES; } buf = cdc_ecm_buf_alloc(cdc_ecm_get_bulk_in(c_data)); if (buf == NULL) { LOG_ERR("Failed to allocate buffer"); return -ENOMEM; } if (net_pkt_read(pkt, buf->data, len)) { LOG_ERR("Failed copy net_pkt"); net_buf_unref(buf); return -ENOBUFS; } net_buf_add(buf, len); if (!(buf->len % cdc_ecm_get_bulk_in_mps(c_data))) { udc_ep_buf_set_zlp(buf); } usbd_ep_enqueue(c_data, buf); k_sem_take(&data->sync_sem, K_FOREVER); net_buf_unref(buf); return 0; } static int cdc_ecm_set_config(const struct device *dev, const enum ethernet_config_type type, const struct ethernet_config *config) { struct cdc_ecm_eth_data *data = dev->data; if (type == ETHERNET_CONFIG_TYPE_MAC_ADDRESS) { memcpy(data->mac_addr, config->mac_address.addr, sizeof(data->mac_addr)); return 0; } return -ENOTSUP; } static int cdc_ecm_get_config(const struct device *dev, enum ethernet_config_type type, struct ethernet_config *config) { return -ENOTSUP; } static enum ethernet_hw_caps cdc_ecm_get_capabilities(const struct device *dev) { ARG_UNUSED(dev); return ETHERNET_LINK_10BASE_T; } static int cdc_ecm_iface_start(const struct device *dev) { struct cdc_ecm_eth_data *data = dev->data; LOG_DBG("Start interface %p", data->iface); atomic_set_bit(&data->state, CDC_ECM_IFACE_UP); if (atomic_test_bit(&data->state, CDC_ECM_DATA_IFACE_ENABLED)) { net_if_carrier_on(data->iface); if (cdc_ecm_send_notification(dev, true)) { LOG_ERR("Failed to send connected notification"); } } return 0; } static int cdc_ecm_iface_stop(const struct device *dev) { struct cdc_ecm_eth_data *data = dev->data; LOG_DBG("Stop interface %p", data->iface); atomic_clear_bit(&data->state, CDC_ECM_IFACE_UP); if (atomic_test_bit(&data->state, CDC_ECM_DATA_IFACE_ENABLED)) { if (cdc_ecm_send_notification(dev, false)) { LOG_ERR("Failed to send disconnected notification"); } } return 0; } static void cdc_ecm_iface_init(struct net_if *const iface) { const struct device *dev = net_if_get_device(iface); struct cdc_ecm_eth_data *data = dev->data; data->iface = iface; ethernet_init(iface); net_if_set_link_addr(iface, data->mac_addr, sizeof(data->mac_addr), NET_LINK_ETHERNET); net_if_carrier_off(iface); LOG_DBG("CDC ECM interface initialized"); } static int usbd_cdc_ecm_preinit(const struct device *dev) { struct cdc_ecm_eth_data *data = dev->data; if (sys_get_le48(data->mac_addr) == sys_cpu_to_le48(0)) { gen_random_mac(data->mac_addr, 0, 0, 0); } LOG_DBG("CDC ECM device initialized"); return 0; } static struct usbd_class_api usbd_cdc_ecm_api = { .request = usbd_cdc_ecm_request, .update = usbd_cdc_ecm_update, .enable = usbd_cdc_ecm_enable, .disable = usbd_cdc_ecm_disable, .suspended = usbd_cdc_ecm_suspended, .resumed = usbd_cdc_ecm_resumed, .control_to_dev = usbd_cdc_ecm_ctd, .init = usbd_cdc_ecm_init, .shutdown = usbd_cdc_ecm_shutdown, .get_desc = usbd_cdc_ecm_get_desc, }; static const struct ethernet_api cdc_ecm_eth_api = { .iface_api.init = cdc_ecm_iface_init, .get_config = cdc_ecm_get_config, .set_config = cdc_ecm_set_config, .get_capabilities = cdc_ecm_get_capabilities, .send = cdc_ecm_send, .start = cdc_ecm_iface_start, .stop = cdc_ecm_iface_stop, }; #define CDC_ECM_DEFINE_DESCRIPTOR(n) \ static struct usbd_cdc_ecm_desc cdc_ecm_desc_##n = { \ .iad = { \ .bLength = sizeof(struct usb_association_descriptor), \ .bDescriptorType = USB_DESC_INTERFACE_ASSOC, \ .bFirstInterface = 0, \ .bInterfaceCount = 0x02, \ .bFunctionClass = USB_BCC_CDC_CONTROL, \ .bFunctionSubClass = ECM_SUBCLASS, \ .bFunctionProtocol = 0, \ .iFunction = 0, \ }, \ \ .if0 = { \ .bLength = sizeof(struct usb_if_descriptor), \ .bDescriptorType = USB_DESC_INTERFACE, \ .bInterfaceNumber = 0, \ .bAlternateSetting = 0, \ .bNumEndpoints = 1, \ .bInterfaceClass = USB_BCC_CDC_CONTROL, \ .bInterfaceSubClass = ECM_SUBCLASS, \ .bInterfaceProtocol = 0, \ .iInterface = 0, \ }, \ \ .if0_header = { \ .bFunctionLength = sizeof(struct cdc_header_descriptor), \ .bDescriptorType = USB_DESC_CS_INTERFACE, \ .bDescriptorSubtype = HEADER_FUNC_DESC, \ .bcdCDC = sys_cpu_to_le16(USB_SRN_1_1), \ }, \ \ .if0_union = { \ .bFunctionLength = sizeof(struct cdc_union_descriptor), \ .bDescriptorType = USB_DESC_CS_INTERFACE, \ .bDescriptorSubtype = UNION_FUNC_DESC, \ .bControlInterface = 0, \ .bSubordinateInterface0 = 1, \ }, \ \ .if0_ecm = { \ .bFunctionLength = sizeof(struct cdc_ecm_descriptor), \ .bDescriptorType = USB_DESC_CS_INTERFACE, \ .bDescriptorSubtype = ETHERNET_FUNC_DESC, \ .iMACAddress = 0, \ .bmEthernetStatistics = sys_cpu_to_le32(0), \ .wMaxSegmentSize = sys_cpu_to_le16(NET_ETH_MAX_FRAME_SIZE), \ .wNumberMCFilters = sys_cpu_to_le16(0), \ .bNumberPowerFilters = 0, \ }, \ \ .if0_int_ep = { \ .bLength = sizeof(struct usb_ep_descriptor), \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = 0x81, \ .bmAttributes = USB_EP_TYPE_INTERRUPT, \ .wMaxPacketSize = sys_cpu_to_le16(CDC_ECM_EP_MPS_INT), \ .bInterval = CDC_ECM_FS_INT_EP_INTERVAL, \ }, \ \ .if0_hs_int_ep = { \ .bLength = sizeof(struct usb_ep_descriptor), \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = 0x81, \ .bmAttributes = USB_EP_TYPE_INTERRUPT, \ .wMaxPacketSize = sys_cpu_to_le16(CDC_ECM_EP_MPS_INT), \ .bInterval = CDC_ECM_HS_INT_EP_INTERVAL, \ }, \ \ .if1_0 = { \ .bLength = sizeof(struct usb_if_descriptor), \ .bDescriptorType = USB_DESC_INTERFACE, \ .bInterfaceNumber = 1, \ .bAlternateSetting = 0, \ .bNumEndpoints = 0, \ .bInterfaceClass = USB_BCC_CDC_DATA, \ .bInterfaceSubClass = 0, \ .bInterfaceProtocol = 0, \ .iInterface = 0, \ }, \ \ .if1_1 = { \ .bLength = sizeof(struct usb_if_descriptor), \ .bDescriptorType = USB_DESC_INTERFACE, \ .bInterfaceNumber = 1, \ .bAlternateSetting = 1, \ .bNumEndpoints = 2, \ .bInterfaceClass = USB_BCC_CDC_DATA, \ .bInterfaceSubClass = ECM_SUBCLASS, \ .bInterfaceProtocol = 0, \ .iInterface = 0, \ }, \ \ .if1_1_in_ep = { \ .bLength = sizeof(struct usb_ep_descriptor), \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = 0x82, \ .bmAttributes = USB_EP_TYPE_BULK, \ .wMaxPacketSize = sys_cpu_to_le16(64U), \ .bInterval = 0, \ }, \ \ .if1_1_out_ep = { \ .bLength = sizeof(struct usb_ep_descriptor), \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = 0x01, \ .bmAttributes = USB_EP_TYPE_BULK, \ .wMaxPacketSize = sys_cpu_to_le16(64U), \ .bInterval = 0, \ }, \ \ .if1_1_hs_in_ep = { \ .bLength = sizeof(struct usb_ep_descriptor), \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = 0x82, \ .bmAttributes = USB_EP_TYPE_BULK, \ .wMaxPacketSize = sys_cpu_to_le16(512U), \ .bInterval = 0, \ }, \ \ .if1_1_hs_out_ep = { \ .bLength = sizeof(struct usb_ep_descriptor), \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = 0x01, \ .bmAttributes = USB_EP_TYPE_BULK, \ .wMaxPacketSize = sys_cpu_to_le16(512U), \ .bInterval = 0, \ }, \ \ .nil_desc = { \ .bLength = 0, \ .bDescriptorType = 0, \ }, \ }; \ \ const static struct usb_desc_header *cdc_ecm_fs_desc_##n[] = { \ (struct usb_desc_header *) &cdc_ecm_desc_##n.iad, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if0, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if0_header, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if0_union, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if0_ecm, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if0_int_ep, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if1_0, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if1_1, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if1_1_in_ep, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if1_1_out_ep, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.nil_desc, \ }; \ \ const static struct usb_desc_header *cdc_ecm_hs_desc_##n[] = { \ (struct usb_desc_header *) &cdc_ecm_desc_##n.iad, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if0, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if0_header, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if0_union, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if0_ecm, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if0_hs_int_ep, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if1_0, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if1_1, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if1_1_hs_in_ep, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.if1_1_hs_out_ep, \ (struct usb_desc_header *) &cdc_ecm_desc_##n.nil_desc, \ } #define USBD_CDC_ECM_DT_DEVICE_DEFINE(n) \ CDC_ECM_DEFINE_DESCRIPTOR(n); \ USBD_DESC_STRING_DEFINE(mac_desc_data_##n, \ DT_INST_PROP(n, remote_mac_address), \ USBD_DUT_STRING_INTERFACE); \ \ USBD_DEFINE_CLASS(cdc_ecm_##n, \ &usbd_cdc_ecm_api, \ (void *)DEVICE_DT_GET(DT_DRV_INST(n)), NULL); \ \ static struct cdc_ecm_eth_data eth_data_##n = { \ .c_data = &cdc_ecm_##n, \ .mac_addr = DT_INST_PROP_OR(n, local_mac_address, {0}), \ .sync_sem = Z_SEM_INITIALIZER(eth_data_##n.sync_sem, 0, 1), \ .mac_desc_data = &mac_desc_data_##n, \ .desc = &cdc_ecm_desc_##n, \ .fs_desc = cdc_ecm_fs_desc_##n, \ .hs_desc = cdc_ecm_hs_desc_##n, \ }; \ \ ETH_NET_DEVICE_DT_INST_DEFINE(n, usbd_cdc_ecm_preinit, NULL, \ ð_data_##n, NULL, \ CONFIG_ETH_INIT_PRIORITY, \ &cdc_ecm_eth_api, \ NET_ETH_MTU); DT_INST_FOREACH_STATUS_OKAY(USBD_CDC_ECM_DT_DEVICE_DEFINE);