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 };