| /* |
| * 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 |