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_device_set_bcd(struct usbd_contex * const uds_ctx,const uint16_t bcd)23 int usbd_device_set_bcd(struct usbd_contex *const uds_ctx,
24 const uint16_t bcd)
25 {
26 struct usb_device_descriptor *desc = uds_ctx->desc;
27 int ret = 0;
28
29 usbd_device_lock(uds_ctx);
30
31 if (usbd_is_enabled(uds_ctx)) {
32 ret = -EALREADY;
33 goto set_bcd_exit;
34 }
35
36 desc->bcdUSB = sys_cpu_to_le16(bcd);
37
38 set_bcd_exit:
39 usbd_device_unlock(uds_ctx);
40 return ret;
41 }
42
usbd_device_set_vid(struct usbd_contex * const uds_ctx,const uint16_t vid)43 int usbd_device_set_vid(struct usbd_contex *const uds_ctx,
44 const uint16_t vid)
45 {
46 struct usb_device_descriptor *desc = uds_ctx->desc;
47 int ret = 0;
48
49 usbd_device_lock(uds_ctx);
50
51 if (usbd_is_enabled(uds_ctx)) {
52 ret = -EALREADY;
53 goto set_vid_exit;
54 }
55
56 desc->idVendor = sys_cpu_to_le16(vid);
57
58 set_vid_exit:
59 usbd_device_unlock(uds_ctx);
60 return ret;
61 }
62
usbd_device_set_pid(struct usbd_contex * const uds_ctx,const uint16_t pid)63 int usbd_device_set_pid(struct usbd_contex *const uds_ctx,
64 const uint16_t pid)
65 {
66 struct usb_device_descriptor *desc = uds_ctx->desc;
67 int ret = 0;
68
69 usbd_device_lock(uds_ctx);
70
71 if (usbd_is_enabled(uds_ctx)) {
72 ret = -EALREADY;
73 goto set_pid_exit;
74 }
75
76 desc->idProduct = sys_cpu_to_le16(pid);
77
78 set_pid_exit:
79 usbd_device_unlock(uds_ctx);
80 return ret;
81 }
82
usbd_device_set_class(struct usbd_contex * const uds_ctx,const uint8_t value)83 int usbd_device_set_class(struct usbd_contex *const uds_ctx,
84 const uint8_t value)
85 {
86 struct usb_device_descriptor *desc = uds_ctx->desc;
87 int ret = 0;
88
89 usbd_device_lock(uds_ctx);
90
91 if (usbd_is_enabled(uds_ctx)) {
92 ret = -EALREADY;
93 goto set_class_exit;
94 }
95
96 desc->bDeviceClass = value;
97
98 set_class_exit:
99 usbd_device_unlock(uds_ctx);
100 return ret;
101 }
102
usbd_device_set_subclass(struct usbd_contex * const uds_ctx,const uint8_t value)103 int usbd_device_set_subclass(struct usbd_contex *const uds_ctx,
104 const uint8_t value)
105 {
106 struct usb_device_descriptor *desc = uds_ctx->desc;
107 int ret = 0;
108
109 usbd_device_lock(uds_ctx);
110
111 if (usbd_is_enabled(uds_ctx)) {
112 ret = -EALREADY;
113 goto set_subclass_exit;
114 }
115
116 desc->bDeviceSubClass = value;
117
118 set_subclass_exit:
119 usbd_device_unlock(uds_ctx);
120 return ret;
121 }
122
usbd_device_set_proto(struct usbd_contex * const uds_ctx,const uint8_t value)123 int usbd_device_set_proto(struct usbd_contex *const uds_ctx,
124 const uint8_t value)
125 {
126 struct usb_device_descriptor *desc = uds_ctx->desc;
127 int ret = 0;
128
129 usbd_device_lock(uds_ctx);
130
131 if (usbd_is_enabled(uds_ctx)) {
132 ret = -EALREADY;
133 goto set_proto_exit;
134 }
135
136 desc->bDeviceProtocol = value;
137
138 set_proto_exit:
139 usbd_device_unlock(uds_ctx);
140 return ret;
141 }
142
usbd_wakeup_request(struct usbd_contex * const uds_ctx)143 int usbd_wakeup_request(struct usbd_contex *const uds_ctx)
144 {
145 struct udc_device_caps caps = udc_caps(uds_ctx->dev);
146 int ret = 0;
147
148 usbd_device_lock(uds_ctx);
149
150 if (!caps.rwup) {
151 LOG_ERR("Remote wakeup feature not supported");
152 ret = -ENOTSUP;
153 goto wakeup_request_error;
154 }
155
156 if (!uds_ctx->status.rwup || !usbd_is_suspended(uds_ctx)) {
157 LOG_ERR("Remote wakeup feature not enabled or not suspended");
158 ret = -EACCES;
159 goto wakeup_request_error;
160 }
161
162 ret = udc_host_wakeup(uds_ctx->dev);
163
164 wakeup_request_error:
165 usbd_device_unlock(uds_ctx);
166
167 return ret;
168 }
169
usbd_is_suspended(struct usbd_contex * uds_ctx)170 bool usbd_is_suspended(struct usbd_contex *uds_ctx)
171 {
172 return uds_ctx->status.suspended;
173 }
174
usbd_init(struct usbd_contex * const uds_ctx)175 int usbd_init(struct usbd_contex *const uds_ctx)
176 {
177 int ret;
178
179 usbd_device_lock(uds_ctx);
180
181 if (uds_ctx->dev == NULL) {
182 ret = -ENODEV;
183 goto init_exit;
184 }
185
186 if (usbd_is_initialized(uds_ctx)) {
187 LOG_WRN("USB device support is already initialized");
188 ret = -EALREADY;
189 goto init_exit;
190 }
191
192 if (!device_is_ready(uds_ctx->dev)) {
193 LOG_ERR("USB device controller is not ready");
194 ret = -ENODEV;
195 goto init_exit;
196 }
197
198 ret = usbd_device_init_core(uds_ctx);
199 if (ret) {
200 goto init_exit;
201 }
202
203 memset(&uds_ctx->ch9_data, 0, sizeof(struct usbd_ch9_data));
204 uds_ctx->status.initialized = true;
205
206 init_exit:
207 usbd_device_unlock(uds_ctx);
208 return ret;
209 }
210
usbd_enable(struct usbd_contex * const uds_ctx)211 int usbd_enable(struct usbd_contex *const uds_ctx)
212 {
213 int ret;
214
215 usbd_device_lock(uds_ctx);
216
217 if (!usbd_is_initialized(uds_ctx)) {
218 LOG_WRN("USB device support is not initialized");
219 ret = -EPERM;
220 goto enable_exit;
221 }
222
223 if (usbd_is_enabled(uds_ctx)) {
224 LOG_WRN("USB device support is already enabled");
225 ret = -EALREADY;
226 goto enable_exit;
227 }
228
229 ret = udc_enable(uds_ctx->dev);
230 if (ret != 0) {
231 LOG_ERR("Failed to enable controller");
232 goto enable_exit;
233 }
234
235 ret = usbd_init_control_pipe(uds_ctx);
236 if (ret != 0) {
237 udc_disable(uds_ctx->dev);
238 goto enable_exit;
239 }
240
241 uds_ctx->status.enabled = true;
242
243 enable_exit:
244 usbd_device_unlock(uds_ctx);
245 return ret;
246 }
247
usbd_disable(struct usbd_contex * const uds_ctx)248 int usbd_disable(struct usbd_contex *const uds_ctx)
249 {
250 int ret;
251
252 if (!usbd_is_enabled(uds_ctx)) {
253 LOG_WRN("USB device support is already disabled");
254 return -EALREADY;
255 }
256
257 usbd_device_lock(uds_ctx);
258
259 ret = usbd_config_set(uds_ctx, 0);
260 if (ret) {
261 LOG_ERR("Failed to reset configuration");
262 }
263
264 ret = udc_disable(uds_ctx->dev);
265 if (ret) {
266 LOG_ERR("Failed to disable USB device");
267 }
268
269 uds_ctx->status.enabled = false;
270
271 usbd_device_unlock(uds_ctx);
272
273 return ret;
274 }
275
usbd_shutdown(struct usbd_contex * const uds_ctx)276 int usbd_shutdown(struct usbd_contex *const uds_ctx)
277 {
278 int ret;
279
280 usbd_device_lock(uds_ctx);
281
282 /* TODO: control request dequeue ? */
283 ret = usbd_device_shutdown_core(uds_ctx);
284 if (ret) {
285 LOG_ERR("Failed to shutdown USB device");
286 }
287
288 uds_ctx->status.initialized = false;
289 usbd_device_unlock(uds_ctx);
290
291 return 0;
292 }
293