1 /*
2  * Copyright (c) 2023, sakumisu
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include "usbd_core.h"
7 #include "usbd_cdc_ecm.h"
8 
9 #define CDC_ECM_OUT_EP_IDX 0
10 #define CDC_ECM_IN_EP_IDX  1
11 #define CDC_ECM_INT_EP_IDX 2
12 
13 /* Ethernet Maximum Segment size, typically 1514 bytes */
14 #define CONFIG_CDC_ECM_ETH_MAX_SEGSZE 1536U
15 
16 /* Describe EndPoints configuration */
17 static struct usbd_endpoint cdc_ecm_ep_data[3];
18 
19 #ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
20 static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_rx_buffer[USB_ALIGN_UP(CONFIG_CDC_ECM_ETH_MAX_SEGSZE, CONFIG_USB_ALIGN_SIZE)];
21 static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_tx_buffer[USB_ALIGN_UP(CONFIG_CDC_ECM_ETH_MAX_SEGSZE, CONFIG_USB_ALIGN_SIZE)];
22 #endif
23 static USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_notify_buf[USB_ALIGN_UP(16, CONFIG_USB_ALIGN_SIZE)];
24 
25 volatile uint32_t g_cdc_ecm_rx_data_length = 0;
26 volatile uint32_t g_cdc_ecm_tx_data_length = 0;
27 
28 static volatile uint8_t g_current_net_status = 0;
29 static volatile uint8_t g_cmd_intf = 0;
30 
31 static uint32_t g_connect_speed_table[2] = { CDC_ECM_CONNECT_SPEED_UPSTREAM,
32                                              CDC_ECM_CONNECT_SPEED_DOWNSTREAM };
33 
usbd_cdc_ecm_send_notify(uint8_t notifycode,uint8_t value,uint32_t * speed)34 void usbd_cdc_ecm_send_notify(uint8_t notifycode, uint8_t value, uint32_t *speed)
35 {
36     struct cdc_eth_notification *notify = (struct cdc_eth_notification *)g_cdc_ecm_notify_buf;
37     uint8_t bytes2send = 0;
38 
39     notify->bmRequestType = CDC_ECM_BMREQUEST_TYPE_ECM;
40     notify->bNotificationType = notifycode;
41 
42     switch (notifycode) {
43         case CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION:
44             notify->wValue = value;
45             notify->wIndex = g_cmd_intf;
46             notify->wLength = 0U;
47 
48             for (uint8_t i = 0U; i < 8U; i++) {
49                 notify->data[i] = 0U;
50             }
51             bytes2send = 8U;
52             break;
53         case CDC_ECM_NOTIFY_CODE_RESPONSE_AVAILABLE:
54             notify->wValue = 0U;
55             notify->wIndex = g_cmd_intf;
56             notify->wLength = 0U;
57             for (uint8_t i = 0U; i < 8U; i++) {
58                 notify->data[i] = 0U;
59             }
60             bytes2send = 8U;
61             break;
62         case CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE:
63             notify->wValue = 0U;
64             notify->wIndex = g_cmd_intf;
65             notify->wLength = 0x0008U;
66             bytes2send = 16U;
67 
68             memcpy(notify->data, speed, 8);
69             break;
70 
71         default:
72             break;
73     }
74 
75     if (usb_device_is_configured(0)) {
76         if (bytes2send) {
77             usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_addr, g_cdc_ecm_notify_buf, bytes2send);
78         }
79     }
80 }
81 
cdc_ecm_class_interface_request_handler(uint8_t busid,struct usb_setup_packet * setup,uint8_t ** data,uint32_t * len)82 static int cdc_ecm_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
83 {
84     USB_LOG_DBG("CDC ECM Class request: "
85                 "bRequest 0x%02x\r\n",
86                 setup->bRequest);
87 
88     (void)busid;
89     (void)data;
90     (void)len;
91 
92     g_cmd_intf = LO_BYTE(setup->wIndex);
93 
94     switch (setup->bRequest) {
95         case CDC_REQUEST_SET_ETHERNET_PACKET_FILTER:
96             /* bit0 Promiscuous
97              * bit1 ALL Multicast
98              * bit2 Directed
99              * bit3 Broadcast
100              * bit4 Multicast
101             */
102 #ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
103             g_connect_speed_table[0] = 100000000; /* 100 Mbps */
104             g_connect_speed_table[1] = 100000000; /* 100 Mbps */
105             usbd_cdc_ecm_set_connect(true, g_connect_speed_table);
106 #endif
107             break;
108         default:
109             USB_LOG_WRN("Unhandled CDC ECM Class bRequest 0x%02x\r\n", setup->bRequest);
110             return -1;
111     }
112 
113     return 0;
114 }
115 
cdc_ecm_notify_handler(uint8_t busid,uint8_t event,void * arg)116 void cdc_ecm_notify_handler(uint8_t busid, uint8_t event, void *arg)
117 {
118     (void)busid;
119     (void)arg;
120 
121     switch (event) {
122         case USBD_EVENT_RESET:
123             g_current_net_status = 0;
124             g_cdc_ecm_rx_data_length = 0;
125             g_cdc_ecm_tx_data_length = 0;
126             break;
127         case USBD_EVENT_CONFIGURED:
128 #ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
129             usbd_cdc_ecm_start_read(g_cdc_ecm_rx_buffer, CONFIG_CDC_ECM_ETH_MAX_SEGSZE);
130 #endif
131             break;
132 
133         default:
134             break;
135     }
136 }
137 
cdc_ecm_bulk_out(uint8_t busid,uint8_t ep,uint32_t nbytes)138 void cdc_ecm_bulk_out(uint8_t busid, uint8_t ep, uint32_t nbytes)
139 {
140     (void)busid;
141 
142     g_cdc_ecm_rx_data_length = nbytes;
143     usbd_cdc_ecm_data_recv_done(g_cdc_ecm_rx_data_length);
144 }
145 
cdc_ecm_bulk_in(uint8_t busid,uint8_t ep,uint32_t nbytes)146 void cdc_ecm_bulk_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
147 {
148     (void)busid;
149 
150     if ((nbytes % usbd_get_ep_mps(0, ep)) == 0 && nbytes) {
151         /* send zlp */
152         usbd_ep_start_write(0, ep, NULL, 0);
153     } else {
154         usbd_cdc_ecm_data_send_done(g_cdc_ecm_tx_data_length);
155         g_cdc_ecm_tx_data_length = 0;
156     }
157 }
158 
cdc_ecm_int_in(uint8_t busid,uint8_t ep,uint32_t nbytes)159 void cdc_ecm_int_in(uint8_t busid, uint8_t ep, uint32_t nbytes)
160 {
161     (void)busid;
162     (void)ep;
163     (void)nbytes;
164 
165     if (g_current_net_status == 2) {
166         g_current_net_status = 3;
167         usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE, 0, g_connect_speed_table);
168     } else {
169         g_current_net_status = 0;
170     }
171 }
172 
usbd_cdc_ecm_start_write(uint8_t * buf,uint32_t len)173 int usbd_cdc_ecm_start_write(uint8_t *buf, uint32_t len)
174 {
175     if (!usb_device_is_configured(0)) {
176         return -USB_ERR_NOTCONN;
177     }
178 
179     if (g_cdc_ecm_tx_data_length > 0) {
180         return -USB_ERR_BUSY;
181     }
182 
183     g_cdc_ecm_tx_data_length = len;
184 
185     USB_LOG_DBG("txlen:%d\r\n", g_cdc_ecm_tx_data_length);
186     return usbd_ep_start_write(0, cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr, buf, len);
187 }
188 
usbd_cdc_ecm_start_read(uint8_t * buf,uint32_t len)189 int usbd_cdc_ecm_start_read(uint8_t *buf, uint32_t len)
190 {
191     if (!usb_device_is_configured(0)) {
192         return -USB_ERR_NOTCONN;
193     }
194 
195     g_cdc_ecm_rx_data_length = 0;
196     return usbd_ep_start_read(0, cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, buf, len);
197 }
198 
199 #ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
usbd_cdc_ecm_eth_rx(void)200 struct pbuf *usbd_cdc_ecm_eth_rx(void)
201 {
202     struct pbuf *p;
203 
204     if (g_cdc_ecm_rx_data_length == 0) {
205         return NULL;
206     }
207     p = pbuf_alloc(PBUF_RAW, g_cdc_ecm_rx_data_length, PBUF_POOL);
208     if (p == NULL) {
209         usbd_cdc_ecm_start_read(g_cdc_ecm_rx_buffer, CONFIG_CDC_ECM_ETH_MAX_SEGSZE);
210         return NULL;
211     }
212     usb_memcpy(p->payload, (uint8_t *)g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_data_length);
213     p->len = g_cdc_ecm_rx_data_length;
214 
215     USB_LOG_DBG("rxlen:%d\r\n", g_cdc_ecm_rx_data_length);
216     usbd_cdc_ecm_start_read(g_cdc_ecm_rx_buffer, CONFIG_CDC_ECM_ETH_MAX_SEGSZE);
217     return p;
218 }
219 
usbd_cdc_ecm_eth_tx(struct pbuf * p)220 int usbd_cdc_ecm_eth_tx(struct pbuf *p)
221 {
222     struct pbuf *q;
223     uint8_t *buffer;
224 
225     if (!usb_device_is_configured(0)) {
226         return -USB_ERR_NOTCONN;
227     }
228 
229     if (g_cdc_ecm_tx_data_length > 0) {
230         return -USB_ERR_BUSY;
231     }
232 
233     if (p->tot_len > sizeof(g_cdc_ecm_tx_buffer)) {
234         p->tot_len = sizeof(g_cdc_ecm_tx_buffer);
235     }
236 
237     buffer = g_cdc_ecm_tx_buffer;
238     for (q = p; q != NULL; q = q->next) {
239         usb_memcpy(buffer, q->payload, q->len);
240         buffer += q->len;
241     }
242 
243     return usbd_cdc_ecm_start_write(g_cdc_ecm_tx_buffer, p->tot_len);
244 }
245 #endif
246 
usbd_cdc_ecm_init_intf(struct usbd_interface * intf,const uint8_t int_ep,const uint8_t out_ep,const uint8_t in_ep)247 struct usbd_interface *usbd_cdc_ecm_init_intf(struct usbd_interface *intf, const uint8_t int_ep, const uint8_t out_ep, const uint8_t in_ep)
248 {
249     intf->class_interface_handler = cdc_ecm_class_interface_request_handler;
250     intf->class_endpoint_handler = NULL;
251     intf->vendor_handler = NULL;
252     intf->notify_handler = cdc_ecm_notify_handler;
253 
254     cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr = out_ep;
255     cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_cb = cdc_ecm_bulk_out;
256     cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr = in_ep;
257     cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_cb = cdc_ecm_bulk_in;
258     cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_addr = int_ep;
259     cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_cb = cdc_ecm_int_in;
260 
261     usbd_add_endpoint(0, &cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX]);
262     usbd_add_endpoint(0, &cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX]);
263     usbd_add_endpoint(0, &cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX]);
264 
265     return intf;
266 }
267 
usbd_cdc_ecm_set_connect(bool connect,uint32_t speed[2])268 int usbd_cdc_ecm_set_connect(bool connect, uint32_t speed[2])
269 {
270     if (!usb_device_is_configured(0)) {
271         return -USB_ERR_NOTCONN;
272     }
273 
274     if (connect) {
275         g_current_net_status = 2;
276         memcpy(g_connect_speed_table, speed, 8);
277         usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, NULL);
278     } else {
279         g_current_net_status = 1;
280         usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_DISCONNECTED, NULL);
281     }
282 
283     return 0;
284 }
285 
usbd_cdc_ecm_data_recv_done(uint32_t len)286 __WEAK void usbd_cdc_ecm_data_recv_done(uint32_t len)
287 {
288     (void)len;
289 }
290 
usbd_cdc_ecm_data_send_done(uint32_t len)291 __WEAK void usbd_cdc_ecm_data_send_done(uint32_t len)
292 {
293     (void)len;
294 }
295