1 /*
2 * Copyright (c) 2022, 2025 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/usb/usbd.h>
8 #include <zephyr/shell/shell.h>
9 #include <zephyr/drivers/usb/udc.h>
10
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_REGISTER(usb_loopback, CONFIG_USBD_LOOPBACK_LOG_LEVEL);
13
14 /*
15 * This function does not define its own pool and requires a large enough UDC
16 * pool. To use it with the Linux kernel tool testusb, we need about 4096 bytes
17 * in the current configuration.
18 */
19
20 /*
21 * NOTE: this class is experimental and is in development.
22 * Primary purpose currently is testing of the class initialization and
23 * interface and endpoint configuration.
24 */
25
26 /* Internal buffer for intermediate test data */
27 static uint8_t lb_buf[1024];
28
29 #define LB_VENDOR_REQ_OUT 0x5b
30 #define LB_VENDOR_REQ_IN 0x5c
31
32 #define LB_ISO_EP_MPS 256
33 #define LB_ISO_EP_INTERVAL 1
34
35 #define LB_FUNCTION_ENABLED 0
36 #define LB_FUNCTION_BULK_MANUAL 1
37 #define LB_FUNCTION_IN_ENGAGED 2
38 #define LB_FUNCTION_OUT_ENGAGED 3
39
40 /* Make supported vendor request visible for the device stack */
41 static const struct usbd_cctx_vendor_req lb_vregs =
42 USBD_VENDOR_REQ(LB_VENDOR_REQ_OUT, LB_VENDOR_REQ_IN);
43
44 struct loopback_desc {
45 struct usb_association_descriptor iad;
46 struct usb_if_descriptor if0;
47 struct usb_ep_descriptor if0_out_ep;
48 struct usb_ep_descriptor if0_in_ep;
49 struct usb_ep_descriptor if0_hs_out_ep;
50 struct usb_ep_descriptor if0_hs_in_ep;
51 struct usb_if_descriptor if1;
52 struct usb_ep_descriptor if1_int_out_ep;
53 struct usb_ep_descriptor if1_int_in_ep;
54 struct usb_if_descriptor if2_0;
55 struct usb_ep_descriptor if2_0_iso_in_ep;
56 struct usb_ep_descriptor if2_0_iso_out_ep;
57 struct usb_if_descriptor if2_1;
58 struct usb_ep_descriptor if2_1_iso_in_ep;
59 struct usb_ep_descriptor if2_1_iso_out_ep;
60 struct usb_desc_header nil_desc;
61 };
62
63 struct lb_data {
64 struct loopback_desc *const desc;
65 const struct usb_desc_header **const fs_desc;
66 const struct usb_desc_header **const hs_desc;
67 atomic_t state;
68 };
69
lb_get_bulk_out(struct usbd_class_data * const c_data)70 static uint8_t lb_get_bulk_out(struct usbd_class_data *const c_data)
71 {
72 struct lb_data *data = usbd_class_get_private(c_data);
73 struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
74 struct loopback_desc *desc = data->desc;
75
76 if (usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) {
77 return desc->if0_hs_out_ep.bEndpointAddress;
78 }
79
80 return desc->if0_out_ep.bEndpointAddress;
81 }
82
lb_get_bulk_in(struct usbd_class_data * const c_data)83 static uint8_t lb_get_bulk_in(struct usbd_class_data *const c_data)
84 {
85 struct lb_data *data = usbd_class_get_private(c_data);
86 struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
87 struct loopback_desc *desc = data->desc;
88
89 if (usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) {
90 return desc->if0_hs_in_ep.bEndpointAddress;
91 }
92
93 return desc->if0_in_ep.bEndpointAddress;
94 }
95
lb_submit_bulk_out(struct usbd_class_data * const c_data)96 static int lb_submit_bulk_out(struct usbd_class_data *const c_data)
97 {
98 struct lb_data *data = usbd_class_get_private(c_data);
99 struct net_buf *buf;
100 int err;
101
102 if (!atomic_test_bit(&data->state, LB_FUNCTION_ENABLED)) {
103 return -EPERM;
104 }
105
106 if (atomic_test_and_set_bit(&data->state, LB_FUNCTION_OUT_ENGAGED)) {
107 return -EBUSY;
108 }
109
110 buf = usbd_ep_buf_alloc(c_data, lb_get_bulk_out(c_data), sizeof(lb_buf));
111 if (buf == NULL) {
112 LOG_ERR("Failed to allocate buffer");
113 return -ENOMEM;
114 }
115
116 err = usbd_ep_enqueue(c_data, buf);
117 if (err) {
118 LOG_ERR("Failed to enqueue buffer");
119 net_buf_unref(buf);
120 }
121
122 return err;
123 }
124
lb_submit_bulk_in(struct usbd_class_data * const c_data)125 static int lb_submit_bulk_in(struct usbd_class_data *const c_data)
126 {
127 struct lb_data *data = usbd_class_get_private(c_data);
128 struct net_buf *buf;
129 int err;
130
131 if (!atomic_test_bit(&data->state, LB_FUNCTION_ENABLED)) {
132 return -EPERM;
133 }
134
135 if (atomic_test_and_set_bit(&data->state, LB_FUNCTION_IN_ENGAGED)) {
136 return -EBUSY;
137 }
138
139 buf = usbd_ep_buf_alloc(c_data, lb_get_bulk_in(c_data), sizeof(lb_buf));
140 if (buf == NULL) {
141 LOG_ERR("Failed to allocate buffer");
142 return -ENOMEM;
143 }
144
145 net_buf_add_mem(buf, lb_buf, MIN(sizeof(lb_buf), net_buf_tailroom(buf)));
146 err = usbd_ep_enqueue(c_data, buf);
147 if (err) {
148 LOG_ERR("Failed to enqueue buffer");
149 net_buf_unref(buf);
150 }
151
152 return err;
153 }
154
lb_request_handler(struct usbd_class_data * const c_data,struct net_buf * const buf,const int err)155 static int lb_request_handler(struct usbd_class_data *const c_data,
156 struct net_buf *const buf, const int err)
157 {
158 struct udc_buf_info *bi = (struct udc_buf_info *)net_buf_user_data(buf);
159 struct lb_data *data = usbd_class_get_private(c_data);
160 const size_t len = buf->len;
161 const uint8_t ep = bi->ep;
162 int ret = 0;
163
164 if (bi->ep == lb_get_bulk_out(c_data)) {
165 atomic_clear_bit(&data->state, LB_FUNCTION_OUT_ENGAGED);
166 if (err == 0) {
167 memcpy(lb_buf, buf->data, MIN(sizeof(lb_buf), buf->len));
168 }
169 }
170
171 if (bi->ep == lb_get_bulk_in(c_data)) {
172 atomic_clear_bit(&data->state, LB_FUNCTION_IN_ENGAGED);
173 }
174
175 net_buf_unref(buf);
176 if (err == -ECONNABORTED) {
177 LOG_INF("Transfer ep 0x%02x, len %zu cancelled", ep, len);
178 } else if (err != 0) {
179 LOG_ERR("Transfer ep 0x%02x, len %zu failed", ep, len);
180 ret = err;
181 } else {
182 LOG_DBG("Transfer ep 0x%02x, len %zu finished", ep, len);
183 }
184
185 if (!atomic_test_bit(&data->state, LB_FUNCTION_BULK_MANUAL)) {
186 if (ep == lb_get_bulk_out(c_data)) {
187 lb_submit_bulk_out(c_data);
188 }
189
190 if (ep == lb_get_bulk_in(c_data)) {
191 lb_submit_bulk_in(c_data);
192 }
193 }
194
195 return ret;
196 }
197
lb_update(struct usbd_class_data * c_data,uint8_t iface,uint8_t alternate)198 static void lb_update(struct usbd_class_data *c_data,
199 uint8_t iface, uint8_t alternate)
200 {
201 LOG_DBG("Instance %p, interface %u alternate %u changed",
202 c_data, iface, alternate);
203 }
204
lb_control_to_host(struct usbd_class_data * c_data,const struct usb_setup_packet * const setup,struct net_buf * const buf)205 static int lb_control_to_host(struct usbd_class_data *c_data,
206 const struct usb_setup_packet *const setup,
207 struct net_buf *const buf)
208 {
209 if (setup->RequestType.recipient != USB_REQTYPE_RECIPIENT_DEVICE) {
210 errno = -ENOTSUP;
211 return 0;
212 }
213
214 if (setup->bRequest == LB_VENDOR_REQ_IN) {
215 net_buf_add_mem(buf, lb_buf,
216 MIN(sizeof(lb_buf), setup->wLength));
217
218 LOG_WRN("Device-to-Host, wLength %u | %zu", setup->wLength,
219 MIN(sizeof(lb_buf), setup->wLength));
220
221 return 0;
222 }
223
224 LOG_ERR("Class request 0x%x not supported", setup->bRequest);
225 errno = -ENOTSUP;
226
227 return 0;
228 }
229
lb_control_to_dev(struct usbd_class_data * c_data,const struct usb_setup_packet * const setup,const struct net_buf * const buf)230 static int lb_control_to_dev(struct usbd_class_data *c_data,
231 const struct usb_setup_packet *const setup,
232 const struct net_buf *const buf)
233 {
234 if (setup->RequestType.recipient != USB_REQTYPE_RECIPIENT_DEVICE) {
235 errno = -ENOTSUP;
236 return 0;
237 }
238
239 if (setup->bRequest == LB_VENDOR_REQ_OUT) {
240 LOG_WRN("Host-to-Device, wLength %u | %zu", setup->wLength,
241 MIN(sizeof(lb_buf), buf->len));
242 memcpy(lb_buf, buf->data, MIN(sizeof(lb_buf), buf->len));
243 return 0;
244 }
245
246 LOG_ERR("Class request 0x%x not supported", setup->bRequest);
247 errno = -ENOTSUP;
248
249 return 0;
250 }
251
lb_get_desc(struct usbd_class_data * const c_data,const enum usbd_speed speed)252 static void *lb_get_desc(struct usbd_class_data *const c_data,
253 const enum usbd_speed speed)
254 {
255 struct lb_data *data = usbd_class_get_private(c_data);
256
257 if (USBD_SUPPORTS_HIGH_SPEED && speed == USBD_SPEED_HS) {
258 return data->hs_desc;
259 }
260
261 return data->fs_desc;
262 }
263
lb_enable(struct usbd_class_data * const c_data)264 static void lb_enable(struct usbd_class_data *const c_data)
265 {
266 struct lb_data *data = usbd_class_get_private(c_data);
267
268 LOG_INF("Enable %s", c_data->name);
269 if (!atomic_test_and_set_bit(&data->state, LB_FUNCTION_ENABLED) &&
270 !atomic_test_bit(&data->state, LB_FUNCTION_BULK_MANUAL)) {
271 lb_submit_bulk_out(c_data);
272 lb_submit_bulk_in(c_data);
273 }
274 }
275
lb_disable(struct usbd_class_data * const c_data)276 static void lb_disable(struct usbd_class_data *const c_data)
277 {
278 struct lb_data *data = usbd_class_get_private(c_data);
279
280 atomic_clear_bit(&data->state, LB_FUNCTION_ENABLED);
281 LOG_INF("Disable %s", c_data->name);
282 }
283
lb_init(struct usbd_class_data * c_data)284 static int lb_init(struct usbd_class_data *c_data)
285 {
286 LOG_DBG("Init class instance %p", c_data);
287
288 return 0;
289 }
290
291 struct usbd_class_api lb_api = {
292 .update = lb_update,
293 .control_to_host = lb_control_to_host,
294 .control_to_dev = lb_control_to_dev,
295 .request = lb_request_handler,
296 .get_desc = lb_get_desc,
297 .enable = lb_enable,
298 .disable = lb_disable,
299 .init = lb_init,
300 };
301
302 #define DEFINE_LOOPBACK_DESCRIPTOR(x, _) \
303 static struct loopback_desc lb_desc_##x = { \
304 .iad = { \
305 .bLength = sizeof(struct usb_association_descriptor), \
306 .bDescriptorType = USB_DESC_INTERFACE_ASSOC, \
307 .bFirstInterface = 0, \
308 .bInterfaceCount = 3, \
309 .bFunctionClass = USB_BCC_VENDOR, \
310 .bFunctionSubClass = 0, \
311 .bFunctionProtocol = 0, \
312 .iFunction = 0, \
313 }, \
314 \
315 /* Interface descriptor 0 */ \
316 .if0 = { \
317 .bLength = sizeof(struct usb_if_descriptor), \
318 .bDescriptorType = USB_DESC_INTERFACE, \
319 .bInterfaceNumber = 0, \
320 .bAlternateSetting = 0, \
321 .bNumEndpoints = 2, \
322 .bInterfaceClass = USB_BCC_VENDOR, \
323 .bInterfaceSubClass = 0, \
324 .bInterfaceProtocol = 0, \
325 .iInterface = 0, \
326 }, \
327 \
328 /* Data Endpoint OUT */ \
329 .if0_out_ep = { \
330 .bLength = sizeof(struct usb_ep_descriptor), \
331 .bDescriptorType = USB_DESC_ENDPOINT, \
332 .bEndpointAddress = 0x01, \
333 .bmAttributes = USB_EP_TYPE_BULK, \
334 .wMaxPacketSize = sys_cpu_to_le16(64U), \
335 .bInterval = 0x00, \
336 }, \
337 \
338 /* Data Endpoint IN */ \
339 .if0_in_ep = { \
340 .bLength = sizeof(struct usb_ep_descriptor), \
341 .bDescriptorType = USB_DESC_ENDPOINT, \
342 .bEndpointAddress = 0x81, \
343 .bmAttributes = USB_EP_TYPE_BULK, \
344 .wMaxPacketSize = sys_cpu_to_le16(64U), \
345 .bInterval = 0x00, \
346 }, \
347 \
348 /* Data Endpoint OUT */ \
349 .if0_hs_out_ep = { \
350 .bLength = sizeof(struct usb_ep_descriptor), \
351 .bDescriptorType = USB_DESC_ENDPOINT, \
352 .bEndpointAddress = 0x01, \
353 .bmAttributes = USB_EP_TYPE_BULK, \
354 .wMaxPacketSize = sys_cpu_to_le16(512), \
355 .bInterval = 0x00, \
356 }, \
357 \
358 /* Data Endpoint IN */ \
359 .if0_hs_in_ep = { \
360 .bLength = sizeof(struct usb_ep_descriptor), \
361 .bDescriptorType = USB_DESC_ENDPOINT, \
362 .bEndpointAddress = 0x81, \
363 .bmAttributes = USB_EP_TYPE_BULK, \
364 .wMaxPacketSize = sys_cpu_to_le16(512), \
365 .bInterval = 0x00, \
366 }, \
367 \
368 /* Interface descriptor 1 */ \
369 .if1 = { \
370 .bLength = sizeof(struct usb_if_descriptor), \
371 .bDescriptorType = USB_DESC_INTERFACE, \
372 .bInterfaceNumber = 1, \
373 .bAlternateSetting = 0, \
374 .bNumEndpoints = 2, \
375 .bInterfaceClass = USB_BCC_VENDOR, \
376 .bInterfaceSubClass = 0, \
377 .bInterfaceProtocol = 0, \
378 .iInterface = 0, \
379 }, \
380 \
381 /* Interface Interrupt Endpoint OUT */ \
382 .if1_int_out_ep = { \
383 .bLength = sizeof(struct usb_ep_descriptor), \
384 .bDescriptorType = USB_DESC_ENDPOINT, \
385 .bEndpointAddress = 0x02, \
386 .bmAttributes = USB_EP_TYPE_INTERRUPT, \
387 .wMaxPacketSize = sys_cpu_to_le16(64), \
388 .bInterval = 0x01, \
389 }, \
390 \
391 /* Interrupt Interrupt Endpoint IN */ \
392 .if1_int_in_ep = { \
393 .bLength = sizeof(struct usb_ep_descriptor), \
394 .bDescriptorType = USB_DESC_ENDPOINT, \
395 .bEndpointAddress = 0x82, \
396 .bmAttributes = USB_EP_TYPE_INTERRUPT, \
397 .wMaxPacketSize = sys_cpu_to_le16(64), \
398 .bInterval = 0x01, \
399 }, \
400 \
401 .if2_0 = { \
402 .bLength = sizeof(struct usb_if_descriptor), \
403 .bDescriptorType = USB_DESC_INTERFACE, \
404 .bInterfaceNumber = 2, \
405 .bAlternateSetting = 0, \
406 .bNumEndpoints = 2, \
407 .bInterfaceClass = USB_BCC_VENDOR, \
408 .bInterfaceSubClass = 0, \
409 .bInterfaceProtocol = 0, \
410 .iInterface = 0, \
411 }, \
412 \
413 .if2_0_iso_in_ep = { \
414 .bLength = sizeof(struct usb_ep_descriptor), \
415 .bDescriptorType = USB_DESC_ENDPOINT, \
416 .bEndpointAddress = 0x83, \
417 .bmAttributes = USB_EP_TYPE_ISO, \
418 .wMaxPacketSize = sys_cpu_to_le16(0), \
419 .bInterval = LB_ISO_EP_INTERVAL, \
420 }, \
421 \
422 .if2_0_iso_out_ep = { \
423 .bLength = sizeof(struct usb_ep_descriptor), \
424 .bDescriptorType = USB_DESC_ENDPOINT, \
425 .bEndpointAddress = 0x03, \
426 .bmAttributes = USB_EP_TYPE_ISO, \
427 .wMaxPacketSize = sys_cpu_to_le16(0), \
428 .bInterval = LB_ISO_EP_INTERVAL, \
429 }, \
430 \
431 .if2_1 = { \
432 .bLength = sizeof(struct usb_if_descriptor), \
433 .bDescriptorType = USB_DESC_INTERFACE, \
434 .bInterfaceNumber = 2, \
435 .bAlternateSetting = 1, \
436 .bNumEndpoints = 2, \
437 .bInterfaceClass = USB_BCC_VENDOR, \
438 .bInterfaceSubClass = 0, \
439 .bInterfaceProtocol = 0, \
440 .iInterface = 0, \
441 }, \
442 \
443 .if2_1_iso_in_ep = { \
444 .bLength = sizeof(struct usb_ep_descriptor), \
445 .bDescriptorType = USB_DESC_ENDPOINT, \
446 .bEndpointAddress = 0x83, \
447 .bmAttributes = USB_EP_TYPE_ISO, \
448 .wMaxPacketSize = sys_cpu_to_le16(LB_ISO_EP_MPS), \
449 .bInterval = LB_ISO_EP_INTERVAL, \
450 }, \
451 \
452 .if2_1_iso_out_ep = { \
453 .bLength = sizeof(struct usb_ep_descriptor), \
454 .bDescriptorType = USB_DESC_ENDPOINT, \
455 .bEndpointAddress = 0x03, \
456 .bmAttributes = USB_EP_TYPE_ISO, \
457 .wMaxPacketSize = sys_cpu_to_le16(LB_ISO_EP_MPS), \
458 .bInterval = LB_ISO_EP_INTERVAL, \
459 }, \
460 \
461 /* Termination descriptor */ \
462 .nil_desc = { \
463 .bLength = 0, \
464 .bDescriptorType = 0, \
465 }, \
466 }; \
467 \
468 const static struct usb_desc_header *lb_fs_desc_##x[] = { \
469 (struct usb_desc_header *) &lb_desc_##x.iad, \
470 (struct usb_desc_header *) &lb_desc_##x.if0, \
471 (struct usb_desc_header *) &lb_desc_##x.if0_in_ep, \
472 (struct usb_desc_header *) &lb_desc_##x.if0_out_ep, \
473 (struct usb_desc_header *) &lb_desc_##x.if1, \
474 (struct usb_desc_header *) &lb_desc_##x.if1_int_in_ep, \
475 (struct usb_desc_header *) &lb_desc_##x.if1_int_out_ep, \
476 (struct usb_desc_header *) &lb_desc_##x.if2_0, \
477 (struct usb_desc_header *) &lb_desc_##x.if2_0_iso_in_ep, \
478 (struct usb_desc_header *) &lb_desc_##x.if2_0_iso_out_ep, \
479 (struct usb_desc_header *) &lb_desc_##x.if2_1, \
480 (struct usb_desc_header *) &lb_desc_##x.if2_1_iso_in_ep, \
481 (struct usb_desc_header *) &lb_desc_##x.if2_1_iso_out_ep, \
482 (struct usb_desc_header *) &lb_desc_##x.nil_desc, \
483 }; \
484 \
485 const static struct usb_desc_header *lb_hs_desc_##x[] = { \
486 (struct usb_desc_header *) &lb_desc_##x.iad, \
487 (struct usb_desc_header *) &lb_desc_##x.if0, \
488 (struct usb_desc_header *) &lb_desc_##x.if0_hs_in_ep, \
489 (struct usb_desc_header *) &lb_desc_##x.if0_hs_out_ep, \
490 (struct usb_desc_header *) &lb_desc_##x.if1, \
491 (struct usb_desc_header *) &lb_desc_##x.if1_int_in_ep, \
492 (struct usb_desc_header *) &lb_desc_##x.if1_int_out_ep, \
493 (struct usb_desc_header *) &lb_desc_##x.if2_0, \
494 (struct usb_desc_header *) &lb_desc_##x.if2_0_iso_in_ep, \
495 (struct usb_desc_header *) &lb_desc_##x.if2_0_iso_out_ep, \
496 (struct usb_desc_header *) &lb_desc_##x.if2_1, \
497 (struct usb_desc_header *) &lb_desc_##x.if2_1_iso_in_ep, \
498 (struct usb_desc_header *) &lb_desc_##x.if2_1_iso_out_ep, \
499 (struct usb_desc_header *) &lb_desc_##x.nil_desc, \
500 };
501
502
503 #define DEFINE_LOOPBACK_CLASS_DATA(x, _) \
504 static struct lb_data lb_data_##x = { \
505 .desc = &lb_desc_##x, \
506 .fs_desc = lb_fs_desc_##x, \
507 .hs_desc = lb_hs_desc_##x, \
508 }; \
509 \
510 USBD_DEFINE_CLASS(loopback_##x, &lb_api, &lb_data_##x, &lb_vregs);
511
512 LISTIFY(CONFIG_USBD_LOOPBACK_INSTANCES_COUNT, DEFINE_LOOPBACK_DESCRIPTOR, ())
513 LISTIFY(CONFIG_USBD_LOOPBACK_INSTANCES_COUNT, DEFINE_LOOPBACK_CLASS_DATA, ())
514
515 #if CONFIG_USBD_SHELL
516
517 /*
518 * Device and Host Troubleshooting Shell Commands
519 *
520 * When set to manual mode, the function does not automatically submit new
521 * transfers. The user can manually enqueue or not enqueue new transfers, so
522 * the NAK behavior can also be tested.
523 *
524 * Only bulk endpoints are supported at this time.
525 */
526
set_manual(struct usbd_class_data * const c_data,const bool on)527 static void set_manual(struct usbd_class_data *const c_data, const bool on)
528 {
529 struct lb_data *data = usbd_class_get_private(c_data);
530
531 if (on) {
532 atomic_set_bit(&data->state, LB_FUNCTION_BULK_MANUAL);
533 } else {
534 atomic_clear_bit(&data->state, LB_FUNCTION_BULK_MANUAL);
535 }
536 }
537
lb_get_node(const struct shell * const sh,const char * const name)538 static struct usbd_class_node *lb_get_node(const struct shell *const sh,
539 const char *const name)
540 {
541 STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) {
542 if (strcmp(c_nd->c_data->name, name) == 0) {
543 return c_nd;
544 }
545 }
546
547 shell_error(sh, "Function %s could not be found", name);
548
549 return NULL;
550 }
551
cmd_manual_on(const struct shell * sh,size_t argc,char ** argv)552 static int cmd_manual_on(const struct shell *sh, size_t argc, char **argv)
553 {
554 struct usbd_class_node *c_nd;
555
556 c_nd = lb_get_node(sh, argv[1]);
557 if (c_nd == NULL) {
558 return ENODEV;
559 }
560
561 shell_print(sh, "%s bulk transfers can be submitted from the shell", argv[1]);
562 set_manual(c_nd->c_data, true);
563
564 return 0;
565 }
566
cmd_manual_off(const struct shell * sh,size_t argc,char ** argv)567 static int cmd_manual_off(const struct shell *sh, size_t argc, char **argv)
568 {
569 struct usbd_class_node *c_nd;
570
571 c_nd = lb_get_node(sh, argv[1]);
572 if (c_nd == NULL) {
573 return ENODEV;
574 }
575
576 shell_print(sh, "%s bulk endpoints are automatically engaged", argv[1]);
577 set_manual(c_nd->c_data, false);
578
579 return 0;
580 }
581
cmd_enqueue_out(const struct shell * sh,size_t argc,char ** argv)582 static int cmd_enqueue_out(const struct shell *sh, size_t argc, char **argv)
583 {
584 struct usbd_class_node *c_nd;
585 int err;
586
587 c_nd = lb_get_node(sh, argv[1]);
588 if (c_nd == NULL) {
589 return ENODEV;
590 }
591
592 err = lb_submit_bulk_out(c_nd->c_data);
593 if (err == -EPERM) {
594 shell_error(sh, "%s is not enabled", argv[1]);
595 } else if (err == -EBUSY) {
596 shell_error(sh, "%s bulk OUT endpoint is busy", argv[1]);
597 } else if (err == -ENOMEM) {
598 shell_error(sh, "%s failed to allocate transfer", argv[1]);
599 } else if (err) {
600 shell_error(sh, "%s failed to enqueue transfer", argv[1]);
601 } else {
602 shell_print(sh, "%s, new transfer enqueued", argv[1]);
603 }
604
605 return err;
606 }
607
cmd_enqueue_in(const struct shell * sh,size_t argc,char ** argv)608 static int cmd_enqueue_in(const struct shell *sh, size_t argc, char **argv)
609 {
610 struct usbd_class_node *c_nd;
611 int err;
612
613 c_nd = lb_get_node(sh, argv[1]);
614 if (c_nd == NULL) {
615 return ENODEV;
616 }
617
618 err = lb_submit_bulk_in(c_nd->c_data);
619 if (err == -EPERM) {
620 shell_error(sh, "%s is not enabled", argv[1]);
621 } else if (err == -EBUSY) {
622 shell_error(sh, "%s bulk IN endpoint is busy", argv[1]);
623 } else if (err == -ENOMEM) {
624 shell_error(sh, "%s failed to allocate transfer", argv[1]);
625 } else if (err) {
626 shell_error(sh, "%s failed to enqueue transfer", argv[1]);
627 } else {
628 shell_print(sh, "%s, new transfer enqueued", argv[1]);
629 }
630
631 return err;
632 }
633
lb_node_name_lookup(size_t idx,struct shell_static_entry * entry)634 static void lb_node_name_lookup(size_t idx, struct shell_static_entry *entry)
635 {
636 size_t match_idx = 0;
637
638 entry->syntax = NULL;
639 entry->handler = NULL;
640 entry->help = NULL;
641 entry->subcmd = NULL;
642
643 STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) {
644 if (c_nd->c_data->name != NULL && strlen(c_nd->c_data->name) != 0) {
645 if (match_idx == idx) {
646 entry->syntax = c_nd->c_data->name;
647 break;
648 }
649
650 ++match_idx;
651 }
652 }
653 }
654
655 SHELL_DYNAMIC_CMD_CREATE(dsub_node_name, lb_node_name_lookup);
656
657 SHELL_STATIC_SUBCMD_SET_CREATE(sub_cmd_manual,
658 SHELL_CMD_ARG(off, &dsub_node_name, "<function name>", cmd_manual_off, 2, 0),
659 SHELL_CMD_ARG(on, &dsub_node_name, "<function name>", cmd_manual_on, 2, 0),
660 SHELL_SUBCMD_SET_END
661 );
662
663 SHELL_STATIC_SUBCMD_SET_CREATE(sub_cmd_enqueue,
664 SHELL_CMD_ARG(out, &dsub_node_name, "<function name>", cmd_enqueue_out, 2, 0),
665 SHELL_CMD_ARG(in, &dsub_node_name, "<function name>", cmd_enqueue_in, 2, 0),
666 SHELL_SUBCMD_SET_END
667 );
668
669 SHELL_STATIC_SUBCMD_SET_CREATE(lb_bulk_cmds,
670 SHELL_CMD_ARG(manual, &sub_cmd_manual, "off on", NULL, 2, 0),
671 SHELL_CMD_ARG(enqueue, &sub_cmd_enqueue, "out in", NULL, 2, 0),
672 SHELL_SUBCMD_SET_END
673 );
674
675 SHELL_STATIC_SUBCMD_SET_CREATE(sub_lb_cmds,
676 SHELL_CMD_ARG(bulk, &lb_bulk_cmds, "bulk endpoint commands", NULL, 2, 0),
677 SHELL_SUBCMD_SET_END
678 );
679
680 SHELL_CMD_REGISTER(lb, &sub_lb_cmds, "USB device loopback function commands", NULL);
681
682 #endif
683