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