| /* |
| * This file is part of the libpayload project. |
| * |
| * Copyright (C) 2008 coresystems GmbH |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| #ifndef __USB_H |
| #define __USB_H |
| #include <libpayload.h> |
| #include <pci/pci.h> |
| |
| typedef enum { host_to_device = 0, device_to_host = 1 } dev_req_dir; |
| typedef enum { standard_type = 0, class_type = 1, vendor_type = |
| 2, reserved_type = 3 |
| } dev_req_type; |
| typedef enum { dev_recp = 0, iface_recp = 1, endp_recp = 2, other_recp = 3 |
| } dev_req_recp; |
| |
| enum { |
| DT_DEV = 1, |
| DT_CFG = 2, |
| DT_STR = 3, |
| DT_INTF = 4, |
| DT_ENDP = 5, |
| }; |
| |
| typedef enum { |
| GET_STATUS = 0, |
| CLEAR_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 |
| } bRequest_Codes; |
| |
| typedef enum { |
| ENDPOINT_HALT = 0, |
| DEVICE_REMOTE_WAKEUP = 1, |
| TEST_MODE = 2 |
| } feature_selectors; |
| |
| /* SetAddress() recovery interval (USB 2.0 specification 9.2.6.3 */ |
| #define SET_ADDRESS_MDELAY 2 |
| |
| typedef struct { |
| unsigned char bDescLength; |
| unsigned char bDescriptorType; |
| unsigned char bNbrPorts; |
| union { |
| struct { |
| unsigned long logicalPowerSwitchingMode:2; |
| unsigned long isCompoundDevice:1; |
| unsigned long overcurrentProtectionMode:2; |
| unsigned long ttThinkTime:2; |
| unsigned long arePortIndicatorsSupported:1; |
| unsigned long:8; |
| } __attribute__ ((packed)); |
| unsigned short wHubCharacteristics; |
| } __attribute__ ((packed)); |
| unsigned char bPowerOn2PwrGood; |
| unsigned char bHubContrCurrent; |
| char DeviceRemovable[]; |
| } __attribute__ ((packed)) hub_descriptor_t; |
| |
| typedef struct { |
| unsigned char bLength; |
| unsigned char bDescriptorType; |
| unsigned short bcdUSB; |
| unsigned char bDeviceClass; |
| unsigned char bDeviceSubClass; |
| unsigned char bDeviceProtocol; |
| unsigned char bMaxPacketSize0; |
| unsigned short idVendor; |
| unsigned short idProduct; |
| unsigned short bcdDevice; |
| unsigned char iManufacturer; |
| unsigned char iProduct; |
| unsigned char iSerialNumber; |
| unsigned char bNumConfigurations; |
| } __attribute__ ((packed)) device_descriptor_t; |
| |
| typedef struct { |
| unsigned char bLength; |
| unsigned char bDescriptorType; |
| unsigned short wTotalLength; |
| unsigned char bNumInterfaces; |
| unsigned char bConfigurationValue; |
| unsigned char iConfiguration; |
| unsigned char bmAttributes; |
| unsigned char bMaxPower; |
| } __attribute__ ((packed)) configuration_descriptor_t; |
| |
| typedef struct { |
| unsigned char bLength; |
| unsigned char bDescriptorType; |
| unsigned char bInterfaceNumber; |
| unsigned char bAlternateSetting; |
| unsigned char bNumEndpoints; |
| unsigned char bInterfaceClass; |
| unsigned char bInterfaceSubClass; |
| unsigned char bInterfaceProtocol; |
| unsigned char iInterface; |
| } __attribute__ ((packed)) interface_descriptor_t; |
| |
| typedef struct { |
| unsigned char bLength; |
| unsigned char bDescriptorType; |
| unsigned char bEndpointAddress; |
| unsigned char bmAttributes; |
| unsigned short wMaxPacketSize; |
| unsigned char bInterval; |
| } __attribute__ ((packed)) endpoint_descriptor_t; |
| |
| typedef struct { |
| unsigned char bLength; |
| unsigned char bDescriptorType; |
| unsigned short bcdHID; |
| unsigned char bCountryCode; |
| unsigned char bNumDescriptors; |
| unsigned char bReportDescriptorType; |
| unsigned short wReportDescriptorLength; |
| } __attribute__ ((packed)) hid_descriptor_t; |
| |
| typedef struct { |
| union { |
| struct { |
| dev_req_recp req_recp:5; |
| dev_req_type req_type:2; |
| dev_req_dir data_dir:1; |
| } __attribute__ ((packed)); |
| unsigned char bmRequestType; |
| } __attribute__ ((packed)); |
| unsigned char bRequest; |
| unsigned short wValue; |
| unsigned short wIndex; |
| unsigned short wLength; |
| } __attribute__ ((packed)) dev_req_t; |
| |
| struct usbdev_hc; |
| typedef struct usbdev_hc hci_t; |
| |
| struct usbdev; |
| typedef struct usbdev usbdev_t; |
| |
| typedef enum { SETUP, IN, OUT } direction_t; |
| typedef enum { CONTROL = 0, ISOCHRONOUS = 1, BULK = 2, INTERRUPT = 3 |
| } endpoint_type; |
| |
| typedef struct { |
| usbdev_t *dev; |
| int endpoint; |
| direction_t direction; |
| int toggle; |
| int maxpacketsize; |
| endpoint_type type; |
| int interval; /* expressed as binary logarithm of the number |
| of microframes (i.e. t = 125us * 2^interval) */ |
| } endpoint_t; |
| |
| typedef enum { |
| FULL_SPEED = 0, LOW_SPEED = 1, HIGH_SPEED = 2, SUPER_SPEED = 3, |
| } usb_speed; |
| |
| struct usbdev { |
| hci_t *controller; |
| endpoint_t endpoints[32]; |
| int num_endp; |
| int address; // usb address |
| int hub; // hub, device is attached to |
| int port; // port where device is attached |
| usb_speed speed; |
| u32 quirks; // quirks field. got to love usb |
| void *data; |
| device_descriptor_t *descriptor; |
| configuration_descriptor_t *configuration; |
| void (*init) (usbdev_t *dev); |
| void (*destroy) (usbdev_t *dev); |
| void (*poll) (usbdev_t *dev); |
| }; |
| |
| typedef enum { OHCI = 0, UHCI = 1, EHCI = 2, XHCI = 3} hc_type; |
| |
| struct usbdev_hc { |
| hci_t *next; |
| u32 reg_base; |
| pcidev_t pcidev; // 0 if not used (eg on ARM) |
| hc_type type; |
| usbdev_t *devices[128]; // dev 0 is root hub, 127 is last addressable |
| |
| /* start(): Resume operation. */ |
| void (*start) (hci_t *controller); |
| /* stop(): Stop operation but keep controller initialized. */ |
| void (*stop) (hci_t *controller); |
| /* reset(): Perform a controller reset. The controller needs to |
| be (re)initialized afterwards to work (again). */ |
| void (*reset) (hci_t *controller); |
| /* init(): Initialize a (previously reset) controller |
| to a working state. */ |
| void (*init) (hci_t *controller); |
| /* shutdown(): Stop operation, detach host controller and shutdown |
| this driver instance. After calling shutdown() any |
| other usage of this hci_t* is invalid. */ |
| void (*shutdown) (hci_t *controller); |
| |
| int (*bulk) (endpoint_t *ep, int size, u8 *data, int finalize); |
| int (*control) (usbdev_t *dev, direction_t pid, int dr_length, |
| void *devreq, int data_length, u8 *data); |
| void* (*create_intr_queue) (endpoint_t *ep, int reqsize, int reqcount, int reqtiming); |
| void (*destroy_intr_queue) (endpoint_t *ep, void *queue); |
| u8* (*poll_intr_queue) (void *queue); |
| void *instance; |
| |
| /* set_address(): Tell the usb device its address (xHCI |
| controllers want to do this by |
| themselves). Also, allocate the usbdev |
| structure, initialize enpoint 0 |
| (including MPS) and return it. */ |
| usbdev_t *(*set_address) (hci_t *controller, usb_speed speed, |
| int hubport, int hubaddr); |
| /* finish_device_config(): Another hook for xHCI, |
| returns 0 on success. */ |
| int (*finish_device_config) (usbdev_t *dev); |
| /* destroy_device(): Finally, destroy all structures that |
| were allocated during set_address() |
| and finish_device_config(). */ |
| void (*destroy_device) (hci_t *controller, int devaddr); |
| }; |
| |
| hci_t *usb_add_mmio_hc(hc_type type, void *bar); |
| hci_t *new_controller (void); |
| void detach_controller (hci_t *controller); |
| void usb_poll (void); |
| usbdev_t *init_device_entry (hci_t *controller, int num); |
| |
| int usb_decode_mps0 (usb_speed speed, u8 bMaxPacketSize0); |
| int set_feature (usbdev_t *dev, int endp, int feature, int rtype); |
| int get_status (usbdev_t *dev, int endp, int rtype, int len, void *data); |
| int get_descriptor (usbdev_t *dev, int rtype, int descType, int descIdx, |
| void *data, size_t len); |
| int set_configuration (usbdev_t *dev); |
| int clear_feature (usbdev_t *dev, int endp, int feature, int rtype); |
| int clear_stall (endpoint_t *ep); |
| |
| void usb_nop_init (usbdev_t *dev); |
| void usb_hub_init (usbdev_t *dev); |
| void usb_hid_init (usbdev_t *dev); |
| void usb_msc_init (usbdev_t *dev); |
| void usb_generic_init (usbdev_t *dev); |
| |
| static inline unsigned char |
| gen_bmRequestType (dev_req_dir dir, dev_req_type type, dev_req_recp recp) |
| { |
| return (dir << 7) | (type << 5) | recp; |
| } |
| |
| /* default "set address" handler */ |
| usbdev_t *generic_set_address (hci_t *controller, usb_speed speed, |
| int hubport, int hubaddr); |
| |
| void usb_detach_device(hci_t *controller, int devno); |
| int usb_attach_device(hci_t *controller, int hubaddress, int port, |
| usb_speed speed); |
| |
| u32 usb_quirk_check(u16 vendor, u16 device); |
| int usb_interface_check(u16 vendor, u16 device); |
| |
| #define USB_QUIRK_MSC_FORCE_PROTO_SCSI (1 << 0) |
| #define USB_QUIRK_MSC_FORCE_PROTO_ATAPI (1 << 1) |
| #define USB_QUIRK_MSC_FORCE_PROTO_UFI (1 << 2) |
| #define USB_QUIRK_MSC_FORCE_PROTO_RBC (1 << 3) |
| #define USB_QUIRK_MSC_FORCE_TRANS_BBB (1 << 4) |
| #define USB_QUIRK_MSC_FORCE_TRANS_CBI (1 << 5) |
| #define USB_QUIRK_MSC_FORCE_TRANS_CBI_I (1 << 6) |
| #define USB_QUIRK_MSC_NO_TEST_UNIT_READY (1 << 7) |
| #define USB_QUIRK_MSC_SHORT_INQUIRY (1 << 8) |
| #define USB_QUIRK_TEST (1 << 31) |
| #define USB_QUIRK_NONE 0 |
| |
| static inline void usb_debug(const char *fmt, ...) |
| { |
| #ifdef USB_DEBUG |
| va_list ap; |
| va_start(ap, fmt); |
| vprintf(fmt, ap); |
| va_end(ap); |
| #endif |
| } |
| |
| /** |
| * To be implemented by libpayload-client. It's called by the USB stack |
| * when a new USB device is found which isn't claimed by a built in driver, |
| * so the client has the chance to know about it. |
| * |
| * @param dev descriptor for the USB device |
| */ |
| void __attribute__((weak)) usb_generic_create (usbdev_t *dev); |
| |
| /** |
| * To be implemented by libpayload-client. It's called by the USB stack |
| * when it finds out that a USB device is removed which wasn't claimed by a |
| * built in driver. |
| * |
| * @param dev descriptor for the USB device |
| */ |
| void __attribute__((weak)) usb_generic_remove (usbdev_t *dev); |
| |
| #endif |