/* * Copyright (c) 2026, 54fire * * SPDX-License-Identifier: Apache-2.0 */ #include "usbd_core.h" #include "usbd_cdc_ncm.h" #ifndef CONFIG_USBDEV_CDC_NCM_USING_LWIP #error "Please enable CONFIG_USBDEV_CDC_NCM_USING_LWIP for this demo" #endif /*!< endpoint address */ #define CDC_IN_EP 0x81 #define CDC_OUT_EP 0x02 #define CDC_INT_EP 0x83 #define USBD_VID 0xFFFF #define USBD_PID 0xFFFF #define USBD_MAX_POWER 100 #define USBD_LANGID_STRING 1033 /*!< config descriptor size */ #define USB_CONFIG_SIZE (9 + CDC_NCM_DESCRIPTOR_LEN) /* str idx = 4 is for mac address: aa:bb:cc:dd:ee:ff*/ #define CDC_NCM_MAC_STRING_INDEX 4 static const uint8_t device_descriptor[] = { USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0xEF, 0x02, 0x01, USBD_VID, USBD_PID, 0x0100, 0x01) }; static const uint8_t config_descriptor_hs[] = { USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x02, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER), CDC_NCM_DESCRIPTOR_INIT(0x00, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, USB_BULK_EP_MPS_HS, CDC_NCM_MAC_STRING_INDEX) }; static const uint8_t config_descriptor_fs[] = { USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x02, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER), CDC_NCM_DESCRIPTOR_INIT(0x00, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, USB_BULK_EP_MPS_FS, CDC_NCM_MAC_STRING_INDEX) }; static const uint8_t device_quality_descriptor[] = { USB_DEVICE_QUALIFIER_DESCRIPTOR_INIT(USB_2_0, 0xEF, 0x02, 0x01, 0x01), }; static const uint8_t other_speed_config_descriptor_hs[] = { USB_OTHER_SPEED_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x02, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER), CDC_NCM_DESCRIPTOR_INIT(0x00, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, USB_BULK_EP_MPS_FS, CDC_NCM_MAC_STRING_INDEX) }; static const uint8_t other_speed_config_descriptor_fs[] = { USB_OTHER_SPEED_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x02, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER), CDC_NCM_DESCRIPTOR_INIT(0x00, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, USB_BULK_EP_MPS_HS, CDC_NCM_MAC_STRING_INDEX) }; static const char *string_descriptors[] = { (const char[]){ 0x09, 0x04 }, /* Langid */ "CherryUSB", /* Manufacturer */ "CherryUSB CDC NCM DEMO", /* Product */ "2026123456", /* Serial Number */ "aabbccddeeff", /* ncm mac address */ }; static const uint8_t *device_descriptor_callback(uint8_t speed) { (void)speed; return device_descriptor; } static const uint8_t *config_descriptor_callback(uint8_t speed) { if (speed == USB_SPEED_HIGH) { return config_descriptor_hs; } else if (speed == USB_SPEED_FULL) { return config_descriptor_fs; } else { return NULL; } } static const uint8_t *device_quality_descriptor_callback(uint8_t speed) { (void)speed; return device_quality_descriptor; } static const uint8_t *other_speed_config_descriptor_callback(uint8_t speed) { if (speed == USB_SPEED_HIGH) { return other_speed_config_descriptor_hs; } else if (speed == USB_SPEED_FULL) { return other_speed_config_descriptor_fs; } else { return NULL; } } static const char *string_descriptor_callback(uint8_t speed, uint8_t index) { (void)speed; if (index >= (sizeof(string_descriptors) / sizeof(char *))) { return NULL; } return string_descriptors[index]; } const struct usb_descriptor cdc_ncm_descriptor = { .device_descriptor_callback = device_descriptor_callback, .config_descriptor_callback = config_descriptor_callback, .device_quality_descriptor_callback = device_quality_descriptor_callback, .other_speed_descriptor_callback = other_speed_config_descriptor_callback, .string_descriptor_callback = string_descriptor_callback }; uint8_t mac[6] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; #ifdef RT_USING_LWIP #ifndef RT_LWIP_DHCP #error cdc_ncm must enable RT_LWIP_DHCP #endif #ifndef LWIP_USING_DHCPD #error cdc_ncm must enable LWIP_USING_DHCPD #endif #include #include #include #include #define CDC_NCM_TX_TIMEOUT_MS 1000 struct eth_device cdc_ncm_dev; static struct rt_semaphore cdc_ncm_tx_sem; void usbd_cdc_ncm_data_send_done(uint32_t len) { (void)len; rt_sem_release(&cdc_ncm_tx_sem); } static rt_err_t rt_usbd_cdc_ncm_control(rt_device_t dev, int cmd, void *args) { switch (cmd) { case NIOCTL_GADDR: /* get mac address */ if (args) { uint8_t *mac_dev = (uint8_t *)args; rt_memcpy(mac_dev, mac, 6); mac_dev[5] = ~mac_dev[5]; /* device mac can't same as host. */ } else return -RT_ERROR; break; default: break; } return RT_EOK; } struct pbuf *rt_usbd_cdc_ncm_eth_rx(rt_device_t dev) { return usbd_cdc_ncm_eth_rx(); } rt_err_t rt_usbd_cdc_ncm_eth_tx(rt_device_t dev, struct pbuf *p) { int ret; ret = usbd_cdc_ncm_eth_tx(p); if (ret == 0) { if (rt_sem_take(&cdc_ncm_tx_sem, rt_tick_from_millisecond(CDC_NCM_TX_TIMEOUT_MS)) != RT_EOK) { return -RT_ETIMEOUT; } return RT_EOK; } else return -RT_ERROR; } void cdc_ncm_lwip_init(void) { rt_sem_init(&cdc_ncm_tx_sem, "ncm_tx", 0, RT_IPC_FLAG_PRIO); cdc_ncm_dev.parent.control = rt_usbd_cdc_ncm_control; cdc_ncm_dev.eth_rx = rt_usbd_cdc_ncm_eth_rx; cdc_ncm_dev.eth_tx = rt_usbd_cdc_ncm_eth_tx; eth_device_init(&cdc_ncm_dev, "u0"); eth_device_linkchange(&cdc_ncm_dev, RT_TRUE); dhcpd_start("u0"); } void usbd_cdc_ncm_data_recv_done(uint32_t len) { eth_device_ready(&cdc_ncm_dev); } #else #include "netif/etharp.h" #include "lwip/init.h" #include "lwip/netif.h" #include "lwip/pbuf.h" #include "dhserver.h" #include "dnserver.h" /* rough bounded wait for the polled demo; in a real application use an os semaphore with timeout */ #define CDC_NCM_TX_TIMEOUT_LOOPS 1000000UL volatile bool cdc_ncm_tx_done = false; void usbd_cdc_ncm_data_send_done(uint32_t len) { (void)len; cdc_ncm_tx_done = true; } /*Static IP ADDRESS: IP_ADDR0.IP_ADDR1.IP_ADDR2.IP_ADDR3 */ #define IP_ADDR0 (uint8_t)192 #define IP_ADDR1 (uint8_t)168 #define IP_ADDR2 (uint8_t)7 #define IP_ADDR3 (uint8_t)1 /*NETMASK*/ #define NETMASK_ADDR0 (uint8_t)255 #define NETMASK_ADDR1 (uint8_t)255 #define NETMASK_ADDR2 (uint8_t)255 #define NETMASK_ADDR3 (uint8_t)0 /*Gateway Address*/ #define GW_ADDR0 (uint8_t)0 #define GW_ADDR1 (uint8_t)0 #define GW_ADDR2 (uint8_t)0 #define GW_ADDR3 (uint8_t)0 const ip_addr_t ipaddr = IPADDR4_INIT_BYTES(IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3); const ip_addr_t netmask = IPADDR4_INIT_BYTES(NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3); const ip_addr_t gateway = IPADDR4_INIT_BYTES(GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3); #define NUM_DHCP_ENTRY 3 static dhcp_entry_t entries[NUM_DHCP_ENTRY] = { /* mac ip address subnet mask lease time */ { { 0 }, { 192, 168, 7, 2 }, { 255, 255, 255, 0 }, 24 * 60 * 60 }, { { 0 }, { 192, 168, 7, 3 }, { 255, 255, 255, 0 }, 24 * 60 * 60 }, { { 0 }, { 192, 168, 7, 4 }, { 255, 255, 255, 0 }, 24 * 60 * 60 } }; static dhcp_config_t dhcp_config = { { 192, 168, 7, 1 }, /* server address */ 67, /* port */ { 192, 168, 7, 1 }, /* dns server */ "cherry", /* dns suffix */ NUM_DHCP_ENTRY, /* num entry */ entries /* entries */ }; static bool dns_query_proc(const char *name, ip_addr_t *addr) { if (strcmp(name, "cdc_ncm.cherry") == 0 || strcmp(name, "www.cdc_ncm.cherry") == 0) { addr->addr = ipaddr.addr; return true; } return false; } static struct netif cdc_ncm_netif; //network interface /* Network interface name */ #define IFNAME0 'N' #define IFNAME1 'X' err_t linkoutput_fn(struct netif *netif, struct pbuf *p) { int ret; uint32_t timeout = CDC_NCM_TX_TIMEOUT_LOOPS; cdc_ncm_tx_done = false; ret = usbd_cdc_ncm_eth_tx(p); if (ret == 0) { while (!cdc_ncm_tx_done) { if (--timeout == 0) { return ERR_TIMEOUT; } } return ERR_OK; } else return ERR_BUF; } err_t 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] = IFNAME0; netif->name[1] = IFNAME1; netif->output = etharp_output; netif->linkoutput = linkoutput_fn; return ERR_OK; } err_t cdc_ncm_if_input(struct netif *netif) { err_t err; struct pbuf *p; p = usbd_cdc_ncm_eth_rx(); if (p != NULL) { err = netif->input(p, netif); if (err != ERR_OK) { pbuf_free(p); } } else { return ERR_BUF; } return err; } void cdc_ncm_lwip_init(void) { struct netif *netif = &cdc_ncm_netif; lwip_init(); netif->hwaddr_len = 6; memcpy(netif->hwaddr, mac, 6); netif->hwaddr[5] = ~netif->hwaddr[5]; /* device mac can't same as host. */ netif = netif_add(netif, &ipaddr, &netmask, &gateway, NULL, cdc_ncm_if_init, netif_input); netif_set_default(netif); while (!netif_is_up(netif)) { } while (dhserv_init(&dhcp_config)) { } while (dnserv_init(IP_ADDR_ANY, 53, dns_query_proc)) { } } void usbd_cdc_ncm_data_recv_done(uint32_t len) { } void cdc_ncm_input_poll(void) { cdc_ncm_if_input(&cdc_ncm_netif); } #endif static void usbd_event_handler(uint8_t busid, uint8_t event) { switch (event) { case USBD_EVENT_RESET: break; case USBD_EVENT_CONNECTED: break; case USBD_EVENT_DISCONNECTED: break; case USBD_EVENT_RESUME: break; case USBD_EVENT_SUSPEND: break; case USBD_EVENT_CONFIGURED: break; case USBD_EVENT_SET_REMOTE_WAKEUP: break; case USBD_EVENT_CLR_REMOTE_WAKEUP: break; default: break; } } struct usbd_interface intf0; struct usbd_interface intf1; /* ncm is supported in linux, windows and macos. In linux, you should input the following command * * sudo ifconfig enxaabbccddeeff up * sudo dhclient enxaabbccddeeff */ void cdc_ncm_init(uint8_t busid, uintptr_t reg_base) { cdc_ncm_lwip_init(); usbd_desc_register(busid, &cdc_ncm_descriptor); usbd_add_interface(busid, usbd_cdc_ncm_init_intf(&intf0, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, mac)); usbd_add_interface(busid, usbd_cdc_ncm_init_intf(&intf1, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, mac)); usbd_initialize(busid, reg_base, usbd_event_handler); }