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 (USBD_SUPPORTS_HIGH_SPEED && 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 		c_nd->c_data->uds_ctx = NULL;
286 		LOG_DBG("Remove class node %p from configuration %u", c_nd, cfg);
287 	}
288 
289 	return 0;
290 }
291 
292 /*
293  * All the functions below are part of public USB device support API.
294  */
295 
usbd_register_class(struct usbd_context * const uds_ctx,const char * name,const enum usbd_speed speed,const uint8_t cfg)296 int usbd_register_class(struct usbd_context *const uds_ctx,
297 			const char *name,
298 			const enum usbd_speed speed, const uint8_t cfg)
299 {
300 	struct usbd_class_node *c_nd;
301 	struct usbd_class_data *c_data;
302 	int ret;
303 
304 	c_nd = usbd_class_node_get(name, speed);
305 	if (c_nd == NULL) {
306 		return -ENODEV;
307 	}
308 
309 	usbd_device_lock(uds_ctx);
310 
311 	if (usbd_is_initialized(uds_ctx)) {
312 		LOG_ERR("USB device support is initialized");
313 		ret = -EBUSY;
314 		goto register_class_error;
315 	}
316 
317 	c_data = c_nd->c_data;
318 
319 	/* TODO: does it still need to be atomic ? */
320 	if (atomic_test_bit(&c_nd->state, USBD_CCTX_REGISTERED)) {
321 		LOG_WRN("Class instance already registered");
322 		ret = -EBUSY;
323 		goto register_class_error;
324 	}
325 
326 	if ((c_data->uds_ctx != NULL) && (c_data->uds_ctx != uds_ctx)) {
327 		LOG_ERR("Class registered to other context at different speed");
328 		ret = -EBUSY;
329 		goto register_class_error;
330 	}
331 
332 	ret = usbd_class_append(uds_ctx, c_nd, speed, cfg);
333 	if (ret == 0) {
334 		/* Initialize pointer back to the device struct */
335 		atomic_set_bit(&c_nd->state, USBD_CCTX_REGISTERED);
336 		c_data->uds_ctx = uds_ctx;
337 	}
338 
339 register_class_error:
340 	usbd_device_unlock(uds_ctx);
341 	return ret;
342 }
343 
is_blocklisted(const struct usbd_class_node * const c_nd,const char * const list[])344 static bool is_blocklisted(const struct usbd_class_node *const c_nd,
345 			   const char *const list[])
346 {
347 	for (int i = 0; list[i] != NULL; i++) {
348 		if (strcmp(c_nd->c_data->name, list[i]) == 0) {
349 			return true;
350 		}
351 	}
352 
353 	return false;
354 }
355 
usbd_register_all_classes(struct usbd_context * const uds_ctx,const enum usbd_speed speed,const uint8_t cfg,const char * const blocklist[])356 int usbd_register_all_classes(struct usbd_context *const uds_ctx,
357 			      const enum usbd_speed speed, const uint8_t cfg,
358 			      const char *const blocklist[])
359 {
360 	int ret;
361 
362 	if (USBD_SUPPORTS_HIGH_SPEED && speed == USBD_SPEED_HS) {
363 		STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_node, c_nd) {
364 			if (blocklist != NULL && is_blocklisted(c_nd, blocklist)) {
365 				continue;
366 			}
367 
368 			ret = usbd_register_class(uds_ctx, c_nd->c_data->name,
369 						  speed, cfg);
370 			if (ret) {
371 				LOG_ERR("Failed to register %s to HS configuration %u",
372 					c_nd->c_data->name, cfg);
373 				return ret;
374 			}
375 		}
376 
377 		return 0;
378 	}
379 
380 	if (speed == USBD_SPEED_FS) {
381 		STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) {
382 			if (blocklist != NULL && is_blocklisted(c_nd, blocklist)) {
383 				continue;
384 			}
385 
386 			ret = usbd_register_class(uds_ctx, c_nd->c_data->name,
387 						  speed, cfg);
388 			if (ret) {
389 				LOG_ERR("Failed to register %s to FS configuration %u",
390 					c_nd->c_data->name, cfg);
391 				return ret;
392 			}
393 		}
394 
395 		return 0;
396 	}
397 
398 	return -ENOTSUP;
399 }
400 
usbd_unregister_class(struct usbd_context * const uds_ctx,const char * name,const enum usbd_speed speed,const uint8_t cfg)401 int usbd_unregister_class(struct usbd_context *const uds_ctx,
402 			  const char *name,
403 			  const enum usbd_speed speed, const uint8_t cfg)
404 {
405 	struct usbd_class_node *c_nd;
406 	struct usbd_class_data *c_data;
407 	bool can_release_data = true;
408 	int ret;
409 
410 	c_nd = usbd_class_node_get(name, speed);
411 	if (c_nd == NULL) {
412 		return -ENODEV;
413 	}
414 
415 	usbd_device_lock(uds_ctx);
416 
417 	if (usbd_is_initialized(uds_ctx)) {
418 		LOG_ERR("USB device support is initialized");
419 		ret = -EBUSY;
420 		goto unregister_class_error;
421 	}
422 
423 	c_data = c_nd->c_data;
424 	/* TODO: does it still need to be atomic ? */
425 	if (!atomic_test_bit(&c_nd->state, USBD_CCTX_REGISTERED)) {
426 		LOG_WRN("Class instance not registered");
427 		ret = -EBUSY;
428 		goto unregister_class_error;
429 	}
430 
431 	/* TODO: The use of atomic here does not make this code thread safe.
432 	 * The atomic should be changed to something else.
433 	 */
434 	if (USBD_SUPPORTS_HIGH_SPEED && speed == USBD_SPEED_HS) {
435 		STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs,
436 						 usbd_class_node, i) {
437 			if ((i->c_data == c_nd->c_data) &&
438 			    atomic_test_bit(&i->state, USBD_CCTX_REGISTERED)) {
439 				can_release_data = false;
440 				break;
441 			}
442 		}
443 	} else {
444 		STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs,
445 						 usbd_class_node, i) {
446 			if ((i->c_data == c_nd->c_data) &&
447 			    atomic_test_bit(&i->state, USBD_CCTX_REGISTERED)) {
448 				can_release_data = false;
449 				break;
450 			}
451 		}
452 	}
453 
454 	ret = usbd_class_remove(uds_ctx, c_nd, speed, cfg);
455 	if (ret == 0) {
456 		atomic_clear_bit(&c_nd->state, USBD_CCTX_REGISTERED);
457 		usbd_class_shutdown(c_nd->c_data);
458 
459 		if (can_release_data) {
460 			c_data->uds_ctx = NULL;
461 		}
462 	}
463 
464 unregister_class_error:
465 	usbd_device_unlock(uds_ctx);
466 	return ret;
467 }
468 
usbd_unregister_all_classes(struct usbd_context * const uds_ctx,const enum usbd_speed speed,const uint8_t cfg)469 int usbd_unregister_all_classes(struct usbd_context *const uds_ctx,
470 				const enum usbd_speed speed, const uint8_t cfg)
471 {
472 	int ret;
473 
474 	if (USBD_SUPPORTS_HIGH_SPEED && speed == USBD_SPEED_HS) {
475 		STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_node, c_nd) {
476 			ret = usbd_unregister_class(uds_ctx, c_nd->c_data->name,
477 						    speed, cfg);
478 			if (ret) {
479 				LOG_ERR("Failed to unregister %s to HS configuration %u",
480 					c_nd->c_data->name, cfg);
481 				return ret;
482 			}
483 		}
484 
485 		return 0;
486 	}
487 
488 	if (speed == USBD_SPEED_FS) {
489 		STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) {
490 			ret = usbd_unregister_class(uds_ctx, c_nd->c_data->name,
491 						    speed, cfg);
492 			if (ret) {
493 				LOG_ERR("Failed to unregister %s to FS configuration %u",
494 					c_nd->c_data->name, cfg);
495 				return ret;
496 			}
497 		}
498 
499 		return 0;
500 	}
501 
502 	return -ENOTSUP;
503 }
504