/* * 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 #include #include // 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_ */