blob: 0b4b31e10aa25758ff93bcf73a6e62e41ee20f2b [file] [log] [blame]
/*
* Copyright 2017, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
#ifndef _USB_USB_H_
#define _USB_USB_H_
#include <platsupport/io.h>
#include <platsupport/sync/sync.h>
#include <usb/usb_host.h>
// Maximum number of devices a host can manage
#define USB_NDEVICES 32
// Maximum number of endpoints per device
#define USB_MAX_EPS 32
// Maximum size of a string descriptor
#define MAX_STRING_SIZE 255
struct usb_dev;
typedef struct usb_dev usb_dev_t;
struct usb {
usb_host_t hdev;
/// Bitmap representation of used addresses
uint32_t addrbm;
/// Next address: delays address recycling
int next_addr;
/// List of devices connected to this host
usb_dev_t *devlist;
};
typedef struct usb usb_t;
enum usb_class {
USB_CLASS_UNSPECIFIED = 0x00,
USB_CLASS_AUDIO = 0x01,
USB_CLASS_COMM = 0x02,
USB_CLASS_HID = 0x03,
/* ------------------------*/
USB_CLASS_PID = 0x05,
USB_CLASS_IMAGE = 0x06,
USB_CLASS_PRINTER = 0x07,
USB_CLASS_STORAGE = 0x08,
USB_CLASS_HUB = 0x09,
USB_CLASS_CDCDATA = 0x0A,
USB_CLASS_CARDREADER = 0x0B,
/* ------------------------*/
USB_CLASS_SECURITY = 0x0D,
USB_CLASS_VIDEO = 0x0E,
USB_CLASS_HEALTH = 0x0F,
USB_CLASS_AV = 0x10,
/* ------------------------*/
USB_CLASS_DIAGNOSTIC = 0xDC,
/* ------------------------*/
USB_CLASS_WIRELESS = 0xE0,
/* ------------------------*/
USB_CLASS_MISC = 0xEF,
/* ------------------------*/
USB_CLASS_APPSPEC = 0xFE,
USB_CLASS_VEND = 0xFF
};
struct usb_dev {
/* Filled on creation */
usb_t *host;
usb_dev_t *hub;
uint8_t port;
uint8_t tt_addr;
uint8_t tt_port;
enum usb_speed speed;
ps_dma_man_t* dman;
/* Filled on creation/init */
uint16_t prod_id;
uint16_t vend_id;
uint8_t class;
uint8_t addr;
/* Filled by driver */
int (*connect)(struct usb_dev* udev);
int (*disconnect)(struct usb_dev* udev);
struct udev_priv *dev_data;
/*
* A device can have up to 32 data endpoints(16 IN, 16 OUT and only 4 for
* low speed devices). The control endpoint is separate and is shared by all
* interfaces.
*/
struct endpoint *ep_ctrl; // Control endpoint of the device
struct endpoint *ep[USB_MAX_EPS]; // Data endpoints of the device
/* For device lists */
struct usb_dev *next;
};
/*
* USB requests
*/
/* Data transfer direction, bit 7 of bmRequestType */
#define USB_DIR_OUT 0
#define USB_DIR_IN BIT(7)
/* Request type, bit 6-5 of bmRequestType */
#define USB_TYPE_STD (0 * BIT(5))
#define USB_TYPE_CLS (1 * BIT(5))
#define USB_TYPE_VEN (2 * BIT(5))
/* Request recipient, bit 4-0 of bmRequestType */
#define USB_RCPT_DEVICE 0
#define USB_RCPT_INTERFACE 1
#define USB_RCPT_ENDPOINT 2
#define USB_RCPT_OTHER 3
/* bRequest */
enum Request {
GET_STATUS = 0,
CLR_FEATURE = 1,
SET_FEATURE = 3,
SET_ADDRESS = 5,
GET_DESCRIPTOR = 6,
SET_DESCRIPTOR = 7,
GET_CONFIGURATION = 8,
SET_CONFIGURATION = 9,
GET_INTERFACE = 10,
SET_INTERFACE = 11,
SYNCH_FRAME = 12
};
/*** bmRequestType ***/
struct usbreq {
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} __attribute__ ((packed));
/*
* USB descriptors
*/
/* bDescriptorType */
enum DescriptorType {
DEVICE = 0x01,
CONFIGURATION = 0x02,
STRING = 0x03,
INTERFACE = 0x04,
ENDPOINT = 0x05,
DEVICE_QUALIFIER = 0x06,
OTHER_SPEED_CONFIGURATION = 0x07,
INTERFACE_POWER = 0x08,
/* Class specific types */
HID = 0x21,
HID_REPORT = 0x22,
HID_PHYSICAL = 0x23,
CS_INTERFACE = 0x24, //USB class-specific
CS_ENDPOINT = 0x25, //USB class-specific
HUB = 0x29
};
struct device_desc {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
uint8_t bNumConfigurations;
} __attribute__ ((packed));
struct device_qualifier_desc {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint8_t bNumConfigurations;
uint8_t bReserved;
} __attribute__ ((packed));
/* Descriptors */
struct anon_desc {
uint8_t bLength;
uint8_t bDescriptorType;
} __attribute__ ((packed));
struct config_desc {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfigurationIndex;
uint8_t bmAttributes;
uint8_t bMaxPower;
} __attribute__ ((packed));
struct iface_desc {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface;
} __attribute__ ((packed));
struct endpoint_desc {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
} __attribute__ ((packed));
struct string_desc {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bString[MAX_STRING_SIZE];
} __attribute__ ((packed));
/* Class HID */
struct hid_desc {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdHID;
uint8_t bCountryCode;
uint8_t bNumDescriptors;
uint8_t bReportDescriptorType;
uint16_t wReportDescriptorLength;
} __attribute__ ((packed));
static inline struct usbreq
__get_descriptor_req(enum DescriptorType t, int value, int index, int size) {
struct usbreq r = {
.bmRequestType = (USB_DIR_IN | USB_TYPE_STD | USB_RCPT_DEVICE),
.bRequest = GET_DESCRIPTOR,
.wValue = (t << 8) + value,
.wIndex = index,
.wLength = size
};
return r;
}
static inline struct usbreq
__set_configuration_req(int index) {
struct usbreq r = {
.bmRequestType = (USB_DIR_OUT | USB_TYPE_STD | USB_RCPT_DEVICE),
.bRequest = SET_CONFIGURATION,
.wValue = index,
.wIndex = 0,
.wLength = 0
};
return r;
}
static inline struct usbreq
__set_interface_req(int index) {
struct usbreq r = {
.bmRequestType = (USB_DIR_OUT | USB_TYPE_STD | USB_RCPT_INTERFACE),
.bRequest = SET_INTERFACE,
.wValue = 0,
.wIndex = index,
.wLength = 0
};
return r;
}
/****************************************/
/** Initialise usb
* @param[in] id the ID of the USB host to
* initialise
* @param[in] ioops a structure defining operations for
* device access.
* @param[in] sync a structure defining operations for
* mutex operations access.
* @param[out] host On success, this will be filled with
* a handle to the usb host controller
* associated with @ref{id}.
* @return 0 on success.
*/
int usb_init(enum usb_host_id id, ps_io_ops_t* ioops, ps_mutex_ops_t *sync, usb_t* host);
/** Probe for a new device on the BUS.
* This function is typically called by a HUB device when it
* detects a new connection. The new device inherits the DMA
* allocator of host device. On success, key device parameters
* will have been cached and the device will have been assigned
* a new address on the bus.
* @param[in] hub The USB hub that the new device is connected
* to.
* @param[in] port The port on the provided hub that the new
* device is connected to.
* @param[in] speed The connection speed of the new device.
* @param[out] d On success, d will contain a reference to
* the new device.
* @return 0 on success.
*/
int usb_new_device(usb_dev_t *hub, int port,
enum usb_speed speed, usb_dev_t **d);
/** A call back for the configuration parser. This function is
* called for each descriptor.
* @param[in] token An unmodified token as passed to the
* configuration parser.
* @param[in] cfg The current configuration number. -1 for
* none.
* @param[in] iface The current interface number. -1 for none.
* @param[in] desc The current descriptor to be parsed.
* @return 0 for next descriptor, otherwise an error
* code to return to the caller.
*/
typedef int (*usb_config_cb)(void* token, int cfg, int iface,
struct anon_desc* desc);
/** Iteratively passed all descriptors to a caller provided
* function.
* Configuring a device is tedious because not only do
* all descriptors come as a giant blob, but they may not
* be aligned correctly and must be copied to aligned memory
* before use.
*/
int usbdev_parse_config(usb_dev_t *udev, usb_config_cb cb,
void* token);
/** Disconnect a USB device. Usually a call to this function
* is driven by the physical disconnection of the device
* from the bus.
* @param[in] dev A reference to the device to disconnect
*/
void usbdev_disconnect(usb_dev_t *dev);
/** Return the class reported by a USB device
* @param[in] The USB device in question
* @return The class ID
*/
enum usb_class usbdev_get_class(usb_dev_t *dev);
/**
* Returns a string representation of the provided USB class code.
* the string contains no leading or trailing white space and does
* not end in a new line character.
* @param[in] usb_class the class id in question.
* @return a string representation of the USB class.
*/
const char* usb_class_get_description(enum usb_class usb_class);
/** Schedule a transaction on the provided USB device
* @param[in] udev The USB device which is to receive the
* transaction.
* @param[in] ep The endpoint to deliver the
* packets to.
* @param[in] xact A structure describing the packets to be
* sent.
* @param[in] nxact The number of entries in the xact
* structure to use.
* @param[in] cb A call back function to call once the
* packet is delivered or if there was a
* transmission error. NULL if blocking
* behaviour is required.
* @param[in] token Passed unmodified to the call back
* function.
* @return 0 on success.
*/
int usbdev_schedule_xact(usb_dev_t *udev, struct endpoint *ep, struct xact* xact,
int nxact, usb_cb_t cb, void* token);
/** Print a list of registered devices
* @param[in] host the USB host device in question
* @param[in] v the level of information to print.
*/
void usb_lsusb(usb_t* host, int v);
/** Retrieve a USB device from the bus
* @param[in] host The USB host device that the USB device is attached to.
* @param[in] addr The address of the USB device requested.
* @return A handle to the device or NULL if there is no device at
* that address.
*/
usb_dev_t *usb_get_device(usb_t *host, int addr);
/** Probes and prints the descriptors for the given device
* @param[in] dev The usb device to probe
*/
void usb_probe_device(usb_dev_t *dev);
/** Pass control to the devices IRQ handler
* @param[in] host The USB host that triggered
* the interrupt event.
*/
void usb_handle_irq(usb_t *host);
/** Allocate transaction buffers for requests
* @param[in] dman A dma allocator instance
* @param[in/out] xact A array structure which provides the sizes of buffers
* to allocate. On success, this array will be filled
* with references to the allocated DMA memory.
* @param[in] nxact The size, in array indexes, of xact.
* @return 0 on success
*/
int usb_alloc_xact(ps_dma_man_t* dman, struct xact* xact, int nxact);
/** Frees transaction buffers
* @param[in] dman The dma allocator instance that was used for the allocation
* @param[in] xact The description of the transaction
* @param[in] nxact The number of entries in xact
*/
void usb_destroy_xact(ps_dma_man_t* dman, struct xact* xact, int nxact);
#endif /* _USB_USB_H_ */