• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022, sakumisu
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include "usbh_core.h"
7 #include "usbh_cdc_ecm.h"
8 
9 #define DEV_FORMAT "/dev/cdc_ether"
10 
11 /* general descriptor field offsets */
12 #define DESC_bLength            0 /** Length offset */
13 #define DESC_bDescriptorType    1 /** Descriptor type offset */
14 #define DESC_bDescriptorSubType 2 /** Descriptor subtype offset */
15 
16 /* interface descriptor field offsets */
17 #define INTF_DESC_bInterfaceNumber  2 /** Interface number offset */
18 #define INTF_DESC_bAlternateSetting 3 /** Alternate setting offset */
19 
20 #define CONFIG_USBHOST_CDC_ECM_PKT_FILTER     0x000C
21 #define CONFIG_USBHOST_CDC_ECM_ETH_MAX_SEGSZE 1514U
22 
23 USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_rx_buffer[CONFIG_USBHOST_CDC_ECM_ETH_MAX_SEGSZE];
24 USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_tx_buffer[CONFIG_USBHOST_CDC_ECM_ETH_MAX_SEGSZE];
25 USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_inttx_buffer[16];
26 
27 static struct usbh_cdc_ecm g_cdc_ecm_class;
28 
usbh_cdc_ecm_set_eth_packet_filter(struct usbh_cdc_ecm * cdc_ecm_class,uint16_t filter_value)29 static int usbh_cdc_ecm_set_eth_packet_filter(struct usbh_cdc_ecm *cdc_ecm_class, uint16_t filter_value)
30 {
31     struct usb_setup_packet *setup = cdc_ecm_class->hport->setup;
32 
33     setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
34     setup->bRequest = CDC_REQUEST_SET_ETHERNET_PACKET_FILTER;
35     setup->wValue = filter_value;
36     setup->wIndex = cdc_ecm_class->ctrl_intf;
37     setup->wLength = 0;
38 
39     return usbh_control_transfer(cdc_ecm_class->hport->ep0, setup, NULL);
40 }
41 
usbh_cdc_ecm_get_notification(struct usbh_cdc_ecm * cdc_ecm_class)42 int usbh_cdc_ecm_get_notification(struct usbh_cdc_ecm *cdc_ecm_class)
43 {
44     int ret;
45 
46     usbh_int_urb_fill(&cdc_ecm_class->intin_urb, cdc_ecm_class->intin, g_cdc_ecm_inttx_buffer, 16, USB_OSAL_WAITING_FOREVER, NULL, NULL);
47     ret = usbh_submit_urb(&cdc_ecm_class->intin_urb);
48     if (ret < 0) {
49         return ret;
50     }
51 
52     if (g_cdc_ecm_inttx_buffer[1] == CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION) {
53         cdc_ecm_class->connect_status = g_cdc_ecm_inttx_buffer[2];
54     } else if (g_cdc_ecm_inttx_buffer[1] == CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE) {
55         memcpy(cdc_ecm_class->speed, &g_cdc_ecm_inttx_buffer[8], 8);
56     }
57     return 0;
58 }
59 
usbh_cdc_ecm_connect(struct usbh_hubport * hport,uint8_t intf)60 static int usbh_cdc_ecm_connect(struct usbh_hubport *hport, uint8_t intf)
61 {
62     struct usb_endpoint_descriptor *ep_desc;
63     int ret;
64     uint8_t altsetting = 0;
65     char mac_buffer[8];
66     uint8_t *p;
67     uint8_t cur_iface = 0xff;
68     uint8_t mac_str_idx = 0xff;
69 
70     struct usbh_cdc_ecm *cdc_ecm_class = &g_cdc_ecm_class;
71 
72     cdc_ecm_class->hport = hport;
73     cdc_ecm_class->ctrl_intf = intf;
74     cdc_ecm_class->data_intf = intf + 1;
75 
76     hport->config.intf[intf].priv = cdc_ecm_class;
77     hport->config.intf[intf + 1].priv = NULL;
78 
79     p = hport->raw_config_desc;
80     while (p[DESC_bLength]) {
81         switch (p[DESC_bDescriptorType]) {
82             case USB_DESCRIPTOR_TYPE_INTERFACE:
83                 cur_iface = p[INTF_DESC_bInterfaceNumber];
84                 //cur_alt_setting = p[INTF_DESC_bAlternateSetting];
85                 break;
86             case CDC_CS_INTERFACE:
87                 if ((cur_iface == cdc_ecm_class->ctrl_intf) && p[DESC_bDescriptorSubType] == CDC_FUNC_DESC_ETHERNET_NETWORKING) {
88                     struct cdc_ecm_descriptor *desc = (struct cdc_ecm_descriptor *)p;
89                     mac_str_idx = desc->iMACAddress;
90                     cdc_ecm_class->max_segment_size = desc->wMaxSegmentSize;
91                     goto get_mac;
92                 }
93                 break;
94 
95             default:
96                 break;
97         }
98         /* skip to next descriptor */
99         p += p[DESC_bLength];
100     }
101 
102 get_mac:
103     if (mac_str_idx == 0xff) {
104         USB_LOG_ERR("Do not find cdc ecm mac string\r\n");
105         return -1;
106     }
107 
108     memset(mac_buffer, 0, 8);
109     ret = usbh_get_string_desc(cdc_ecm_class->hport, mac_str_idx, (uint8_t *)mac_buffer);
110     if (ret < 0) {
111         return ret;
112     }
113 
114     uint8_t len = strlen(mac_buffer);
115 
116     for (int i = 0, j = 0; i < len; i += 2, j++) {
117         char byte_str[3];
118         byte_str[0] = mac_buffer[i];
119         byte_str[1] = mac_buffer[i + 1];
120         byte_str[2] = '\0';
121 
122         uint32_t byte = strtoul(byte_str, NULL, 16);
123         cdc_ecm_class->mac[j] = (unsigned char)byte;
124     }
125 
126     USB_LOG_INFO("CDC ECM mac address %02x: %02x: %02x: %02x: %02x: %02x\r\n",
127                  cdc_ecm_class->mac[0],
128                  cdc_ecm_class->mac[1],
129                  cdc_ecm_class->mac[2],
130                  cdc_ecm_class->mac[3],
131                  cdc_ecm_class->mac[4],
132                  cdc_ecm_class->mac[5]);
133 
134     if (cdc_ecm_class->max_segment_size > CONFIG_USBHOST_CDC_ECM_ETH_MAX_SEGSZE) {
135         USB_LOG_ERR("CDC ECM Max Segment Size is overflow, default is %u, but now %u\r\n", CONFIG_USBHOST_CDC_ECM_ETH_MAX_SEGSZE, cdc_ecm_class->max_segment_size);
136     } else {
137         USB_LOG_INFO("CDC ECM Max Segment Size:%u\r\n", cdc_ecm_class->max_segment_size);
138     }
139 
140     /* enable int ep */
141     ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc;
142     usbh_hport_activate_epx(&cdc_ecm_class->intin, hport, ep_desc);
143 
144     if (hport->config.intf[intf + 1].altsetting_num > 1) {
145         altsetting = hport->config.intf[intf + 1].altsetting_num - 1;
146 
147         for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[altsetting].intf_desc.bNumEndpoints; i++) {
148             ep_desc = &hport->config.intf[intf + 1].altsetting[altsetting].ep[i].ep_desc;
149 
150             if (ep_desc->bEndpointAddress & 0x80) {
151                 usbh_hport_activate_epx(&cdc_ecm_class->bulkin, hport, ep_desc);
152             } else {
153                 usbh_hport_activate_epx(&cdc_ecm_class->bulkout, hport, ep_desc);
154             }
155         }
156 
157         USB_LOG_INFO("Select cdc ecm altsetting: %d\r\n", altsetting);
158         usbh_set_interface(cdc_ecm_class->hport, cdc_ecm_class->data_intf, altsetting);
159     } else {
160         for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[0].intf_desc.bNumEndpoints; i++) {
161             ep_desc = &hport->config.intf[intf + 1].altsetting[0].ep[i].ep_desc;
162 
163             if (ep_desc->bEndpointAddress & 0x80) {
164                 usbh_hport_activate_epx(&cdc_ecm_class->bulkin, hport, ep_desc);
165             } else {
166                 usbh_hport_activate_epx(&cdc_ecm_class->bulkout, hport, ep_desc);
167             }
168         }
169     }
170 
171     /* bit0 Promiscuous
172     * bit1 ALL Multicast
173     * bit2 Directed
174     * bit3 Broadcast
175     * bit4 Multicast
176     */
177     ret = usbh_cdc_ecm_set_eth_packet_filter(cdc_ecm_class, CONFIG_USBHOST_CDC_ECM_PKT_FILTER);
178     if (ret < 0) {
179         return ret;
180     }
181     USB_LOG_INFO("Set CDC ECM packet filter:%04x\r\n", CONFIG_USBHOST_CDC_ECM_PKT_FILTER);
182 
183     memcpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN);
184 
185     USB_LOG_INFO("Register CDC ECM Class:%s\r\n", hport->config.intf[intf].devname);
186 
187     usbh_cdc_ecm_run(cdc_ecm_class);
188     return ret;
189 }
190 
usbh_cdc_ecm_disconnect(struct usbh_hubport * hport,uint8_t intf)191 static int usbh_cdc_ecm_disconnect(struct usbh_hubport *hport, uint8_t intf)
192 {
193     int ret = 0;
194 
195     struct usbh_cdc_ecm *cdc_ecm_class = (struct usbh_cdc_ecm *)hport->config.intf[intf].priv;
196 
197     if (cdc_ecm_class) {
198         if (cdc_ecm_class->bulkin) {
199             usbh_pipe_free(cdc_ecm_class->bulkin);
200         }
201 
202         if (cdc_ecm_class->bulkout) {
203             usbh_pipe_free(cdc_ecm_class->bulkout);
204         }
205 
206         if (cdc_ecm_class->intin) {
207             usbh_pipe_free(cdc_ecm_class->intin);
208         }
209 
210         if (hport->config.intf[intf].devname[0] != '\0') {
211             USB_LOG_INFO("Unregister CDC ECM Class:%s\r\n", hport->config.intf[intf].devname);
212             usbh_cdc_ecm_stop(cdc_ecm_class);
213         }
214 
215         memset(cdc_ecm_class, 0, sizeof(struct usbh_cdc_ecm));
216     }
217 
218     return ret;
219 }
220 
usbh_cdc_ecm_rx_thread(void * argument)221 static void usbh_cdc_ecm_rx_thread(void *argument)
222 {
223     uint32_t g_cdc_ecm_rx_length;
224     int ret;
225     err_t err;
226     struct pbuf *p;
227     struct netif *netif = (struct netif *)argument;
228     uint16_t ep_mps;
229 
230     // clang-format off
231 find_class:
232     // clang-format on
233     while (usbh_find_class_instance("/dev/cdc_ether") == NULL) {
234         usb_osal_msleep(1000);
235     }
236 
237     while (g_cdc_ecm_class.connect_status == CDC_ECM_NET_DISCONNECTED) {
238         ret = usbh_cdc_ecm_get_notification(&g_cdc_ecm_class);
239         if (ret < 0) {
240             goto find_class;
241         }
242     }
243 
244     if (g_cdc_ecm_class.hport->speed == USB_SPEED_FULL) {
245         ep_mps = 64;
246     } else {
247         ep_mps = 512;
248     }
249     g_cdc_ecm_rx_length = 0;
250     while (1) {
251         usbh_bulk_urb_fill(&g_cdc_ecm_class.bulkin_urb, g_cdc_ecm_class.bulkin, &g_cdc_ecm_rx_buffer[g_cdc_ecm_rx_length], ep_mps, USB_OSAL_WAITING_FOREVER, NULL, NULL);
252         ret = usbh_submit_urb(&g_cdc_ecm_class.bulkin_urb);
253         if (ret < 0) {
254             goto find_class;
255         }
256 
257         g_cdc_ecm_rx_length += g_cdc_ecm_class.bulkin_urb.actual_length;
258 
259         if (g_cdc_ecm_rx_length % ep_mps) {
260             USB_LOG_DBG("rxlen:%d\r\n", g_cdc_ecm_rx_length);
261 
262             p = pbuf_alloc(PBUF_RAW, g_cdc_ecm_rx_length, PBUF_POOL);
263             if (p != NULL) {
264                 memcpy(p->payload, (uint8_t *)g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_length);
265                 g_cdc_ecm_rx_length = 0;
266                 err = netif->input(p, netif);
267                 if (err != ERR_OK) {
268                     pbuf_free(p);
269                 }
270             } else {
271                 g_cdc_ecm_rx_length = 0;
272                 USB_LOG_ERR("No memory to alloc pbuf for cdc ecm rx\r\n");
273             }
274         } else {
275         }
276     }
277 }
278 
usbh_cdc_ecm_linkoutput(struct netif * netif,struct pbuf * p)279 err_t usbh_cdc_ecm_linkoutput(struct netif *netif, struct pbuf *p)
280 {
281     int ret;
282     struct pbuf *q;
283     uint8_t *buffer = g_cdc_ecm_tx_buffer;
284 
285     if (g_cdc_ecm_class.connect_status == CDC_ECM_NET_DISCONNECTED) {
286         return ERR_BUF;
287     }
288 
289     for (q = p; q != NULL; q = q->next) {
290         memcpy(buffer, q->payload, q->len);
291         buffer += q->len;
292     }
293 
294     USB_LOG_DBG("txlen:%d\r\n", p->tot_len);
295 
296     usbh_bulk_urb_fill(&g_cdc_ecm_class.bulkout_urb, g_cdc_ecm_class.bulkout, g_cdc_ecm_tx_buffer, p->tot_len, USB_OSAL_WAITING_FOREVER, NULL, NULL);
297     ret = usbh_submit_urb(&g_cdc_ecm_class.bulkout_urb);
298     if (ret < 0) {
299         return ERR_BUF;
300     }
301 
302     return ERR_OK;
303 }
304 
usbh_cdc_ecm_lwip_thread_init(struct netif * netif)305 void usbh_cdc_ecm_lwip_thread_init(struct netif *netif)
306 {
307     usb_osal_thread_create("usbh_cdc_ecm_rx", 2048, CONFIG_USBHOST_PSC_PRIO + 1, usbh_cdc_ecm_rx_thread, netif);
308 }
309 
usbh_cdc_ecm_run(struct usbh_cdc_ecm * cdc_ecm_class)310 __WEAK void usbh_cdc_ecm_run(struct usbh_cdc_ecm *cdc_ecm_class)
311 {
312 }
313 
usbh_cdc_ecm_stop(struct usbh_cdc_ecm * cdc_ecm_class)314 __WEAK void usbh_cdc_ecm_stop(struct usbh_cdc_ecm *cdc_ecm_class)
315 {
316 }
317 
318 const struct usbh_class_driver cdc_ecm_class_driver = {
319     .driver_name = "cdc_ecm",
320     .connect = usbh_cdc_ecm_connect,
321     .disconnect = usbh_cdc_ecm_disconnect
322 };
323 
324 CLASS_INFO_DEFINE const struct usbh_class_info cdc_ecm_class_info = {
325     .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
326     .class = USB_DEVICE_CLASS_CDC,
327     .subclass = CDC_ETHERNET_NETWORKING_CONTROL_MODEL,
328     .protocol = CDC_COMMON_PROTOCOL_NONE,
329     .vid = 0x00,
330     .pid = 0x00,
331     .class_driver = &cdc_ecm_class_driver
332 };