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