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