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_node * const c_nd)27 size_t usbd_class_desc_len(struct usbd_class_node *const c_nd)
28 {
29 	struct usbd_class_data *data = c_nd->data;
30 	struct usb_desc_header *dh;
31 	uint8_t *ptr;
32 	size_t len = 0;
33 
34 	if (data->desc != NULL) {
35 		dh = data->desc;
36 		ptr = (uint8_t *)dh;
37 
38 		while (dh->bLength != 0) {
39 			len += dh->bLength;
40 			ptr += dh->bLength;
41 			dh = (struct usb_desc_header *)ptr;
42 		}
43 	}
44 
45 	return len;
46 }
47 
48 struct usbd_class_node *
usbd_class_get_by_config(struct usbd_contex * const uds_ctx,const uint8_t cnum,const uint8_t inum)49 usbd_class_get_by_config(struct usbd_contex *const uds_ctx,
50 			 const uint8_t cnum,
51 			 const uint8_t inum)
52 {
53 	struct usbd_class_node *c_nd;
54 	struct usbd_config_node *cfg_nd;
55 
56 	cfg_nd = usbd_config_get(uds_ctx, cnum);
57 	if (cfg_nd == NULL) {
58 		return NULL;
59 	}
60 
61 	SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
62 		if (c_nd->data->iface_bm & BIT(inum)) {
63 			return c_nd;
64 		}
65 	}
66 
67 	return NULL;
68 }
69 
70 struct usbd_class_node *
usbd_class_get_by_iface(struct usbd_contex * const uds_ctx,const uint8_t inum)71 usbd_class_get_by_iface(struct usbd_contex *const uds_ctx,
72 			const uint8_t inum)
73 {
74 	struct usbd_class_node *c_nd;
75 	struct usbd_config_node *cfg_nd;
76 
77 	cfg_nd = usbd_config_get_current(uds_ctx);
78 	if (cfg_nd == NULL) {
79 		return NULL;
80 	}
81 
82 	SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
83 		if (c_nd->data->iface_bm & BIT(inum)) {
84 			return c_nd;
85 		}
86 	}
87 
88 	return NULL;
89 }
90 
xfer_owner_exist(struct usbd_contex * const uds_ctx,struct usbd_config_node * const cfg_nd,struct net_buf * const buf)91 static bool xfer_owner_exist(struct usbd_contex *const uds_ctx,
92 			     struct usbd_config_node *const cfg_nd,
93 			     struct net_buf *const buf)
94 {
95 	struct udc_buf_info *bi = udc_get_buf_info(buf);
96 	struct usbd_class_node *c_nd;
97 
98 	SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
99 		if (bi->owner == c_nd) {
100 			uint32_t ep_active = c_nd->data->ep_active;
101 			uint32_t ep_assigned = c_nd->data->ep_assigned;
102 
103 			if (!usbd_ep_bm_is_set(&ep_active, bi->ep)) {
104 				LOG_DBG("ep 0x%02x is not active", bi->ep);
105 			}
106 
107 			if (!usbd_ep_bm_is_set(&ep_assigned, bi->ep)) {
108 				LOG_DBG("ep 0x%02x is not assigned", bi->ep);
109 			}
110 
111 			return true;
112 		}
113 	}
114 
115 	return false;
116 }
117 
usbd_class_handle_xfer(struct usbd_contex * const uds_ctx,struct net_buf * const buf,const int err)118 int usbd_class_handle_xfer(struct usbd_contex *const uds_ctx,
119 			   struct net_buf *const buf,
120 			   const int err)
121 {
122 	struct udc_buf_info *bi = udc_get_buf_info(buf);
123 
124 	if (unlikely(USBD_CLASS_LOG_LEVEL == LOG_LEVEL_DBG)) {
125 		struct usbd_config_node *cfg_nd;
126 
127 		if (usbd_state_is_configured(uds_ctx)) {
128 			cfg_nd = usbd_config_get_current(uds_ctx);
129 			if (xfer_owner_exist(uds_ctx, cfg_nd, buf)) {
130 				return usbd_class_request(bi->owner, buf, err);
131 			}
132 		}
133 
134 		SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->configs, cfg_nd, node) {
135 			if (xfer_owner_exist(uds_ctx, cfg_nd, buf)) {
136 				return usbd_class_request(bi->owner, buf, err);
137 			}
138 		}
139 
140 		return -ENODATA;
141 	}
142 
143 	return usbd_class_request(bi->owner, buf, err);
144 }
145 
146 struct usbd_class_node *
usbd_class_get_by_ep(struct usbd_contex * const uds_ctx,const uint8_t ep)147 usbd_class_get_by_ep(struct usbd_contex *const uds_ctx,
148 		     const uint8_t ep)
149 {
150 	struct usbd_class_node *c_nd;
151 	struct usbd_config_node *cfg_nd;
152 	uint8_t ep_idx = USB_EP_GET_IDX(ep);
153 	uint8_t cfg;
154 	uint32_t ep_bm;
155 
156 	if (USB_EP_DIR_IS_IN(ep)) {
157 		ep_bm = BIT(ep_idx + 16);
158 	} else {
159 		ep_bm = BIT(ep_idx);
160 	}
161 
162 	if (!usbd_state_is_configured(uds_ctx)) {
163 		LOG_ERR("No configuration set (Address state)");
164 		return NULL;
165 	}
166 
167 	cfg = usbd_get_config_value(uds_ctx);
168 	cfg_nd = usbd_config_get(uds_ctx, cfg);
169 	if (cfg_nd == NULL) {
170 		return NULL;
171 	}
172 
173 	SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
174 		if (c_nd->data->ep_assigned & ep_bm) {
175 			return c_nd;
176 		}
177 	}
178 
179 	return NULL;
180 }
181 
182 struct usbd_class_node *
usbd_class_get_by_req(struct usbd_contex * const uds_ctx,const uint8_t request)183 usbd_class_get_by_req(struct usbd_contex *const uds_ctx,
184 		      const uint8_t request)
185 {
186 	struct usbd_config_node *cfg_nd;
187 	struct usbd_class_node *c_nd;
188 
189 	cfg_nd = usbd_config_get_current(uds_ctx);
190 	if (cfg_nd == NULL) {
191 		return NULL;
192 	}
193 
194 	SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
195 		if (c_nd->data->v_reqs == NULL) {
196 			continue;
197 		}
198 
199 		for (int i = 0; i < c_nd->data->v_reqs->len; i++) {
200 			/*
201 			 * First instance always wins.
202 			 * There is no other way to determine the recipient.
203 			 */
204 			if (c_nd->data->v_reqs->reqs[i] == request) {
205 				return c_nd;
206 			}
207 		}
208 	}
209 
210 	return NULL;
211 }
212 
usbd_class_node_get(const char * name)213 static struct usbd_class_node *usbd_class_node_get(const char *name)
214 {
215 	STRUCT_SECTION_FOREACH(usbd_class_node, c_nd) {
216 		if (strcmp(name, c_nd->name) == 0) {
217 			return c_nd;
218 		}
219 	}
220 
221 	LOG_ERR("USB device class %s not found", name);
222 
223 	return NULL;
224 }
225 
usbd_class_append(struct usbd_contex * const uds_ctx,struct usbd_class_node * const c_nd,const uint8_t cfg)226 static int usbd_class_append(struct usbd_contex *const uds_ctx,
227 			     struct usbd_class_node *const c_nd,
228 			     const uint8_t cfg)
229 {
230 	struct usbd_config_node *cfg_nd;
231 
232 	cfg_nd = usbd_config_get(uds_ctx, cfg);
233 	if (cfg_nd == NULL) {
234 		return -ENODATA;
235 	}
236 
237 	sys_slist_append(&cfg_nd->class_list, &c_nd->node);
238 
239 	return 0;
240 }
241 
usbd_class_remove(struct usbd_contex * const uds_ctx,struct usbd_class_node * const c_nd,const uint8_t cfg)242 static int usbd_class_remove(struct usbd_contex *const uds_ctx,
243 			     struct usbd_class_node *const c_nd,
244 			     const uint8_t cfg)
245 {
246 	struct usbd_config_node *cfg_nd;
247 
248 	cfg_nd = usbd_config_get(uds_ctx, cfg);
249 	if (cfg_nd == NULL) {
250 		return -ENODATA;
251 	}
252 
253 	if (!sys_slist_find_and_remove(&cfg_nd->class_list, &c_nd->node)) {
254 		return -ENODATA;
255 	}
256 
257 	return 0;
258 }
259 
usbd_class_remove_all(struct usbd_contex * const uds_ctx,const uint8_t cfg)260 int usbd_class_remove_all(struct usbd_contex *const uds_ctx,
261 			  const uint8_t cfg)
262 {
263 	struct usbd_config_node *cfg_nd;
264 	struct usbd_class_node *c_nd;
265 	sys_snode_t *node;
266 
267 	cfg_nd = usbd_config_get(uds_ctx, cfg);
268 	if (cfg_nd == NULL) {
269 		return -ENODATA;
270 	}
271 
272 	while ((node = sys_slist_get(&cfg_nd->class_list))) {
273 		c_nd = CONTAINER_OF(node, struct usbd_class_node, node);
274 		atomic_clear_bit(&c_nd->data->state, USBD_CCTX_REGISTERED);
275 		usbd_class_shutdown(c_nd);
276 		LOG_DBG("Remove class node %p from configuration %u", c_nd, cfg);
277 	}
278 
279 	return 0;
280 }
281 
282 /*
283  * All the functions below are part of public USB device support API.
284  */
285 
usbd_register_class(struct usbd_contex * const uds_ctx,const char * name,const uint8_t cfg)286 int usbd_register_class(struct usbd_contex *const uds_ctx,
287 			const char *name,
288 			const uint8_t cfg)
289 {
290 	struct usbd_class_node *c_nd;
291 	struct usbd_class_data *data;
292 	int ret;
293 
294 	c_nd = usbd_class_node_get(name);
295 	if (c_nd == NULL) {
296 		return -ENODEV;
297 	}
298 
299 	usbd_device_lock(uds_ctx);
300 
301 	if (usbd_is_initialized(uds_ctx)) {
302 		LOG_ERR("USB device support is initialized");
303 		ret = -EBUSY;
304 		goto register_class_error;
305 	}
306 
307 	data = c_nd->data;
308 	if (data->desc == NULL) {
309 		ret = -EINVAL;
310 		goto register_class_error;
311 	}
312 
313 
314 	/* TODO: does it still need to be atomic ? */
315 	if (atomic_test_bit(&data->state, USBD_CCTX_REGISTERED)) {
316 		LOG_WRN("Class instance already registered");
317 		ret = -EBUSY;
318 		goto register_class_error;
319 	}
320 
321 	ret = usbd_class_append(uds_ctx, c_nd, cfg);
322 	if (ret == 0) {
323 		/* Initialize pointer back to the device struct */
324 		atomic_set_bit(&data->state, USBD_CCTX_REGISTERED);
325 		data->uds_ctx = uds_ctx;
326 	}
327 
328 register_class_error:
329 	usbd_device_unlock(uds_ctx);
330 	return ret;
331 }
332 
usbd_unregister_class(struct usbd_contex * const uds_ctx,const char * name,const uint8_t cfg)333 int usbd_unregister_class(struct usbd_contex *const uds_ctx,
334 			  const char *name,
335 			  const uint8_t cfg)
336 {
337 	struct usbd_class_node *c_nd;
338 	struct usbd_class_data *data;
339 	int ret;
340 
341 	c_nd = usbd_class_node_get(name);
342 	if (c_nd == NULL) {
343 		return -ENODEV;
344 	}
345 
346 	usbd_device_lock(uds_ctx);
347 
348 	if (usbd_is_initialized(uds_ctx)) {
349 		LOG_ERR("USB device support is initialized");
350 		ret = -EBUSY;
351 		goto unregister_class_error;
352 	}
353 
354 	data = c_nd->data;
355 	/* TODO: does it still need to be atomic ? */
356 	if (!atomic_test_bit(&data->state, USBD_CCTX_REGISTERED)) {
357 		LOG_WRN("Class instance not registered");
358 		ret = -EBUSY;
359 		goto unregister_class_error;
360 	}
361 
362 	ret = usbd_class_remove(uds_ctx, c_nd, cfg);
363 	if (ret == 0) {
364 		atomic_clear_bit(&data->state, USBD_CCTX_REGISTERED);
365 		usbd_class_shutdown(c_nd);
366 		data->uds_ctx = NULL;
367 	}
368 
369 unregister_class_error:
370 	usbd_device_unlock(uds_ctx);
371 	return ret;
372 }
373