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