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