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