1 /*
2 * Copyright (c) 2022 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/drivers/usb/udc.h>
8 #include <zephyr/usb/usbd.h>
9
10 #include "usbd_device.h"
11 #include "usbd_config.h"
12 #include "usbd_class.h"
13 #include "usbd_ch9.h"
14 #include "usbd_desc.h"
15
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(usbd_dev, CONFIG_USBD_LOG_LEVEL);
18
19 /*
20 * All the functions below are part of public USB device support API.
21 */
22
usbd_bus_speed(const struct usbd_context * const uds_ctx)23 enum usbd_speed usbd_bus_speed(const struct usbd_context *const uds_ctx)
24 {
25 return uds_ctx->status.speed;
26 }
27
usbd_caps_speed(const struct usbd_context * const uds_ctx)28 enum usbd_speed usbd_caps_speed(const struct usbd_context *const uds_ctx)
29 {
30 struct udc_device_caps caps = udc_caps(uds_ctx->dev);
31
32 /* For now, either high speed is supported or not. */
33 if (caps.hs) {
34 return USBD_SPEED_HS;
35 }
36
37 return USBD_SPEED_FS;
38 }
39
40 static struct usb_device_descriptor *
get_device_descriptor(struct usbd_context * const uds_ctx,const enum usbd_speed speed)41 get_device_descriptor(struct usbd_context *const uds_ctx,
42 const enum usbd_speed speed)
43 {
44 switch (speed) {
45 case USBD_SPEED_FS:
46 return uds_ctx->fs_desc;
47 case USBD_SPEED_HS:
48 return uds_ctx->hs_desc;
49 default:
50 __ASSERT(false, "Not supported speed");
51 return NULL;
52 }
53 }
54
usbd_device_set_bcd_usb(struct usbd_context * const uds_ctx,const enum usbd_speed speed,const uint16_t bcd)55 int usbd_device_set_bcd_usb(struct usbd_context *const uds_ctx,
56 const enum usbd_speed speed, const uint16_t bcd)
57 {
58 struct usb_device_descriptor *desc;
59 int ret = 0;
60
61 usbd_device_lock(uds_ctx);
62
63 if (usbd_is_enabled(uds_ctx)) {
64 ret = -EALREADY;
65 goto set_bcd_exit;
66 }
67
68 desc = get_device_descriptor(uds_ctx, speed);
69 if (desc == NULL) {
70 ret = -EINVAL;
71 goto set_bcd_exit;
72 }
73
74 desc->bcdUSB = sys_cpu_to_le16(bcd);
75
76 set_bcd_exit:
77 usbd_device_unlock(uds_ctx);
78 return ret;
79 }
80
usbd_device_set_vid(struct usbd_context * const uds_ctx,const uint16_t vid)81 int usbd_device_set_vid(struct usbd_context *const uds_ctx,
82 const uint16_t vid)
83 {
84 struct usb_device_descriptor *fs_desc, *hs_desc;
85 int ret = 0;
86
87 usbd_device_lock(uds_ctx);
88
89 if (usbd_is_enabled(uds_ctx)) {
90 ret = -EALREADY;
91 goto set_vid_exit;
92 }
93
94 fs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_FS);
95 fs_desc->idVendor = sys_cpu_to_le16(vid);
96
97 if (USBD_SUPPORTS_HIGH_SPEED) {
98 hs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_HS);
99 hs_desc->idVendor = sys_cpu_to_le16(vid);
100 }
101
102 set_vid_exit:
103 usbd_device_unlock(uds_ctx);
104 return ret;
105 }
106
usbd_device_set_pid(struct usbd_context * const uds_ctx,const uint16_t pid)107 int usbd_device_set_pid(struct usbd_context *const uds_ctx,
108 const uint16_t pid)
109 {
110 struct usb_device_descriptor *fs_desc, *hs_desc;
111 int ret = 0;
112
113 usbd_device_lock(uds_ctx);
114
115 if (usbd_is_enabled(uds_ctx)) {
116 ret = -EALREADY;
117 goto set_pid_exit;
118 }
119
120 fs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_FS);
121 fs_desc->idProduct = sys_cpu_to_le16(pid);
122
123 if (USBD_SUPPORTS_HIGH_SPEED) {
124 hs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_HS);
125 hs_desc->idProduct = sys_cpu_to_le16(pid);
126 }
127
128 set_pid_exit:
129 usbd_device_unlock(uds_ctx);
130 return ret;
131 }
132
usbd_device_set_bcd_device(struct usbd_context * const uds_ctx,const uint16_t bcd)133 int usbd_device_set_bcd_device(struct usbd_context *const uds_ctx,
134 const uint16_t bcd)
135 {
136 struct usb_device_descriptor *fs_desc, *hs_desc;
137 int ret = 0;
138
139 usbd_device_lock(uds_ctx);
140
141 if (usbd_is_enabled(uds_ctx)) {
142 ret = -EALREADY;
143 goto set_bcd_device_exit;
144 }
145
146 fs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_FS);
147 fs_desc->bcdDevice = sys_cpu_to_le16(bcd);
148
149 if (USBD_SUPPORTS_HIGH_SPEED) {
150 hs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_HS);
151 hs_desc->bcdDevice = sys_cpu_to_le16(bcd);
152 }
153
154 set_bcd_device_exit:
155 usbd_device_unlock(uds_ctx);
156 return ret;
157 }
158
usbd_device_set_code_triple(struct usbd_context * const uds_ctx,const enum usbd_speed speed,const uint8_t base_class,const uint8_t subclass,const uint8_t protocol)159 int usbd_device_set_code_triple(struct usbd_context *const uds_ctx,
160 const enum usbd_speed speed,
161 const uint8_t base_class,
162 const uint8_t subclass, const uint8_t protocol)
163 {
164 struct usb_device_descriptor *desc;
165 int ret = 0;
166
167 usbd_device_lock(uds_ctx);
168
169 if (usbd_is_enabled(uds_ctx)) {
170 ret = -EALREADY;
171 goto set_code_triple_exit;
172 }
173
174 desc = get_device_descriptor(uds_ctx, speed);
175 if (desc == NULL) {
176 ret = -EINVAL;
177 goto set_code_triple_exit;
178 }
179
180 desc->bDeviceClass = base_class;
181 desc->bDeviceSubClass = subclass;
182 desc->bDeviceProtocol = protocol;
183
184 set_code_triple_exit:
185 usbd_device_unlock(uds_ctx);
186 return ret;
187 }
188
usbd_wakeup_request(struct usbd_context * const uds_ctx)189 int usbd_wakeup_request(struct usbd_context *const uds_ctx)
190 {
191 struct udc_device_caps caps = udc_caps(uds_ctx->dev);
192 int ret = 0;
193
194 usbd_device_lock(uds_ctx);
195
196 if (!caps.rwup) {
197 LOG_ERR("Remote wakeup feature not supported");
198 ret = -ENOTSUP;
199 goto wakeup_request_error;
200 }
201
202 if (!uds_ctx->status.rwup || !usbd_is_suspended(uds_ctx)) {
203 LOG_WRN("Remote wakeup feature not enabled or not suspended");
204 ret = -EACCES;
205 goto wakeup_request_error;
206 }
207
208 ret = udc_host_wakeup(uds_ctx->dev);
209
210 wakeup_request_error:
211 usbd_device_unlock(uds_ctx);
212
213 return ret;
214 }
215
usbd_self_powered(struct usbd_context * uds_ctx,const bool status)216 void usbd_self_powered(struct usbd_context *uds_ctx, const bool status)
217 {
218 usbd_device_lock(uds_ctx);
219 uds_ctx->status.self_powered = status;
220 usbd_device_unlock(uds_ctx);
221 }
222
usbd_is_suspended(struct usbd_context * uds_ctx)223 bool usbd_is_suspended(struct usbd_context *uds_ctx)
224 {
225 return uds_ctx->status.suspended;
226 }
227
usbd_init(struct usbd_context * const uds_ctx)228 int usbd_init(struct usbd_context *const uds_ctx)
229 {
230 int ret;
231
232 /*
233 * Lock the scheduler to ensure that the context is not preempted
234 * before it is fully initialized.
235 */
236 k_sched_lock();
237 usbd_device_lock(uds_ctx);
238
239 if (uds_ctx->dev == NULL) {
240 ret = -ENODEV;
241 goto init_exit;
242 }
243
244 if (usbd_is_initialized(uds_ctx)) {
245 LOG_WRN("USB device support is already initialized");
246 ret = -EALREADY;
247 goto init_exit;
248 }
249
250 if (!device_is_ready(uds_ctx->dev)) {
251 LOG_ERR("USB device controller is not ready");
252 ret = -ENODEV;
253 goto init_exit;
254 }
255
256 ret = usbd_device_init_core(uds_ctx);
257 if (ret) {
258 goto init_exit;
259 }
260
261 memset(&uds_ctx->ch9_data, 0, sizeof(struct usbd_ch9_data));
262 uds_ctx->status.initialized = true;
263
264 init_exit:
265 usbd_device_unlock(uds_ctx);
266 k_sched_unlock();
267
268 return ret;
269 }
270
usbd_enable(struct usbd_context * const uds_ctx)271 int usbd_enable(struct usbd_context *const uds_ctx)
272 {
273 int ret;
274
275 k_sched_lock();
276 usbd_device_lock(uds_ctx);
277
278 if (!usbd_is_initialized(uds_ctx)) {
279 LOG_WRN("USB device support is not initialized");
280 ret = -EPERM;
281 goto enable_exit;
282 }
283
284 if (usbd_is_enabled(uds_ctx)) {
285 LOG_WRN("USB device support is already enabled");
286 ret = -EALREADY;
287 goto enable_exit;
288 }
289
290 ret = udc_enable(uds_ctx->dev);
291 if (ret != 0) {
292 LOG_ERR("Failed to enable controller");
293 goto enable_exit;
294 }
295
296 ret = usbd_init_control_pipe(uds_ctx);
297 if (ret != 0) {
298 udc_disable(uds_ctx->dev);
299 goto enable_exit;
300 }
301
302 uds_ctx->status.enabled = true;
303
304 enable_exit:
305 usbd_device_unlock(uds_ctx);
306 k_sched_unlock();
307
308 return ret;
309 }
310
usbd_disable(struct usbd_context * const uds_ctx)311 int usbd_disable(struct usbd_context *const uds_ctx)
312 {
313 int ret;
314
315 if (!usbd_is_enabled(uds_ctx)) {
316 LOG_WRN("USB device support is already disabled");
317 return -EALREADY;
318 }
319
320 usbd_device_lock(uds_ctx);
321
322 ret = usbd_config_set(uds_ctx, 0);
323 if (ret) {
324 LOG_ERR("Failed to reset configuration");
325 }
326
327 ret = udc_disable(uds_ctx->dev);
328 if (ret) {
329 LOG_ERR("Failed to disable USB device");
330 }
331
332 uds_ctx->status.enabled = false;
333
334 usbd_device_unlock(uds_ctx);
335
336 return ret;
337 }
338
usbd_shutdown(struct usbd_context * const uds_ctx)339 int usbd_shutdown(struct usbd_context *const uds_ctx)
340 {
341 int ret;
342
343 usbd_device_lock(uds_ctx);
344
345 /* TODO: control request dequeue ? */
346 ret = usbd_device_shutdown_core(uds_ctx);
347 if (ret) {
348 LOG_ERR("Failed to shutdown USB device");
349 }
350
351 uds_ctx->status.initialized = false;
352 usbd_device_unlock(uds_ctx);
353
354 return 0;
355 }
356
usbd_can_detect_vbus(struct usbd_context * const uds_ctx)357 bool usbd_can_detect_vbus(struct usbd_context *const uds_ctx)
358 {
359 const struct udc_device_caps caps = udc_caps(uds_ctx->dev);
360
361 return caps.can_detect_vbus;
362 }
363
usbd_device_get_vreq(struct usbd_context * const uds_ctx,const uint8_t code)364 struct usbd_vreq_node *usbd_device_get_vreq(struct usbd_context *const uds_ctx,
365 const uint8_t code)
366 {
367 struct usbd_vreq_node *vreq_nd;
368
369 SYS_DLIST_FOR_EACH_CONTAINER(&uds_ctx->vreqs, vreq_nd, node) {
370 if (vreq_nd->code == code) {
371 return vreq_nd;
372 }
373 }
374
375 return NULL;
376 }
377
usbd_device_register_vreq(struct usbd_context * const uds_ctx,struct usbd_vreq_node * const vreq_nd)378 int usbd_device_register_vreq(struct usbd_context *const uds_ctx,
379 struct usbd_vreq_node *const vreq_nd)
380 {
381 int ret = 0;
382
383 if (!IS_ENABLED(CONFIG_USBD_VREQ_SUPPORT)) {
384 return -ENOTSUP;
385 }
386
387 usbd_device_lock(uds_ctx);
388
389 if (usbd_is_initialized(uds_ctx)) {
390 ret = -EPERM;
391 goto error;
392 }
393
394 if (vreq_nd->to_dev == NULL && vreq_nd->to_host == NULL) {
395 ret = -EINVAL;
396 goto error;
397 }
398
399 if (!sys_dnode_is_linked(&uds_ctx->vreqs)) {
400 LOG_DBG("Initialize vendor request list");
401 sys_dlist_init(&uds_ctx->vreqs);
402 }
403
404 if (sys_dnode_is_linked(&vreq_nd->node)) {
405 ret = -EALREADY;
406 goto error;
407 }
408
409 sys_dlist_append(&uds_ctx->vreqs, &vreq_nd->node);
410 LOG_DBG("Registered vendor request 0x%02x", vreq_nd->code);
411
412 error:
413 usbd_device_unlock(uds_ctx);
414 return ret;
415 }
416
usbd_device_unregister_all_vreq(struct usbd_context * const uds_ctx)417 void usbd_device_unregister_all_vreq(struct usbd_context *const uds_ctx)
418 {
419 struct usbd_vreq_node *tmp;
420 sys_dnode_t *node;
421
422 if (!sys_dnode_is_linked(&uds_ctx->vreqs)) {
423 return;
424 }
425
426 while ((node = sys_dlist_get(&uds_ctx->vreqs))) {
427 tmp = CONTAINER_OF(node, struct usbd_vreq_node, node);
428 LOG_DBG("Remove vendor request 0x%02x", tmp->code);
429 }
430 }
431