Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

297 lines
7.5 KiB

/*
* 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 <zephyr/sys/util.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/usb/usbd.h>
#include <zephyr/drivers/usb/udc.h>
#include <tracing_core.h>
#include <tracing_buffer.h>
#include <tracing_backend.h>
/* 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);