1 /*
2  * Copyright (c) 2022,2025 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <errno.h>
8 #include <zephyr/device.h>
9 #include <zephyr/sys/byteorder.h>
10 #include <zephyr/devicetree.h>
11 #include <zephyr/init.h>
12 #include <zephyr/sys/iterable_sections.h>
13 
14 #include "usbh_internal.h"
15 #include "usbh_device.h"
16 
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(uhs, CONFIG_USBH_LOG_LEVEL);
19 
20 static K_KERNEL_STACK_DEFINE(usbh_stack, CONFIG_USBH_STACK_SIZE);
21 static struct k_thread usbh_thread_data;
22 
23 static K_KERNEL_STACK_DEFINE(usbh_bus_stack, CONFIG_USBH_STACK_SIZE);
24 static struct k_thread usbh_bus_thread_data;
25 
26 K_MSGQ_DEFINE(usbh_msgq, sizeof(struct uhc_event),
27 	      CONFIG_USBH_MAX_UHC_MSG, sizeof(uint32_t));
28 
29 K_MSGQ_DEFINE(usbh_bus_msgq, sizeof(struct uhc_event),
30 	      CONFIG_USBH_MAX_UHC_MSG, sizeof(uint32_t));
31 
usbh_event_carrier(const struct device * dev,const struct uhc_event * const event)32 static int usbh_event_carrier(const struct device *dev,
33 			      const struct uhc_event *const event)
34 {
35 	int err;
36 
37 	if (event->type == UHC_EVT_EP_REQUEST) {
38 		err = k_msgq_put(&usbh_msgq, event, K_NO_WAIT);
39 	} else {
40 		err = k_msgq_put(&usbh_bus_msgq, event, K_NO_WAIT);
41 	}
42 
43 	return err;
44 }
45 
dev_connected_handler(struct usbh_contex * const ctx,const struct uhc_event * const event)46 static void dev_connected_handler(struct usbh_contex *const ctx,
47 				  const struct uhc_event *const event)
48 {
49 
50 	LOG_DBG("Device connected event");
51 	if (ctx->root != NULL) {
52 		LOG_ERR("Device already connected");
53 		usbh_device_free(ctx->root);
54 		ctx->root = NULL;
55 	}
56 
57 	ctx->root = usbh_device_alloc(ctx);
58 	if (ctx->root == NULL) {
59 		LOG_ERR("Failed allocate new device");
60 		return;
61 	}
62 
63 	ctx->root->state = USB_STATE_DEFAULT;
64 
65 	if (event->type == UHC_EVT_DEV_CONNECTED_HS) {
66 		ctx->root->speed = USB_SPEED_SPEED_HS;
67 	} else {
68 		ctx->root->speed = USB_SPEED_SPEED_FS;
69 	}
70 
71 	if (usbh_device_init(ctx->root)) {
72 		LOG_ERR("Failed to reset new USB device");
73 	}
74 }
75 
dev_removed_handler(struct usbh_contex * const ctx)76 static void dev_removed_handler(struct usbh_contex *const ctx)
77 {
78 	if (ctx->root != NULL) {
79 		usbh_device_free(ctx->root);
80 		ctx->root = NULL;
81 		LOG_DBG("Device removed");
82 	} else {
83 		LOG_DBG("Spurious device removed event");
84 	}
85 }
86 
discard_ep_request(struct usbh_contex * const ctx,struct uhc_transfer * const xfer)87 static int discard_ep_request(struct usbh_contex *const ctx,
88 			      struct uhc_transfer *const xfer)
89 {
90 	const struct device *dev = ctx->dev;
91 
92 	if (xfer->buf) {
93 		LOG_HEXDUMP_INF(xfer->buf->data, xfer->buf->len, "buf");
94 		uhc_xfer_buf_free(dev, xfer->buf);
95 	}
96 
97 	return uhc_xfer_free(dev, xfer);
98 }
99 
usbh_event_handler(struct usbh_contex * const ctx,struct uhc_event * const event)100 static ALWAYS_INLINE int usbh_event_handler(struct usbh_contex *const ctx,
101 					    struct uhc_event *const event)
102 {
103 	int ret = 0;
104 
105 	switch (event->type) {
106 	case UHC_EVT_DEV_CONNECTED_LS:
107 		LOG_ERR("Low speed device not supported (connected event)");
108 		break;
109 	case UHC_EVT_DEV_CONNECTED_FS:
110 	case UHC_EVT_DEV_CONNECTED_HS:
111 		dev_connected_handler(ctx, event);
112 		break;
113 	case UHC_EVT_DEV_REMOVED:
114 		dev_removed_handler(ctx);
115 		break;
116 	case UHC_EVT_RESETED:
117 		LOG_DBG("Bus reset");
118 		break;
119 	case UHC_EVT_SUSPENDED:
120 		LOG_DBG("Bus suspended");
121 		break;
122 	case UHC_EVT_RESUMED:
123 		LOG_DBG("Bus resumed");
124 		break;
125 	case UHC_EVT_RWUP:
126 		LOG_DBG("RWUP event");
127 		break;
128 	case UHC_EVT_ERROR:
129 		LOG_DBG("Error event %d", event->status);
130 		break;
131 	default:
132 		break;
133 	};
134 
135 	return ret;
136 }
137 
usbh_bus_thread(void * p1,void * p2,void * p3)138 static void usbh_bus_thread(void *p1, void *p2, void *p3)
139 {
140 	ARG_UNUSED(p1);
141 	ARG_UNUSED(p2);
142 	ARG_UNUSED(p3);
143 
144 	struct usbh_contex *uhs_ctx;
145 	struct uhc_event event;
146 
147 	while (true) {
148 		k_msgq_get(&usbh_bus_msgq, &event, K_FOREVER);
149 
150 		uhs_ctx = (void *)uhc_get_event_ctx(event.dev);
151 		usbh_event_handler(uhs_ctx, &event);
152 	}
153 }
154 
usbh_thread(void * p1,void * p2,void * p3)155 static void usbh_thread(void *p1, void *p2, void *p3)
156 {
157 	ARG_UNUSED(p1);
158 	ARG_UNUSED(p2);
159 	ARG_UNUSED(p3);
160 
161 	struct usbh_contex *uhs_ctx;
162 	struct uhc_event event;
163 	usbh_udev_cb_t cb;
164 	int ret;
165 
166 	while (true) {
167 		k_msgq_get(&usbh_msgq, &event, K_FOREVER);
168 
169 		__ASSERT(event.type == UHC_EVT_EP_REQUEST, "Wrong event type");
170 		uhs_ctx = (void *)uhc_get_event_ctx(event.dev);
171 		cb = event.xfer->cb;
172 
173 		if (event.xfer->cb) {
174 			ret = cb(event.xfer->udev, event.xfer);
175 		} else {
176 			ret = discard_ep_request(uhs_ctx, event.xfer);
177 		}
178 
179 		if (ret) {
180 			LOG_ERR("Failed to handle request completion callback");
181 		}
182 	}
183 }
184 
usbh_init_device_intl(struct usbh_contex * const uhs_ctx)185 int usbh_init_device_intl(struct usbh_contex *const uhs_ctx)
186 {
187 	int ret;
188 
189 	ret = uhc_init(uhs_ctx->dev, usbh_event_carrier, uhs_ctx);
190 	if (ret != 0) {
191 		LOG_ERR("Failed to init device driver");
192 		return ret;
193 	}
194 
195 	sys_dlist_init(&uhs_ctx->udevs);
196 
197 	STRUCT_SECTION_FOREACH(usbh_class_data, cdata) {
198 		/*
199 		 * For now, we have not implemented any class drivers,
200 		 * so just keep it as placeholder.
201 		 */
202 		break;
203 	}
204 
205 	return 0;
206 }
207 
uhs_pre_init(void)208 static int uhs_pre_init(void)
209 {
210 	k_thread_create(&usbh_thread_data, usbh_stack,
211 			K_KERNEL_STACK_SIZEOF(usbh_stack),
212 			usbh_thread,
213 			NULL, NULL, NULL,
214 			K_PRIO_COOP(9), 0, K_NO_WAIT);
215 
216 	k_thread_name_set(&usbh_thread_data, "usbh");
217 
218 	k_thread_create(&usbh_bus_thread_data, usbh_bus_stack,
219 			K_KERNEL_STACK_SIZEOF(usbh_bus_stack),
220 			usbh_bus_thread,
221 			NULL, NULL, NULL,
222 			K_PRIO_COOP(9), 0, K_NO_WAIT);
223 
224 	k_thread_name_set(&usbh_thread_data, "usbh_bus");
225 
226 	return 0;
227 }
228 
229 SYS_INIT(uhs_pre_init, POST_KERNEL, CONFIG_USBH_INIT_PRIO);
230