1 /*
2 * Copyright (c) 2017 Linaro Ltd
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(usb_eem, CONFIG_USB_DEVICE_NETWORK_LOG_LEVEL);
9
10 #include <zephyr/net/net_pkt.h>
11 #include <zephyr/net/ethernet.h>
12 #include <net_private.h>
13
14 #include <zephyr/usb/usb_device.h>
15 #include <zephyr/usb/class/usb_cdc.h>
16
17 #include "netusb.h"
18
19 static uint8_t sentinel[] = { 0xde, 0xad, 0xbe, 0xef };
20
21 #define EEM_FRAME_SIZE (NET_ETH_MAX_FRAME_SIZE + sizeof(sentinel) + \
22 sizeof(uint16_t)) /* EEM header */
23
24 static uint8_t tx_buf[EEM_FRAME_SIZE], rx_buf[EEM_FRAME_SIZE];
25
26 struct usb_cdc_eem_config {
27 struct usb_if_descriptor if0;
28 struct usb_ep_descriptor if0_in_ep;
29 struct usb_ep_descriptor if0_out_ep;
30 } __packed;
31
32 USBD_CLASS_DESCR_DEFINE(primary, 0) struct usb_cdc_eem_config cdc_eem_cfg = {
33 /* Interface descriptor 0 */
34 /* CDC Communication interface */
35 .if0 = {
36 .bLength = sizeof(struct usb_if_descriptor),
37 .bDescriptorType = USB_DESC_INTERFACE,
38 .bInterfaceNumber = 0,
39 .bAlternateSetting = 0,
40 .bNumEndpoints = 2,
41 .bInterfaceClass = USB_BCC_CDC_CONTROL,
42 .bInterfaceSubClass = EEM_SUBCLASS,
43 .bInterfaceProtocol = EEM_PROTOCOL,
44 .iInterface = 0,
45 },
46
47 /* Data Endpoint IN */
48 .if0_in_ep = {
49 .bLength = sizeof(struct usb_ep_descriptor),
50 .bDescriptorType = USB_DESC_ENDPOINT,
51 .bEndpointAddress = CDC_EEM_IN_EP_ADDR,
52 .bmAttributes = USB_DC_EP_BULK,
53 .wMaxPacketSize =
54 sys_cpu_to_le16(CONFIG_CDC_EEM_BULK_EP_MPS),
55 .bInterval = 0x00,
56 },
57
58 /* Data Endpoint OUT */
59 .if0_out_ep = {
60 .bLength = sizeof(struct usb_ep_descriptor),
61 .bDescriptorType = USB_DESC_ENDPOINT,
62 .bEndpointAddress = CDC_EEM_OUT_EP_ADDR,
63 .bmAttributes = USB_DC_EP_BULK,
64 .wMaxPacketSize =
65 sys_cpu_to_le16(CONFIG_CDC_EEM_BULK_EP_MPS),
66 .bInterval = 0x00,
67 },
68 };
69
eem_get_first_iface_number(void)70 static uint8_t eem_get_first_iface_number(void)
71 {
72 return cdc_eem_cfg.if0.bInterfaceNumber;
73 }
74
75 #define EEM_OUT_EP_IDX 0
76 #define EEM_IN_EP_IDX 1
77
78 static struct usb_ep_cfg_data eem_ep_data[] = {
79 {
80 /* Use transfer API */
81 .ep_cb = usb_transfer_ep_callback,
82 .ep_addr = CDC_EEM_OUT_EP_ADDR
83 },
84 {
85 /* Use transfer API */
86 .ep_cb = usb_transfer_ep_callback,
87 .ep_addr = CDC_EEM_IN_EP_ADDR
88 },
89 };
90
eem_pkt_size(uint16_t hdr)91 static inline uint16_t eem_pkt_size(uint16_t hdr)
92 {
93 if (hdr & BIT(15)) {
94 return hdr & 0x07ff;
95 } else {
96 return hdr & 0x3fff;
97 }
98 }
99
eem_send(struct net_pkt * pkt)100 static int eem_send(struct net_pkt *pkt)
101 {
102 uint16_t *hdr = (uint16_t *)&tx_buf[0];
103 int ret, len, b_idx = 0;
104
105 /* With EEM, it's possible to send multiple ethernet packets in one
106 * transfer, we don't do that for now.
107 */
108 len = net_pkt_get_len(pkt) + sizeof(sentinel);
109
110 if (len + sizeof(uint16_t) > sizeof(tx_buf)) {
111 LOG_WRN("Trying to send too large packet, drop");
112 return -ENOMEM;
113 }
114
115 /* Add EEM header */
116 *hdr = sys_cpu_to_le16(0x3FFF & len);
117 b_idx += sizeof(uint16_t);
118
119 if (net_pkt_read(pkt, &tx_buf[b_idx], net_pkt_get_len(pkt))) {
120 return -ENOBUFS;
121 }
122
123 b_idx += len - sizeof(sentinel);
124
125 /* Add crc-sentinel */
126 memcpy(&tx_buf[b_idx], sentinel, sizeof(sentinel));
127 b_idx += sizeof(sentinel);
128
129 /* transfer data to host */
130 ret = usb_transfer_sync(eem_ep_data[EEM_IN_EP_IDX].ep_addr,
131 tx_buf, b_idx,
132 USB_TRANS_WRITE);
133 if (ret != b_idx) {
134 LOG_ERR("Transfer failure");
135 return -EIO;
136 }
137
138 return 0;
139 }
140
eem_read_cb(uint8_t ep,int size,void * priv)141 static void eem_read_cb(uint8_t ep, int size, void *priv)
142 {
143 uint8_t *ptr = rx_buf;
144
145 do {
146 uint16_t eem_hdr, eem_size;
147 struct net_pkt *pkt;
148
149 if (size < sizeof(uint16_t)) {
150 break;
151 }
152
153 eem_hdr = sys_get_le16(ptr);
154 eem_size = eem_pkt_size(eem_hdr);
155
156 if (eem_size + sizeof(uint16_t) > size) {
157 /* eem pkt greater than transferred data */
158 LOG_ERR("pkt size error");
159 break;
160 }
161
162 size -= sizeof(uint16_t);
163 ptr += sizeof(uint16_t);
164
165 if (eem_hdr & BIT(15)) {
166 /* EEM Command, do nothing for now */
167 goto done;
168 }
169
170 LOG_DBG("hdr 0x%x, eem_size %d, size %d",
171 eem_hdr, eem_size, size);
172
173 if (!size || !eem_size) {
174 LOG_DBG("no payload");
175 break;
176 }
177
178 pkt = net_pkt_rx_alloc_with_buffer(netusb_net_iface(),
179 eem_size - sizeof(sentinel),
180 AF_UNSPEC, 0, K_FOREVER);
181 if (!pkt) {
182 LOG_ERR("Unable to alloc pkt");
183 break;
184 }
185
186 /* copy payload and discard 32-bit sentinel */
187 if (net_pkt_write(pkt, ptr, eem_size - sizeof(sentinel))) {
188 LOG_ERR("Unable to write into pkt");
189 net_pkt_unref(pkt);
190 break;
191 }
192
193 netusb_recv(pkt);
194
195 done:
196 size -= eem_size;
197 ptr += eem_size;
198 } while (size);
199
200 usb_transfer(eem_ep_data[EEM_OUT_EP_IDX].ep_addr, rx_buf,
201 sizeof(rx_buf), USB_TRANS_READ, eem_read_cb, NULL);
202 }
203
eem_connect(bool connected)204 static int eem_connect(bool connected)
205 {
206 if (connected) {
207 eem_read_cb(eem_ep_data[EEM_OUT_EP_IDX].ep_addr, 0, NULL);
208 } else {
209 /* Cancel any transfer */
210 usb_cancel_transfer(eem_ep_data[EEM_OUT_EP_IDX].ep_addr);
211 usb_cancel_transfer(eem_ep_data[EEM_IN_EP_IDX].ep_addr);
212 }
213
214 return 0;
215 }
216
217 static struct netusb_function eem_function = {
218 .connect_media = eem_connect,
219 .send_pkt = eem_send,
220 };
221
eem_status_interface(const uint8_t * desc)222 static inline void eem_status_interface(const uint8_t *desc)
223 {
224 const struct usb_if_descriptor *if_desc = (void *)desc;
225 uint8_t iface_num = if_desc->bInterfaceNumber;
226
227 LOG_DBG("");
228
229 if (iface_num != eem_get_first_iface_number()) {
230 return;
231 }
232
233 netusb_enable(&eem_function);
234 }
235
eem_status_cb(struct usb_cfg_data * cfg,enum usb_dc_status_code status,const uint8_t * param)236 static void eem_status_cb(struct usb_cfg_data *cfg,
237 enum usb_dc_status_code status,
238 const uint8_t *param)
239 {
240 ARG_UNUSED(cfg);
241
242 /* Check the USB status and do needed action if required */
243 switch (status) {
244 case USB_DC_DISCONNECTED:
245 LOG_DBG("USB device disconnected");
246 netusb_disable();
247 break;
248
249 case USB_DC_INTERFACE:
250 LOG_DBG("USB interface selected");
251 eem_status_interface(param);
252 break;
253
254 case USB_DC_ERROR:
255 case USB_DC_RESET:
256 case USB_DC_CONNECTED:
257 case USB_DC_CONFIGURED:
258 case USB_DC_SUSPEND:
259 case USB_DC_RESUME:
260 LOG_DBG("USB unhandled state: %d", status);
261 break;
262
263 case USB_DC_SOF:
264 break;
265
266 case USB_DC_UNKNOWN:
267 default:
268 LOG_DBG("USB unknown state: %d", status);
269 break;
270 }
271 }
272
eem_interface_config(struct usb_desc_header * head,uint8_t bInterfaceNumber)273 static void eem_interface_config(struct usb_desc_header *head,
274 uint8_t bInterfaceNumber)
275 {
276 ARG_UNUSED(head);
277
278 cdc_eem_cfg.if0.bInterfaceNumber = bInterfaceNumber;
279 }
280
281 USBD_DEFINE_CFG_DATA(cdc_eem_config) = {
282 .usb_device_description = NULL,
283 .interface_config = eem_interface_config,
284 .interface_descriptor = &cdc_eem_cfg.if0,
285 .cb_usb_status = eem_status_cb,
286 .interface = {
287 .class_handler = NULL,
288 .custom_handler = NULL,
289 .vendor_handler = NULL,
290 },
291 .num_endpoints = ARRAY_SIZE(eem_ep_data),
292 .endpoint = eem_ep_data,
293 };
294