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 /* Describe EndPoints configuration */
14 static struct usbd_endpoint cdc_ecm_ep_data[3];
15
16 #ifdef CONFIG_USB_HS
17 #define CDC_ECM_MAX_PACKET_SIZE 512
18 #else
19 #define CDC_ECM_MAX_PACKET_SIZE 64
20 #endif
21
22 USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_rx_buffer[CONFIG_CDC_ECM_ETH_MAX_SEGSZE];
23 USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_tx_buffer[CONFIG_CDC_ECM_ETH_MAX_SEGSZE];
24 USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_cdc_ecm_notify_buf[16];
25
26 volatile uint8_t *g_cdc_ecm_rx_data_buffer = NULL;
27 volatile uint32_t g_cdc_ecm_rx_data_length = 0;
28 volatile uint32_t g_cdc_ecm_tx_data_length = 0;
29
30 static volatile uint8_t g_current_net_status = 0;
31 static volatile uint8_t g_cmd_intf = 0;
32
33 static uint32_t g_connect_speed_table[2] = { CDC_ECM_CONNECT_SPEED_UPSTREAM,
34 CDC_ECM_CONNECT_SPEED_DOWNSTREAM };
35
usbd_cdc_ecm_send_notify(uint8_t notifycode,uint8_t value,uint32_t * speed)36 void usbd_cdc_ecm_send_notify(uint8_t notifycode, uint8_t value, uint32_t *speed)
37 {
38 struct cdc_ecm_notification *notify = (struct cdc_ecm_notification *)g_cdc_ecm_notify_buf;
39 uint8_t bytes2send = 0;
40
41 notify->bmRequestType = CDC_ECM_BMREQUEST_TYPE_ECM;
42 notify->bNotificationType = notifycode;
43
44 switch (notifycode) {
45 case CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION:
46 notify->wValue = value;
47 notify->wIndex = g_cmd_intf;
48 notify->wLength = 0U;
49
50 for (uint8_t i = 0U; i < 8U; i++) {
51 notify->data[i] = 0U;
52 }
53 bytes2send = 8U;
54 break;
55 case CDC_ECM_NOTIFY_CODE_RESPONSE_AVAILABLE:
56 notify->wValue = 0U;
57 notify->wIndex = g_cmd_intf;
58 notify->wLength = 0U;
59 for (uint8_t i = 0U; i < 8U; i++) {
60 notify->data[i] = 0U;
61 }
62 bytes2send = 8U;
63 break;
64 case CDC_ECM_NOTIFY_CODE_CONNECTION_SPEED_CHANGE:
65 notify->wValue = 0U;
66 notify->wIndex = g_cmd_intf;
67 notify->wLength = 0x0008U;
68 bytes2send = 16U;
69
70 memcpy(notify->data, speed, 8);
71 break;
72
73 default:
74 break;
75 }
76
77 if (bytes2send) {
78 usbd_ep_start_write(cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_addr, g_cdc_ecm_notify_buf, bytes2send);
79 }
80 }
81
cdc_ecm_class_interface_request_handler(struct usb_setup_packet * setup,uint8_t ** data,uint32_t * len)82 static int cdc_ecm_class_interface_request_handler(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 g_cmd_intf = LO_BYTE(setup->wIndex);
89
90 switch (setup->bRequest) {
91 case CDC_REQUEST_SET_ETHERNET_PACKET_FILTER:
92 /* bit0 Promiscuous
93 * bit1 ALL Multicast
94 * bit2 Directed
95 * bit3 Broadcast
96 * bit4 Multicast
97 */
98 if (g_current_net_status == 0) {
99 g_current_net_status = 1;
100 usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, NULL);
101 }
102
103 break;
104 default:
105 USB_LOG_WRN("Unhandled CDC ECM Class bRequest 0x%02x\r\n", setup->bRequest);
106 return -1;
107 }
108
109 return 0;
110 }
111
cdc_ecm_notify_handler(uint8_t event,void * arg)112 void cdc_ecm_notify_handler(uint8_t event, void *arg)
113 {
114 switch (event) {
115 case USBD_EVENT_RESET:
116 g_current_net_status = 0;
117 g_cdc_ecm_rx_data_length = 0;
118 g_cdc_ecm_tx_data_length = 0;
119 g_cdc_ecm_rx_data_buffer = NULL;
120 break;
121 case USBD_EVENT_CONFIGURED:
122 usbd_ep_start_read(cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, &g_cdc_ecm_rx_buffer[g_cdc_ecm_rx_data_length], CDC_ECM_MAX_PACKET_SIZE);
123 break;
124
125 default:
126 break;
127 }
128 }
129
cdc_ecm_bulk_out(uint8_t ep,uint32_t nbytes)130 void cdc_ecm_bulk_out(uint8_t ep, uint32_t nbytes)
131 {
132 g_cdc_ecm_rx_data_length += nbytes;
133
134 if (nbytes < CDC_ECM_MAX_PACKET_SIZE) {
135 g_cdc_ecm_rx_data_buffer = g_cdc_ecm_rx_buffer;
136 usbd_cdc_ecm_data_recv_done();
137 } else {
138 usbd_ep_start_read(ep, &g_cdc_ecm_rx_buffer[g_cdc_ecm_rx_data_length], CDC_ECM_MAX_PACKET_SIZE);
139 }
140 }
141
cdc_ecm_bulk_in(uint8_t ep,uint32_t nbytes)142 void cdc_ecm_bulk_in(uint8_t ep, uint32_t nbytes)
143 {
144 if ((nbytes % CDC_ECM_MAX_PACKET_SIZE) == 0 && nbytes) {
145 /* send zlp */
146 usbd_ep_start_write(ep, NULL, 0);
147 } else {
148 g_cdc_ecm_tx_data_length = 0;
149 }
150 }
151
cdc_ecm_int_in(uint8_t ep,uint32_t nbytes)152 void cdc_ecm_int_in(uint8_t ep, uint32_t nbytes)
153 {
154 if (g_current_net_status == 1) {
155 g_current_net_status = 2;
156 usbd_cdc_ecm_send_notify(CDC_ECM_NOTIFY_CODE_NETWORK_CONNECTION, CDC_ECM_NET_CONNECTED, g_connect_speed_table);
157 }
158 }
159
160 #ifdef CONFIG_USBDEV_CDC_ECM_USING_LWIP
usbd_cdc_ecm_eth_rx(void)161 struct pbuf *usbd_cdc_ecm_eth_rx(void)
162 {
163 struct pbuf *p;
164
165 if (g_cdc_ecm_rx_data_buffer == NULL) {
166 return NULL;
167 }
168 p = pbuf_alloc(PBUF_RAW, g_cdc_ecm_rx_data_length, PBUF_POOL);
169 if (p == NULL) {
170 return NULL;
171 }
172 memcpy(p->payload, (uint8_t *)g_cdc_ecm_rx_buffer, g_cdc_ecm_rx_data_length);
173 p->len = g_cdc_ecm_rx_data_length;
174
175 USB_LOG_DBG("rxlen:%d\r\n", g_cdc_ecm_rx_data_length);
176 g_cdc_ecm_rx_data_length = 0;
177 g_cdc_ecm_rx_data_buffer = NULL;
178 usbd_ep_start_read(cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr, g_cdc_ecm_rx_buffer, CDC_ECM_MAX_PACKET_SIZE);
179
180 return p;
181 }
182
usbd_cdc_ecm_eth_tx(struct pbuf * p)183 int usbd_cdc_ecm_eth_tx(struct pbuf *p)
184 {
185 struct pbuf *q;
186 uint8_t *buffer;
187
188 if (g_cdc_ecm_tx_data_length > 0) {
189 return -EBUSY;
190 }
191
192 if (p->tot_len > sizeof(g_cdc_ecm_tx_buffer)) {
193 p->tot_len = sizeof(g_cdc_ecm_tx_buffer);
194 }
195
196 buffer = g_cdc_ecm_tx_buffer;
197 for (q = p; q != NULL; q = q->next) {
198 memcpy(buffer, q->payload, q->len);
199 buffer += q->len;
200 }
201
202 g_cdc_ecm_tx_data_length = p->tot_len;
203
204 USB_LOG_DBG("txlen:%d\r\n", g_cdc_ecm_tx_data_length);
205 return usbd_ep_start_write(cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr, g_cdc_ecm_tx_buffer, g_cdc_ecm_tx_data_length);
206 }
207 #endif
208
usbd_cdc_ecm_init_intf(struct usbd_interface * intf,const uint8_t int_ep,const uint8_t out_ep,const uint8_t in_ep)209 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)
210 {
211 intf->class_interface_handler = cdc_ecm_class_interface_request_handler;
212 intf->class_endpoint_handler = NULL;
213 intf->vendor_handler = NULL;
214 intf->notify_handler = cdc_ecm_notify_handler;
215
216 cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_addr = out_ep;
217 cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX].ep_cb = cdc_ecm_bulk_out;
218 cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_addr = in_ep;
219 cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX].ep_cb = cdc_ecm_bulk_in;
220 cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_addr = int_ep;
221 cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX].ep_cb = cdc_ecm_int_in;
222
223 usbd_add_endpoint(&cdc_ecm_ep_data[CDC_ECM_OUT_EP_IDX]);
224 usbd_add_endpoint(&cdc_ecm_ep_data[CDC_ECM_IN_EP_IDX]);
225 usbd_add_endpoint(&cdc_ecm_ep_data[CDC_ECM_INT_EP_IDX]);
226
227 return intf;
228 }
229
usbd_cdc_ecm_set_connect_speed(uint32_t speed[2])230 void usbd_cdc_ecm_set_connect_speed(uint32_t speed[2])
231 {
232 memcpy(g_connect_speed_table, speed, 8);
233 }
234
usbd_cdc_ecm_data_recv_done(void)235 __WEAK void usbd_cdc_ecm_data_recv_done(void)
236 {
237 }