357 lines
11 KiB
C
357 lines
11 KiB
C
/*
|
|
* Driver for USB ported from CoreBoot
|
|
*
|
|
* Copyright (C) 2014 BALATON Zoltan
|
|
*
|
|
* This file was 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 <drivers/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;
|
|
|
|
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;
|
|
|
|
enum {
|
|
audio_device = 0x01,
|
|
comm_device = 0x02,
|
|
hid_device = 0x03,
|
|
physical_device = 0x05,
|
|
imaging_device = 0x06,
|
|
printer_device = 0x07,
|
|
msc_device = 0x08,
|
|
hub_device = 0x09,
|
|
cdc_device = 0x0a,
|
|
ccid_device = 0x0b,
|
|
security_device = 0x0d,
|
|
video_device = 0x0e,
|
|
healthcare_device = 0x0f,
|
|
diagnostic_device = 0xdc,
|
|
wireless_device = 0xe0,
|
|
misc_device = 0xef,
|
|
};
|
|
|
|
enum { hid_subclass_none = 0, hid_subclass_boot = 1 };
|
|
|
|
enum {
|
|
hid_boot_proto_none = 0,
|
|
hid_boot_proto_keyboard = 1,
|
|
hid_boot_proto_mouse = 2
|
|
};
|
|
|
|
typedef struct {
|
|
union {
|
|
struct {
|
|
#ifdef CONFIG_BIG_ENDIAN
|
|
dev_req_dir data_dir:1;
|
|
dev_req_type req_type:2;
|
|
dev_req_recp req_recp:5;
|
|
#else
|
|
dev_req_recp req_recp:5;
|
|
dev_req_type req_type:2;
|
|
dev_req_dir data_dir:1;
|
|
#endif
|
|
} __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;
|
|
|
|
enum { FULL_SPEED = 0, LOW_SPEED = 1, HIGH_SPEED = 2, SUPER_SPEED = 3 };
|
|
|
|
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
|
|
int speed; // 1: lowspeed, 0: fullspeed, 2: highspeed
|
|
u32 quirks; // quirks field. got to love usb
|
|
void *data;
|
|
u8 *descriptor;
|
|
u8 *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;
|
|
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 and
|
|
return it. xHCI controllers want to
|
|
do this by themself. Also, the usbdev
|
|
structure has to be allocated and
|
|
initialized. */
|
|
int (*set_address) (hci_t *controller, int 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);
|
|
};
|
|
|
|
typedef struct {
|
|
unsigned char bDescLength;
|
|
unsigned char bDescriptorType;
|
|
unsigned char bNbrPorts;
|
|
union {
|
|
struct {
|
|
#ifdef CONFIG_BIG_ENDIAN
|
|
unsigned long:8;
|
|
unsigned long arePortIndicatorsSupported:1;
|
|
unsigned long ttThinkTime:2;
|
|
unsigned long overcurrentProtectionMode:2;
|
|
unsigned long isCompoundDevice:1;
|
|
unsigned long logicalPowerSwitchingMode:2;
|
|
#else
|
|
unsigned long logicalPowerSwitchingMode:2;
|
|
unsigned long isCompoundDevice:1;
|
|
unsigned long overcurrentProtectionMode:2;
|
|
unsigned long ttThinkTime:2;
|
|
unsigned long arePortIndicatorsSupported:1;
|
|
unsigned long:8;
|
|
#endif
|
|
} __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;
|
|
|
|
hci_t *new_controller (void);
|
|
void detach_controller (hci_t *controller);
|
|
void usb_poll (void);
|
|
void init_device_entry (hci_t *controller, int num);
|
|
|
|
void set_feature (usbdev_t *dev, int endp, int feature, int rtype);
|
|
void get_status (usbdev_t *dev, int endp, int rtype, int len, void *data);
|
|
void 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_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);
|
|
|
|
u8 *get_descriptor (usbdev_t *dev, unsigned char bmRequestType,
|
|
int descType, int descIdx, int langID);
|
|
|
|
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 */
|
|
int generic_set_address (hci_t *controller, int 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, int 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
|
|
|
|
#ifdef CONFIG_DEBUG_USB
|
|
#define usb_debug(fmt, args...) do { printk(fmt , ##args); } while (0)
|
|
#else
|
|
#define usb_debug(fmt, args...)
|
|
#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
|