1 /*
2  * Copyright (c) 2024, sakumisu
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include "usbd_core.h"
7 #include "usbd_cdc_ecm.h"
8 
9 #ifndef CONFIG_USBDEV_CDC_ECM_USING_LWIP
10 #error "Please enable CONFIG_USBDEV_CDC_ECM_USING_LWIP for this demo"
11 #endif
12 
13 /*!< endpoint address */
14 #define CDC_IN_EP  0x81
15 #define CDC_OUT_EP 0x02
16 #define CDC_INT_EP 0x83
17 
18 #define USBD_VID           0xFFFF
19 #define USBD_PID           0xFFFF
20 #define USBD_MAX_POWER     100
21 #define USBD_LANGID_STRING 1033
22 
23 /*!< config descriptor size */
24 #define USB_CONFIG_SIZE (9 + CDC_ECM_DESCRIPTOR_LEN)
25 
26 #ifdef CONFIG_USB_HS
27 #define CDC_MAX_MPS 512
28 #else
29 #define CDC_MAX_MPS 64
30 #endif
31 
32 #define CDC_ECM_ETH_STATISTICS_BITMAP 0x00000000
33 
34 /* str idx = 4 is for mac address: aa:bb:cc:dd:ee:ff*/
35 #define CDC_ECM_MAC_STRING_INDEX 4
36 
37 /* Ethernet Maximum Segment size, typically 1514 bytes */
38 #define CONFIG_CDC_ECM_ETH_MAX_SEGSZE 1514U
39 
40 #ifdef CONFIG_USBDEV_ADVANCE_DESC
41 static const uint8_t device_descriptor[] = {
42     USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0xEF, 0x02, 0x01, USBD_VID, USBD_PID, 0x0100, 0x01)
43 };
44 
45 static const uint8_t config_descriptor[] = {
46     USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x02, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
47     CDC_ECM_DESCRIPTOR_INIT(0x00, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, CDC_MAX_MPS, CDC_ECM_ETH_STATISTICS_BITMAP, CONFIG_CDC_ECM_ETH_MAX_SEGSZE, 0, 0, CDC_ECM_MAC_STRING_INDEX)
48 };
49 
50 static const uint8_t device_quality_descriptor[] = {
51     ///////////////////////////////////////
52     /// device qualifier descriptor
53     ///////////////////////////////////////
54     0x0a,
55     USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER,
56     0x00,
57     0x02,
58     0x00,
59     0x00,
60     0x00,
61     0x40,
62     0x00,
63     0x00,
64 };
65 
66 static const char *string_descriptors[] = {
67     (const char[]){ 0x09, 0x04 }, /* Langid */
68     "CherryUSB",                  /* Manufacturer */
69     "CherryUSB CDC ECM DEMO",     /* Product */
70     "2022123456",                 /* Serial Number */
71 };
72 
device_descriptor_callback(uint8_t speed)73 static const uint8_t *device_descriptor_callback(uint8_t speed)
74 {
75     return device_descriptor;
76 }
77 
config_descriptor_callback(uint8_t speed)78 static const uint8_t *config_descriptor_callback(uint8_t speed)
79 {
80     return config_descriptor;
81 }
82 
device_quality_descriptor_callback(uint8_t speed)83 static const uint8_t *device_quality_descriptor_callback(uint8_t speed)
84 {
85     return device_quality_descriptor;
86 }
87 
string_descriptor_callback(uint8_t speed,uint8_t index)88 static const char *string_descriptor_callback(uint8_t speed, uint8_t index)
89 {
90     if (index > 3) {
91         return NULL;
92     }
93     return string_descriptors[index];
94 }
95 
96 const struct usb_descriptor cdc_ecm_descriptor = {
97     .device_descriptor_callback = device_descriptor_callback,
98     .config_descriptor_callback = config_descriptor_callback,
99     .device_quality_descriptor_callback = device_quality_descriptor_callback,
100     .string_descriptor_callback = string_descriptor_callback
101 };
102 #else
103 /*!< global descriptor */
104 static const uint8_t cdc_ecm_descriptor[] = {
105     USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0xEF, 0x02, 0x01, USBD_VID, USBD_PID, 0x0100, 0x01),
106     USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x02, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
107     CDC_ECM_DESCRIPTOR_INIT(0x00, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, CDC_MAX_MPS, CDC_ECM_ETH_STATISTICS_BITMAP, CONFIG_CDC_ECM_ETH_MAX_SEGSZE, 0, 0, CDC_ECM_MAC_STRING_INDEX),
108     ///////////////////////////////////////
109     /// string0 descriptor
110     ///////////////////////////////////////
111     USB_LANGID_INIT(USBD_LANGID_STRING),
112     ///////////////////////////////////////
113     /// string1 descriptor
114     ///////////////////////////////////////
115     0x14,                       /* bLength */
116     USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
117     'C', 0x00,                  /* wcChar0 */
118     'h', 0x00,                  /* wcChar1 */
119     'e', 0x00,                  /* wcChar2 */
120     'r', 0x00,                  /* wcChar3 */
121     'r', 0x00,                  /* wcChar4 */
122     'y', 0x00,                  /* wcChar5 */
123     'U', 0x00,                  /* wcChar6 */
124     'S', 0x00,                  /* wcChar7 */
125     'B', 0x00,                  /* wcChar8 */
126     ///////////////////////////////////////
127     /// string2 descriptor
128     ///////////////////////////////////////
129     0x2E,                       /* bLength */
130     USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
131     'C', 0x00,                  /* wcChar0 */
132     'h', 0x00,                  /* wcChar1 */
133     'e', 0x00,                  /* wcChar2 */
134     'r', 0x00,                  /* wcChar3 */
135     'r', 0x00,                  /* wcChar4 */
136     'y', 0x00,                  /* wcChar5 */
137     'U', 0x00,                  /* wcChar6 */
138     'S', 0x00,                  /* wcChar7 */
139     'B', 0x00,                  /* wcChar8 */
140     ' ', 0x00,                  /* wcChar9 */
141     'C', 0x00,                  /* wcChar10 */
142     'D', 0x00,                  /* wcChar11 */
143     'C', 0x00,                  /* wcChar12 */
144     ' ', 0x00,                  /* wcChar13 */
145     'E', 0x00,                  /* wcChar14 */
146     'C', 0x00,                  /* wcChar15 */
147     'M', 0x00,                  /* wcChar16 */
148     ' ', 0x00,                  /* wcChar17 */
149     'D', 0x00,                  /* wcChar18 */
150     'E', 0x00,                  /* wcChar19 */
151     'M', 0x00,                  /* wcChar20 */
152     'O', 0x00,                  /* wcChar21 */
153     ///////////////////////////////////////
154     /// string3 descriptor
155     ///////////////////////////////////////
156     0x16,                       /* bLength */
157     USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
158     '2', 0x00,                  /* wcChar0 */
159     '0', 0x00,                  /* wcChar1 */
160     '2', 0x00,                  /* wcChar2 */
161     '2', 0x00,                  /* wcChar3 */
162     '1', 0x00,                  /* wcChar4 */
163     '2', 0x00,                  /* wcChar5 */
164     '3', 0x00,                  /* wcChar6 */
165     '4', 0x00,                  /* wcChar7 */
166     '5', 0x00,                  /* wcChar8 */
167     '6', 0x00,                  /* wcChar9 */
168     ///////////////////////////////////////
169     /// string4 descriptor
170     ///////////////////////////////////////
171     0x1A,                       /* bLength */
172     USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
173     'a', 0x00,                  /* wcChar0 */
174     'a', 0x00,                  /* wcChar1 */
175     'b', 0x00,                  /* wcChar2 */
176     'b', 0x00,                  /* wcChar3 */
177     'c', 0x00,                  /* wcChar4 */
178     'c', 0x00,                  /* wcChar5 */
179     'd', 0x00,                  /* wcChar6 */
180     'd', 0x00,                  /* wcChar7 */
181     'e', 0x00,                  /* wcChar8 */
182     'e', 0x00,                  /* wcChar9 */
183     'f', 0x00,                  /* wcChar10 */
184     'f', 0x00,                  /* wcChar11 */
185 #ifdef CONFIG_USB_HS
186     ///////////////////////////////////////
187     /// device qualifier descriptor
188     ///////////////////////////////////////
189     0x0a,
190     USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER,
191     0x00,
192     0x02,
193     0x00,
194     0x00,
195     0x00,
196     0x40,
197     0x00,
198     0x00,
199 #endif
200     0x00
201 };
202 #endif
203 
204 const uint8_t mac[6] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
205 
206 volatile bool cdc_ecm_tx_done = false;
207 
usbd_cdc_ecm_data_send_done(uint32_t len)208 void usbd_cdc_ecm_data_send_done(uint32_t len)
209 {
210     cdc_ecm_tx_done = true; // suggest you to use semaphore in os
211 }
212 
213 #ifdef RT_USING_LWIP
214 
215 #ifndef RT_LWIP_DHCP
216 #error cdc_ecm must enable RT_LWIP_DHCP
217 #endif
218 
219 #ifndef LWIP_USING_DHCPD
220 #error cdc_ecm must enable LWIP_USING_DHCPD
221 #endif
222 
223 #include <rtthread.h>
224 #include <rtdevice.h>
225 #include <netif/ethernetif.h>
226 #include <dhcp_server.h>
227 
228 struct eth_device cdc_ecm_dev;
229 
rt_usbd_cdc_ecm_control(rt_device_t dev,int cmd,void * args)230 static rt_err_t rt_usbd_cdc_ecm_control(rt_device_t dev, int cmd, void *args)
231 {
232     switch (cmd) {
233         case NIOCTL_GADDR:
234 
235             /* get mac address */
236             if (args) {
237                 uint8_t *mac_dev = (uint8_t *)args;
238                 rt_memcpy(mac_dev, mac, 6);
239                 mac_dev[5] = ~mac_dev[5]; /* device mac can't same as host. */
240             } else
241                 return -RT_ERROR;
242 
243             break;
244 
245         default:
246             break;
247     }
248 
249     return RT_EOK;
250 }
251 
rt_usbd_cdc_ecm_eth_rx(rt_device_t dev)252 struct pbuf *rt_usbd_cdc_ecm_eth_rx(rt_device_t dev)
253 {
254     return usbd_cdc_ecm_eth_rx();
255 }
256 
rt_usbd_cdc_ecm_eth_tx(rt_device_t dev,struct pbuf * p)257 rt_err_t rt_usbd_cdc_ecm_eth_tx(rt_device_t dev, struct pbuf *p)
258 {
259     int ret;
260 
261     cdc_ecm_tx_done = false;
262     ret = usbd_cdc_ecm_eth_tx(p);
263     if (ret == 0) {
264         while (!cdc_ecm_tx_done) {
265         }
266         return RT_EOK;
267     } else
268         return -RT_ERROR;
269 }
270 
cdc_ecm_lwip_init(void)271 void cdc_ecm_lwip_init(void)
272 {
273     cdc_ecm_dev.parent.control = rt_usbd_cdc_ecm_control;
274     cdc_ecm_dev.eth_rx = rt_usbd_cdc_ecm_eth_rx;
275     cdc_ecm_dev.eth_tx = rt_usbd_cdc_ecm_eth_tx;
276 
277     eth_device_init(&cdc_ecm_dev, "u0");
278 
279     eth_device_linkchange(&cdc_ecm_dev, RT_TRUE);
280     dhcpd_start("u0");
281 }
282 
usbd_cdc_ecm_data_recv_done(uint32_t len)283 void usbd_cdc_ecm_data_recv_done(uint32_t len)
284 {
285     eth_device_ready(&cdc_ecm_dev);
286 }
287 
288 #else
289 #include "netif/etharp.h"
290 #include "lwip/init.h"
291 #include "lwip/netif.h"
292 #include "lwip/pbuf.h"
293 
294 #include "dhserver.h"
295 #include "dnserver.h"
296 
297 /*Static IP ADDRESS: IP_ADDR0.IP_ADDR1.IP_ADDR2.IP_ADDR3 */
298 #define IP_ADDR0      (uint8_t)192
299 #define IP_ADDR1      (uint8_t)168
300 #define IP_ADDR2      (uint8_t)7
301 #define IP_ADDR3      (uint8_t)1
302 
303 /*NETMASK*/
304 #define NETMASK_ADDR0 (uint8_t)255
305 #define NETMASK_ADDR1 (uint8_t)255
306 #define NETMASK_ADDR2 (uint8_t)255
307 #define NETMASK_ADDR3 (uint8_t)0
308 
309 /*Gateway Address*/
310 #define GW_ADDR0      (uint8_t)0
311 #define GW_ADDR1      (uint8_t)0
312 #define GW_ADDR2      (uint8_t)0
313 #define GW_ADDR3      (uint8_t)0
314 
315 const ip_addr_t ipaddr = IPADDR4_INIT_BYTES(IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
316 const ip_addr_t netmask = IPADDR4_INIT_BYTES(NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
317 const ip_addr_t gateway = IPADDR4_INIT_BYTES(GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
318 
319 #define NUM_DHCP_ENTRY 3
320 
321 static dhcp_entry_t entries[NUM_DHCP_ENTRY] = {
322     /* mac    ip address        subnet mask        lease time */
323     { { 0 }, { 192, 168, 7, 2 }, { 255, 255, 255, 0 }, 24 * 60 * 60 },
324     { { 0 }, { 192, 168, 7, 3 }, { 255, 255, 255, 0 }, 24 * 60 * 60 },
325     { { 0 }, { 192, 168, 7, 4 }, { 255, 255, 255, 0 }, 24 * 60 * 60 }
326 };
327 
328 static dhcp_config_t dhcp_config = {
329     { 192, 168, 7, 1 }, /* server address */
330     67,                 /* port */
331     { 192, 168, 7, 1 }, /* dns server */
332     "cherry",           /* dns suffix */
333     NUM_DHCP_ENTRY,     /* num entry */
334     entries             /* entries */
335 };
336 
dns_query_proc(const char * name,ip_addr_t * addr)337 static bool dns_query_proc(const char *name, ip_addr_t *addr)
338 {
339     if (strcmp(name, "cdc_ecm.cherry") == 0 || strcmp(name, "www.cdc_ecm.cherry") == 0) {
340         addr->addr = ipaddr.addr;
341         return true;
342     }
343     return false;
344 }
345 
346 static struct netif cdc_ecm_netif; //network interface
347 
348 /* Network interface name */
349 #define IFNAME0        'E'
350 #define IFNAME1        'X'
351 
linkoutput_fn(struct netif * netif,struct pbuf * p)352 err_t linkoutput_fn(struct netif *netif, struct pbuf *p)
353 {
354     int ret;
355 
356     cdc_ecm_tx_done = false;
357     ret = usbd_cdc_ecm_eth_tx(p);
358     if (ret == 0) {
359         while (!cdc_ecm_tx_done) {
360         }
361         return ERR_OK;
362     } else
363         return ERR_BUF;
364 }
365 
cdc_ecm_if_init(struct netif * netif)366 err_t cdc_ecm_if_init(struct netif *netif)
367 {
368     LWIP_ASSERT("netif != NULL", (netif != NULL));
369 
370     netif->mtu = 1500;
371     netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP;
372     netif->state = NULL;
373     netif->name[0] = IFNAME0;
374     netif->name[1] = IFNAME1;
375     netif->output = etharp_output;
376     netif->linkoutput = linkoutput_fn;
377     return ERR_OK;
378 }
379 
cdc_ecm_if_input(struct netif * netif)380 err_t cdc_ecm_if_input(struct netif *netif)
381 {
382     err_t err;
383     struct pbuf *p;
384 
385     p = usbd_cdc_ecm_eth_rx();
386     if (p != NULL) {
387         err = netif->input(p, netif);
388         if (err != ERR_OK) {
389             pbuf_free(p);
390         }
391     } else {
392         return ERR_BUF;
393     }
394     return err;
395 }
396 
cdc_ecm_lwip_init(void)397 void cdc_ecm_lwip_init(void)
398 {
399     struct netif *netif = &cdc_ecm_netif;
400 
401     lwip_init();
402 
403     netif->hwaddr_len = 6;
404     memcpy(netif->hwaddr, mac, 6);
405     netif->hwaddr[5] = ~netif->hwaddr[5]; /* device mac can't same as host. */
406 
407     netif = netif_add(netif, &ipaddr, &netmask, &gateway, NULL, cdc_ecm_if_init, netif_input);
408     netif_set_default(netif);
409     while (!netif_is_up(netif)) {
410     }
411 
412     while (dhserv_init(&dhcp_config)) {
413     }
414 
415     while (dnserv_init(IP_ADDR_ANY, 53, dns_query_proc)) {
416     }
417 }
418 
usbd_cdc_ecm_data_recv_done(uint32_t len)419 void usbd_cdc_ecm_data_recv_done(uint32_t len)
420 {
421 }
422 
cdc_ecm_input_poll(void)423 void cdc_ecm_input_poll(void)
424 {
425     cdc_ecm_if_input(&cdc_ecm_netif);
426 }
427 #endif
428 
usbd_event_handler(uint8_t busid,uint8_t event)429 static void usbd_event_handler(uint8_t busid, uint8_t event)
430 {
431     switch (event) {
432         case USBD_EVENT_RESET:
433             break;
434         case USBD_EVENT_CONNECTED:
435             break;
436         case USBD_EVENT_DISCONNECTED:
437             break;
438         case USBD_EVENT_RESUME:
439             break;
440         case USBD_EVENT_SUSPEND:
441             break;
442         case USBD_EVENT_CONFIGURED:
443             break;
444         case USBD_EVENT_SET_REMOTE_WAKEUP:
445             break;
446         case USBD_EVENT_CLR_REMOTE_WAKEUP:
447             break;
448 
449         default:
450             break;
451     }
452 }
453 
454 struct usbd_interface intf0;
455 struct usbd_interface intf1;
456 
457 /* ecm only supports in linux, and you should input the following command
458  *
459  * sudo ifconfig enxaabbccddeeff up
460  * sudo dhcpclient enxaabbccddeeff
461 */
cdc_ecm_init(uint8_t busid,uintptr_t reg_base)462 void cdc_ecm_init(uint8_t busid, uintptr_t reg_base)
463 {
464     cdc_ecm_lwip_init();
465 
466 #ifdef CONFIG_USBDEV_ADVANCE_DESC
467     usbd_desc_register(busid, &cdc_ecm_descriptor);
468 #else
469     usbd_desc_register(busid, cdc_ecm_descriptor);
470 #endif
471     usbd_add_interface(busid, usbd_cdc_ecm_init_intf(&intf0, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP));
472     usbd_add_interface(busid, usbd_cdc_ecm_init_intf(&intf1, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP));
473     usbd_initialize(busid, reg_base, usbd_event_handler);
474 }
475