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/toolchain.h>
9 #include <zephyr/drivers/usb/udc.h>
10 #include <zephyr/sys/slist.h>
11 #include <zephyr/sys/iterable_sections.h>
12
13 #include "usbd_device.h"
14 #include "usbd_class_api.h"
15 #include "usbd_config.h"
16 #include "usbd_endpoint.h"
17 #include "usbd_ch9.h"
18
19 #include <zephyr/logging/log.h>
20 #if defined(CONFIG_USBD_LOG_LEVEL)
21 #define USBD_CLASS_LOG_LEVEL CONFIG_USBD_LOG_LEVEL
22 #else
23 #define USBD_CLASS_LOG_LEVEL LOG_LEVEL_NONE
24 #endif
25 LOG_MODULE_REGISTER(usbd_class, CONFIG_USBD_LOG_LEVEL);
26
usbd_class_desc_len(struct usbd_class_data * const c_data,const enum usbd_speed speed)27 size_t usbd_class_desc_len(struct usbd_class_data *const c_data,
28 const enum usbd_speed speed)
29 {
30 struct usb_desc_header **dhp;
31 size_t len = 0;
32
33 dhp = usbd_class_get_desc(c_data, speed);
34 /*
35 * If the desired descriptor is available, count to the last element,
36 * which must be a pointer to a nil descriptor.
37 */
38 while (dhp != NULL && (*dhp != NULL) && (*dhp)->bLength != 0U) {
39 len += (*dhp)->bLength;
40 dhp++;
41 }
42
43 return len;
44 }
45
46 struct usbd_class_node *
usbd_class_get_by_config(struct usbd_context * const uds_ctx,const enum usbd_speed speed,const uint8_t cnum,const uint8_t inum)47 usbd_class_get_by_config(struct usbd_context *const uds_ctx,
48 const enum usbd_speed speed,
49 const uint8_t cnum,
50 const uint8_t inum)
51 {
52 struct usbd_class_node *c_nd;
53 struct usbd_config_node *cfg_nd;
54
55 cfg_nd = usbd_config_get(uds_ctx, speed, cnum);
56 if (cfg_nd == NULL) {
57 return NULL;
58 }
59
60 SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
61 if (c_nd->iface_bm & BIT(inum)) {
62 return c_nd;
63 }
64 }
65
66 return NULL;
67 }
68
69 struct usbd_class_node *
usbd_class_get_by_iface(struct usbd_context * const uds_ctx,const uint8_t inum)70 usbd_class_get_by_iface(struct usbd_context *const uds_ctx,
71 const uint8_t inum)
72 {
73 struct usbd_class_node *c_nd;
74 struct usbd_config_node *cfg_nd;
75
76 cfg_nd = usbd_config_get_current(uds_ctx);
77 if (cfg_nd == NULL) {
78 return NULL;
79 }
80
81 SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
82 if (c_nd->iface_bm & BIT(inum)) {
83 return c_nd;
84 }
85 }
86
87 return NULL;
88 }
89
xfer_owner_exist(struct usbd_context * const uds_ctx,struct usbd_config_node * const cfg_nd,struct net_buf * const buf)90 static bool xfer_owner_exist(struct usbd_context *const uds_ctx,
91 struct usbd_config_node *const cfg_nd,
92 struct net_buf *const buf)
93 {
94 struct udc_buf_info *bi = udc_get_buf_info(buf);
95 struct usbd_class_node *c_nd;
96
97 SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
98 if (bi->owner == c_nd->c_data) {
99 uint32_t ep_active = c_nd->ep_active;
100 uint32_t ep_assigned = c_nd->ep_assigned;
101
102 if (!usbd_ep_bm_is_set(&ep_active, bi->ep)) {
103 LOG_DBG("ep 0x%02x is not active", bi->ep);
104 }
105
106 if (!usbd_ep_bm_is_set(&ep_assigned, bi->ep)) {
107 LOG_DBG("ep 0x%02x is not assigned", bi->ep);
108 }
109
110 return true;
111 }
112 }
113
114 return false;
115 }
116
usbd_class_handle_xfer(struct usbd_context * const uds_ctx,struct net_buf * const buf,const int err)117 int usbd_class_handle_xfer(struct usbd_context *const uds_ctx,
118 struct net_buf *const buf,
119 const int err)
120 {
121 struct udc_buf_info *bi = udc_get_buf_info(buf);
122
123 if (unlikely(USBD_CLASS_LOG_LEVEL == LOG_LEVEL_DBG)) {
124 struct usbd_config_node *cfg_nd;
125
126 if (usbd_state_is_configured(uds_ctx)) {
127 cfg_nd = usbd_config_get_current(uds_ctx);
128 if (!xfer_owner_exist(uds_ctx, cfg_nd, buf)) {
129 LOG_DBG("Class request without owner");
130 }
131 } else {
132 LOG_DBG("Class request on not configured device");
133 }
134 }
135
136 return usbd_class_request(bi->owner, buf, err);
137 }
138
139 struct usbd_class_node *
usbd_class_get_by_ep(struct usbd_context * const uds_ctx,const uint8_t ep)140 usbd_class_get_by_ep(struct usbd_context *const uds_ctx,
141 const uint8_t ep)
142 {
143 struct usbd_class_node *c_nd;
144 struct usbd_config_node *cfg_nd;
145 enum usbd_speed speed;
146 uint8_t ep_idx = USB_EP_GET_IDX(ep);
147 uint8_t cfg;
148 uint32_t ep_bm;
149
150 if (USB_EP_DIR_IS_IN(ep)) {
151 ep_bm = BIT(ep_idx + 16);
152 } else {
153 ep_bm = BIT(ep_idx);
154 }
155
156 if (!usbd_state_is_configured(uds_ctx)) {
157 LOG_ERR("No configuration set (Address state)");
158 return NULL;
159 }
160
161 cfg = usbd_get_config_value(uds_ctx);
162 speed = usbd_bus_speed(uds_ctx);
163 cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
164 if (cfg_nd == NULL) {
165 return NULL;
166 }
167
168 SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
169 if (c_nd->ep_assigned & ep_bm) {
170 return c_nd;
171 }
172 }
173
174 return NULL;
175 }
176
177 struct usbd_class_node *
usbd_class_get_by_req(struct usbd_context * const uds_ctx,const uint8_t request)178 usbd_class_get_by_req(struct usbd_context *const uds_ctx,
179 const uint8_t request)
180 {
181 struct usbd_config_node *cfg_nd;
182 struct usbd_class_node *c_nd;
183
184 cfg_nd = usbd_config_get_current(uds_ctx);
185 if (cfg_nd == NULL) {
186 return NULL;
187 }
188
189 SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
190 if (c_nd->c_data->v_reqs == NULL) {
191 continue;
192 }
193
194 for (int i = 0; i < c_nd->c_data->v_reqs->len; i++) {
195 /*
196 * First instance always wins.
197 * There is no other way to determine the recipient.
198 */
199 if (c_nd->c_data->v_reqs->reqs[i] == request) {
200 return c_nd;
201 }
202 }
203 }
204
205 return NULL;
206 }
207
208 static struct usbd_class_node *
usbd_class_node_get(const char * name,const enum usbd_speed speed)209 usbd_class_node_get(const char *name, const enum usbd_speed speed)
210 {
211 if (speed == USBD_SPEED_FS) {
212 STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs,
213 usbd_class_node, c_nd) {
214 if (strcmp(name, c_nd->c_data->name) == 0) {
215 return c_nd;
216 }
217 }
218 } else if (speed == USBD_SPEED_HS) {
219 STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs,
220 usbd_class_node, c_nd) {
221 if (strcmp(name, c_nd->c_data->name) == 0) {
222 return c_nd;
223 }
224 }
225 }
226
227 LOG_ERR("USB device class %s not found", name);
228
229 return NULL;
230 }
231
usbd_class_append(struct usbd_context * const uds_ctx,struct usbd_class_node * const c_nd,const enum usbd_speed speed,const uint8_t cfg)232 static int usbd_class_append(struct usbd_context *const uds_ctx,
233 struct usbd_class_node *const c_nd,
234 const enum usbd_speed speed,
235 const uint8_t cfg)
236 {
237 struct usbd_config_node *cfg_nd;
238
239 cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
240 if (cfg_nd == NULL) {
241 return -ENODATA;
242 }
243
244 sys_slist_append(&cfg_nd->class_list, &c_nd->node);
245
246 return 0;
247 }
248
usbd_class_remove(struct usbd_context * const uds_ctx,struct usbd_class_node * const c_nd,const enum usbd_speed speed,const uint8_t cfg)249 static int usbd_class_remove(struct usbd_context *const uds_ctx,
250 struct usbd_class_node *const c_nd,
251 const enum usbd_speed speed,
252 const uint8_t cfg)
253 {
254 struct usbd_config_node *cfg_nd;
255
256 cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
257 if (cfg_nd == NULL) {
258 return -ENODATA;
259 }
260
261 if (!sys_slist_find_and_remove(&cfg_nd->class_list, &c_nd->node)) {
262 return -ENODATA;
263 }
264
265 return 0;
266 }
267
usbd_class_remove_all(struct usbd_context * const uds_ctx,const enum usbd_speed speed,const uint8_t cfg)268 int usbd_class_remove_all(struct usbd_context *const uds_ctx,
269 const enum usbd_speed speed,
270 const uint8_t cfg)
271 {
272 struct usbd_config_node *cfg_nd;
273 struct usbd_class_node *c_nd;
274 sys_snode_t *node;
275
276 cfg_nd = usbd_config_get(uds_ctx, speed, cfg);
277 if (cfg_nd == NULL) {
278 return -ENODATA;
279 }
280
281 while ((node = sys_slist_get(&cfg_nd->class_list))) {
282 c_nd = CONTAINER_OF(node, struct usbd_class_node, node);
283 atomic_clear_bit(&c_nd->state, USBD_CCTX_REGISTERED);
284 usbd_class_shutdown(c_nd->c_data);
285 LOG_DBG("Remove class node %p from configuration %u", c_nd, cfg);
286 }
287
288 return 0;
289 }
290
291 /*
292 * All the functions below are part of public USB device support API.
293 */
294
usbd_register_class(struct usbd_context * const uds_ctx,const char * name,const enum usbd_speed speed,const uint8_t cfg)295 int usbd_register_class(struct usbd_context *const uds_ctx,
296 const char *name,
297 const enum usbd_speed speed, const uint8_t cfg)
298 {
299 struct usbd_class_node *c_nd;
300 struct usbd_class_data *c_data;
301 int ret;
302
303 c_nd = usbd_class_node_get(name, speed);
304 if (c_nd == NULL) {
305 return -ENODEV;
306 }
307
308 usbd_device_lock(uds_ctx);
309
310 if (usbd_is_initialized(uds_ctx)) {
311 LOG_ERR("USB device support is initialized");
312 ret = -EBUSY;
313 goto register_class_error;
314 }
315
316 c_data = c_nd->c_data;
317
318 /* TODO: does it still need to be atomic ? */
319 if (atomic_test_bit(&c_nd->state, USBD_CCTX_REGISTERED)) {
320 LOG_WRN("Class instance already registered");
321 ret = -EBUSY;
322 goto register_class_error;
323 }
324
325 if ((c_data->uds_ctx != NULL) && (c_data->uds_ctx != uds_ctx)) {
326 LOG_ERR("Class registered to other context at different speed");
327 ret = -EBUSY;
328 goto register_class_error;
329 }
330
331 ret = usbd_class_append(uds_ctx, c_nd, speed, cfg);
332 if (ret == 0) {
333 /* Initialize pointer back to the device struct */
334 atomic_set_bit(&c_nd->state, USBD_CCTX_REGISTERED);
335 c_data->uds_ctx = uds_ctx;
336 }
337
338 register_class_error:
339 usbd_device_unlock(uds_ctx);
340 return ret;
341 }
342
usbd_register_all_classes(struct usbd_context * const uds_ctx,const enum usbd_speed speed,const uint8_t cfg)343 int usbd_register_all_classes(struct usbd_context *const uds_ctx,
344 const enum usbd_speed speed, const uint8_t cfg)
345 {
346 int ret;
347
348 if (speed == USBD_SPEED_HS) {
349 STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_node, c_nd) {
350 ret = usbd_register_class(uds_ctx, c_nd->c_data->name,
351 speed, cfg);
352 if (ret) {
353 LOG_ERR("Failed to register %s to HS configuration %u",
354 c_nd->c_data->name, cfg);
355 return ret;
356 }
357 }
358
359 return 0;
360 }
361
362 if (speed == USBD_SPEED_FS) {
363 STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) {
364 ret = usbd_register_class(uds_ctx, c_nd->c_data->name,
365 speed, cfg);
366 if (ret) {
367 LOG_ERR("Failed to register %s to FS configuration %u",
368 c_nd->c_data->name, cfg);
369 return ret;
370 }
371 }
372
373 return 0;
374 }
375
376 return -ENOTSUP;
377 }
378
usbd_unregister_class(struct usbd_context * const uds_ctx,const char * name,const enum usbd_speed speed,const uint8_t cfg)379 int usbd_unregister_class(struct usbd_context *const uds_ctx,
380 const char *name,
381 const enum usbd_speed speed, const uint8_t cfg)
382 {
383 struct usbd_class_node *c_nd;
384 struct usbd_class_data *c_data;
385 bool can_release_data = true;
386 int ret;
387
388 c_nd = usbd_class_node_get(name, speed);
389 if (c_nd == NULL) {
390 return -ENODEV;
391 }
392
393 usbd_device_lock(uds_ctx);
394
395 if (usbd_is_initialized(uds_ctx)) {
396 LOG_ERR("USB device support is initialized");
397 ret = -EBUSY;
398 goto unregister_class_error;
399 }
400
401 c_data = c_nd->c_data;
402 /* TODO: does it still need to be atomic ? */
403 if (!atomic_test_bit(&c_nd->state, USBD_CCTX_REGISTERED)) {
404 LOG_WRN("Class instance not registered");
405 ret = -EBUSY;
406 goto unregister_class_error;
407 }
408
409 /* TODO: The use of atomic here does not make this code thread safe.
410 * The atomic should be changed to something else.
411 */
412 if (speed == USBD_SPEED_HS) {
413 STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs,
414 usbd_class_node, i) {
415 if ((i->c_data == c_nd->c_data) &&
416 atomic_test_bit(&i->state, USBD_CCTX_REGISTERED)) {
417 can_release_data = false;
418 break;
419 }
420 }
421 } else {
422 STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs,
423 usbd_class_node, i) {
424 if ((i->c_data == c_nd->c_data) &&
425 atomic_test_bit(&i->state, USBD_CCTX_REGISTERED)) {
426 can_release_data = false;
427 break;
428 }
429 }
430 }
431
432 ret = usbd_class_remove(uds_ctx, c_nd, speed, cfg);
433 if (ret == 0) {
434 atomic_clear_bit(&c_nd->state, USBD_CCTX_REGISTERED);
435 usbd_class_shutdown(c_nd->c_data);
436
437 if (can_release_data) {
438 c_data->uds_ctx = NULL;
439 }
440 }
441
442 unregister_class_error:
443 usbd_device_unlock(uds_ctx);
444 return ret;
445 }
446
usbd_unregister_all_classes(struct usbd_context * const uds_ctx,const enum usbd_speed speed,const uint8_t cfg)447 int usbd_unregister_all_classes(struct usbd_context *const uds_ctx,
448 const enum usbd_speed speed, const uint8_t cfg)
449 {
450 int ret;
451
452 if (speed == USBD_SPEED_HS) {
453 STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_node, c_nd) {
454 ret = usbd_unregister_class(uds_ctx, c_nd->c_data->name,
455 speed, cfg);
456 if (ret) {
457 LOG_ERR("Failed to unregister %s to HS configuration %u",
458 c_nd->c_data->name, cfg);
459 return ret;
460 }
461 }
462
463 return 0;
464 }
465
466 if (speed == USBD_SPEED_FS) {
467 STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) {
468 ret = usbd_unregister_class(uds_ctx, c_nd->c_data->name,
469 speed, cfg);
470 if (ret) {
471 LOG_ERR("Failed to unregister %s to FS configuration %u",
472 c_nd->c_data->name, cfg);
473 return ret;
474 }
475 }
476
477 return 0;
478 }
479
480 return -ENOTSUP;
481 }
482