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