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 
is_blocklisted(const struct usbd_class_node * const c_nd,const char * const list[])343 static bool is_blocklisted(const struct usbd_class_node *const c_nd,
344 			   const char *const list[])
345 {
346 	for (int i = 0; list[i] != NULL; i++) {
347 		if (strcmp(c_nd->c_data->name, list[i]) == 0) {
348 			return true;
349 		}
350 	}
351 
352 	return false;
353 }
354 
usbd_register_all_classes(struct usbd_context * const uds_ctx,const enum usbd_speed speed,const uint8_t cfg,const char * const blocklist[])355 int usbd_register_all_classes(struct usbd_context *const uds_ctx,
356 			      const enum usbd_speed speed, const uint8_t cfg,
357 			      const char *const blocklist[])
358 {
359 	int ret;
360 
361 	if (speed == USBD_SPEED_HS) {
362 		STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_node, c_nd) {
363 			if (blocklist != NULL && is_blocklisted(c_nd, blocklist)) {
364 				continue;
365 			}
366 
367 			ret = usbd_register_class(uds_ctx, c_nd->c_data->name,
368 						  speed, cfg);
369 			if (ret) {
370 				LOG_ERR("Failed to register %s to HS configuration %u",
371 					c_nd->c_data->name, cfg);
372 				return ret;
373 			}
374 		}
375 
376 		return 0;
377 	}
378 
379 	if (speed == USBD_SPEED_FS) {
380 		STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) {
381 			if (blocklist != NULL && is_blocklisted(c_nd, blocklist)) {
382 				continue;
383 			}
384 
385 			ret = usbd_register_class(uds_ctx, c_nd->c_data->name,
386 						  speed, cfg);
387 			if (ret) {
388 				LOG_ERR("Failed to register %s to FS configuration %u",
389 					c_nd->c_data->name, cfg);
390 				return ret;
391 			}
392 		}
393 
394 		return 0;
395 	}
396 
397 	return -ENOTSUP;
398 }
399 
usbd_unregister_class(struct usbd_context * const uds_ctx,const char * name,const enum usbd_speed speed,const uint8_t cfg)400 int usbd_unregister_class(struct usbd_context *const uds_ctx,
401 			  const char *name,
402 			  const enum usbd_speed speed, const uint8_t cfg)
403 {
404 	struct usbd_class_node *c_nd;
405 	struct usbd_class_data *c_data;
406 	bool can_release_data = true;
407 	int ret;
408 
409 	c_nd = usbd_class_node_get(name, speed);
410 	if (c_nd == NULL) {
411 		return -ENODEV;
412 	}
413 
414 	usbd_device_lock(uds_ctx);
415 
416 	if (usbd_is_initialized(uds_ctx)) {
417 		LOG_ERR("USB device support is initialized");
418 		ret = -EBUSY;
419 		goto unregister_class_error;
420 	}
421 
422 	c_data = c_nd->c_data;
423 	/* TODO: does it still need to be atomic ? */
424 	if (!atomic_test_bit(&c_nd->state, USBD_CCTX_REGISTERED)) {
425 		LOG_WRN("Class instance not registered");
426 		ret = -EBUSY;
427 		goto unregister_class_error;
428 	}
429 
430 	/* TODO: The use of atomic here does not make this code thread safe.
431 	 * The atomic should be changed to something else.
432 	 */
433 	if (speed == USBD_SPEED_HS) {
434 		STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs,
435 						 usbd_class_node, i) {
436 			if ((i->c_data == c_nd->c_data) &&
437 			    atomic_test_bit(&i->state, USBD_CCTX_REGISTERED)) {
438 				can_release_data = false;
439 				break;
440 			}
441 		}
442 	} else {
443 		STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs,
444 						 usbd_class_node, i) {
445 			if ((i->c_data == c_nd->c_data) &&
446 			    atomic_test_bit(&i->state, USBD_CCTX_REGISTERED)) {
447 				can_release_data = false;
448 				break;
449 			}
450 		}
451 	}
452 
453 	ret = usbd_class_remove(uds_ctx, c_nd, speed, cfg);
454 	if (ret == 0) {
455 		atomic_clear_bit(&c_nd->state, USBD_CCTX_REGISTERED);
456 		usbd_class_shutdown(c_nd->c_data);
457 
458 		if (can_release_data) {
459 			c_data->uds_ctx = NULL;
460 		}
461 	}
462 
463 unregister_class_error:
464 	usbd_device_unlock(uds_ctx);
465 	return ret;
466 }
467 
usbd_unregister_all_classes(struct usbd_context * const uds_ctx,const enum usbd_speed speed,const uint8_t cfg)468 int usbd_unregister_all_classes(struct usbd_context *const uds_ctx,
469 				const enum usbd_speed speed, const uint8_t cfg)
470 {
471 	int ret;
472 
473 	if (speed == USBD_SPEED_HS) {
474 		STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_node, c_nd) {
475 			ret = usbd_unregister_class(uds_ctx, c_nd->c_data->name,
476 						    speed, cfg);
477 			if (ret) {
478 				LOG_ERR("Failed to unregister %s to HS configuration %u",
479 					c_nd->c_data->name, cfg);
480 				return ret;
481 			}
482 		}
483 
484 		return 0;
485 	}
486 
487 	if (speed == USBD_SPEED_FS) {
488 		STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) {
489 			ret = usbd_unregister_class(uds_ctx, c_nd->c_data->name,
490 						    speed, cfg);
491 			if (ret) {
492 				LOG_ERR("Failed to unregister %s to FS configuration %u",
493 					c_nd->c_data->name, cfg);
494 				return ret;
495 			}
496 		}
497 
498 		return 0;
499 	}
500 
501 	return -ENOTSUP;
502 }
503