Hi Emmanuel,
I can only share both USB3 Vision control and stream classes I’ve implemented, which I believe that can be easily ported to any ARM environment by replacing all the related USB drivers with the specific processor drivers.
Cheers,
João
uvsp.c
/***************************** Include Files *********************************/
/* Xilinx includes */
#include <string.h>
#include "xil_printf.h"
/* USB3 includes */
#include "xusb/ch9_usb3.h"
#include "xusb/class_usb3.h"
#include "xusb/usb_wrapper.h"
/* U3V includes */
#include "vision/uvsp.h"
#include "vision/regmap.h"
/************************** Constant Definitions *****************************/
#define UVSP_LEADER_MAGIC 0x4C563355 ///< UVSP leader prefix magic number
#define UVSP_TRAILER_MAGIC 0x54563355 ///< UVSP trailer prefix magic number
#define UVSP_PAYLOAD_TYPE_IMAGE 0x0001 ///< UVSP streaming image payload type
#define UVSP_PIXEL_FORMAT_RGB8 0x02180014u ///< RGB8 pixel format
/**************************** Type Definitions *******************************/
/**
* @brief TODO
*/
typedef struct
{
u8 isStreamActive;
u8 busySending;
u8 frameSendPending;
u64 frame_id;
u8 *frameBaseAddr;
size_t frame_count;
uvspState state;
} uvsp_ctx_t;
/************************** Variable Definitions *****************************/
extern struct Usb_DevData UsbInstance; ///< Main instance for USB communication
extern regmap_t U3V_regmap; ///< USB3 Vision register map
static uvsp_ctx_t _ctx = {}; ///< Private UVSP struct declaration
/************************** Interrupt Routine ******************************/
/**
* @brief Stream in endpoint interrupt routine.
*
* Interrupt routine called after an USB transmission to host on the stream
* endpoint. Releases @p busySending for a new streaming data to be sent
* afterwards. Also updates the streaming state for the next transmission.
*
* @param[in] CallBackRef
* @param[in] RequestedBytes bytes requested to send
* @param[in] BytesTxed bytes transmitted
*
* @note Reporting when @p RequestedBytes and @p BytesTxed have different
* values. This may occur when the host limits the transmitted size, which
* shall not happen.
*/
void StreamInHandler(void *CallBackRef, u32 RequestedBytes, u32 BytesTxed)
{
if (RequestedBytes != BytesTxed)
xil_printf("StreamInHandler: RequestedBytes %d != BytesTxed %d\n",
RequestedBytes, BytesTxed);
switch (_ctx.state)
{
case UVSP_SEND_LEADER:
_ctx.state = UVSP_SEND_EQ_DATA;
_ctx.frame_count = 0;
break;
case UVSP_SEND_EQ_DATA:
if (++_ctx.frame_count == U3V_regmap.sirm.payload_count)
{
/* Check if Final Transfer 1 is needed */
if (U3V_regmap.sirm.transfer1_size)
_ctx.state = UVSP_SEND_TRF1_DATA;
else
_ctx.state = UVSP_SEND_TRAILER;
}
break;
case UVSP_SEND_TRF1_DATA:
/* Check if Final Transfer 2 is needed */
if (U3V_regmap.sirm.transfer2_size)
_ctx.state = UVSP_SEND_TRF2_DATA;
else
_ctx.state = UVSP_SEND_TRAILER;
break;
case UVSP_SEND_TRF2_DATA:
_ctx.state = UVSP_SEND_TRAILER;
break;
case UVSP_SEND_TRAILER:
_ctx.state = UVSP_SEND_LEADER;
_ctx.frame_id++;
_ctx.frameSendPending = FALSE;
break;
default:
xil_printf("StreamInHandler: Unkwown state\n");
return;
}
_ctx.busySending = FALSE;
}
/************************** Private Routines ******************************/
/**
* @brief Creates new UVSP leader packet.
*
* @param[in] frame_id frame ID of current transferred frame
* @param[out] packet_id created UVSP leader packet
*
* @return @e UVSP_SUCCESS for new UVSP leader packet @n
* @e UVSP_ERR_NULL if any pointed arguments is NULL
*/
static uvspStatus _uvsp_packet_new_stream_leader(u64 frame_id, uvspLeader *packet)
{
if (packet == NULL)
{
xil_printf("UVSP_ERR_NULL\n");
return UVSP_ERR_NULL;
}
packet->header.magic = UVSP_LEADER_MAGIC;
packet->header.reserved0 = 0;
packet->header.size = sizeof(uvspLeader);
packet->header.frame_id = frame_id;
packet->infos.reserved0 = packet->infos.reserved1 = 0;
packet->infos.payload_type = UVSP_PAYLOAD_TYPE_IMAGE;
packet->infos.timestamp = -1; // @TODO Update with device timestamp
packet->infos.pixel_format = UVSP_PIXEL_FORMAT_RGB8;
packet->infos.width = STREAM_DEF_WIDTH;
packet->infos.height = STREAM_DEF_HEIGHT;
packet->infos.x_offset = 0;
packet->infos.y_offset = 0;
packet->infos.x_padding = 0;
return UVSP_SUCCESS;
}
/**
* @brief Creates new UVSP trailer packet.
*
* @param[in] frame_id frame ID of current transferred frame
* @param[out] packet_id created UVSP trailer packet
*
* @return @e UVSP_SUCCESS for new UVSP trailer packet @n
* @e UVSP_ERR_NULL if any pointed arguments is NULL
*/
static uvspStatus _uvsp_packet_new_stream_trailer(u64 frame_id, uvspTrailer *packet)
{
if (packet == NULL)
{
xil_printf("UVSP_ERR_NULL\n");
return UVSP_ERR_NULL;
}
packet->header.magic = UVSP_TRAILER_MAGIC;
packet->header.reserved0 = 0;
packet->header.size = sizeof(uvspTrailer);
packet->header.frame_id = frame_id;
packet->infos.status = 0; // @TODO Check if aravis deals with different status
packet->infos.reserved0 = 0;
packet->infos.payload_size = U3V_regmap.sirm.req_payload_size;
packet->infos.size_y = STREAM_DEF_HEIGHT;
return UVSP_SUCCESS;
}
/**
* @brief Resets UVSP streaming state to starting state.
*/
static void _uvsp_reset_stream(void)
{
_ctx.frame_id = _ctx.frame_count = 0;
_ctx.frame_id = UVSP_SEND_LEADER;
_ctx.frameSendPending = FALSE;
_ctx.busySending = FALSE;
}
/**
* @brief Sends streaming data to host.
*
* @param[in] data streaming data to be sent
* @param[in] size streaming data size
*
* @return @e UVSP_SUCCESS for new UVSP trailer packet @n
* @e UVSP_ERR_SENDING if streaming data could not be sent to host
*/
static uvspStatus _uvsp_send_data(u8 *data, size_t size)
{
u32 status;
_ctx.busySending = TRUE;
status = EpBufferSend(UsbInstance.PrivateData, USB_EPOINT_STREAM, data, size);
if (status != XST_SUCCESS)
{
xil_printf("UVSP_ERR_SENDING\n");
return UVSP_ERR_SENDING;
}
return UVSP_SUCCESS;
}
/**
* @brief Verifies if streaming is active.
*
* Verifies if streaming is active from the control register on SIRM.
*
* @return @e TRUE for active stream @n
* @e FALSE for inactive stream
*/
u8 uvsp_isStreamActive(void)
{
if (U3V_regmap.sirm.control & 0b1)
return TRUE;
else
return FALSE;
}
/**
* @brief Checks if a frame send is pending.
*
* @return @e TRUE for pending frame @n
* @e FALSE for no frame to be sent
*/
inline u32 usvp_isFrame_pending(void)
{
return _ctx.frameSendPending;
}
/**
* @brief Sets if a new frame send is pending or not.
*
* @param[in] state pending frame state
*/
inline void uvsp_set_pending_frame(u8 state)
{
_ctx.frameSendPending = state;
}
/**
* @brief Sets a new memory base address for a frame.
*
* @param[in] addr frame base address
*
* @return @e UVSP_SUCCESS with new address set @n
* @e UVSP_ERR_NULL if address is NULL
*/
uvspStatus uvsp_set_frame_base_addr(u8 *addr)
{
if (addr == NULL)
{
xil_printf("UVSP_ERR_NULL\n");
return UVSP_ERR_NULL;
}
_ctx.frameBaseAddr = addr;
return UVSP_SUCCESS;
}
/**
* @brief Main UVSP handler sending streaming data.
*
* Being the streaming active, confirms if a new frame send is pending and if
* any data send from a current frame is pending. Afterwards, streaming data is
* generated depending on the streaming state of the current frame. This data is
* then sent to the host.
*
* @return @e UVSP_SUCCESS with queued event sent to host @n
* @e UVSP_NO_STREAM if streaming is not active @n
* @e UVSP_NO_FRAME if no frame send is pending @n
* @e UVSP_BUSY if stream endpoint is busy sending last frame data @n
* @e UVSP_ERR_NULL if frame address is NULL @n
* @e UVSP_ERR_PACKET_CREATE if UVSP leader or trailer is not created @n
* @e UVSP_ERR_SENDING if streaming data could not be sent to host
*/
uvspStatus uvsp_state_handler(void)
{
/* Verify if streaming has been stopped by host */
_ctx.isStreamActive = uvsp_isStreamActive();
if (!_ctx.isStreamActive)
{
_uvsp_reset_stream();
return UVSP_NO_STREAM;
}
/* Check if there is a new frame to be sent */
if (!_ctx.frameSendPending)
return UVSP_NO_FRAME;
/* Last Buffer is still being sending */
if (_ctx.busySending)
return UVSP_BUSY;
if (_ctx.frameBaseAddr == NULL)
{
xil_printf("UVSP_ERR_NULL\n");
return UVSP_ERR_NULL;
}
uvspLeader pkt_leader;
uvspTrailer pkt_trailer;
u8 *data_start;
/* Stream is active and endpoint is free, send something */
switch (_ctx.state)
{
case UVSP_SEND_LEADER:
if (_uvsp_packet_new_stream_leader(_ctx.frame_id, &pkt_leader) != UVSP_SUCCESS)
{
xil_printf("UVSP_ERR_PACKET_CREATE\n");
return UVSP_ERR_PACKET_CREATE;
}
if (_uvsp_send_data((u8 *)&pkt_leader, pkt_leader.header.size) != UVSP_SUCCESS)
{
xil_printf("UVSP_ERR_SENDING\n");
return UVSP_ERR_SENDING;
}
break;
case UVSP_SEND_EQ_DATA:
data_start = _ctx.frameBaseAddr + _ctx.frame_count * U3V_regmap.sirm.payload_size;
if (_uvsp_send_data(data_start, U3V_regmap.sirm.payload_size) != UVSP_SUCCESS)
{
xil_printf("UVSP_ERR_SENDING\n");
return UVSP_ERR_SENDING;
}
break;
case UVSP_SEND_TRF1_DATA:
data_start = _ctx.frameBaseAddr + U3V_regmap.sirm.payload_count * U3V_regmap.sirm.payload_size;
if (_uvsp_send_data(data_start, U3V_regmap.sirm.transfer1_size) != UVSP_SUCCESS)
{
xil_printf("UVSP_ERR_SENDING\n");
return UVSP_ERR_SENDING;
}
break;
case UVSP_SEND_TRF2_DATA:
data_start = _ctx.frameBaseAddr + (U3V_regmap.sirm.payload_count * U3V_regmap.sirm.payload_size) + U3V_regmap.sirm.transfer1_size;
if (_uvsp_send_data(data_start, U3V_regmap.sirm.transfer2_size) != UVSP_SUCCESS)
{
xil_printf("UVSP_ERR_SENDING\n");
return UVSP_ERR_SENDING;
}
break;
case UVSP_SEND_TRAILER:
if (_uvsp_packet_new_stream_trailer(_ctx.frame_id, &pkt_trailer) != UVSP_SUCCESS)
{
xil_printf("UVSP_ERR_PACKET_CREATE\n");
return UVSP_ERR_PACKET_CREATE;
}
if (_uvsp_send_data((u8 *)&pkt_trailer, pkt_trailer.header.size) != UVSP_SUCCESS)
{
xil_printf("UVSP_ERR_SENDING\n");
return UVSP_ERR_SENDING;
}
break;
default:
xil_printf("UVSP_ERR_UNKNOWN\n");
return UVSP_ERR_UNKNOWN;
break;
}
return UVSP_SUCCESS;
}
uvsp.h
#ifndef UVSP_H
#define UVSP_H
/************************** Include Files ****************************/
#include "xil_types.h"
/**************************** Type Definitions *******************************/
/**
* @brief UVSP streaming data states.
*/
typedef enum
{
UVSP_SEND_LEADER,
UVSP_SEND_EQ_DATA,
UVSP_SEND_TRF1_DATA,
UVSP_SEND_TRF2_DATA,
UVSP_SEND_TRAILER,
} uvspState;
/**
* @brief UVSP status codes.
*/
typedef enum
{
UVSP_SUCCESS,
UVSP_NO_STREAM,
UVSP_NO_FRAME,
UVSP_BUSY,
UVSP_ERR_UNKNOWN,
UVSP_ERR_NULL,
UVSP_ERR_SENDING,
UVSP_ERR_PACKET_CREATE,
} uvspStatus;
/**
* @brief UVSP packet header.
*
* UVSP header format as specified by U3V specification.
*/
typedef struct __attribute__((__packed__))
{
u32 magic;
u16 reserved0;
u16 size;
u64 frame_id;
} uvspHeader;
/**
* @brief UVSP leader format.
*
* UVSP leader data format for an image payload type as specified by U3V
* specification.
*/
typedef struct __attribute__((__packed__))
{
u16 reserved0;
u16 payload_type;
u64 timestamp;
u32 pixel_format;
u32 width;
u32 height;
u32 x_offset;
u32 y_offset;
u16 x_padding;
u16 reserved1;
} uvspLeaderInfos;
/**
* @brief UVSP leader packet.
*
* UVSP leader packet as specified by U3V specification.
*/
typedef struct __attribute__((__packed__))
{
uvspHeader header;
uvspLeaderInfos infos;
} uvspLeader;
/**
* @brief UVSP trailer format.
*
* UVSP trailer data format for an image payload type as specified by U3V
* specification.
*/
typedef struct __attribute__((__packed__))
{
u16 status;
u16 reserved0;
u64 payload_size;
u32 size_y;
} uvspTrailerInfos;
/**
* @brief UVSP trailer packet.
*
* UVSP trailer packet as specified by U3V specification.
*/
typedef struct __attribute__((__packed__))
{
uvspHeader header;
uvspTrailerInfos infos;
} uvspTrailer;
/**************************** Function Prototypes *******************************/
u8 uvsp_isStreamActive(void);
u32 usvp_isFrame_pending(void);
void uvsp_set_pending_frame(u8 state);
uvspStatus uvsp_set_frame_base_addr(u8 *addr);
uvspStatus uvsp_state_handler(void);
#endif // UVSP_H
uvcp.c
/************************** Include Files ****************************/
/* Xilinx includes */
#include <string.h>
#include "xil_printf.h"
/* USB3 includes */
#include "xusb/ch9_usb3.h"
#include "xusb/class_usb3.h"
#include "xusb/usb_wrapper.h"
/* U3V includes */
#include "vision/uvcp.h"
#include "vision/regmap.h"
/************************** Constant Definitions ****************************/
#define UVCP_OUT_BUF_SIZE 128 ///< Control Out Endpoint buffer size
#define UVCP_CONTROL_MAGIC 0x43563355 ///< UVCP prefix magic number
/**************************** Type Definitions *******************************/
/**
* @brief TODO
*/
typedef struct
{
u8 newCommandRxed;
u8 busySending;
u8 EpControl_OutData[UVCP_OUT_BUF_SIZE];
} uvcp_ctx_t;
/************************** Variable Definitions *****************************/
extern struct Usb_DevData UsbInstance; ///< Main instance for USB communication
extern regmap_t U3V_regmap; ///< USB3 Vision register map
static size_t _reg_map_size; ///< Size of register map and manifest XML
static uvcp_ctx_t _ctx = {}; ///< Private UVCP struct declaration
/************************** Interrupt Routines ******************************/
/**
* @brief Control out endpoint interrupt routine.
*
* Interrupt routine called after an USB reception from host on the control
* endpoint. Simply set @p newCommandRxed since a new command candidate as been
* received.
*
* @param[in] CallBackRef
* @param[in] RequestedBytes maximum bytes set to be received
* @param[in] BytesTxed bytes received
*/
void ControlOutHandler(void *CallBackRef, u32 RequestedBytes, u32 BytesTxed)
{
_ctx.newCommandRxed = TRUE;
}
/**
* @brief Control in endpoint interrupt routine.
*
* Interrupt routine called after an USB transmission to host on the control
* endpoint. Releases @p newCommandRxed and @p busySending for a new command
* to be sent afterwards. Also resets reception buffer for a new data to be
* received on the Control Out endpoint.
*
* @param[in] CallBackRef
* @param[in] RequestedBytes bytes requested to send
* @param[in] BytesTxed bytes transmitted
*
* @note Reporting when @p RequestedBytes and @p BytesTxed have different
* values. This may occur when the host limits the transmitted size, which
* shall not happen.
*/
void ControlInHandler(void *CallBackRef, u32 RequestedBytes, u32 BytesTxed)
{
if (RequestedBytes != BytesTxed)
xil_printf("ControlInHandler: RequestedBytes %d != BytesTxed %d\n",
RequestedBytes, BytesTxed);
_ctx.newCommandRxed = FALSE;
_ctx.busySending = FALSE;
/* Reset Rx buffer */
uvcp_set_rx_buf();
}
/************************** Private Routines ******************************/
/**
* @brief Initializes U3V register map base addresses.
*
* The ABRM and SBRM keep the base addresses of other register maps as defined
* in the USB3 Vision specification, as well the manifest table that keeps the
* start address of the manifest XML file. These must be initialised at startup.
*/
static void _ucvp_setU3V_regmap(void)
{
U3V_regmap.abrm.sbrm_addr = ((u32)&U3V_regmap.sbrm & 0x00000FFF);
U3V_regmap.abrm.manifest_table_addr = ((u32)&U3V_regmap.manifest_table & 0x00000FFF);
U3V_regmap.sbrm.sirm_addr = ((u32)&U3V_regmap.sirm & 0x00000FFF);
U3V_regmap.sbrm.eirm_addr = ((u32)&U3V_regmap.eirm & 0x00000FFF);
U3V_regmap.manifest_table.address = ((u32)&U3V_regmap.manifest_xml & 0x00000FFF);
U3V_regmap.manifest_table.size = strlen(U3V_regmap.manifest_xml);
_reg_map_size = sizeof(regmap_t) + U3V_regmap.manifest_table.size + 3;
}
/**
* @brief Reads data from specified address of the U3V register map.
*
* @param[in] addr register map base address to be read
* @param[out] data buffer with read data
* @param[in] size bytes to be read
*
* @return @e UVCP_SUCCESS for write success @n
* @e UVCP_ERR_INV_ADDR if given address and size result in data
* outside the U3V register map.
*/
static uvcpStatus _uvcp_read_address(u64 addr, u8 *data, size_t size)
{
if (addr < 0 || (addr + size) > _reg_map_size)
{
xil_printf("UVCP_ERR_INV_ADDR\n");
return UVCP_ERR_INV_ADDR;
}
addr |= UV_REGMAP_ADDR;
u8 *src = (u8 *)(u32)addr;
memcpy(data, src, size);
return UVCP_SUCCESS;
}
/**
* @brief Writes data to specified address of the U3V register map.
*
* @param[in] addr register map base address to be written
* @param[in] data buffer with written data
* @param[in] size bytes to be written
*
* @return @e UVCP_SUCCESS for write success @n
* @e UVCP_ERR_INV_ADDR if given address and size result in data
* outside the register maps.
*/
static uvcpStatus _uvcp_write_address(u64 addr, u8 *data, size_t size)
{
if (addr < 0 || (addr + size) > _reg_map_size)
{
xil_printf("UVCP_ERR_INV_ADDR\n");
return UVCP_ERR_INV_ADDR;
}
addr |= UV_REGMAP_ADDR;
u8 *dest = (u8 *)(u32)addr;
memcpy(dest, data, size);
return UVCP_SUCCESS;
}
/**
* @brief Returns the packet type field of an UVCP packet.
*
* @param[in] packet UVCP packet
*
* @return packet type @n
* @e UVCP_PACKET_TYPE_ERROR if @p packet is NULL.
*/
static uvcpPacketType _uvcp_packet_get_type(const uvcpPacket *packet)
{
if (packet == NULL)
{
xil_printf("UVCP_PACKET_TYPE_ERROR\n");
return UVCP_PACKET_TYPE_ERROR;
}
return (uvcpPacketType)(packet->header.packet_type);
}
/**
* @brief Returns the command type field of an UVCP packet.
*
* @param[in] packet UVCP packet
*
* @return packet command @n
* 0 if @p packet is NULL.
*/
static uvcpCommand _uvcp_packet_get_command(const uvcpPacket *packet)
{
if (packet == NULL)
return 0;
return (uvcpCommand)(packet->header.command);
}
/**
* @brief Returns the ID field of an UVCP packet.
*
* @param[in] packet UVCP packet
*
* @return packet ID @n
* 0 if @p packet is NULL.
*/
static u16 _uvcp_packet_get_id(const uvcpPacket *packet)
{
if (packet == NULL)
return 0;
return (packet->header.id);
}
/**
* @brief Returns the size field of an UVCP packet.
*
* @param[in] packet UVCP packet
*
* @return packet size @n
* 0 if @p packet is NULL.
*/
static u16 _uvcp_packet_get_size(const uvcpPacket *packet)
{
if (packet == NULL)
return 0;
return (packet->header.size);
}
/**
* @brief Returns the start of an UVCP packet command data.
*
* @param[in] packet UVCP packet
*
* @return pointer to packet data.
*/
static void *_uvcp_packet_get_cmd_data(const uvcpPacket *packet)
{
return (u8 *)packet + sizeof(uvcpHeader);
}
/**
* @brief Creates a UVCP packet for a memory read acknowledge.
*
* @param[in] data read data buffer
* @param[in] read_size size of read bytes
* @param[in] packet_id UVCP packet ID
* @param[out] packet new UVCP packet
* @param[out] packet_size size of new UVCP packet
*
* @return @e UVCP_SUCCESS for new UVCP packet @n
* @e UVCP_ERR_NULL if any pointed arguments is NULL
*/
static uvcpStatus _uvcp_packet_new_read_memory_ack(u8 *data, u16 read_size, u16 packet_id, uvcpPacket *packet, size_t *packet_size)
{
if (packet == NULL || packet_size == NULL || data == NULL)
{
xil_printf("UVCP_ERR_NULL\n");
return UVCP_ERR_NULL;
}
packet->header.magic = UVCP_CONTROL_MAGIC;
packet->header.packet_type = UVCP_PACKET_TYPE_ACK;
packet->header.command = UVCP_READMEM_ACK;
packet->header.size = read_size;
packet->header.id = packet_id;
memcpy(packet->data, data, read_size);
*packet_size = sizeof(uvcpHeader) + read_size;
return UVCP_SUCCESS;
}
/**
* @brief Creates a UVCP packet for a memory write acknowledge.
*
* @param[in] length_written bytes written to memory
* @param[in] packet_id UVCP packet ID
* @param[out] packet new UVCP packet
* @param[out] packet_size size of new UVCP packet
*
* @return @e UVCP_SUCCESS for new UVCP packet @n
* @e UVCP_ERR_NULL if any pointed arguments is NULL
*/
static uvcpStatus _uvcp_packet_new_write_memory_ack(u16 length_written, u16 packet_id, uvcpPacket *packet, size_t *packet_size)
{
uvcpWriteMemoryAckInfos ack_data;
if (packet == NULL || packet_size == NULL)
{
xil_printf("UVCP_ERR_NULL\n");
return UVCP_ERR_NULL;
}
packet->header.magic = UVCP_CONTROL_MAGIC;
packet->header.packet_type = UVCP_PACKET_TYPE_ACK;
packet->header.command = UVCP_WRITEMEM_ACK;
packet->header.size = sizeof(uvcpWriteMemoryAckInfos);
packet->header.id = packet_id;
ack_data.reserved = 0;
ack_data.bytes_written = length_written;
memcpy(packet->data, &ack_data, sizeof(uvcpWriteMemoryAckInfos));
*packet_size = sizeof(uvcpWriteMemoryAck);
return UVCP_SUCCESS;
}
/**
* @brief Creates a UVCP packet for a pending acknowledge.
*
* @param[in] temp_timeout new temporary timeout for host
* @param[in] packet_id UVCP packet ID
* @param[out] packet new UVCP packet
* @param[out] packet_size size of new UVCP packet
*
* @return @e UVCP_SUCCESS for new UVCP packet @n
* @e UVCP_ERR_NULL if any pointed arguments is NULL
*
* @todo Implement pending acknowledge scheme on UVCP if necessary.
*/
static uvcpStatus _uvcp_packet_new_pending_ack(u16 temp_timeout, u16 packet_id, uvcpPacket *packet, size_t *packet_size)
{
uvcpPendingAckInfos ack_data;
if (packet == NULL || packet_size == NULL)
{
xil_printf("UVCP_ERR_NULL\n");
return UVCP_ERR_NULL;
}
packet->header.magic = UVCP_CONTROL_MAGIC;
packet->header.packet_type = UVCP_PACKET_TYPE_ACK;
packet->header.command = UVCP_PENDING_ACK;
packet->header.size = sizeof(uvcpPendingAckInfos);
packet->header.id = packet_id;
ack_data.reserved = 0;
ack_data.timeout = temp_timeout;
memcpy(packet->data, &ack_data, sizeof(uvcpPendingAckInfos));
*packet_size = sizeof(uvcpPendingAck);
return UVCP_SUCCESS;
}
/**
* @brief Decodes a valid UVCP packet and generates an acknowledge.
*
* @param[in] buf buffer with data to be decoded
* @param[out] ack_pkt new UVCP acknowledge packet
* @param[out] ack_pkt_size size of new UVCP acknowledge packet
*
* @return @e UVCP_SUCCESS if command was decoded and new acknowledge packet
* generated @n
* @e UVCP_ERR_NULL if any pointed arguments is NULL @n
* @e UVCP_ERR_INVALID_PACKET for invalid packet prefix @n
* @e UVCP_ERR_MEMORY for invalid UVCP register map access @n
* @e UVCP_ERR_PACKET_CREATE if acknowledge is not created @n
* @e UVCP_ERR_INVALID_CMD for invalid packet command
*/
static uvcpStatus _ucvp_decode_control_packet(u8 *buf, uvcpPacket *ack_pkt, size_t *ack_pkt_size)
{
u32 pkt_magic;
u16 pkt_type, pkt_cmd, pkt_id;
if (buf == NULL || ack_pkt == NULL || ack_pkt_size == NULL)
{
xil_printf("UVCP_ERR_NULL\n");
return UVCP_ERR_NULL;
}
memcpy(&pkt_magic, buf, sizeof(u32));
if (pkt_magic != UVCP_CONTROL_MAGIC)
{
xil_printf("UVCP_ERR_INVALID_PACKET\n");
return UVCP_ERR_INVALID_PACKET;
}
pkt_type = _uvcp_packet_get_type((uvcpPacket *)buf);
/* Only command type packets are received from the host as per U3V spec */
if (pkt_type != UVCP_PACKET_TYPE_CMD)
{
xil_printf("UVCP_ERR_INVALID_PACKET\n");
return UVCP_ERR_INVALID_PACKET;
}
pkt_cmd = _uvcp_packet_get_command((uvcpPacket *)buf);
pkt_id = _uvcp_packet_get_id((uvcpPacket *)buf);
uvcpReadMemoryCmdInfos *read_cmd;
uvcpWriteMemoryCmdInfos *write_cmd;
u8 read_buf[64];
size_t write_size;
switch (pkt_cmd)
{
case UVCP_READMEM_CMD:
read_cmd = _uvcp_packet_get_cmd_data((uvcpPacket *)buf);
if (_uvcp_read_address(read_cmd->address, read_buf, read_cmd->size) != UVCP_SUCCESS)
{
xil_printf("UVCP_ERR_MEMORY\n");
return UVCP_ERR_MEMORY;
}
if (_uvcp_packet_new_read_memory_ack(read_buf, read_cmd->size, pkt_id, ack_pkt, ack_pkt_size) != UVCP_SUCCESS)
{
xil_printf("UVCP_ERR_PACKET_CREATE\n");
return UVCP_ERR_PACKET_CREATE;
}
break;
case UVCP_WRITEMEM_CMD:
write_size = _uvcp_packet_get_size((uvcpPacket *)buf) - sizeof(write_cmd->address);
write_cmd = _uvcp_packet_get_cmd_data((uvcpPacket *)buf);
if (_uvcp_write_address(write_cmd->address, write_cmd->data, write_size) != UVCP_SUCCESS)
{
xil_printf("UVCP_ERR_MEMORY\n");
return UVCP_ERR_MEMORY;
}
_uvcp_read_address(write_cmd->address, read_buf, write_size);
if (memcmp(read_buf, write_cmd->data, write_size))
{
xil_printf("UVCP_ERR_VERIFY\n");
return UVCP_ERR_VERIFY;
}
if (_uvcp_packet_new_write_memory_ack(write_size, pkt_id, ack_pkt, ack_pkt_size) != UVCP_SUCCESS)
{
xil_printf("UVCP_ERR_PACKET_CREATE\n");
return UVCP_ERR_PACKET_CREATE;
}
break;
default:
xil_printf("UVCP_ERR_INVALID_CMD\n");
return UVCP_ERR_INVALID_CMD;
break;
}
return UVCP_SUCCESS;
}
/************************** Public Routines ******************************/
/**
* @brief Initializes UVCP class.
*/
void uvcp_init(void)
{
_ucvp_setU3V_regmap();
}
/**
* @brief Setup reception buffer for Control Out Endpoint.
*/
void uvcp_set_rx_buf(void)
{
EpBufferRecv(UsbInstance.PrivateData, USB_EPOINT_CONTROL, _ctx.EpControl_OutData, UVCP_OUT_BUF_SIZE);
}
/**
* @brief Main UVCP handler decoding commands and sending acknowledges.
*
* Confirms if a new command has been received and if no acknowledge send is
* pending. Afterwards, tries to decode a valid control packet from a received
* data from the host. On a valid command, an acknowledge is created and sent
* to the host.
*
* @return @e UVCP_SUCCESS for correct command decoding and acknowledge sending @n
* @e UVCP_NO_CMD if no command data has been received @n
* @e UVCP_BUSY if control endpoint is busy sending an acknowledge @n
* status code from @p _ucvp_decode_control_packet() call on its failure @n
* @e UVCP_ERR_SENDING if acknowledge could not be sent to host
*/
uvcpStatus uvcp_handler(void)
{
uvcpPacket ack_pkt;
size_t ack_pkt_size;
u32 status;
/* No command to process */
if (!_ctx.newCommandRxed)
return UVCP_NO_CMD;
/* Last Acknowledge is still being sent */
if (_ctx.busySending)
return UVCP_BUSY;
status = _ucvp_decode_control_packet(_ctx.EpControl_OutData, &ack_pkt, &ack_pkt_size);
if (status != UVCP_SUCCESS)
return status;
_ctx.busySending = TRUE;
status = EpBufferSend(UsbInstance.PrivateData, USB_EPOINT_CONTROL, (u8 *)&ack_pkt, ack_pkt_size);
if (status != XST_SUCCESS)
{
xil_printf("UVCP_ERR_SENDING\n");
return UVCP_ERR_SENDING;
}
return UVCP_SUCCESS;
}
uvcp.h
#ifndef UVCP_H
#define UVCP_H
/************************** Include Files ****************************/
#include "xil_types.h"
/**************************** Type Definitions *******************************/
/**
* @brief UVCP packet types.
*/
typedef enum
{
UVCP_PACKET_TYPE_ACK = 0x0000,
UVCP_PACKET_TYPE_CMD = 0x4000,
UVCP_PACKET_TYPE_ERROR = 0xffff
} uvcpPacketType;
/**
* @brief UVCP command types.
*/
typedef enum
{
UVCP_READMEM_CMD = 0x0800,
UVCP_READMEM_ACK = 0x0801,
UVCP_WRITEMEM_CMD = 0x0802,
UVCP_WRITEMEM_ACK = 0x0803,
UVCP_PENDING_ACK = 0x0805,
} uvcpCommand;
/**
* @brief UVCP status codes.
*/
typedef enum
{
UVCP_SUCCESS,
UVCP_NO_CMD,
UVCP_BUSY,
UVCP_ERR_UNKNOWN,
UVCP_ERR_NULL,
UVCP_ERR_SENDING,
UVCP_ERR_INVALID_PACKET,
UVCP_ERR_INVALID_CMD,
UVCP_ERR_NO_CMD,
UVCP_ERR_MEMORY,
UVCP_ERR_VERIFY,
UVCP_ERR_PACKET_CREATE,
UVCP_ERR_INV_ADDR,
} uvcpStatus;
/**
* @brief UVCP packet header.
*
* UVCP header format as specified by U3V specification.
*/
typedef struct __attribute__((__packed__))
{
u32 magic;
u16 packet_type;
u16 command;
u16 size;
u16 id;
} uvcpHeader;
/**
* @brief UVCP read command format.
*
* UVCP read command data format as specified by U3V specification.
*/
typedef struct __attribute__((__packed__))
{
u64 address;
u16 reserved; /* Listed as reserved, always 0 */
u16 size;
} uvcpReadMemoryCmdInfos;
/**
* @brief UVCP read command packet.
*
* UVCP read command packet as specified by U3V specification.
*/
typedef struct __attribute__((__packed__))
{
uvcpHeader header;
uvcpReadMemoryCmdInfos infos;
} uvcpReadMemoryCmd;
/**
* @brief UVCP write command format.
*
* UVCP write command data format as specified by U3V specification.
*/
typedef struct __attribute__((__packed__))
{
u64 address;
u8 data[64];
} uvcpWriteMemoryCmdInfos;
/**
* @brief UVCP write command packet.
*
* UVCP write command packet as specified by U3V specification.
*/
typedef struct __attribute__((__packed__))
{
uvcpHeader header;
uvcpWriteMemoryCmdInfos infos;
} uvcpWriteMemoryCmd;
/**
* @brief UVCP write acknowledge format.
*
* UVCP write acknowledge data format as specified by U3V specification.
*/
typedef struct __attribute__((__packed__))
{
u16 reserved; /* Listed as reserved, always 0 */
u16 bytes_written;
} uvcpWriteMemoryAckInfos;
/**
* @brief UVCP write acknowledge packet.
*
* UVCP write acknowledge packet as specified by U3V specification.
*/
typedef struct __attribute__((__packed__))
{
uvcpHeader header;
uvcpWriteMemoryAckInfos infos;
} uvcpWriteMemoryAck;
/**
* @brief UVCP pending acknowledge format.
*
* UVCP pending acknowledge data format as specified by U3V specification.
*/
typedef struct __attribute__((__packed__))
{
u16 reserved; /* Listed as reserved, always 0 */
u16 timeout;
} uvcpPendingAckInfos;
/**
* @brief UVCP pending acknowledge packet.
*
* UVCP pending acknowledge packet as specified by U3V specification.
*/
typedef struct __attribute__((__packed__))
{
uvcpHeader header;
uvcpPendingAckInfos infos;
} uvcpPendingAck;
/**
* @brief UVCP packet.
*
* UVCP packet as specified by U3V specification.
*/
typedef struct __attribute__((__packed__))
{
uvcpHeader header;
u8 data[128];
} uvcpPacket;
/**************************** Function Prototypes *******************************/
void uvcp_init(void);
void uvcp_set_rx_buf(void);
uvcpStatus uvcp_handler(void);
#endif // UVCP_H