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