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
161 LOG_DBG("Transfer finished %s -> ep 0x%02x, len %u, err %d",
162 c_data->name, bi->ep, buf->len, err);
163
164 if (bi->ep == lb_get_bulk_out(c_data)) {
165 atomic_clear_bit(&data->state, LB_FUNCTION_OUT_ENGAGED);
166 }
167
168 if (bi->ep == lb_get_bulk_in(c_data)) {
169 atomic_clear_bit(&data->state, LB_FUNCTION_IN_ENGAGED);
170 }
171
172 if (err) {
173 if (err == -ECONNABORTED) {
174 LOG_INF("request ep 0x%02x, len %u cancelled",
175 bi->ep, buf->len);
176 } else {
177 LOG_ERR("request ep 0x%02x, len %u failed",
178 bi->ep, buf->len);
179 }
180
181 net_buf_unref(buf);
182
183 return err;
184 }
185
186 if (bi->ep == lb_get_bulk_out(c_data)) {
187 memcpy(lb_buf, buf->data, MIN(sizeof(lb_buf), buf->len));
188 net_buf_unref(buf);
189 if (!atomic_test_bit(&data->state, LB_FUNCTION_BULK_MANUAL)) {
190 lb_submit_bulk_out(c_data);
191 }
192 }
193
194 if (bi->ep == lb_get_bulk_in(c_data)) {
195 bi->ep = lb_get_bulk_out(c_data);
196 net_buf_unref(buf);
197 if (!atomic_test_bit(&data->state, LB_FUNCTION_BULK_MANUAL)) {
198 lb_submit_bulk_in(c_data);
199 }
200 }
201
202 return 0;
203 }
204
lb_update(struct usbd_class_data * c_data,uint8_t iface,uint8_t alternate)205 static void lb_update(struct usbd_class_data *c_data,
206 uint8_t iface, uint8_t alternate)
207 {
208 LOG_DBG("Instance %p, interface %u alternate %u changed",
209 c_data, iface, alternate);
210 }
211
lb_control_to_host(struct usbd_class_data * c_data,const struct usb_setup_packet * const setup,struct net_buf * const buf)212 static int lb_control_to_host(struct usbd_class_data *c_data,
213 const struct usb_setup_packet *const setup,
214 struct net_buf *const buf)
215 {
216 if (setup->RequestType.recipient != USB_REQTYPE_RECIPIENT_DEVICE) {
217 errno = -ENOTSUP;
218 return 0;
219 }
220
221 if (setup->bRequest == LB_VENDOR_REQ_IN) {
222 net_buf_add_mem(buf, lb_buf,
223 MIN(sizeof(lb_buf), setup->wLength));
224
225 LOG_WRN("Device-to-Host, wLength %u | %zu", setup->wLength,
226 MIN(sizeof(lb_buf), setup->wLength));
227
228 return 0;
229 }
230
231 LOG_ERR("Class request 0x%x not supported", setup->bRequest);
232 errno = -ENOTSUP;
233
234 return 0;
235 }
236
lb_control_to_dev(struct usbd_class_data * c_data,const struct usb_setup_packet * const setup,const struct net_buf * const buf)237 static int lb_control_to_dev(struct usbd_class_data *c_data,
238 const struct usb_setup_packet *const setup,
239 const struct net_buf *const buf)
240 {
241 if (setup->RequestType.recipient != USB_REQTYPE_RECIPIENT_DEVICE) {
242 errno = -ENOTSUP;
243 return 0;
244 }
245
246 if (setup->bRequest == LB_VENDOR_REQ_OUT) {
247 LOG_WRN("Host-to-Device, wLength %u | %zu", setup->wLength,
248 MIN(sizeof(lb_buf), buf->len));
249 memcpy(lb_buf, buf->data, MIN(sizeof(lb_buf), buf->len));
250 return 0;
251 }
252
253 LOG_ERR("Class request 0x%x not supported", setup->bRequest);
254 errno = -ENOTSUP;
255
256 return 0;
257 }
258
lb_get_desc(struct usbd_class_data * const c_data,const enum usbd_speed speed)259 static void *lb_get_desc(struct usbd_class_data *const c_data,
260 const enum usbd_speed speed)
261 {
262 struct lb_data *data = usbd_class_get_private(c_data);
263
264 if (speed == USBD_SPEED_HS) {
265 return data->hs_desc;
266 }
267
268 return data->fs_desc;
269 }
270
lb_enable(struct usbd_class_data * const c_data)271 static void lb_enable(struct usbd_class_data *const c_data)
272 {
273 struct lb_data *data = usbd_class_get_private(c_data);
274
275 LOG_INF("Enable %s", c_data->name);
276 if (!atomic_test_and_set_bit(&data->state, LB_FUNCTION_ENABLED) &&
277 !atomic_test_bit(&data->state, LB_FUNCTION_BULK_MANUAL)) {
278 lb_submit_bulk_out(c_data);
279 lb_submit_bulk_in(c_data);
280 }
281 }
282
lb_disable(struct usbd_class_data * const c_data)283 static void lb_disable(struct usbd_class_data *const c_data)
284 {
285 struct lb_data *data = usbd_class_get_private(c_data);
286
287 atomic_clear_bit(&data->state, LB_FUNCTION_ENABLED);
288 LOG_INF("Disable %s", c_data->name);
289 }
290
lb_init(struct usbd_class_data * c_data)291 static int lb_init(struct usbd_class_data *c_data)
292 {
293 LOG_DBG("Init class instance %p", c_data);
294
295 return 0;
296 }
297
298 struct usbd_class_api lb_api = {
299 .update = lb_update,
300 .control_to_host = lb_control_to_host,
301 .control_to_dev = lb_control_to_dev,
302 .request = lb_request_handler,
303 .get_desc = lb_get_desc,
304 .enable = lb_enable,
305 .disable = lb_disable,
306 .init = lb_init,
307 };
308
309 #define DEFINE_LOOPBACK_DESCRIPTOR(x, _) \
310 static struct loopback_desc lb_desc_##x = { \
311 .iad = { \
312 .bLength = sizeof(struct usb_association_descriptor), \
313 .bDescriptorType = USB_DESC_INTERFACE_ASSOC, \
314 .bFirstInterface = 0, \
315 .bInterfaceCount = 3, \
316 .bFunctionClass = USB_BCC_VENDOR, \
317 .bFunctionSubClass = 0, \
318 .bFunctionProtocol = 0, \
319 .iFunction = 0, \
320 }, \
321 \
322 /* Interface descriptor 0 */ \
323 .if0 = { \
324 .bLength = sizeof(struct usb_if_descriptor), \
325 .bDescriptorType = USB_DESC_INTERFACE, \
326 .bInterfaceNumber = 0, \
327 .bAlternateSetting = 0, \
328 .bNumEndpoints = 2, \
329 .bInterfaceClass = USB_BCC_VENDOR, \
330 .bInterfaceSubClass = 0, \
331 .bInterfaceProtocol = 0, \
332 .iInterface = 0, \
333 }, \
334 \
335 /* Data Endpoint OUT */ \
336 .if0_out_ep = { \
337 .bLength = sizeof(struct usb_ep_descriptor), \
338 .bDescriptorType = USB_DESC_ENDPOINT, \
339 .bEndpointAddress = 0x01, \
340 .bmAttributes = USB_EP_TYPE_BULK, \
341 .wMaxPacketSize = sys_cpu_to_le16(64U), \
342 .bInterval = 0x00, \
343 }, \
344 \
345 /* Data Endpoint IN */ \
346 .if0_in_ep = { \
347 .bLength = sizeof(struct usb_ep_descriptor), \
348 .bDescriptorType = USB_DESC_ENDPOINT, \
349 .bEndpointAddress = 0x81, \
350 .bmAttributes = USB_EP_TYPE_BULK, \
351 .wMaxPacketSize = sys_cpu_to_le16(64U), \
352 .bInterval = 0x00, \
353 }, \
354 \
355 /* Data Endpoint OUT */ \
356 .if0_hs_out_ep = { \
357 .bLength = sizeof(struct usb_ep_descriptor), \
358 .bDescriptorType = USB_DESC_ENDPOINT, \
359 .bEndpointAddress = 0x01, \
360 .bmAttributes = USB_EP_TYPE_BULK, \
361 .wMaxPacketSize = sys_cpu_to_le16(512), \
362 .bInterval = 0x00, \
363 }, \
364 \
365 /* Data Endpoint IN */ \
366 .if0_hs_in_ep = { \
367 .bLength = sizeof(struct usb_ep_descriptor), \
368 .bDescriptorType = USB_DESC_ENDPOINT, \
369 .bEndpointAddress = 0x81, \
370 .bmAttributes = USB_EP_TYPE_BULK, \
371 .wMaxPacketSize = sys_cpu_to_le16(512), \
372 .bInterval = 0x00, \
373 }, \
374 \
375 /* Interface descriptor 1 */ \
376 .if1 = { \
377 .bLength = sizeof(struct usb_if_descriptor), \
378 .bDescriptorType = USB_DESC_INTERFACE, \
379 .bInterfaceNumber = 1, \
380 .bAlternateSetting = 0, \
381 .bNumEndpoints = 2, \
382 .bInterfaceClass = USB_BCC_VENDOR, \
383 .bInterfaceSubClass = 0, \
384 .bInterfaceProtocol = 0, \
385 .iInterface = 0, \
386 }, \
387 \
388 /* Interface Interrupt Endpoint OUT */ \
389 .if1_int_out_ep = { \
390 .bLength = sizeof(struct usb_ep_descriptor), \
391 .bDescriptorType = USB_DESC_ENDPOINT, \
392 .bEndpointAddress = 0x02, \
393 .bmAttributes = USB_EP_TYPE_INTERRUPT, \
394 .wMaxPacketSize = sys_cpu_to_le16(64), \
395 .bInterval = 0x01, \
396 }, \
397 \
398 /* Interrupt Interrupt Endpoint IN */ \
399 .if1_int_in_ep = { \
400 .bLength = sizeof(struct usb_ep_descriptor), \
401 .bDescriptorType = USB_DESC_ENDPOINT, \
402 .bEndpointAddress = 0x82, \
403 .bmAttributes = USB_EP_TYPE_INTERRUPT, \
404 .wMaxPacketSize = sys_cpu_to_le16(64), \
405 .bInterval = 0x01, \
406 }, \
407 \
408 .if2_0 = { \
409 .bLength = sizeof(struct usb_if_descriptor), \
410 .bDescriptorType = USB_DESC_INTERFACE, \
411 .bInterfaceNumber = 2, \
412 .bAlternateSetting = 0, \
413 .bNumEndpoints = 2, \
414 .bInterfaceClass = USB_BCC_VENDOR, \
415 .bInterfaceSubClass = 0, \
416 .bInterfaceProtocol = 0, \
417 .iInterface = 0, \
418 }, \
419 \
420 .if2_0_iso_in_ep = { \
421 .bLength = sizeof(struct usb_ep_descriptor), \
422 .bDescriptorType = USB_DESC_ENDPOINT, \
423 .bEndpointAddress = 0x83, \
424 .bmAttributes = USB_EP_TYPE_ISO, \
425 .wMaxPacketSize = sys_cpu_to_le16(0), \
426 .bInterval = LB_ISO_EP_INTERVAL, \
427 }, \
428 \
429 .if2_0_iso_out_ep = { \
430 .bLength = sizeof(struct usb_ep_descriptor), \
431 .bDescriptorType = USB_DESC_ENDPOINT, \
432 .bEndpointAddress = 0x03, \
433 .bmAttributes = USB_EP_TYPE_ISO, \
434 .wMaxPacketSize = sys_cpu_to_le16(0), \
435 .bInterval = LB_ISO_EP_INTERVAL, \
436 }, \
437 \
438 .if2_1 = { \
439 .bLength = sizeof(struct usb_if_descriptor), \
440 .bDescriptorType = USB_DESC_INTERFACE, \
441 .bInterfaceNumber = 2, \
442 .bAlternateSetting = 1, \
443 .bNumEndpoints = 2, \
444 .bInterfaceClass = USB_BCC_VENDOR, \
445 .bInterfaceSubClass = 0, \
446 .bInterfaceProtocol = 0, \
447 .iInterface = 0, \
448 }, \
449 \
450 .if2_1_iso_in_ep = { \
451 .bLength = sizeof(struct usb_ep_descriptor), \
452 .bDescriptorType = USB_DESC_ENDPOINT, \
453 .bEndpointAddress = 0x83, \
454 .bmAttributes = USB_EP_TYPE_ISO, \
455 .wMaxPacketSize = sys_cpu_to_le16(LB_ISO_EP_MPS), \
456 .bInterval = LB_ISO_EP_INTERVAL, \
457 }, \
458 \
459 .if2_1_iso_out_ep = { \
460 .bLength = sizeof(struct usb_ep_descriptor), \
461 .bDescriptorType = USB_DESC_ENDPOINT, \
462 .bEndpointAddress = 0x03, \
463 .bmAttributes = USB_EP_TYPE_ISO, \
464 .wMaxPacketSize = sys_cpu_to_le16(LB_ISO_EP_MPS), \
465 .bInterval = LB_ISO_EP_INTERVAL, \
466 }, \
467 \
468 /* Termination descriptor */ \
469 .nil_desc = { \
470 .bLength = 0, \
471 .bDescriptorType = 0, \
472 }, \
473 }; \
474 \
475 const static struct usb_desc_header *lb_fs_desc_##x[] = { \
476 (struct usb_desc_header *) &lb_desc_##x.iad, \
477 (struct usb_desc_header *) &lb_desc_##x.if0, \
478 (struct usb_desc_header *) &lb_desc_##x.if0_in_ep, \
479 (struct usb_desc_header *) &lb_desc_##x.if0_out_ep, \
480 (struct usb_desc_header *) &lb_desc_##x.if1, \
481 (struct usb_desc_header *) &lb_desc_##x.if1_int_in_ep, \
482 (struct usb_desc_header *) &lb_desc_##x.if1_int_out_ep, \
483 (struct usb_desc_header *) &lb_desc_##x.if2_0, \
484 (struct usb_desc_header *) &lb_desc_##x.if2_0_iso_in_ep, \
485 (struct usb_desc_header *) &lb_desc_##x.if2_0_iso_out_ep, \
486 (struct usb_desc_header *) &lb_desc_##x.if2_1, \
487 (struct usb_desc_header *) &lb_desc_##x.if2_1_iso_in_ep, \
488 (struct usb_desc_header *) &lb_desc_##x.if2_1_iso_out_ep, \
489 (struct usb_desc_header *) &lb_desc_##x.nil_desc, \
490 }; \
491 \
492 const static struct usb_desc_header *lb_hs_desc_##x[] = { \
493 (struct usb_desc_header *) &lb_desc_##x.iad, \
494 (struct usb_desc_header *) &lb_desc_##x.if0, \
495 (struct usb_desc_header *) &lb_desc_##x.if0_hs_in_ep, \
496 (struct usb_desc_header *) &lb_desc_##x.if0_hs_out_ep, \
497 (struct usb_desc_header *) &lb_desc_##x.if1, \
498 (struct usb_desc_header *) &lb_desc_##x.if1_int_in_ep, \
499 (struct usb_desc_header *) &lb_desc_##x.if1_int_out_ep, \
500 (struct usb_desc_header *) &lb_desc_##x.if2_0, \
501 (struct usb_desc_header *) &lb_desc_##x.if2_0_iso_in_ep, \
502 (struct usb_desc_header *) &lb_desc_##x.if2_0_iso_out_ep, \
503 (struct usb_desc_header *) &lb_desc_##x.if2_1, \
504 (struct usb_desc_header *) &lb_desc_##x.if2_1_iso_in_ep, \
505 (struct usb_desc_header *) &lb_desc_##x.if2_1_iso_out_ep, \
506 (struct usb_desc_header *) &lb_desc_##x.nil_desc, \
507 };
508
509
510 #define DEFINE_LOOPBACK_CLASS_DATA(x, _) \
511 static struct lb_data lb_data_##x = { \
512 .desc = &lb_desc_##x, \
513 .fs_desc = lb_fs_desc_##x, \
514 .hs_desc = lb_hs_desc_##x, \
515 }; \
516 \
517 USBD_DEFINE_CLASS(loopback_##x, &lb_api, &lb_data_##x, &lb_vregs);
518
519 LISTIFY(CONFIG_USBD_LOOPBACK_INSTANCES_COUNT, DEFINE_LOOPBACK_DESCRIPTOR, ())
520 LISTIFY(CONFIG_USBD_LOOPBACK_INSTANCES_COUNT, DEFINE_LOOPBACK_CLASS_DATA, ())
521
522 #if CONFIG_USBD_SHELL
523
524 /*
525 * Device and Host Troubleshooting Shell Commands
526 *
527 * When set to manual mode, the function does not automatically submit new
528 * transfers. The user can manually enqueue or not enqueue new transfers, so
529 * the NAK behavior can also be tested.
530 *
531 * Only bulk endpoints are supported at this time.
532 */
533
set_manual(struct usbd_class_data * const c_data,const bool on)534 static void set_manual(struct usbd_class_data *const c_data, const bool on)
535 {
536 struct lb_data *data = usbd_class_get_private(c_data);
537
538 if (on) {
539 atomic_set_bit(&data->state, LB_FUNCTION_BULK_MANUAL);
540 } else {
541 atomic_clear_bit(&data->state, LB_FUNCTION_BULK_MANUAL);
542 }
543 }
544
lb_get_node(const struct shell * const sh,const char * const name)545 static struct usbd_class_node *lb_get_node(const struct shell *const sh,
546 const char *const name)
547 {
548 STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) {
549 if (strcmp(c_nd->c_data->name, name) == 0) {
550 return c_nd;
551 }
552 }
553
554 shell_error(sh, "Function %s could not be found", name);
555
556 return NULL;
557 }
558
cmd_manual_on(const struct shell * sh,size_t argc,char ** argv)559 static int cmd_manual_on(const struct shell *sh, size_t argc, char **argv)
560 {
561 struct usbd_class_node *c_nd;
562
563 c_nd = lb_get_node(sh, argv[1]);
564 if (c_nd == NULL) {
565 return ENODEV;
566 }
567
568 shell_print(sh, "%s bulk transfers can be submitted from the shell", argv[1]);
569 set_manual(c_nd->c_data, true);
570
571 return 0;
572 }
573
cmd_manual_off(const struct shell * sh,size_t argc,char ** argv)574 static int cmd_manual_off(const struct shell *sh, size_t argc, char **argv)
575 {
576 struct usbd_class_node *c_nd;
577
578 c_nd = lb_get_node(sh, argv[1]);
579 if (c_nd == NULL) {
580 return ENODEV;
581 }
582
583 shell_print(sh, "%s bulk endpoints are automatically engaged", argv[1]);
584 set_manual(c_nd->c_data, false);
585
586 return 0;
587 }
588
cmd_enqueue_out(const struct shell * sh,size_t argc,char ** argv)589 static int cmd_enqueue_out(const struct shell *sh, size_t argc, char **argv)
590 {
591 struct usbd_class_node *c_nd;
592 int err;
593
594 c_nd = lb_get_node(sh, argv[1]);
595 if (c_nd == NULL) {
596 return ENODEV;
597 }
598
599 err = lb_submit_bulk_out(c_nd->c_data);
600 if (err == -EPERM) {
601 shell_error(sh, "%s is not enabled", argv[1]);
602 } else if (err == -EBUSY) {
603 shell_error(sh, "%s bulk OUT endpoint is busy", argv[1]);
604 } else if (err == -ENOMEM) {
605 shell_error(sh, "%s failed to allocate transfer", argv[1]);
606 } else if (err) {
607 shell_error(sh, "%s failed to enqueue transfer", argv[1]);
608 } else {
609 shell_print(sh, "%s, new transfer enqueued", argv[1]);
610 }
611
612 return err;
613 }
614
cmd_enqueue_in(const struct shell * sh,size_t argc,char ** argv)615 static int cmd_enqueue_in(const struct shell *sh, size_t argc, char **argv)
616 {
617 struct usbd_class_node *c_nd;
618 int err;
619
620 c_nd = lb_get_node(sh, argv[1]);
621 if (c_nd == NULL) {
622 return ENODEV;
623 }
624
625 err = lb_submit_bulk_in(c_nd->c_data);
626 if (err == -EPERM) {
627 shell_error(sh, "%s is not enabled", argv[1]);
628 } else if (err == -EBUSY) {
629 shell_error(sh, "%s bulk IN endpoint is busy", argv[1]);
630 } else if (err == -ENOMEM) {
631 shell_error(sh, "%s failed to allocate transfer", argv[1]);
632 } else if (err) {
633 shell_error(sh, "%s failed to enqueue transfer", argv[1]);
634 } else {
635 shell_print(sh, "%s, new transfer enqueued", argv[1]);
636 }
637
638 return err;
639 }
640
lb_node_name_lookup(size_t idx,struct shell_static_entry * entry)641 static void lb_node_name_lookup(size_t idx, struct shell_static_entry *entry)
642 {
643 size_t match_idx = 0;
644
645 entry->syntax = NULL;
646 entry->handler = NULL;
647 entry->help = NULL;
648 entry->subcmd = NULL;
649
650 STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) {
651 if (c_nd->c_data->name != NULL && strlen(c_nd->c_data->name) != 0) {
652 if (match_idx == idx) {
653 entry->syntax = c_nd->c_data->name;
654 break;
655 }
656
657 ++match_idx;
658 }
659 }
660 }
661
662 SHELL_DYNAMIC_CMD_CREATE(dsub_node_name, lb_node_name_lookup);
663
664 SHELL_STATIC_SUBCMD_SET_CREATE(sub_cmd_manual,
665 SHELL_CMD_ARG(off, &dsub_node_name, "<function name>", cmd_manual_off, 2, 0),
666 SHELL_CMD_ARG(on, &dsub_node_name, "<function name>", cmd_manual_on, 2, 0),
667 SHELL_SUBCMD_SET_END
668 );
669
670 SHELL_STATIC_SUBCMD_SET_CREATE(sub_cmd_enqueue,
671 SHELL_CMD_ARG(out, &dsub_node_name, "<function name>", cmd_enqueue_out, 2, 0),
672 SHELL_CMD_ARG(in, &dsub_node_name, "<function name>", cmd_enqueue_in, 2, 0),
673 SHELL_SUBCMD_SET_END
674 );
675
676 SHELL_STATIC_SUBCMD_SET_CREATE(lb_bulk_cmds,
677 SHELL_CMD_ARG(manual, &sub_cmd_manual, "off on", NULL, 2, 0),
678 SHELL_CMD_ARG(enqueue, &sub_cmd_enqueue, "out in", NULL, 2, 0),
679 SHELL_SUBCMD_SET_END
680 );
681
682 SHELL_STATIC_SUBCMD_SET_CREATE(sub_lb_cmds,
683 SHELL_CMD_ARG(bulk, &lb_bulk_cmds, "bulk endpoint commands", NULL, 2, 0),
684 SHELL_SUBCMD_SET_END
685 );
686
687 SHELL_CMD_REGISTER(lb, &sub_lb_cmds, "USB device loopback function commands", NULL);
688
689 #endif
690