@ -1,5 +1,5 @@
@@ -1,5 +1,5 @@
/*
* Copyright ( c ) 2019 Intel corporation
* Copyright The Zephyr Project Contributors
*
* SPDX - License - Identifier : Apache - 2.0
*/
@ -13,30 +13,160 @@
@@ -13,30 +13,160 @@
# include <zephyr/sys/atomic.h>
# include <zephyr/sys/__assert.h>
# include <zephyr/sys/byteorder.h>
# include <zephyr/usb/usb_device.h>
# include <zephyr/usb/usbd.h>
# include <zephyr/drivers/usb/udc.h>
# include <tracing_core.h>
# include <tracing_buffer.h>
# include <tracing_backend.h>
# define USB_TRANSFER_ONGOING 1
# define USB_TRANSFER_FREE 0
/* 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 ) ;
# define TRACING_IF_IN_EP_ADDR 0x81
# define TRACING_IF_OUT_EP_ADDR 0x01
struct usb_device_desc {
struct tracing_func_desc {
struct usb_if_descriptor if0 ;
struct usb_ep_descriptor if0_in_ep ;
struct usb_ep_descriptor if0_out_ep ;
} __packed ;
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 ) ;
static volatile int transfer_state ;
static enum usb_dc_status_code usb_device_status = USB_DC_UNKNOWN ;
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 ,
} ;
USBD_CLASS_DESCR_DEFINE ( primary , 0 ) struct usb_device_desc dev_desc = {
/*
* Interface descriptor 0
*/
static struct tracing_func_desc func_desc = {
. if0 = {
. bLength = sizeof ( struct usb_if_descriptor ) ,
. bDescriptorType = USB_DESC_INTERFACE ,
@ -49,124 +179,114 @@ USBD_CLASS_DESCR_DEFINE(primary, 0) struct usb_device_desc dev_desc = {
@@ -49,124 +179,114 @@ USBD_CLASS_DESCR_DEFINE(primary, 0) struct usb_device_desc dev_desc = {
. iInterface = 0 ,
} ,
/*
* Data Endpoint IN
*/
. if0_in_ep = {
. bLength = sizeof ( struct usb_ep_descriptor ) ,
. bDescriptorType = USB_DESC_ENDPOINT ,
. bEndpointAddress = TRACING_IF_IN_EP_ADDR ,
. bmAttributes = USB_DC_ EP_BULK ,
. wMaxPacketSize = sys_cpu_to_le16 ( CONFIG_TRACING_USB_MPS ) ,
. bEndpointAddress = 0x81 ,
. bmAttributes = USB_EP_TYPE _BULK ,
. wMaxPacketSize = sys_cpu_to_le16 ( 64 ) ,
. bInterval = 0x00 ,
} ,
/*
* Data Endpoint OUT
*/
. if0_out_ep = {
. bLength = sizeof ( struct usb_ep_descriptor ) ,
. bDescriptorType = USB_DESC_ENDPOINT ,
. bEndpointAddress = TRACING_IF_OUT_EP_ADDR ,
. bmAttributes = USB_DC_ EP_BULK ,
. wMaxPacketSize = sys_cpu_to_le16 ( CONFIG_TRACING_USB_MPS ) ,
. bEndpointAddress = 0x01 ,
. bmAttributes = USB_EP_TYPE _BULK ,
. wMaxPacketSize = sys_cpu_to_le16 ( 64 ) ,
. bInterval = 0x00 ,
} ,
} ;
static void dev_status_cb ( struct usb_cfg_data * cfg ,
enum usb_dc_status_code status ,
const uint8_t * param )
{
ARG_UNUSED ( cfg ) ;
ARG_UNUSED ( param ) ;
usb_device_status = status ;
}
/* 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 ,
} ,
static void tracing_ep_out_cb ( uint8_t ep , enum usb_dc_ep_cb_status_code ep_status )
{
uint8_t * cmd = NULL ;
uint32_t bytes_to_read , length ;
/* 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 ,
} ,
} ;
usb_read ( ep , NULL , 0 , & bytes_to_read ) ;
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 ,
} ;
while ( bytes_to_read ) {
length = tracing_cmd_buffer_alloc ( & cmd ) ;
if ( cmd ) {
length = MIN ( length , bytes_to_read ) ;
usb_read ( ep , cmd , length , NULL ) ;
tracing_cmd_handle ( cmd , length ) ;
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 ,
} ;
bytes_to_read - = length ;
}
}
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 ) ,
} ;
/*
* send ZLP to sync with host receive thread
*/
usb_write ( TRACING_IF_IN_EP_ADDR , NULL , 0 , NULL ) ;
}
USBD_DEFINE_CLASS ( tracing_func , & tracing_func_api , & func_data , NULL ) ;
static void tracing_ep_in_cb ( uint8_t ep , enum usb_dc_ep_cb_status_code ep_status )
struct net_buf * tracing_func_buf_alloc ( struct usbd_class_data * const c_data )
{
ARG_UNUSED ( ep ) ;
ARG_UNUSED ( ep_status ) ;
struct udc_buf_info * bi ;
struct net_buf * buf ;
transfer_state = USB_TRANSFER_FREE ;
}
buf = net_buf_alloc ( & tracing_data_pool , K_NO_WAIT ) ;
if ( ! buf ) {
return NULL ;
}
static struct usb_ep_cfg_data ep_cfg [ ] = {
{
. ep_cb = tracing_ep_out_cb ,
. ep_addr = TRACING_IF_OUT_EP_ADDR ,
} ,
{
. ep_cb = tracing_ep_in_cb ,
. ep_addr = TRACING_IF_IN_EP_ADDR ,
} ,
} ;
bi = udc_get_buf_info ( buf ) ;
bi - > ep = tracing_func_get_bulk_in ( c_data ) ;
USBD_DEFINE_CFG_DATA ( tracing_backend_usb_config ) = {
. usb_device_description = NULL ,
. interface_descriptor = & dev_desc . if0 ,
. cb_usb_status = dev_status_cb ,
. interface = {
. class_handler = NULL ,
. custom_handler = NULL ,
. vendor_handler = NULL ,
} ,
. num_endpoints = ARRAY_SIZE ( ep_cfg ) ,
. endpoint = ep_cfg ,
} ;
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 ) {
transfer_state = USB_TRANSFER_ONGOING ;
/*
* make sure every USB transfer no need ZLP at all
* because we are in lowest priority thread content
* there are no deterministic time between real USB
* packet and ZLP
*/
ret = usb_write ( TRACING_IF_IN_EP_ADDR , data ,
length > = CONFIG_TRACING_USB_MPS ?
CONFIG_TRACING_USB_MPS - 1 : length , & bytes ) ;
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 ;
while ( transfer_state = = USB_TRANSFER_ONGOING ) {
}
k_sem_take ( & func_data . sync_sem , K_FOREVER ) ;
}
}