1 /*
2 * Copyright (c) 2022 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/usb/usbd.h>
8 #include <zephyr/drivers/usb/udc.h>
9
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(usb_loopback, CONFIG_USBD_LOOPBACK_LOG_LEVEL);
12
13 /*
14 * NOTE: this class is experimental and is in development.
15 * Primary purpose currently is testing of the class initialization and
16 * interface and endpoint configuration.
17 */
18
19 /* Internal buffer for intermediate test data */
20 static uint8_t lb_buf[1024];
21
22 #define LB_VENDOR_REQ_OUT 0x5b
23 #define LB_VENDOR_REQ_IN 0x5c
24
25 #define LB_ISO_EP_MPS 256
26 #define LB_ISO_EP_INTERVAL 1
27
28 /* Make supported vendor request visible for the device stack */
29 static const struct usbd_cctx_vendor_req lb_vregs =
30 USBD_VENDOR_REQ(LB_VENDOR_REQ_OUT, LB_VENDOR_REQ_IN);
31
32 struct loopback_desc {
33 struct usb_association_descriptor iad;
34 struct usb_if_descriptor if0;
35 struct usb_ep_descriptor if0_out_ep;
36 struct usb_ep_descriptor if0_in_ep;
37 struct usb_ep_descriptor if0_hs_out_ep;
38 struct usb_ep_descriptor if0_hs_in_ep;
39 struct usb_if_descriptor if1;
40 struct usb_ep_descriptor if1_int_out_ep;
41 struct usb_ep_descriptor if1_int_in_ep;
42 struct usb_if_descriptor if2_0;
43 struct usb_ep_descriptor if2_0_iso_in_ep;
44 struct usb_ep_descriptor if2_0_iso_out_ep;
45 struct usb_if_descriptor if2_1;
46 struct usb_ep_descriptor if2_1_iso_in_ep;
47 struct usb_ep_descriptor if2_1_iso_out_ep;
48 struct usb_desc_header nil_desc;
49 };
50
51 struct lb_data {
52 struct loopback_desc *const desc;
53 const struct usb_desc_header **const fs_desc;
54 const struct usb_desc_header **const hs_desc;
55 atomic_t state;
56 };
57
lb_update(struct usbd_class_data * c_data,uint8_t iface,uint8_t alternate)58 static void lb_update(struct usbd_class_data *c_data,
59 uint8_t iface, uint8_t alternate)
60 {
61 LOG_DBG("Instance %p, interface %u alternate %u changed",
62 c_data, iface, alternate);
63 }
64
lb_control_to_host(struct usbd_class_data * c_data,const struct usb_setup_packet * const setup,struct net_buf * const buf)65 static int lb_control_to_host(struct usbd_class_data *c_data,
66 const struct usb_setup_packet *const setup,
67 struct net_buf *const buf)
68 {
69 if (setup->RequestType.recipient != USB_REQTYPE_RECIPIENT_DEVICE) {
70 errno = -ENOTSUP;
71 return 0;
72 }
73
74 if (setup->bRequest == LB_VENDOR_REQ_IN) {
75 net_buf_add_mem(buf, lb_buf,
76 MIN(sizeof(lb_buf), setup->wLength));
77
78 LOG_WRN("Device-to-Host, wLength %u | %zu", setup->wLength,
79 MIN(sizeof(lb_buf), setup->wLength));
80
81 return 0;
82 }
83
84 LOG_ERR("Class request 0x%x not supported", setup->bRequest);
85 errno = -ENOTSUP;
86
87 return 0;
88 }
89
lb_control_to_dev(struct usbd_class_data * c_data,const struct usb_setup_packet * const setup,const struct net_buf * const buf)90 static int lb_control_to_dev(struct usbd_class_data *c_data,
91 const struct usb_setup_packet *const setup,
92 const struct net_buf *const buf)
93 {
94 if (setup->RequestType.recipient != USB_REQTYPE_RECIPIENT_DEVICE) {
95 errno = -ENOTSUP;
96 return 0;
97 }
98
99 if (setup->bRequest == LB_VENDOR_REQ_OUT) {
100 LOG_WRN("Host-to-Device, wLength %u | %zu", setup->wLength,
101 MIN(sizeof(lb_buf), buf->len));
102 memcpy(lb_buf, buf->data, MIN(sizeof(lb_buf), buf->len));
103 return 0;
104 }
105
106 LOG_ERR("Class request 0x%x not supported", setup->bRequest);
107 errno = -ENOTSUP;
108
109 return 0;
110 }
111
lb_request_handler(struct usbd_class_data * c_data,struct net_buf * buf,int err)112 static int lb_request_handler(struct usbd_class_data *c_data,
113 struct net_buf *buf, int err)
114 {
115 struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
116 struct udc_buf_info *bi = NULL;
117
118 bi = (struct udc_buf_info *)net_buf_user_data(buf);
119 LOG_DBG("%p -> ep 0x%02x, len %u, err %d", c_data, bi->ep, buf->len, err);
120
121 return usbd_ep_buf_free(uds_ctx, buf);
122 }
123
lb_get_desc(struct usbd_class_data * const c_data,const enum usbd_speed speed)124 static void *lb_get_desc(struct usbd_class_data *const c_data,
125 const enum usbd_speed speed)
126 {
127 struct lb_data *data = usbd_class_get_private(c_data);
128
129 if (speed == USBD_SPEED_HS) {
130 return data->hs_desc;
131 }
132
133 return data->fs_desc;
134 }
135
lb_init(struct usbd_class_data * c_data)136 static int lb_init(struct usbd_class_data *c_data)
137 {
138 LOG_DBG("Init class instance %p", c_data);
139
140 return 0;
141 }
142
143 struct usbd_class_api lb_api = {
144 .update = lb_update,
145 .control_to_host = lb_control_to_host,
146 .control_to_dev = lb_control_to_dev,
147 .request = lb_request_handler,
148 .get_desc = lb_get_desc,
149 .init = lb_init,
150 };
151
152 #define DEFINE_LOOPBACK_DESCRIPTOR(x, _) \
153 static struct loopback_desc lb_desc_##x = { \
154 .iad = { \
155 .bLength = sizeof(struct usb_association_descriptor), \
156 .bDescriptorType = USB_DESC_INTERFACE_ASSOC, \
157 .bFirstInterface = 0, \
158 .bInterfaceCount = 3, \
159 .bFunctionClass = USB_BCC_VENDOR, \
160 .bFunctionSubClass = 0, \
161 .bFunctionProtocol = 0, \
162 .iFunction = 0, \
163 }, \
164 \
165 /* Interface descriptor 0 */ \
166 .if0 = { \
167 .bLength = sizeof(struct usb_if_descriptor), \
168 .bDescriptorType = USB_DESC_INTERFACE, \
169 .bInterfaceNumber = 0, \
170 .bAlternateSetting = 0, \
171 .bNumEndpoints = 2, \
172 .bInterfaceClass = USB_BCC_VENDOR, \
173 .bInterfaceSubClass = 0, \
174 .bInterfaceProtocol = 0, \
175 .iInterface = 0, \
176 }, \
177 \
178 /* Data Endpoint OUT */ \
179 .if0_out_ep = { \
180 .bLength = sizeof(struct usb_ep_descriptor), \
181 .bDescriptorType = USB_DESC_ENDPOINT, \
182 .bEndpointAddress = 0x01, \
183 .bmAttributes = USB_EP_TYPE_BULK, \
184 .wMaxPacketSize = sys_cpu_to_le16(64U), \
185 .bInterval = 0x00, \
186 }, \
187 \
188 /* Data Endpoint IN */ \
189 .if0_in_ep = { \
190 .bLength = sizeof(struct usb_ep_descriptor), \
191 .bDescriptorType = USB_DESC_ENDPOINT, \
192 .bEndpointAddress = 0x81, \
193 .bmAttributes = USB_EP_TYPE_BULK, \
194 .wMaxPacketSize = sys_cpu_to_le16(64U), \
195 .bInterval = 0x00, \
196 }, \
197 \
198 /* Data Endpoint OUT */ \
199 .if0_hs_out_ep = { \
200 .bLength = sizeof(struct usb_ep_descriptor), \
201 .bDescriptorType = USB_DESC_ENDPOINT, \
202 .bEndpointAddress = 0x01, \
203 .bmAttributes = USB_EP_TYPE_BULK, \
204 .wMaxPacketSize = sys_cpu_to_le16(512), \
205 .bInterval = 0x00, \
206 }, \
207 \
208 /* Data Endpoint IN */ \
209 .if0_hs_in_ep = { \
210 .bLength = sizeof(struct usb_ep_descriptor), \
211 .bDescriptorType = USB_DESC_ENDPOINT, \
212 .bEndpointAddress = 0x81, \
213 .bmAttributes = USB_EP_TYPE_BULK, \
214 .wMaxPacketSize = sys_cpu_to_le16(512), \
215 .bInterval = 0x00, \
216 }, \
217 \
218 /* Interface descriptor 1 */ \
219 .if1 = { \
220 .bLength = sizeof(struct usb_if_descriptor), \
221 .bDescriptorType = USB_DESC_INTERFACE, \
222 .bInterfaceNumber = 1, \
223 .bAlternateSetting = 0, \
224 .bNumEndpoints = 2, \
225 .bInterfaceClass = USB_BCC_VENDOR, \
226 .bInterfaceSubClass = 0, \
227 .bInterfaceProtocol = 0, \
228 .iInterface = 0, \
229 }, \
230 \
231 /* Interface Interrupt Endpoint OUT */ \
232 .if1_int_out_ep = { \
233 .bLength = sizeof(struct usb_ep_descriptor), \
234 .bDescriptorType = USB_DESC_ENDPOINT, \
235 .bEndpointAddress = 0x02, \
236 .bmAttributes = USB_EP_TYPE_INTERRUPT, \
237 .wMaxPacketSize = sys_cpu_to_le16(64), \
238 .bInterval = 0x01, \
239 }, \
240 \
241 /* Interrupt Interrupt Endpoint IN */ \
242 .if1_int_in_ep = { \
243 .bLength = sizeof(struct usb_ep_descriptor), \
244 .bDescriptorType = USB_DESC_ENDPOINT, \
245 .bEndpointAddress = 0x82, \
246 .bmAttributes = USB_EP_TYPE_INTERRUPT, \
247 .wMaxPacketSize = sys_cpu_to_le16(64), \
248 .bInterval = 0x01, \
249 }, \
250 \
251 .if2_0 = { \
252 .bLength = sizeof(struct usb_if_descriptor), \
253 .bDescriptorType = USB_DESC_INTERFACE, \
254 .bInterfaceNumber = 2, \
255 .bAlternateSetting = 0, \
256 .bNumEndpoints = 2, \
257 .bInterfaceClass = USB_BCC_VENDOR, \
258 .bInterfaceSubClass = 0, \
259 .bInterfaceProtocol = 0, \
260 .iInterface = 0, \
261 }, \
262 \
263 .if2_0_iso_in_ep = { \
264 .bLength = sizeof(struct usb_ep_descriptor), \
265 .bDescriptorType = USB_DESC_ENDPOINT, \
266 .bEndpointAddress = 0x83, \
267 .bmAttributes = USB_EP_TYPE_ISO, \
268 .wMaxPacketSize = sys_cpu_to_le16(0), \
269 .bInterval = LB_ISO_EP_INTERVAL, \
270 }, \
271 \
272 .if2_0_iso_out_ep = { \
273 .bLength = sizeof(struct usb_ep_descriptor), \
274 .bDescriptorType = USB_DESC_ENDPOINT, \
275 .bEndpointAddress = 0x03, \
276 .bmAttributes = USB_EP_TYPE_ISO, \
277 .wMaxPacketSize = sys_cpu_to_le16(0), \
278 .bInterval = LB_ISO_EP_INTERVAL, \
279 }, \
280 \
281 .if2_1 = { \
282 .bLength = sizeof(struct usb_if_descriptor), \
283 .bDescriptorType = USB_DESC_INTERFACE, \
284 .bInterfaceNumber = 2, \
285 .bAlternateSetting = 1, \
286 .bNumEndpoints = 2, \
287 .bInterfaceClass = USB_BCC_VENDOR, \
288 .bInterfaceSubClass = 0, \
289 .bInterfaceProtocol = 0, \
290 .iInterface = 0, \
291 }, \
292 \
293 .if2_1_iso_in_ep = { \
294 .bLength = sizeof(struct usb_ep_descriptor), \
295 .bDescriptorType = USB_DESC_ENDPOINT, \
296 .bEndpointAddress = 0x83, \
297 .bmAttributes = USB_EP_TYPE_ISO, \
298 .wMaxPacketSize = sys_cpu_to_le16(LB_ISO_EP_MPS), \
299 .bInterval = LB_ISO_EP_INTERVAL, \
300 }, \
301 \
302 .if2_1_iso_out_ep = { \
303 .bLength = sizeof(struct usb_ep_descriptor), \
304 .bDescriptorType = USB_DESC_ENDPOINT, \
305 .bEndpointAddress = 0x03, \
306 .bmAttributes = USB_EP_TYPE_ISO, \
307 .wMaxPacketSize = sys_cpu_to_le16(LB_ISO_EP_MPS), \
308 .bInterval = LB_ISO_EP_INTERVAL, \
309 }, \
310 \
311 /* Termination descriptor */ \
312 .nil_desc = { \
313 .bLength = 0, \
314 .bDescriptorType = 0, \
315 }, \
316 }; \
317 \
318 const static struct usb_desc_header *lb_fs_desc_##x[] = { \
319 (struct usb_desc_header *) &lb_desc_##x.iad, \
320 (struct usb_desc_header *) &lb_desc_##x.if0, \
321 (struct usb_desc_header *) &lb_desc_##x.if0_in_ep, \
322 (struct usb_desc_header *) &lb_desc_##x.if0_out_ep, \
323 (struct usb_desc_header *) &lb_desc_##x.if1, \
324 (struct usb_desc_header *) &lb_desc_##x.if1_int_in_ep, \
325 (struct usb_desc_header *) &lb_desc_##x.if1_int_out_ep, \
326 (struct usb_desc_header *) &lb_desc_##x.if2_0, \
327 (struct usb_desc_header *) &lb_desc_##x.if2_0_iso_in_ep, \
328 (struct usb_desc_header *) &lb_desc_##x.if2_0_iso_out_ep, \
329 (struct usb_desc_header *) &lb_desc_##x.if2_1, \
330 (struct usb_desc_header *) &lb_desc_##x.if2_1_iso_in_ep, \
331 (struct usb_desc_header *) &lb_desc_##x.if2_1_iso_out_ep, \
332 (struct usb_desc_header *) &lb_desc_##x.nil_desc, \
333 }; \
334 \
335 const static struct usb_desc_header *lb_hs_desc_##x[] = { \
336 (struct usb_desc_header *) &lb_desc_##x.iad, \
337 (struct usb_desc_header *) &lb_desc_##x.if0, \
338 (struct usb_desc_header *) &lb_desc_##x.if0_hs_in_ep, \
339 (struct usb_desc_header *) &lb_desc_##x.if0_hs_out_ep, \
340 (struct usb_desc_header *) &lb_desc_##x.if1, \
341 (struct usb_desc_header *) &lb_desc_##x.if1_int_in_ep, \
342 (struct usb_desc_header *) &lb_desc_##x.if1_int_out_ep, \
343 (struct usb_desc_header *) &lb_desc_##x.if2_0, \
344 (struct usb_desc_header *) &lb_desc_##x.if2_0_iso_in_ep, \
345 (struct usb_desc_header *) &lb_desc_##x.if2_0_iso_out_ep, \
346 (struct usb_desc_header *) &lb_desc_##x.if2_1, \
347 (struct usb_desc_header *) &lb_desc_##x.if2_1_iso_in_ep, \
348 (struct usb_desc_header *) &lb_desc_##x.if2_1_iso_out_ep, \
349 (struct usb_desc_header *) &lb_desc_##x.nil_desc, \
350 };
351
352
353 #define DEFINE_LOOPBACK_CLASS_DATA(x, _) \
354 static struct lb_data lb_data_##x = { \
355 .desc = &lb_desc_##x, \
356 .fs_desc = lb_fs_desc_##x, \
357 .hs_desc = lb_hs_desc_##x, \
358 }; \
359 \
360 USBD_DEFINE_CLASS(loopback_##x, &lb_api, &lb_data_##x, &lb_vregs);
361
362 LISTIFY(CONFIG_USBD_LOOPBACK_INSTANCES_COUNT, DEFINE_LOOPBACK_DESCRIPTOR, ())
363 LISTIFY(CONFIG_USBD_LOOPBACK_INSTANCES_COUNT, DEFINE_LOOPBACK_CLASS_DATA, ())
364