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