blob: 5cbb915ebe1f4efd2d4adf418afaa06283ccba5c [file] [log] [blame]
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#ifndef OPENTITAN_SW_HOST_TESTS_USBDEV_USBDEV_STREAM_USB_DEVICE_H_
#define OPENTITAN_SW_HOST_TESTS_USBDEV_USBDEV_STREAM_USB_DEVICE_H_
#include <iostream>
// The 'usbdev_serial' class may be used on platforms where libusb support is
// not available; libusb is required to exercise Isochronous streams, Interrupt
// streams and Control Transfers.
#if STREAMTEST_LIBUSB
#include <libusb-1.0/libusb.h>
#endif
class USBDevice {
public:
#if STREAMTEST_LIBUSB
USBDevice(bool verbose = false)
: verbose_(verbose),
state_(StateStreaming),
ctx_(nullptr),
devh_(nullptr) {}
#else
USBDevice(bool verbose = false)
: verbose_(verbose), state_(StateStreaming), devh_(false) {}
#endif
// Device states.
enum USBDevState {
StateStreaming,
StateSuspending,
StateSuspended,
StateResuming
};
// DPI test numbers.
typedef enum usb_testutils_test_number {
kUsbTestNumberSmoke = 0,
kUsbTestNumberStreams,
kUsbTestNumberIso,
kUsbTestNumberMixed,
kUsbTestNumberSuspend,
kUsbTestNumberExc,
} usb_testutils_test_number_t;
// Test/stream flags; these are common with device-side software.
typedef enum {
/**
* Mask for extracting the stream ID number.
*/
kUsbdevStreamFlagID = 0x0FU,
/**
* Host shall retrieve IN data from the device for this stream.
*/
kUsbdevStreamFlagRetrieve = 0x10U,
/**
* Host shall check that IN data matches as expected.
*/
kUsbdevStreamFlagCheck = 0x20U,
/**
* DPI model (or Host) shall retry IN data fetches, where possible.
*/
kUsbdevStreamFlagRetry = 0x40U,
/**
* Host shall send OUT data to the device for this stream.
*/
kUsbdevStreamFlagSend = 0x80U,
/**
* Default stream flags; for non-libusb builds.
*/
kUsbdevStreamFlagsDefault = kUsbdevStreamFlagRetrieve |
kUsbdevStreamFlagCheck |
kUsbdevStreamFlagRetry | kUsbdevStreamFlagSend
} usbdev_stream_flags_t;
// Vendor-Specific requests.
static constexpr uint8_t kVendorGetData = 0x7du;
static constexpr uint8_t kVendorPutData = 0x7fu;
// Maximum packet size of 64 bytes is used for Control, Interrupt and Bulk
// Transfers.
static constexpr unsigned kDevDataMaxPacketSize = 0x40U;
// Our USB device has a maximum packet size of just 64 bytes even for
// Isochronous transfers; this may one day be increased.
static constexpr unsigned kDevIsoMaxPacketSize = 0x40U;
/**
* Initialize USB device before test; called once at startup.
*
* @param vendorID Vendor ID code of the USB device.
* @param productID Product ID code of the USB device.
* @param devAddress Device address (0 for first accessible device).
* @param busNumber Bus number (if devAddress non-zero).
* @return true iff initialization was successful.
*/
bool Init(uint16_t productID, uint16_t vendorID, uint8_t devAddress = 0u,
uint8_t busNumber = 0u);
/**
* Finalize USB device before completing test.
*
* @return true iff finalization was successful.
*/
bool Fin();
/**
* (Re)open the device. If the device is already open, this function just
* returns immediately.
*
* @return true iff opening was successful, or the device was already open.
*/
bool Open();
/**
* Close the device, if open. If the device is already closed, this function
* just returns immediately.
*
* @return true iff closing was successful, or the device was already closed.
*/
bool Close();
/**
* Service the device; keep libusb transfers being processed.
*
* @return true iff the device is still operational.
*/
bool Service();
#if STREAMTEST_LIBUSB
/**
* Claim an interface on the device.
*
* @param interface The interface number of the interface to be claimed.
* @return The result of the operation.
*/
int ClaimInterface(unsigned interface) const {
return libusb_claim_interface(devh_, (int)interface);
}
/**
* Release an interface on the device.
*
* @param interface The interface number of the interface to be claimed.
* @return The result of the operation.
*/
int ReleaseInterface(unsigned interface) const {
return libusb_release_interface(devh_, (int)interface);
}
/**
* Emit a textual report of an error returned by libusb.
*
* @param rc Return code from libusb function.
* @return false as a convenience for indicating failure to caller.
*/
inline bool ErrorUSB(const char *prefix, int rc) {
std::cerr << prefix << " (" << rc << ", " << libusb_error_name(rc) << ")"
<< std::endl;
return false;
}
/**
* Retrieve libusb device handle.
*
* @return The device handle, or nullptr if none eg. device not open.
*/
libusb_device_handle *DeviceHandle() const { return devh_; }
/**
* Fill Control Transfer descriptor.
*/
void FillControlTransfer(struct libusb_transfer *xfr, uint8_t ep,
uint8_t *buffer, libusb_transfer_cb_fn callback,
void *user_data, unsigned timeout_ms) {
libusb_fill_control_transfer(xfr, devh_, buffer, callback, user_data,
timeout_ms);
// Complete the endpoint number because we're _not_ always targeting
// Endpoint Zero.
xfr->endpoint = ep;
}
/**
* Fill Bulk Transfer descriptor.
*/
void FillBulkTransfer(struct libusb_transfer *xfr, uint8_t ep,
uint8_t *buffer, size_t len,
libusb_transfer_cb_fn callback, void *user_data,
unsigned timeout_ms) const {
libusb_fill_bulk_transfer(xfr, devh_, ep, buffer, len, callback, user_data,
timeout_ms);
}
/**
* Fill Interrupt Transfer descriptor.
*/
void FillIntTransfer(struct libusb_transfer *xfr, uint8_t ep, uint8_t *buffer,
size_t len, libusb_transfer_cb_fn callback,
void *user_data, unsigned timeout_ms) const {
libusb_fill_interrupt_transfer(xfr, devh_, ep, buffer, len, callback,
user_data, timeout_ms);
}
/**
* Fill Isochronous transfer descriptor,
*/
void FillIsoTransfer(struct libusb_transfer *xfr, uint8_t ep, uint8_t *buffer,
size_t len, unsigned num_iso_packets,
libusb_transfer_cb_fn callback, void *user_data,
unsigned timeout_ms) const {
libusb_fill_iso_transfer(xfr, devh_, ep, buffer, len, num_iso_packets,
callback, user_data, timeout_ms);
}
/**
* Complete the lengths of the packets in this Isochronous transfer, assuming
* that packets are of uniform size.
*
* @param xfr Isochronous transfer to be completed.
* @param len Total length of all packets within the transfer.
*/
void SetIsoPacketLengths(struct libusb_transfer *xfr, unsigned len) const {
libusb_set_iso_packet_lengths(xfr, len);
}
/**
* Allocate a transfer descriptor to be completed by the caller.
*
* @return Pointer to transfer transcript, or NULL if allocation failed.
*/
struct libusb_transfer *AllocTransfer(unsigned iso_packets) const {
return libusb_alloc_transfer((int)iso_packets);
}
/**
* Free an allocated transfer descriptor.
*
* @param xfr The transfer descriptor to be freed.
*/
void FreeTransfer(struct libusb_transfer *xfr) const {
return libusb_free_transfer(xfr);
}
/**
* Submit a transfer for processing.
*
* @param xfr The transfer to be submitted.
*/
int SubmitTransfer(struct libusb_transfer *xfr) const {
return libusb_submit_transfer(xfr);
}
/**
* Cancel a pending transfer.
*
* @param xfr The transfer to be cancelled.
*/
int CancelTransfer(struct libusb_transfer *xfr) const {
return libusb_cancel_transfer(xfr);
}
#endif
/**
* Read Test Descriptor from the DUT using a Vendor-Specific command.
*
* @return true iff the operation was successful.
*/
bool ReadTestDesc();
/**
* Return the test number from the test descriptor.
*
* @return test number
*/
uint8_t TestNumber() const { return testNumber_; }
/**
* Return the specified test argument from the test descriptor.
*
* @param arg Argument number.
* @return test argument
*/
uint8_t TestArg(unsigned arg) const {
// Presently all defined tests support up to 4 arguments.
return (arg < 4U) ? testArg_[arg] : 0U;
}
/**
* Reset and Reconfigure the DUT.
*
* @return true iff the operation was successful.
*/
bool Reset();
/**
* Suspend device.
*
* @return true iff the operation was successful.
*/
bool Suspend();
/**
* Resume operation of suspended device.
*/
bool Resume();
/**
* Disconnect and reconnect device.
*
* @param true iff the operation was succesful.
*/
bool Disconnect();
/**
* Returns the current state of the device.
*
* @return current state.
*/
USBDevState CurrentState() const { return state_; }
/**
* Sets the current state of the device in the Suspend/Resume signaling.
*
* @param state The current state of the device.
*/
void SetState(USBDevState state) { state_ = state; }
private:
// Verbose logging/reporting.
bool verbose_;
// Current device state.
USBDevState state_;
// Vendor ID of USB device.
uint16_t vendorID_;
// Product ID of USB device.
uint16_t productID_;
// Specified bus number.
uint8_t busSpec_;
// Specified device address.
uint8_t addrSpec_;
// Bus number of the device we're using.
uint8_t busNumber_;
// (Bus-local) device address of the device we're using.
uint8_t devAddress_;
#if STREAMTEST_LIBUSB
// Context information for libusb.
libusb_context *ctx_;
// Device handle.
libusb_device_handle *devh_;
// Device descriptor.
libusb_device_descriptor devDesc_;
#else
// Device handle; just retain whether open/closed.
bool devh_;
#endif
// Device path (bus number - ports numbers).
std::string devPath_;
usb_testutils_test_number_t testNumber_;
uint8_t testArg_[4];
// TODO: We should introduce a timeout on libusb Control Transfers.
static const unsigned kControlTransferTimeout = 0u;
// Vendor-Specific Commands.
static const uint8_t kVendorTestConfig = 0x7cu;
static const uint8_t kVendorTestStatus = 0x7eu;
};
#endif // OPENTITAN_SW_HOST_TESTS_USBDEV_USBDEV_STREAM_USB_DEVICE_H_