1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/init.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/device.h>
10 #include <zephyr/toolchain.h>
11 #include <zephyr/sys/slist.h>
12 #include <zephyr/sys/iterable_sections.h>
13 #include <zephyr/drivers/usb/udc.h>
14 #include <zephyr/usb/usbd.h>
15 
16 #include "usbd_device.h"
17 #include "usbd_desc.h"
18 #include "usbd_config.h"
19 #include "usbd_init.h"
20 #include "usbd_ch9.h"
21 #include "usbd_class.h"
22 #include "usbd_class_api.h"
23 #include "usbd_msg.h"
24 
25 #include <zephyr/logging/log.h>
26 LOG_MODULE_REGISTER(usbd_core, CONFIG_USBD_LOG_LEVEL);
27 
28 static K_KERNEL_STACK_DEFINE(usbd_stack, CONFIG_USBD_THREAD_STACK_SIZE);
29 static struct k_thread usbd_thread_data;
30 
31 K_MSGQ_DEFINE(usbd_msgq, sizeof(struct udc_event),
32 	      CONFIG_USBD_MAX_UDC_MSG, sizeof(uint32_t));
33 
usbd_event_carrier(const struct device * dev,const struct udc_event * const event)34 static int usbd_event_carrier(const struct device *dev,
35 			      const struct udc_event *const event)
36 {
37 	return k_msgq_put(&usbd_msgq, event, K_NO_WAIT);
38 }
39 
event_handler_ep_request(struct usbd_context * const uds_ctx,const struct udc_event * const event)40 static int event_handler_ep_request(struct usbd_context *const uds_ctx,
41 				    const struct udc_event *const event)
42 {
43 	struct udc_buf_info *bi;
44 	int ret;
45 
46 	bi = udc_get_buf_info(event->buf);
47 
48 	if (USB_EP_GET_IDX(bi->ep) == 0) {
49 		ret = usbd_handle_ctrl_xfer(uds_ctx, event->buf, bi->err);
50 	} else {
51 		ret = usbd_class_handle_xfer(uds_ctx, event->buf, bi->err);
52 	}
53 
54 	if (ret) {
55 		LOG_ERR("unrecoverable error %d, ep 0x%02x, buf %p",
56 			ret, bi->ep, event->buf);
57 	}
58 
59 	return ret;
60 }
61 
usbd_class_bcast_event(struct usbd_context * const uds_ctx,struct udc_event * const event)62 static void usbd_class_bcast_event(struct usbd_context *const uds_ctx,
63 				   struct udc_event *const event)
64 {
65 	struct usbd_config_node *cfg_nd;
66 	struct usbd_class_node *c_nd;
67 
68 	if (!usbd_state_is_configured(uds_ctx)) {
69 		return;
70 	}
71 
72 	cfg_nd = usbd_config_get_current(uds_ctx);
73 	if (cfg_nd == NULL) {
74 		LOG_ERR("Failed to get cfg_nd, despite configured state");
75 		return;
76 	}
77 
78 	SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
79 		switch (event->type) {
80 		case UDC_EVT_SUSPEND:
81 			usbd_class_suspended(c_nd->c_data);
82 			break;
83 		case UDC_EVT_RESUME:
84 			usbd_class_resumed(c_nd->c_data);
85 			break;
86 		case UDC_EVT_SOF:
87 			usbd_class_sof(c_nd->c_data);
88 			break;
89 		default:
90 			break;
91 		}
92 	}
93 }
94 
event_handler_bus_reset(struct usbd_context * const uds_ctx)95 static int event_handler_bus_reset(struct usbd_context *const uds_ctx)
96 {
97 	enum udc_bus_speed udc_speed;
98 	int ret;
99 
100 	usbd_status_suspended(uds_ctx, false);
101 	ret = udc_set_address(uds_ctx->dev, 0);
102 	if (ret) {
103 		LOG_ERR("Failed to set default address after bus reset");
104 		return ret;
105 	}
106 
107 	ret = usbd_config_set(uds_ctx, 0);
108 	if (ret) {
109 		LOG_ERR("Failed to set default state after bus reset");
110 		return ret;
111 	}
112 
113 	/* There might be pending data stage transfer */
114 	if (usbd_ep_dequeue(uds_ctx, USB_CONTROL_EP_IN)) {
115 		LOG_ERR("Failed to dequeue control IN");
116 	}
117 
118 	LOG_INF("Actual device speed %u", udc_device_speed(uds_ctx->dev));
119 	udc_speed = udc_device_speed(uds_ctx->dev);
120 	switch (udc_speed) {
121 	case UDC_BUS_SPEED_HS:
122 		uds_ctx->status.speed = USBD_SPEED_HS;
123 		break;
124 	default:
125 		uds_ctx->status.speed = USBD_SPEED_FS;
126 	}
127 
128 	uds_ctx->ch9_data.state = USBD_STATE_DEFAULT;
129 
130 	uds_ctx->status.rwup = false;
131 
132 	return 0;
133 }
134 
135 
usbd_event_handler(struct usbd_context * const uds_ctx,struct udc_event * const event)136 static ALWAYS_INLINE void usbd_event_handler(struct usbd_context *const uds_ctx,
137 					     struct udc_event *const event)
138 {
139 	int err = 0;
140 
141 	switch (event->type) {
142 	case UDC_EVT_VBUS_REMOVED:
143 		LOG_DBG("VBUS remove event");
144 		usbd_msg_pub_simple(uds_ctx, USBD_MSG_VBUS_REMOVED, 0);
145 		break;
146 	case UDC_EVT_VBUS_READY:
147 		LOG_DBG("VBUS detected event");
148 		usbd_msg_pub_simple(uds_ctx, USBD_MSG_VBUS_READY, 0);
149 		break;
150 	case UDC_EVT_SUSPEND:
151 		LOG_DBG("SUSPEND event");
152 		usbd_status_suspended(uds_ctx, true);
153 		usbd_class_bcast_event(uds_ctx, event);
154 		usbd_msg_pub_simple(uds_ctx, USBD_MSG_SUSPEND, 0);
155 		break;
156 	case UDC_EVT_RESUME:
157 		LOG_DBG("RESUME event");
158 		usbd_status_suspended(uds_ctx, false);
159 		usbd_class_bcast_event(uds_ctx, event);
160 		usbd_msg_pub_simple(uds_ctx, USBD_MSG_RESUME, 0);
161 		break;
162 	case UDC_EVT_SOF:
163 		usbd_class_bcast_event(uds_ctx, event);
164 		break;
165 	case UDC_EVT_RESET:
166 		LOG_DBG("RESET event");
167 		err = event_handler_bus_reset(uds_ctx);
168 		usbd_msg_pub_simple(uds_ctx, USBD_MSG_RESET, 0);
169 		break;
170 	case UDC_EVT_EP_REQUEST:
171 		err = event_handler_ep_request(uds_ctx, event);
172 		break;
173 	case UDC_EVT_ERROR:
174 		LOG_ERR("UDC error event");
175 		usbd_msg_pub_simple(uds_ctx, USBD_MSG_UDC_ERROR, event->status);
176 		break;
177 	default:
178 		break;
179 	};
180 
181 	if (err) {
182 		usbd_msg_pub_simple(uds_ctx, USBD_MSG_STACK_ERROR, err);
183 	}
184 }
185 
usbd_thread(void * p1,void * p2,void * p3)186 static void usbd_thread(void *p1, void *p2, void *p3)
187 {
188 	ARG_UNUSED(p1);
189 	ARG_UNUSED(p2);
190 	ARG_UNUSED(p3);
191 
192 	struct usbd_context *uds_ctx;
193 	struct udc_event event;
194 
195 	while (true) {
196 		k_msgq_get(&usbd_msgq, &event, K_FOREVER);
197 
198 		uds_ctx = (void *)udc_get_event_ctx(event.dev);
199 		__ASSERT(uds_ctx != NULL && usbd_is_initialized(uds_ctx),
200 			 "USB device is not initialized");
201 		usbd_event_handler(uds_ctx, &event);
202 	}
203 }
204 
usbd_device_init_core(struct usbd_context * const uds_ctx)205 int usbd_device_init_core(struct usbd_context *const uds_ctx)
206 {
207 	int ret;
208 
209 	ret = udc_init(uds_ctx->dev, usbd_event_carrier, uds_ctx);
210 	if (ret != 0) {
211 		LOG_ERR("Failed to init device driver");
212 		return ret;
213 	}
214 
215 	usbd_set_config_value(uds_ctx, 0);
216 
217 	ret = usbd_init_configurations(uds_ctx);
218 	if (ret != 0) {
219 		udc_shutdown(uds_ctx->dev);
220 		return ret;
221 	}
222 
223 	return ret;
224 }
225 
usbd_device_shutdown_core(struct usbd_context * const uds_ctx)226 int usbd_device_shutdown_core(struct usbd_context *const uds_ctx)
227 {
228 	struct usbd_config_node *cfg_nd;
229 	int ret;
230 
231 	SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->hs_configs, cfg_nd, node) {
232 		uint8_t cfg_value = usbd_config_get_value(cfg_nd);
233 
234 		ret = usbd_class_remove_all(uds_ctx, USBD_SPEED_HS, cfg_value);
235 		if (ret) {
236 			LOG_ERR("Failed to cleanup registered classes, %d", ret);
237 		}
238 	}
239 
240 	SYS_SLIST_FOR_EACH_CONTAINER(&uds_ctx->fs_configs, cfg_nd, node) {
241 		uint8_t cfg_value = usbd_config_get_value(cfg_nd);
242 
243 		ret = usbd_class_remove_all(uds_ctx, USBD_SPEED_FS, cfg_value);
244 		if (ret) {
245 			LOG_ERR("Failed to cleanup registered classes, %d", ret);
246 		}
247 	}
248 
249 	ret = usbd_desc_remove_all(uds_ctx);
250 	if (ret) {
251 		LOG_ERR("Failed to cleanup descriptors, %d", ret);
252 	}
253 
254 	usbd_device_unregister_all_vreq(uds_ctx);
255 
256 	return udc_shutdown(uds_ctx->dev);
257 }
258 
usbd_pre_init(void)259 static int usbd_pre_init(void)
260 {
261 	k_thread_create(&usbd_thread_data, usbd_stack,
262 			K_KERNEL_STACK_SIZEOF(usbd_stack),
263 			usbd_thread,
264 			NULL, NULL, NULL,
265 			K_PRIO_COOP(8), 0, K_NO_WAIT);
266 
267 	k_thread_name_set(&usbd_thread_data, "usbd");
268 
269 	LOG_DBG("Available USB class iterators:");
270 	STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) {
271 		atomic_set(&c_nd->state, 0);
272 		LOG_DBG("\t%p->%p, name %s", c_nd, c_nd->c_data, c_nd->c_data->name);
273 	}
274 	STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_node, c_nd) {
275 		atomic_set(&c_nd->state, 0);
276 		LOG_DBG("\t%p->%p, name %s", c_nd, c_nd->c_data, c_nd->c_data->name);
277 	}
278 
279 	return 0;
280 }
281 
282 SYS_INIT(usbd_pre_init, POST_KERNEL, CONFIG_USBD_THREAD_INIT_PRIO);
283