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