blob: 6dcfc96af6ec9b4c926f248d80283b31bf4c0f8a [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#ifndef OPENTITAN_SW_DEVICE_LIB_TESTING_USB_TESTUTILS_H_
#define OPENTITAN_SW_DEVICE_LIB_TESTING_USB_TESTUTILS_H_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "sw/device/lib/dif/dif_usbdev.h"
#include "usb_testutils_diags.h"
// Result codes to rx/tx callback handlers
typedef enum {
/**
* Successful completion.
*/
kUsbTestutilsXfrResultOk = 0u,
/**
* Failed to transfer because of internal error,
* eg. buffer exhaustion.
*/
kUsbTestutilsXfrResultFailed = 1u,
/**
* Link reset interrupted transfer.
*/
kUsbTestutilsXfrResultLinkReset = 2u,
/**
* Canceled by suspend, endpoint removal or finalization.
*/
kUsbTestutilsXfrResultCanceled = 3u,
} usb_testutils_xfr_result_t;
// Flags affecting the transfer of larger data buffers
typedef enum {
kUsbTestutilsXfrMaxPacketMask = 0x7fu, // Max packet size [0,0x40U]
/**
* Explicitly specify the maximum packet size; otherwise the device default
* of USBDEV_MAX_PACKET_SIZE shall be assumed.
*/
kUsbTestutilsXfrMaxPacketSupplied = 0x100u,
/**
* Employ double-buffering to minimize the response time to notification of
* each packet transmission. This does require that two device packet buffers
* be available for use, but it increases the transfer rate.
*/
kUsbTestutilsXfrDoubleBuffered = 0x200u,
/**
* Emit/Expect Zero Length Packet as termination of data stage in the event
* that the final packet of the transfer is maximum length
*/
kUsbTestutilsXfrEmployZLP = 0x400u,
} usb_testutils_xfr_flags_t;
// In-progress larger buffer transfer to/from host
typedef struct usb_testutils_transfer {
/**
* Start of buffer for transfer
*/
const uint8_t *buffer;
/**
* Total number of bytes to be transferred to/from buffer
*/
uint32_t length;
/**
* Byte offset of the _next_ packet to be transferred
*/
uint32_t offset;
/**
* Flags modifying the transfer
*/
usb_testutils_xfr_flags_t flags;
/**
* Indicates that the last packet of the transfer has been reached;
* if 'next_valid' is true, then 'next_part' holds the last packet, already
* prepared for sending; if next_valid is false, then the last packet has
* already been supplied to usbdev and we're just awaiting the 'pkt_sent'
* interrupt.
*/
bool last;
/**
* The next part has been prepared and is ready to send to usbdev
*/
bool next_valid;
/**
* When sending IN data to the host, we may employ double-buffering and keep
* an additional buffer ready to be sent as soon as we're notified of the
* transfer of its predecessor
*/
dif_usbdev_buffer_t next_part;
} usb_testutils_transfer_t;
typedef struct usb_testutils_ctx usb_testutils_ctx_t;
struct usb_testutils_ctx {
dif_usbdev_t *dev;
dif_usbdev_buffer_pool_t *buffer_pool;
int flushed;
/**
* Have we received an indication of USB activity?
*/
bool got_frame;
/**
* Most recent bus frame number received from host
*/
uint16_t frame;
/**
* IN endpoints
*/
struct {
/**
* Opaque context handle for callback functions
*/
void *ep_ctx;
/**
* Callback for transmission of IN packet
*/
void (*tx_done_callback)(void *, usb_testutils_xfr_result_t);
/**
* Callback for periodically flushing IN data to host
*/
void (*flush)(void *);
/**
* Callback for link reset
*/
void (*reset)(void *);
/**
* Current in-progress transfer, if any
*/
usb_testutils_transfer_t transfer;
} in[USBDEV_NUM_ENDPOINTS];
/**
* OUT endpoints
*/
struct {
/**
* Opaque context handle for callback functions
*/
void *ep_ctx;
/**
* Callback for reception of IN packet
*/
void (*rx_callback)(void *, dif_usbdev_rx_packet_info_t,
dif_usbdev_buffer_t);
/**
* Callback for link reset
*/
void (*reset)(void *);
} out[USBDEV_NUM_ENDPOINTS];
};
typedef enum usb_testutils_out_transfer_mode {
/**
* The endpoint does not support OUT transactions.
*/
kUsbdevOutDisabled = 0,
/**
* Software does NOT need to call usb_testutils_clear_out_nak() after every
* received transaction. If software takes no action, usbdev will allow an
* endpoint's transactions to proceed as long as a buffer is available.
*/
kUsbdevOutStream = 1,
/**
* Software must call usb_testutils_clear_out_nak() after every received
* transaction to re-enable packet reception. This gives software time to
* respond with the appropriate handshake when it's ready.
*/
kUsbdevOutMessage = 2,
} usb_testutils_out_transfer_mode_t;
/**
* Call to set up IN endpoint.
*
* @param ctx usb test utils context pointer
* @param ep endpoint number
* @param ep_ctx context pointer for callee
* @param tx_done(void *ep_ctx) callback once send has been Acked
* @param flush(void *ep_ctx) called every 16ms based USB host timebase
* @param reset(void *ep_ctx) called when an USB link reset is detected
*/
void usb_testutils_in_endpoint_setup(
usb_testutils_ctx_t *ctx, uint8_t ep, void *ep_ctx,
void (*tx_done)(void *, usb_testutils_xfr_result_t), void (*flush)(void *),
void (*reset)(void *));
/**
* Call to set up OUT endpoint.
*
* @param ctx usb test utils context pointer
* @param ep endpoint number
* @param out_mode the transfer mode for OUT transactions
* @param ep_ctx context pointer for callee
* @param rx(void *ep_ctx, usbbufid_t buf, int size, int setup)
called when a packet is received
* @param reset(void *ep_ctx) called when an USB link reset is detected
*/
void usb_testutils_out_endpoint_setup(
usb_testutils_ctx_t *ctx, uint8_t ep,
usb_testutils_out_transfer_mode_t out_mode, void *ep_ctx,
void (*rx)(void *, dif_usbdev_rx_packet_info_t, dif_usbdev_buffer_t),
void (*reset)(void *));
/**
* Call to set up a pair of IN and OUT endpoints.
*
* @param ctx usb test utils context pointer
* @param ep endpoint number
* @param out_mode the transfer mode for OUT transactions
* @param ep_ctx context pointer for callee
* @param tx_done(void *ep_ctx) callback once send has been Acked
* @param rx(void *ep_ctx, usbbufid_t buf, int size, int setup)
called when a packet is received
* @param flush(void *ep_ctx) called every 16ms based USB host timebase
* @param reset(void *ep_ctx) called when an USB link reset is detected
*/
void usb_testutils_endpoint_setup(
usb_testutils_ctx_t *ctx, uint8_t ep,
usb_testutils_out_transfer_mode_t out_mode, void *ep_ctx,
void (*tx_done)(void *, usb_testutils_xfr_result_t),
void (*rx)(void *, dif_usbdev_rx_packet_info_t, dif_usbdev_buffer_t),
void (*flush)(void *), void (*reset)(void *));
/**
* Remove an IN endpoint.
*
* @param ctx usb test utils context pointer
* @param ep endpoint number
*/
void usb_testutils_in_endpoint_remove(usb_testutils_ctx_t *ctx, uint8_t ep);
/**
* Remove an OUT endpoint.
*
* @param ctx usb test utils context pointer
* @param ep endpoint number
*/
void usb_testutils_out_endpoint_remove(usb_testutils_ctx_t *ctx, uint8_t ep);
/**
* Remove a pair of IN and OUT endpoints
*
* @param ctx usb test utils context pointer
* @param ep endpoint number
*/
void usb_testutils_endpoint_remove(usb_testutils_ctx_t *ctx, uint8_t ep);
/**
* Returns an indication of whether an endpoint is currently halted because
* of the occurrence of an error.
*
* @param ctx usb test utils context pointer
* @param ep endpoint number
* @return true iff the endpoint is halted as a result of an error condition
*/
inline bool usb_testutils_endpoint_halted(usb_testutils_ctx_t *ctx,
dif_usbdev_endpoint_id_t endpoint);
/**
* Initialize the usbdev interface
*
* Does not connect the device, since the default endpoint is not yet enabled.
* See usb_testutils_connect().
*
* @param ctx uninitialized usb test utils context pointer
* @param pinflip boolean to indicate if PHY should be configured for D+/D- flip
* @param en_diff_rcvr boolean to indicate if PHY should enable an external
* differential receiver, activating the single-ended D
* input
* @param tx_use_d_se0 boolean to indicate if PHY uses D/SE0 for TX instead of
* Dp/Dn
*/
void usb_testutils_init(usb_testutils_ctx_t *ctx, bool pinflip,
bool en_diff_rcvr, bool tx_use_d_se0);
/**
* Send a larger data transfer from the given endpoint
*
* The usb_testutils layer will, if necessary, break this transfer into multiple
* packet buffers to be transferred in turn across the USB. The caller shall be
* notified via the tx_done_callback handler of successful completion of the
* entire transfer, or failure, and the caller must guarantee the availability
* of the supplied data throughout the operation.
*
* @param ctx usb test utils context pointer
* @param ep endpoint number
* @param data buffer of data to be transferred
* @param length number of bytes to be transferred
* @param flags flags modifying the transfer operation
* @return true iff the data has been accepted for transmission
*/
bool usb_testutils_transfer_send(usb_testutils_ctx_t *ctx, uint8_t ep,
const uint8_t *data, uint32_t length,
usb_testutils_xfr_flags_t flags);
/**
* Call regularly to poll the usbdev interface
*
* @param ctx usb test utils context pointer
*/
void usb_testutils_poll(usb_testutils_ctx_t *ctx);
/**
* Finalize the usbdev interface
*
* Removes all endpoint handlers and disconnects the device from the USB.
* This should be used only if the USB device is no longer required, or if it is
* required to be restarted with, for example, a different bus configuration.
*
* @param ctx initialized usb test utils context pointer
*/
void usb_testutils_fin(usb_testutils_ctx_t *ctx);
#endif // OPENTITAN_SW_DEVICE_LIB_TESTING_USB_TESTUTILS_H_