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