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 /* Make supported vendor request visible for the device stack */
26 static const struct usbd_cctx_vendor_req lb_vregs =
27 	USBD_VENDOR_REQ(LB_VENDOR_REQ_OUT, LB_VENDOR_REQ_IN);
28 
29 struct loopback_desc {
30 	struct usb_if_descriptor if0;
31 	struct usb_ep_descriptor if0_out_ep;
32 	struct usb_ep_descriptor if0_in_ep;
33 	struct usb_ep_descriptor if0_int_out_ep;
34 	struct usb_ep_descriptor if0_int_in_ep;
35 	struct usb_ep_descriptor if0_iso_out_ep;
36 	struct usb_ep_descriptor if0_iso_in_ep;
37 	struct usb_if_descriptor if1;
38 	struct usb_if_descriptor if2;
39 	struct usb_ep_descriptor if2_out_ep;
40 	struct usb_ep_descriptor if2_in_ep;
41 	struct usb_if_descriptor if3;
42 	struct usb_ep_descriptor if3_out_ep;
43 	struct usb_ep_descriptor if3_in_ep;
44 	struct usb_desc_header nil_desc;
45 } __packed;
46 
47 #define DEFINE_LOOPBACK_DESCRIPTOR(x, _)			\
48 static struct loopback_desc lb_desc_##x = {			\
49 	/* Interface descriptor 0 */				\
50 	.if0 = {						\
51 		.bLength = sizeof(struct usb_if_descriptor),	\
52 		.bDescriptorType = USB_DESC_INTERFACE,		\
53 		.bInterfaceNumber = 0,				\
54 		.bAlternateSetting = 0,				\
55 		.bNumEndpoints = 6,				\
56 		.bInterfaceClass = USB_BCC_VENDOR,		\
57 		.bInterfaceSubClass = 0,			\
58 		.bInterfaceProtocol = 0,			\
59 		.iInterface = 0,				\
60 	},							\
61 								\
62 	/* Data Endpoint OUT */					\
63 	.if0_out_ep = {						\
64 		.bLength = sizeof(struct usb_ep_descriptor),	\
65 		.bDescriptorType = USB_DESC_ENDPOINT,		\
66 		.bEndpointAddress = 0x01,			\
67 		.bmAttributes = USB_EP_TYPE_BULK,		\
68 		.wMaxPacketSize = 0,				\
69 		.bInterval = 0x00,				\
70 	},							\
71 								\
72 	/* Data Endpoint IN */					\
73 	.if0_in_ep = {						\
74 		.bLength = sizeof(struct usb_ep_descriptor),	\
75 		.bDescriptorType = USB_DESC_ENDPOINT,		\
76 		.bEndpointAddress = 0x81,			\
77 		.bmAttributes = USB_EP_TYPE_BULK,		\
78 		.wMaxPacketSize = 0,				\
79 		.bInterval = 0x00,				\
80 	},							\
81 								\
82 	/* Interface Endpoint OUT */				\
83 	.if0_int_out_ep = {					\
84 		.bLength = sizeof(struct usb_ep_descriptor),	\
85 		.bDescriptorType = USB_DESC_ENDPOINT,		\
86 		.bEndpointAddress = 0x03,			\
87 		.bmAttributes = USB_EP_TYPE_INTERRUPT,		\
88 		.wMaxPacketSize = 0,				\
89 		.bInterval = 0x01,				\
90 	},							\
91 								\
92 	/* Interrupt Endpoint IN */				\
93 	.if0_int_in_ep = {					\
94 		.bLength = sizeof(struct usb_ep_descriptor),	\
95 		.bDescriptorType = USB_DESC_ENDPOINT,		\
96 		.bEndpointAddress = 0x83,			\
97 		.bmAttributes = USB_EP_TYPE_INTERRUPT,		\
98 		.wMaxPacketSize = 0,				\
99 		.bInterval = 0x01,				\
100 	},							\
101 								\
102 	/* Endpoint ISO OUT */					\
103 	.if0_iso_out_ep = {					\
104 		.bLength = sizeof(struct usb_ep_descriptor),	\
105 		.bDescriptorType = USB_DESC_ENDPOINT,		\
106 		.bEndpointAddress = 0x03,			\
107 		.bmAttributes = USB_EP_TYPE_ISO,		\
108 		.wMaxPacketSize = 0,				\
109 		.bInterval = 0x01,				\
110 	},							\
111 								\
112 	/* Endpoint ISO IN */					\
113 	.if0_iso_in_ep = {					\
114 		.bLength = sizeof(struct usb_ep_descriptor),	\
115 		.bDescriptorType = USB_DESC_ENDPOINT,		\
116 		.bEndpointAddress = 0x83,			\
117 		.bmAttributes = USB_EP_TYPE_ISO,		\
118 		.wMaxPacketSize = 0,				\
119 		.bInterval = 0x01,				\
120 	},							\
121 								\
122 	/* Interface descriptor 1, no endpoints */		\
123 	.if1 = {						\
124 		.bLength = sizeof(struct usb_if_descriptor),	\
125 		.bDescriptorType = USB_DESC_INTERFACE,		\
126 		.bInterfaceNumber = 1,				\
127 		.bAlternateSetting = 0,				\
128 		.bNumEndpoints = 0,				\
129 		.bInterfaceClass = USB_BCC_VENDOR,		\
130 		.bInterfaceSubClass = 0,			\
131 		.bInterfaceProtocol = 0,			\
132 		.iInterface = 0,				\
133 	},							\
134 								\
135 	/* Interface descriptor 1 */				\
136 	.if2 = {						\
137 		.bLength = sizeof(struct usb_if_descriptor),	\
138 		.bDescriptorType = USB_DESC_INTERFACE,		\
139 		.bInterfaceNumber = 1,				\
140 		.bAlternateSetting = 1,				\
141 		.bNumEndpoints = 2,				\
142 		.bInterfaceClass = USB_BCC_VENDOR,		\
143 		.bInterfaceSubClass = 0,			\
144 		.bInterfaceProtocol = 0,			\
145 		.iInterface = 0,				\
146 	},							\
147 								\
148 	/* Data Endpoint OUT */					\
149 	.if2_out_ep = {						\
150 		.bLength = sizeof(struct usb_ep_descriptor),	\
151 		.bDescriptorType = USB_DESC_ENDPOINT,		\
152 		.bEndpointAddress = 0x02,			\
153 		.bmAttributes = USB_EP_TYPE_BULK,		\
154 		.wMaxPacketSize = 32,				\
155 		.bInterval = 0x00,				\
156 	},							\
157 								\
158 	/* Data Endpoint IN */					\
159 	.if2_in_ep = {						\
160 		.bLength = sizeof(struct usb_ep_descriptor),	\
161 		.bDescriptorType = USB_DESC_ENDPOINT,		\
162 		.bEndpointAddress = 0x82,			\
163 		.bmAttributes = USB_EP_TYPE_BULK,		\
164 		.wMaxPacketSize = 32,				\
165 		.bInterval = 0x00,				\
166 	},							\
167 								\
168 	/* Interface descriptor 1 */				\
169 	.if3 = {						\
170 		.bLength = sizeof(struct usb_if_descriptor),	\
171 		.bDescriptorType = USB_DESC_INTERFACE,		\
172 		.bInterfaceNumber = 1,				\
173 		.bAlternateSetting = 2,				\
174 		.bNumEndpoints = 2,				\
175 		.bInterfaceClass = USB_BCC_VENDOR,		\
176 		.bInterfaceSubClass = 0,			\
177 		.bInterfaceProtocol = 0,			\
178 		.iInterface = 0,				\
179 	},							\
180 								\
181 	/* Data Endpoint OUT, get wMaxPacketSize from UDC */	\
182 	.if3_out_ep = {						\
183 		.bLength = sizeof(struct usb_ep_descriptor),	\
184 		.bDescriptorType = USB_DESC_ENDPOINT,		\
185 		.bEndpointAddress = 0x02,			\
186 		.bmAttributes = USB_EP_TYPE_BULK,		\
187 		.wMaxPacketSize = 0,				\
188 		.bInterval = 0x00,				\
189 	},							\
190 								\
191 	/* Data Endpoint IN, get wMaxPacketSize from UDC */	\
192 	.if3_in_ep = {						\
193 		.bLength = sizeof(struct usb_ep_descriptor),	\
194 		.bDescriptorType = USB_DESC_ENDPOINT,		\
195 		.bEndpointAddress = 0x82,			\
196 		.bmAttributes = USB_EP_TYPE_BULK,		\
197 		.wMaxPacketSize = 0,				\
198 		.bInterval = 0x00,				\
199 	},							\
200 								\
201 	/* Termination descriptor */				\
202 	.nil_desc = {						\
203 		.bLength = 0,					\
204 		.bDescriptorType = 0,				\
205 	},							\
206 };								\
207 
lb_update(struct usbd_class_node * c_nd,uint8_t iface,uint8_t alternate)208 static void lb_update(struct usbd_class_node *c_nd,
209 		      uint8_t iface, uint8_t alternate)
210 {
211 	LOG_DBG("Instance %p, interface %u alternate %u changed",
212 		c_nd, iface, alternate);
213 }
214 
lb_control_to_host(struct usbd_class_node * c_nd,const struct usb_setup_packet * const setup,struct net_buf * const buf)215 static int lb_control_to_host(struct usbd_class_node *c_nd,
216 			      const struct usb_setup_packet *const setup,
217 			      struct net_buf *const buf)
218 {
219 	if (setup->RequestType.recipient != USB_REQTYPE_RECIPIENT_DEVICE) {
220 		errno = -ENOTSUP;
221 		return 0;
222 	}
223 
224 	if (setup->bRequest == LB_VENDOR_REQ_IN) {
225 		net_buf_add_mem(buf, lb_buf,
226 				MIN(sizeof(lb_buf), setup->wLength));
227 
228 		LOG_WRN("Device-to-Host, wLength %u | %zu", setup->wLength,
229 			MIN(sizeof(lb_buf), setup->wLength));
230 
231 		return 0;
232 	}
233 
234 	LOG_ERR("Class request 0x%x not supported", setup->bRequest);
235 	errno = -ENOTSUP;
236 
237 	return 0;
238 }
239 
lb_control_to_dev(struct usbd_class_node * c_nd,const struct usb_setup_packet * const setup,const struct net_buf * const buf)240 static int lb_control_to_dev(struct usbd_class_node *c_nd,
241 			     const struct usb_setup_packet *const setup,
242 			     const struct net_buf *const buf)
243 {
244 	if (setup->RequestType.recipient != USB_REQTYPE_RECIPIENT_DEVICE) {
245 		errno = -ENOTSUP;
246 		return 0;
247 	}
248 
249 	if (setup->bRequest == LB_VENDOR_REQ_OUT) {
250 		LOG_WRN("Host-to-Device, wLength %u | %zu", setup->wLength,
251 			MIN(sizeof(lb_buf), buf->len));
252 		memcpy(lb_buf, buf->data, MIN(sizeof(lb_buf), buf->len));
253 		return 0;
254 	}
255 
256 	LOG_ERR("Class request 0x%x not supported", setup->bRequest);
257 	errno = -ENOTSUP;
258 
259 	return 0;
260 }
261 
lb_request_handler(struct usbd_class_node * c_nd,struct net_buf * buf,int err)262 static int lb_request_handler(struct usbd_class_node *c_nd,
263 			      struct net_buf *buf, int err)
264 {
265 	struct udc_buf_info *bi = NULL;
266 
267 	bi = (struct udc_buf_info *)net_buf_user_data(buf);
268 	LOG_DBG("%p -> ep 0x%02x, len %u, err %d", c_nd, bi->ep, buf->len, err);
269 	usbd_ep_buf_free(c_nd->data->uds_ctx, buf);
270 
271 	return 0;
272 }
273 
lb_init(struct usbd_class_node * c_nd)274 static int lb_init(struct usbd_class_node *c_nd)
275 {
276 	LOG_DBG("Init class instance %p", c_nd);
277 
278 	return 0;
279 }
280 
281 struct usbd_class_api lb_api = {
282 	.update = lb_update,
283 	.control_to_host = lb_control_to_host,
284 	.control_to_dev = lb_control_to_dev,
285 	.request = lb_request_handler,
286 	.init = lb_init,
287 };
288 
289 #define DEFINE_LOOPBACK_CLASS_DATA(x, _)				\
290 	static struct usbd_class_data lb_class_##x = {			\
291 		.desc = (struct usb_desc_header *)&lb_desc_##x,		\
292 		.v_reqs = &lb_vregs,					\
293 	};								\
294 									\
295 	USBD_DEFINE_CLASS(loopback_##x, &lb_api, &lb_class_##x);
296 
297 LISTIFY(CONFIG_USBD_LOOPBACK_INSTANCES_COUNT, DEFINE_LOOPBACK_DESCRIPTOR, ())
298 LISTIFY(CONFIG_USBD_LOOPBACK_INSTANCES_COUNT, DEFINE_LOOPBACK_CLASS_DATA, ())
299