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 desc->bcdUSB = sys_cpu_to_le16(bcd);
70
71 set_bcd_exit:
72 usbd_device_unlock(uds_ctx);
73 return ret;
74 }
75
usbd_device_set_vid(struct usbd_context * const uds_ctx,const uint16_t vid)76 int usbd_device_set_vid(struct usbd_context *const uds_ctx,
77 const uint16_t vid)
78 {
79 struct usb_device_descriptor *fs_desc, *hs_desc;
80 int ret = 0;
81
82 usbd_device_lock(uds_ctx);
83
84 if (usbd_is_enabled(uds_ctx)) {
85 ret = -EALREADY;
86 goto set_vid_exit;
87 }
88
89 fs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_FS);
90 fs_desc->idVendor = sys_cpu_to_le16(vid);
91
92 hs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_HS);
93 hs_desc->idVendor = sys_cpu_to_le16(vid);
94
95 set_vid_exit:
96 usbd_device_unlock(uds_ctx);
97 return ret;
98 }
99
usbd_device_set_pid(struct usbd_context * const uds_ctx,const uint16_t pid)100 int usbd_device_set_pid(struct usbd_context *const uds_ctx,
101 const uint16_t pid)
102 {
103 struct usb_device_descriptor *fs_desc, *hs_desc;
104 int ret = 0;
105
106 usbd_device_lock(uds_ctx);
107
108 if (usbd_is_enabled(uds_ctx)) {
109 ret = -EALREADY;
110 goto set_pid_exit;
111 }
112
113 fs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_FS);
114 fs_desc->idProduct = sys_cpu_to_le16(pid);
115
116 hs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_HS);
117 hs_desc->idProduct = sys_cpu_to_le16(pid);
118
119 set_pid_exit:
120 usbd_device_unlock(uds_ctx);
121 return ret;
122 }
123
usbd_device_set_bcd_device(struct usbd_context * const uds_ctx,const uint16_t bcd)124 int usbd_device_set_bcd_device(struct usbd_context *const uds_ctx,
125 const uint16_t bcd)
126 {
127 struct usb_device_descriptor *fs_desc, *hs_desc;
128 int ret = 0;
129
130 usbd_device_lock(uds_ctx);
131
132 if (usbd_is_enabled(uds_ctx)) {
133 ret = -EALREADY;
134 goto set_bcd_device_exit;
135 }
136
137 fs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_FS);
138 fs_desc->bcdDevice = sys_cpu_to_le16(bcd);
139
140 hs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_HS);
141 hs_desc->bcdDevice = sys_cpu_to_le16(bcd);
142
143 set_bcd_device_exit:
144 usbd_device_unlock(uds_ctx);
145 return ret;
146 }
147
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)148 int usbd_device_set_code_triple(struct usbd_context *const uds_ctx,
149 const enum usbd_speed speed,
150 const uint8_t base_class,
151 const uint8_t subclass, const uint8_t protocol)
152 {
153 struct usb_device_descriptor *desc;
154 int ret = 0;
155
156 usbd_device_lock(uds_ctx);
157
158 if (usbd_is_enabled(uds_ctx)) {
159 ret = -EALREADY;
160 goto set_code_triple_exit;
161 }
162
163 desc = get_device_descriptor(uds_ctx, speed);
164 desc->bDeviceClass = base_class;
165 desc->bDeviceSubClass = subclass;
166 desc->bDeviceProtocol = protocol;
167
168 set_code_triple_exit:
169 usbd_device_unlock(uds_ctx);
170 return ret;
171 }
172
usbd_wakeup_request(struct usbd_context * const uds_ctx)173 int usbd_wakeup_request(struct usbd_context *const uds_ctx)
174 {
175 struct udc_device_caps caps = udc_caps(uds_ctx->dev);
176 int ret = 0;
177
178 usbd_device_lock(uds_ctx);
179
180 if (!caps.rwup) {
181 LOG_ERR("Remote wakeup feature not supported");
182 ret = -ENOTSUP;
183 goto wakeup_request_error;
184 }
185
186 if (!uds_ctx->status.rwup || !usbd_is_suspended(uds_ctx)) {
187 LOG_WRN("Remote wakeup feature not enabled or not suspended");
188 ret = -EACCES;
189 goto wakeup_request_error;
190 }
191
192 ret = udc_host_wakeup(uds_ctx->dev);
193
194 wakeup_request_error:
195 usbd_device_unlock(uds_ctx);
196
197 return ret;
198 }
199
usbd_self_powered(struct usbd_context * uds_ctx,const bool status)200 void usbd_self_powered(struct usbd_context *uds_ctx, const bool status)
201 {
202 usbd_device_lock(uds_ctx);
203 uds_ctx->status.self_powered = status;
204 usbd_device_unlock(uds_ctx);
205 }
206
usbd_is_suspended(struct usbd_context * uds_ctx)207 bool usbd_is_suspended(struct usbd_context *uds_ctx)
208 {
209 return uds_ctx->status.suspended;
210 }
211
usbd_init(struct usbd_context * const uds_ctx)212 int usbd_init(struct usbd_context *const uds_ctx)
213 {
214 int ret;
215
216 /*
217 * Lock the scheduler to ensure that the context is not preempted
218 * before it is fully initialized.
219 */
220 k_sched_lock();
221 usbd_device_lock(uds_ctx);
222
223 if (uds_ctx->dev == NULL) {
224 ret = -ENODEV;
225 goto init_exit;
226 }
227
228 if (usbd_is_initialized(uds_ctx)) {
229 LOG_WRN("USB device support is already initialized");
230 ret = -EALREADY;
231 goto init_exit;
232 }
233
234 if (!device_is_ready(uds_ctx->dev)) {
235 LOG_ERR("USB device controller is not ready");
236 ret = -ENODEV;
237 goto init_exit;
238 }
239
240 ret = usbd_device_init_core(uds_ctx);
241 if (ret) {
242 goto init_exit;
243 }
244
245 memset(&uds_ctx->ch9_data, 0, sizeof(struct usbd_ch9_data));
246 uds_ctx->status.initialized = true;
247
248 init_exit:
249 usbd_device_unlock(uds_ctx);
250 k_sched_unlock();
251
252 return ret;
253 }
254
usbd_enable(struct usbd_context * const uds_ctx)255 int usbd_enable(struct usbd_context *const uds_ctx)
256 {
257 int ret;
258
259 k_sched_lock();
260 usbd_device_lock(uds_ctx);
261
262 if (!usbd_is_initialized(uds_ctx)) {
263 LOG_WRN("USB device support is not initialized");
264 ret = -EPERM;
265 goto enable_exit;
266 }
267
268 if (usbd_is_enabled(uds_ctx)) {
269 LOG_WRN("USB device support is already enabled");
270 ret = -EALREADY;
271 goto enable_exit;
272 }
273
274 ret = udc_enable(uds_ctx->dev);
275 if (ret != 0) {
276 LOG_ERR("Failed to enable controller");
277 goto enable_exit;
278 }
279
280 ret = usbd_init_control_pipe(uds_ctx);
281 if (ret != 0) {
282 udc_disable(uds_ctx->dev);
283 goto enable_exit;
284 }
285
286 uds_ctx->status.enabled = true;
287
288 enable_exit:
289 usbd_device_unlock(uds_ctx);
290 k_sched_unlock();
291
292 return ret;
293 }
294
usbd_disable(struct usbd_context * const uds_ctx)295 int usbd_disable(struct usbd_context *const uds_ctx)
296 {
297 int ret;
298
299 if (!usbd_is_enabled(uds_ctx)) {
300 LOG_WRN("USB device support is already disabled");
301 return -EALREADY;
302 }
303
304 usbd_device_lock(uds_ctx);
305
306 ret = usbd_config_set(uds_ctx, 0);
307 if (ret) {
308 LOG_ERR("Failed to reset configuration");
309 }
310
311 ret = udc_disable(uds_ctx->dev);
312 if (ret) {
313 LOG_ERR("Failed to disable USB device");
314 }
315
316 uds_ctx->status.enabled = false;
317
318 usbd_device_unlock(uds_ctx);
319
320 return ret;
321 }
322
usbd_shutdown(struct usbd_context * const uds_ctx)323 int usbd_shutdown(struct usbd_context *const uds_ctx)
324 {
325 int ret;
326
327 usbd_device_lock(uds_ctx);
328
329 /* TODO: control request dequeue ? */
330 ret = usbd_device_shutdown_core(uds_ctx);
331 if (ret) {
332 LOG_ERR("Failed to shutdown USB device");
333 }
334
335 uds_ctx->status.initialized = false;
336 usbd_device_unlock(uds_ctx);
337
338 return 0;
339 }
340
usbd_can_detect_vbus(struct usbd_context * const uds_ctx)341 bool usbd_can_detect_vbus(struct usbd_context *const uds_ctx)
342 {
343 const struct udc_device_caps caps = udc_caps(uds_ctx->dev);
344
345 return caps.can_detect_vbus;
346 }
347
usbd_device_get_vreq(struct usbd_context * const uds_ctx,const uint8_t code)348 struct usbd_vreq_node *usbd_device_get_vreq(struct usbd_context *const uds_ctx,
349 const uint8_t code)
350 {
351 struct usbd_vreq_node *vreq_nd;
352
353 SYS_DLIST_FOR_EACH_CONTAINER(&uds_ctx->vreqs, vreq_nd, node) {
354 if (vreq_nd->code == code) {
355 return vreq_nd;
356 }
357 }
358
359 return NULL;
360 }
361
usbd_device_register_vreq(struct usbd_context * const uds_ctx,struct usbd_vreq_node * const vreq_nd)362 int usbd_device_register_vreq(struct usbd_context *const uds_ctx,
363 struct usbd_vreq_node *const vreq_nd)
364 {
365 int ret = 0;
366
367 usbd_device_lock(uds_ctx);
368
369 if (usbd_is_initialized(uds_ctx)) {
370 ret = -EPERM;
371 goto error;
372 }
373
374 if (vreq_nd->to_dev == NULL && vreq_nd->to_host == NULL) {
375 ret = -EINVAL;
376 goto error;
377 }
378
379 if (!sys_dnode_is_linked(&uds_ctx->vreqs)) {
380 LOG_DBG("Initialize vendor request list");
381 sys_dlist_init(&uds_ctx->vreqs);
382 }
383
384 if (sys_dnode_is_linked(&vreq_nd->node)) {
385 ret = -EALREADY;
386 goto error;
387 }
388
389 sys_dlist_append(&uds_ctx->vreqs, &vreq_nd->node);
390 LOG_DBG("Registered vendor request 0x%02x", vreq_nd->code);
391
392 error:
393 usbd_device_unlock(uds_ctx);
394 return ret;
395 }
396
usbd_device_unregister_all_vreq(struct usbd_context * const uds_ctx)397 void usbd_device_unregister_all_vreq(struct usbd_context *const uds_ctx)
398 {
399 struct usbd_vreq_node *tmp;
400 sys_dnode_t *node;
401
402 if (!sys_dnode_is_linked(&uds_ctx->vreqs)) {
403 return;
404 }
405
406 while ((node = sys_dlist_get(&uds_ctx->vreqs))) {
407 tmp = CONTAINER_OF(node, struct usbd_vreq_node, node);
408 LOG_DBG("Remove vendor request 0x%02x", tmp->code);
409 }
410 }
411