/* * Copyright The Zephyr Project Contributors * * SPDX-License-Identifier: Apache-2.0 */ /* Disable syscall tracing for all calls from this compilation unit to avoid * undefined symbols as the macros are not expanded recursively */ #define DISABLE_SYSCALL_TRACING #include #include #include #include #include #include #include #include #include /* Single bounce buffer for bulk IN transfer */ UDC_BUF_POOL_DEFINE(tracing_data_pool, 1, CONFIG_TRACING_BUFFER_SIZE, sizeof(struct udc_buf_info), NULL); struct tracing_func_desc { struct usb_if_descriptor if0; struct usb_ep_descriptor if0_in_ep; struct usb_ep_descriptor if0_out_ep; struct usb_ep_descriptor if0_hs_out_ep; struct usb_ep_descriptor if0_hs_in_ep; }; struct tracing_func_data { struct tracing_func_desc *const desc; const struct usb_desc_header **const fs_desc; const struct usb_desc_header **const hs_desc; struct k_sem sync_sem; atomic_t state; }; #define TRACING_FUNCTION_ENABLED 0 static uint8_t tracing_func_get_bulk_out(struct usbd_class_data *const c_data) { struct tracing_func_data *data = usbd_class_get_private(c_data); struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data); struct tracing_func_desc *desc = data->desc; if (USBD_SUPPORTS_HIGH_SPEED && usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) { return desc->if0_hs_out_ep.bEndpointAddress; } return desc->if0_out_ep.bEndpointAddress; } static uint8_t tracing_func_get_bulk_in(struct usbd_class_data *const c_data) { struct tracing_func_data *data = usbd_class_get_private(c_data); struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data); struct tracing_func_desc *desc = data->desc; if (USBD_SUPPORTS_HIGH_SPEED && usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) { return desc->if0_hs_in_ep.bEndpointAddress; } return desc->if0_in_ep.bEndpointAddress; } static void tracing_func_out_next(struct usbd_class_data *const c_data) { struct tracing_func_data *data = usbd_class_get_private(c_data); struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data); struct net_buf *buf; uint8_t ep; if (!atomic_test_bit(&data->state, TRACING_FUNCTION_ENABLED)) { return; } ep = tracing_func_get_bulk_out(c_data); if (USBD_SUPPORTS_HIGH_SPEED && usbd_bus_speed(uds_ctx) == USBD_SPEED_HS) { buf = usbd_ep_buf_alloc(c_data, ep, 512); } else { buf = usbd_ep_buf_alloc(c_data, ep, 64); } if (buf == NULL) { return; } if (usbd_ep_enqueue(c_data, buf)) { net_buf_unref(buf); } } static int tracing_func_request_handler(struct usbd_class_data *const c_data, struct net_buf *const buf, const int err) { struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data); struct tracing_func_data *data = usbd_class_get_private(c_data); struct udc_buf_info *bi = NULL; bi = (struct udc_buf_info *)net_buf_user_data(buf); if (bi->ep == tracing_func_get_bulk_out(c_data)) { if (!err) { tracing_cmd_handle(buf->data, buf->len); } usbd_ep_buf_free(uds_ctx, buf); tracing_func_out_next(c_data); } if (bi->ep == tracing_func_get_bulk_in(c_data)) { usbd_ep_buf_free(uds_ctx, buf); k_sem_give(&data->sync_sem); } return 0; } static void *tracing_func_get_desc(struct usbd_class_data *const c_data, const enum usbd_speed speed) { struct tracing_func_data *data = usbd_class_get_private(c_data); if (USBD_SUPPORTS_HIGH_SPEED && speed == USBD_SPEED_HS) { return data->hs_desc; } return data->fs_desc; } static void tracing_func_enable(struct usbd_class_data *const c_data) { struct tracing_func_data *data = usbd_class_get_private(c_data); if (!atomic_test_and_set_bit(&data->state, TRACING_FUNCTION_ENABLED)) { tracing_func_out_next(c_data); } } static void tracing_func_disable(struct usbd_class_data *const c_data) { struct tracing_func_data *data = usbd_class_get_private(c_data); atomic_clear_bit(&data->state, TRACING_FUNCTION_ENABLED); } static int tracing_func_init(struct usbd_class_data *c_data) { ARG_UNUSED(c_data); return 0; } struct usbd_class_api tracing_func_api = { .request = tracing_func_request_handler, .get_desc = tracing_func_get_desc, .enable = tracing_func_enable, .disable = tracing_func_disable, .init = tracing_func_init, }; static struct tracing_func_desc func_desc = { .if0 = { .bLength = sizeof(struct usb_if_descriptor), .bDescriptorType = USB_DESC_INTERFACE, .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 2, .bInterfaceClass = USB_BCC_VENDOR, .bInterfaceSubClass = 0, .bInterfaceProtocol = 0, .iInterface = 0, }, .if0_in_ep = { .bLength = sizeof(struct usb_ep_descriptor), .bDescriptorType = USB_DESC_ENDPOINT, .bEndpointAddress = 0x81, .bmAttributes = USB_EP_TYPE_BULK, .wMaxPacketSize = sys_cpu_to_le16(64), .bInterval = 0x00, }, .if0_out_ep = { .bLength = sizeof(struct usb_ep_descriptor), .bDescriptorType = USB_DESC_ENDPOINT, .bEndpointAddress = 0x01, .bmAttributes = USB_EP_TYPE_BULK, .wMaxPacketSize = sys_cpu_to_le16(64), .bInterval = 0x00, }, /* High-speed Endpoint IN */ .if0_hs_in_ep = { .bLength = sizeof(struct usb_ep_descriptor), .bDescriptorType = USB_DESC_ENDPOINT, .bEndpointAddress = 0x81, .bmAttributes = USB_EP_TYPE_BULK, .wMaxPacketSize = sys_cpu_to_le16(512), .bInterval = 0x00, }, /* High-speed Endpoint OUT */ .if0_hs_out_ep = { .bLength = sizeof(struct usb_ep_descriptor), .bDescriptorType = USB_DESC_ENDPOINT, .bEndpointAddress = 0x01, .bmAttributes = USB_EP_TYPE_BULK, .wMaxPacketSize = sys_cpu_to_le16(512), .bInterval = 0x00, }, }; const static struct usb_desc_header *tracing_func_fs_desc[] = { (struct usb_desc_header *) &func_desc.if0, (struct usb_desc_header *) &func_desc.if0_in_ep, (struct usb_desc_header *) &func_desc.if0_out_ep, NULL, }; const static __maybe_unused struct usb_desc_header *tracing_func_hs_desc[] = { (struct usb_desc_header *) &func_desc.if0, (struct usb_desc_header *) &func_desc.if0_hs_in_ep, (struct usb_desc_header *) &func_desc.if0_hs_out_ep, NULL, }; static struct tracing_func_data func_data = { .desc = &func_desc, .fs_desc = tracing_func_fs_desc, .hs_desc = COND_CODE_1(USBD_SUPPORTS_HIGH_SPEED, (tracing_func_hs_desc), (NULL)), .sync_sem = Z_SEM_INITIALIZER(func_data.sync_sem, 0, 1), }; USBD_DEFINE_CLASS(tracing_func, &tracing_func_api, &func_data, NULL); struct net_buf *tracing_func_buf_alloc(struct usbd_class_data *const c_data) { struct udc_buf_info *bi; struct net_buf *buf; buf = net_buf_alloc(&tracing_data_pool, K_NO_WAIT); if (!buf) { return NULL; } bi = udc_get_buf_info(buf); bi->ep = tracing_func_get_bulk_in(c_data); return buf; } static void tracing_backend_usb_output(const struct tracing_backend *backend, uint8_t *data, uint32_t length) { int ret = 0; uint32_t bytes; struct net_buf *buf; while (length > 0) { if (!atomic_test_bit(&func_data.state, TRACING_FUNCTION_ENABLED) || !is_tracing_enabled()) { return; } buf = tracing_func_buf_alloc(&tracing_func); if (buf == NULL) { return; } bytes = MIN(length, net_buf_tailroom(buf)); net_buf_add_mem(buf, data, bytes); ret = usbd_ep_enqueue(&tracing_func, buf); if (ret) { net_buf_unref(buf); continue; } data += bytes; length -= bytes; k_sem_take(&func_data.sync_sem, K_FOREVER); } } const struct tracing_backend_api tracing_backend_usb_api = { .output = tracing_backend_usb_output }; TRACING_BACKEND_DEFINE(tracing_backend_usb, tracing_backend_usb_api);