1 /*
2  * Copyright The Zephyr Project Contributors
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /* Disable syscall tracing for all calls from this compilation unit to avoid
8  * undefined symbols as the macros are not expanded recursively
9  */
10 #define DISABLE_SYSCALL_TRACING
11 
12 #include <zephyr/sys/util.h>
13 #include <zephyr/sys/atomic.h>
14 #include <zephyr/sys/__assert.h>
15 #include <zephyr/sys/byteorder.h>
16 #include <zephyr/usb/usbd.h>
17 #include <zephyr/drivers/usb/udc.h>
18 #include <tracing_core.h>
19 #include <tracing_buffer.h>
20 #include <tracing_backend.h>
21 
22 /* Single bounce buffer for bulk IN transfer */
23 UDC_BUF_POOL_DEFINE(tracing_data_pool, 1, CONFIG_TRACING_BUFFER_SIZE,
24 		    sizeof(struct udc_buf_info), NULL);
25 
26 struct tracing_func_desc {
27 	struct usb_if_descriptor if0;
28 	struct usb_ep_descriptor if0_in_ep;
29 	struct usb_ep_descriptor if0_out_ep;
30 	struct usb_ep_descriptor if0_hs_out_ep;
31 	struct usb_ep_descriptor if0_hs_in_ep;
32 };
33 
34 struct tracing_func_data {
35 	struct tracing_func_desc *const desc;
36 	const struct usb_desc_header **const fs_desc;
37 	const struct usb_desc_header **const hs_desc;
38 	struct k_sem sync_sem;
39 	atomic_t state;
40 };
41 
42 #define TRACING_FUNCTION_ENABLED		0
43 
tracing_func_get_bulk_out(struct usbd_class_data * const c_data)44 static uint8_t tracing_func_get_bulk_out(struct usbd_class_data *const c_data)
45 {
46 	struct tracing_func_data *data = usbd_class_get_private(c_data);
47 	struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
48 	struct tracing_func_desc *desc = data->desc;
49 
50 	if (USBD_SUPPORTS_HIGH_SPEED &&
51 	    usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) {
52 		return desc->if0_hs_out_ep.bEndpointAddress;
53 	}
54 
55 	return desc->if0_out_ep.bEndpointAddress;
56 }
57 
tracing_func_get_bulk_in(struct usbd_class_data * const c_data)58 static uint8_t tracing_func_get_bulk_in(struct usbd_class_data *const c_data)
59 {
60 	struct tracing_func_data *data = usbd_class_get_private(c_data);
61 	struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
62 	struct tracing_func_desc *desc = data->desc;
63 
64 	if (USBD_SUPPORTS_HIGH_SPEED &&
65 	    usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) {
66 		return desc->if0_hs_in_ep.bEndpointAddress;
67 	}
68 
69 	return desc->if0_in_ep.bEndpointAddress;
70 }
71 
tracing_func_out_next(struct usbd_class_data * const c_data)72 static void tracing_func_out_next(struct usbd_class_data *const c_data)
73 {
74 	struct tracing_func_data *data = usbd_class_get_private(c_data);
75 	struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
76 	struct net_buf *buf;
77 	uint8_t ep;
78 
79 	if (!atomic_test_bit(&data->state, TRACING_FUNCTION_ENABLED)) {
80 		return;
81 	}
82 
83 	ep = tracing_func_get_bulk_out(c_data);
84 	if (USBD_SUPPORTS_HIGH_SPEED &&
85 	    usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) {
86 		buf = usbd_ep_buf_alloc(c_data, ep, 512);
87 	} else {
88 		buf = usbd_ep_buf_alloc(c_data, ep, 64);
89 	}
90 
91 	if (buf == NULL) {
92 		return;
93 	}
94 
95 	if (usbd_ep_enqueue(c_data, buf)) {
96 		net_buf_unref(buf);
97 	}
98 }
99 
tracing_func_request_handler(struct usbd_class_data * const c_data,struct net_buf * const buf,const int err)100 static int tracing_func_request_handler(struct usbd_class_data *const c_data,
101 					struct net_buf *const buf, const int err)
102 {
103 	struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data);
104 	struct tracing_func_data *data = usbd_class_get_private(c_data);
105 	struct udc_buf_info *bi = NULL;
106 
107 	bi = (struct udc_buf_info *)net_buf_user_data(buf);
108 
109 	if (bi->ep == tracing_func_get_bulk_out(c_data)) {
110 		if (!err) {
111 			tracing_cmd_handle(buf->data, buf->len);
112 		}
113 
114 		usbd_ep_buf_free(uds_ctx, buf);
115 		tracing_func_out_next(c_data);
116 	}
117 
118 	if (bi->ep == tracing_func_get_bulk_in(c_data)) {
119 		usbd_ep_buf_free(uds_ctx, buf);
120 		k_sem_give(&data->sync_sem);
121 	}
122 
123 	return 0;
124 }
125 
tracing_func_get_desc(struct usbd_class_data * const c_data,const enum usbd_speed speed)126 static void *tracing_func_get_desc(struct usbd_class_data *const c_data,
127 				   const enum usbd_speed speed)
128 {
129 	struct tracing_func_data *data = usbd_class_get_private(c_data);
130 
131 	if (USBD_SUPPORTS_HIGH_SPEED && speed == USBD_SPEED_HS) {
132 		return data->hs_desc;
133 	}
134 
135 	return data->fs_desc;
136 }
137 
tracing_func_enable(struct usbd_class_data * const c_data)138 static void tracing_func_enable(struct usbd_class_data *const c_data)
139 {
140 	struct tracing_func_data *data = usbd_class_get_private(c_data);
141 
142 	if (!atomic_test_and_set_bit(&data->state, TRACING_FUNCTION_ENABLED)) {
143 		tracing_func_out_next(c_data);
144 	}
145 }
146 
tracing_func_disable(struct usbd_class_data * const c_data)147 static void tracing_func_disable(struct usbd_class_data *const c_data)
148 {
149 	struct tracing_func_data *data = usbd_class_get_private(c_data);
150 
151 	atomic_clear_bit(&data->state, TRACING_FUNCTION_ENABLED);
152 }
153 
tracing_func_init(struct usbd_class_data * c_data)154 static int tracing_func_init(struct usbd_class_data *c_data)
155 {
156 	ARG_UNUSED(c_data);
157 
158 	return 0;
159 }
160 
161 struct usbd_class_api tracing_func_api = {
162 	.request = tracing_func_request_handler,
163 	.get_desc = tracing_func_get_desc,
164 	.enable = tracing_func_enable,
165 	.disable = tracing_func_disable,
166 	.init = tracing_func_init,
167 };
168 
169 static struct tracing_func_desc func_desc = {
170 	.if0 = {
171 		.bLength = sizeof(struct usb_if_descriptor),
172 		.bDescriptorType = USB_DESC_INTERFACE,
173 		.bInterfaceNumber = 0,
174 		.bAlternateSetting = 0,
175 		.bNumEndpoints = 2,
176 		.bInterfaceClass = USB_BCC_VENDOR,
177 		.bInterfaceSubClass = 0,
178 		.bInterfaceProtocol = 0,
179 		.iInterface = 0,
180 	},
181 
182 	.if0_in_ep = {
183 		.bLength = sizeof(struct usb_ep_descriptor),
184 		.bDescriptorType = USB_DESC_ENDPOINT,
185 		.bEndpointAddress = 0x81,
186 		.bmAttributes = USB_EP_TYPE_BULK,
187 		.wMaxPacketSize = sys_cpu_to_le16(64),
188 		.bInterval = 0x00,
189 	},
190 
191 	.if0_out_ep = {
192 		.bLength = sizeof(struct usb_ep_descriptor),
193 		.bDescriptorType = USB_DESC_ENDPOINT,
194 		.bEndpointAddress = 0x01,
195 		.bmAttributes = USB_EP_TYPE_BULK,
196 		.wMaxPacketSize = sys_cpu_to_le16(64),
197 		.bInterval = 0x00,
198 	},
199 
200 	/* High-speed Endpoint IN */
201 	.if0_hs_in_ep = {
202 		.bLength = sizeof(struct usb_ep_descriptor),
203 		.bDescriptorType = USB_DESC_ENDPOINT,
204 		.bEndpointAddress = 0x81,
205 		.bmAttributes = USB_EP_TYPE_BULK,
206 		.wMaxPacketSize = sys_cpu_to_le16(512),
207 		.bInterval = 0x00,
208 	},
209 
210 	/* High-speed Endpoint OUT */
211 	.if0_hs_out_ep = {
212 		.bLength = sizeof(struct usb_ep_descriptor),
213 		.bDescriptorType = USB_DESC_ENDPOINT,
214 		.bEndpointAddress = 0x01,
215 		.bmAttributes = USB_EP_TYPE_BULK,
216 		.wMaxPacketSize = sys_cpu_to_le16(512),
217 		.bInterval = 0x00,
218 	},
219 };
220 
221 const static struct usb_desc_header *tracing_func_fs_desc[] = {
222 	(struct usb_desc_header *) &func_desc.if0,
223 	(struct usb_desc_header *) &func_desc.if0_in_ep,
224 	(struct usb_desc_header *) &func_desc.if0_out_ep,
225 	NULL,
226 };
227 
228 const static __maybe_unused struct usb_desc_header *tracing_func_hs_desc[] = {
229 	(struct usb_desc_header *) &func_desc.if0,
230 	(struct usb_desc_header *) &func_desc.if0_hs_in_ep,
231 	(struct usb_desc_header *) &func_desc.if0_hs_out_ep,
232 	NULL,
233 };
234 
235 static struct tracing_func_data func_data = {
236 	.desc = &func_desc,
237 	.fs_desc = tracing_func_fs_desc,
238 	.hs_desc = COND_CODE_1(USBD_SUPPORTS_HIGH_SPEED, (tracing_func_hs_desc), (NULL)),
239 	.sync_sem = Z_SEM_INITIALIZER(func_data.sync_sem, 0, 1),
240 };
241 
242 USBD_DEFINE_CLASS(tracing_func, &tracing_func_api, &func_data, NULL);
243 
tracing_func_buf_alloc(struct usbd_class_data * const c_data)244 struct net_buf *tracing_func_buf_alloc(struct usbd_class_data *const c_data)
245 {
246 	struct udc_buf_info *bi;
247 	struct net_buf *buf;
248 
249 	buf = net_buf_alloc(&tracing_data_pool, K_NO_WAIT);
250 	if (!buf) {
251 		return NULL;
252 	}
253 
254 	bi = udc_get_buf_info(buf);
255 	bi->ep = tracing_func_get_bulk_in(c_data);
256 
257 	return buf;
258 }
259 
tracing_backend_usb_output(const struct tracing_backend * backend,uint8_t * data,uint32_t length)260 static void tracing_backend_usb_output(const struct tracing_backend *backend,
261 				       uint8_t *data, uint32_t length)
262 {
263 	int ret = 0;
264 	uint32_t bytes;
265 	struct net_buf *buf;
266 
267 	while (length > 0) {
268 		if (!atomic_test_bit(&func_data.state, TRACING_FUNCTION_ENABLED) ||
269 		    !is_tracing_enabled()) {
270 			return;
271 		}
272 
273 		buf = tracing_func_buf_alloc(&tracing_func);
274 		if (buf == NULL) {
275 			return;
276 		}
277 
278 		bytes = MIN(length, net_buf_tailroom(buf));
279 		net_buf_add_mem(buf, data, bytes);
280 
281 		ret = usbd_ep_enqueue(&tracing_func, buf);
282 		if (ret) {
283 			net_buf_unref(buf);
284 			continue;
285 		}
286 
287 		data += bytes;
288 		length -= bytes;
289 		k_sem_take(&func_data.sync_sem, K_FOREVER);
290 	}
291 }
292 
293 const struct tracing_backend_api tracing_backend_usb_api = {
294 	.output = tracing_backend_usb_output
295 };
296 
297 TRACING_BACKEND_DEFINE(tracing_backend_usb, tracing_backend_usb_api);
298