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_is_suspended(struct usbd_context * uds_ctx)200 bool usbd_is_suspended(struct usbd_context *uds_ctx)
201 {
202 return uds_ctx->status.suspended;
203 }
204
usbd_init(struct usbd_context * const uds_ctx)205 int usbd_init(struct usbd_context *const uds_ctx)
206 {
207 int ret;
208
209 /*
210 * Lock the scheduler to ensure that the context is not preempted
211 * before it is fully initialized.
212 */
213 k_sched_lock();
214 usbd_device_lock(uds_ctx);
215
216 if (uds_ctx->dev == NULL) {
217 ret = -ENODEV;
218 goto init_exit;
219 }
220
221 if (usbd_is_initialized(uds_ctx)) {
222 LOG_WRN("USB device support is already initialized");
223 ret = -EALREADY;
224 goto init_exit;
225 }
226
227 if (!device_is_ready(uds_ctx->dev)) {
228 LOG_ERR("USB device controller is not ready");
229 ret = -ENODEV;
230 goto init_exit;
231 }
232
233 ret = usbd_device_init_core(uds_ctx);
234 if (ret) {
235 goto init_exit;
236 }
237
238 memset(&uds_ctx->ch9_data, 0, sizeof(struct usbd_ch9_data));
239 uds_ctx->status.initialized = true;
240
241 init_exit:
242 usbd_device_unlock(uds_ctx);
243 k_sched_unlock();
244
245 return ret;
246 }
247
usbd_enable(struct usbd_context * const uds_ctx)248 int usbd_enable(struct usbd_context *const uds_ctx)
249 {
250 int ret;
251
252 usbd_device_lock(uds_ctx);
253
254 if (!usbd_is_initialized(uds_ctx)) {
255 LOG_WRN("USB device support is not initialized");
256 ret = -EPERM;
257 goto enable_exit;
258 }
259
260 if (usbd_is_enabled(uds_ctx)) {
261 LOG_WRN("USB device support is already enabled");
262 ret = -EALREADY;
263 goto enable_exit;
264 }
265
266 ret = udc_enable(uds_ctx->dev);
267 if (ret != 0) {
268 LOG_ERR("Failed to enable controller");
269 goto enable_exit;
270 }
271
272 ret = usbd_init_control_pipe(uds_ctx);
273 if (ret != 0) {
274 udc_disable(uds_ctx->dev);
275 goto enable_exit;
276 }
277
278 uds_ctx->status.enabled = true;
279
280 enable_exit:
281 usbd_device_unlock(uds_ctx);
282 return ret;
283 }
284
usbd_disable(struct usbd_context * const uds_ctx)285 int usbd_disable(struct usbd_context *const uds_ctx)
286 {
287 int ret;
288
289 if (!usbd_is_enabled(uds_ctx)) {
290 LOG_WRN("USB device support is already disabled");
291 return -EALREADY;
292 }
293
294 usbd_device_lock(uds_ctx);
295
296 ret = usbd_config_set(uds_ctx, 0);
297 if (ret) {
298 LOG_ERR("Failed to reset configuration");
299 }
300
301 ret = udc_disable(uds_ctx->dev);
302 if (ret) {
303 LOG_ERR("Failed to disable USB device");
304 }
305
306 uds_ctx->status.enabled = false;
307
308 usbd_device_unlock(uds_ctx);
309
310 return ret;
311 }
312
usbd_shutdown(struct usbd_context * const uds_ctx)313 int usbd_shutdown(struct usbd_context *const uds_ctx)
314 {
315 int ret;
316
317 usbd_device_lock(uds_ctx);
318
319 /* TODO: control request dequeue ? */
320 ret = usbd_device_shutdown_core(uds_ctx);
321 if (ret) {
322 LOG_ERR("Failed to shutdown USB device");
323 }
324
325 uds_ctx->status.initialized = false;
326 usbd_device_unlock(uds_ctx);
327
328 return 0;
329 }
330
usbd_can_detect_vbus(struct usbd_context * const uds_ctx)331 bool usbd_can_detect_vbus(struct usbd_context *const uds_ctx)
332 {
333 const struct udc_device_caps caps = udc_caps(uds_ctx->dev);
334
335 return caps.can_detect_vbus;
336 }
337