Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the libpayload project. |
| 3 | * |
| 4 | * Copyright (C) 2008 coresystems GmbH |
| 5 | * |
| 6 | * Redistribution and use in source and binary forms, with or without |
| 7 | * modification, are permitted provided that the following conditions |
| 8 | * are met: |
| 9 | * 1. Redistributions of source code must retain the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer. |
| 11 | * 2. Redistributions in binary form must reproduce the above copyright |
| 12 | * notice, this list of conditions and the following disclaimer in the |
| 13 | * documentation and/or other materials provided with the distribution. |
| 14 | * 3. The name of the author may not be used to endorse or promote products |
| 15 | * derived from this software without specific prior written permission. |
| 16 | * |
| 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 27 | * SUCH DAMAGE. |
| 28 | */ |
| 29 | |
| 30 | #ifndef __USB_H |
| 31 | #define __USB_H |
| 32 | #include <libpayload.h> |
| 33 | #include <pci.h> |
| 34 | |
| 35 | typedef enum { host_to_device = 0, device_to_host = 1 } dev_req_dir; |
| 36 | typedef enum { standard_type = 0, class_type = 1, vendor_type = |
| 37 | 2, reserved_type = 3 |
| 38 | } dev_req_type; |
| 39 | typedef enum { dev_recp = 0, iface_recp = 1, endp_recp = 2, other_recp = 3 |
| 40 | } dev_req_recp; |
| 41 | |
| 42 | typedef enum { |
| 43 | GET_STATUS = 0, |
| 44 | CLEAR_FEATURE = 1, |
| 45 | SET_FEATURE = 3, |
| 46 | SET_ADDRESS = 5, |
| 47 | GET_DESCRIPTOR = 6, |
| 48 | SET_DESCRIPTOR = 7, |
| 49 | GET_CONFIGURATION = 8, |
| 50 | SET_CONFIGURATION = 9, |
| 51 | GET_INTERFACE = 10, |
| 52 | SET_INTERFACE = 11, |
| 53 | SYNCH_FRAME = 12 |
| 54 | } bRequest_Codes; |
| 55 | |
| 56 | typedef enum { |
| 57 | ENDPOINT_HALT = 0, |
| 58 | DEVICE_REMOTE_WAKEUP = 1, |
| 59 | TEST_MODE = 2 |
| 60 | } feature_selectors; |
| 61 | |
| 62 | typedef struct { |
| 63 | union { |
| 64 | struct { |
| 65 | dev_req_recp req_recp:5; |
| 66 | dev_req_type req_type:2; |
| 67 | dev_req_dir data_dir:1; |
| 68 | } __attribute__ ((packed)); |
| 69 | unsigned char bmRequestType; |
| 70 | } __attribute__ ((packed)); |
| 71 | unsigned char bRequest; |
| 72 | unsigned short wValue; |
| 73 | unsigned short wIndex; |
| 74 | unsigned short wLength; |
| 75 | } __attribute__ ((packed)) dev_req_t; |
| 76 | |
| 77 | struct usbdev_hc; |
| 78 | typedef struct usbdev_hc hci_t; |
| 79 | |
| 80 | struct usbdev; |
| 81 | typedef struct usbdev usbdev_t; |
| 82 | |
Patrick Georgi | d78691d | 2010-06-07 13:58:17 +0000 | [diff] [blame] | 83 | typedef enum { SETUP, IN, OUT } direction_t; |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 84 | typedef enum { CONTROL = 0, ISOCHRONOUS = 1, BULK = 2, INTERRUPT = 3 |
| 85 | } endpoint_type; |
| 86 | |
| 87 | typedef struct { |
| 88 | usbdev_t *dev; |
| 89 | int endpoint; |
Patrick Georgi | d78691d | 2010-06-07 13:58:17 +0000 | [diff] [blame] | 90 | direction_t direction; |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 91 | int toggle; |
| 92 | int maxpacketsize; |
| 93 | endpoint_type type; |
| 94 | } endpoint_t; |
| 95 | |
Nico Huber | 4fc7b6c | 2013-06-04 10:03:45 +0200 | [diff] [blame^] | 96 | enum { FULL_SPEED = 0, LOW_SPEED = 1, HIGH_SPEED = 2, SUPER_SPEED = 3 }; |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 97 | |
| 98 | struct usbdev { |
| 99 | hci_t *controller; |
| 100 | endpoint_t endpoints[32]; |
| 101 | int num_endp; |
| 102 | int address; // usb address |
| 103 | int hub; // hub, device is attached to |
| 104 | int port; // port where device is attached |
Stefan Reinauer | b56f2d0 | 2010-03-25 22:17:36 +0000 | [diff] [blame] | 105 | int speed; // 1: lowspeed, 0: fullspeed, 2: highspeed |
| 106 | u32 quirks; // quirks field. got to love usb |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 107 | void *data; |
| 108 | u8 *descriptor; |
| 109 | u8 *configuration; |
| 110 | void (*init) (usbdev_t *dev); |
| 111 | void (*destroy) (usbdev_t *dev); |
| 112 | void (*poll) (usbdev_t *dev); |
| 113 | }; |
| 114 | |
Anton Kochkov | 1c36ead | 2012-06-28 08:30:15 +0400 | [diff] [blame] | 115 | typedef enum { OHCI = 0, UHCI = 1, EHCI = 2, XHCI = 3} hc_type; |
| 116 | |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 117 | struct usbdev_hc { |
| 118 | struct usbdev_hc *next; |
| 119 | pcidev_t bus_address; |
| 120 | u32 reg_base; |
Anton Kochkov | 1c36ead | 2012-06-28 08:30:15 +0400 | [diff] [blame] | 121 | hc_type type; |
Patrick Georgi | 4727c07 | 2008-10-16 19:20:51 +0000 | [diff] [blame] | 122 | usbdev_t *devices[128]; // dev 0 is root hub, 127 is last addressable |
Nico Huber | a482701 | 2012-11-12 15:12:35 +0100 | [diff] [blame] | 123 | |
| 124 | /* start(): Resume operation. */ |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 125 | void (*start) (hci_t *controller); |
Nico Huber | a482701 | 2012-11-12 15:12:35 +0100 | [diff] [blame] | 126 | /* stop(): Stop operation but keep controller initialized. */ |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 127 | void (*stop) (hci_t *controller); |
Nico Huber | a482701 | 2012-11-12 15:12:35 +0100 | [diff] [blame] | 128 | /* reset(): Perform a controller reset. The controller needs to |
| 129 | be (re)initialized afterwards to work (again). */ |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 130 | void (*reset) (hci_t *controller); |
Nico Huber | 6e711c6 | 2012-11-12 16:20:32 +0100 | [diff] [blame] | 131 | /* init(): Initialize a (previously reset) controller |
| 132 | to a working state. */ |
| 133 | void (*init) (hci_t *controller); |
Nico Huber | a482701 | 2012-11-12 15:12:35 +0100 | [diff] [blame] | 134 | /* shutdown(): Stop operation, detach host controller and shutdown |
| 135 | this driver instance. After calling shutdown() any |
| 136 | other usage of this hci_t* is invalid. */ |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 137 | void (*shutdown) (hci_t *controller); |
Nico Huber | a482701 | 2012-11-12 15:12:35 +0100 | [diff] [blame] | 138 | |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 139 | int (*bulk) (endpoint_t *ep, int size, u8 *data, int finalize); |
Patrick Georgi | d78691d | 2010-06-07 13:58:17 +0000 | [diff] [blame] | 140 | int (*control) (usbdev_t *dev, direction_t pid, int dr_length, |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 141 | void *devreq, int data_length, u8 *data); |
Patrick Georgi | 4727c07 | 2008-10-16 19:20:51 +0000 | [diff] [blame] | 142 | void* (*create_intr_queue) (endpoint_t *ep, int reqsize, int reqcount, int reqtiming); |
| 143 | void (*destroy_intr_queue) (endpoint_t *ep, void *queue); |
| 144 | u8* (*poll_intr_queue) (void *queue); |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 145 | void *instance; |
| 146 | }; |
| 147 | |
| 148 | typedef struct { |
| 149 | unsigned char bDescLength; |
| 150 | unsigned char bDescriptorType; |
| 151 | unsigned char bNbrPorts; |
| 152 | union { |
| 153 | struct { |
| 154 | unsigned long logicalPowerSwitchingMode:2; |
| 155 | unsigned long isCompoundDevice:1; |
| 156 | unsigned long overcurrentProtectionMode:2; |
| 157 | unsigned long ttThinkTime:2; |
| 158 | unsigned long arePortIndicatorsSupported:1; |
| 159 | unsigned long:8; |
| 160 | } __attribute__ ((packed)); |
| 161 | unsigned short wHubCharacteristics; |
| 162 | } __attribute__ ((packed)); |
| 163 | unsigned char bPowerOn2PwrGood; |
| 164 | unsigned char bHubContrCurrent; |
| 165 | char DeviceRemovable[]; |
| 166 | } __attribute__ ((packed)) hub_descriptor_t; |
| 167 | |
| 168 | typedef struct { |
| 169 | unsigned char bLength; |
| 170 | unsigned char bDescriptorType; |
| 171 | unsigned short bcdUSB; |
| 172 | unsigned char bDeviceClass; |
| 173 | unsigned char bDeviceSubClass; |
| 174 | unsigned char bDeviceProtocol; |
| 175 | unsigned char bMaxPacketSize0; |
| 176 | unsigned short idVendor; |
| 177 | unsigned short idProduct; |
| 178 | unsigned short bcdDevice; |
| 179 | unsigned char iManufacturer; |
| 180 | unsigned char iProduct; |
| 181 | unsigned char iSerialNumber; |
| 182 | unsigned char bNumConfigurations; |
| 183 | } __attribute__ ((packed)) device_descriptor_t; |
| 184 | |
| 185 | typedef struct { |
| 186 | unsigned char bLength; |
| 187 | unsigned char bDescriptorType; |
| 188 | unsigned short wTotalLength; |
| 189 | unsigned char bNumInterfaces; |
| 190 | unsigned char bConfigurationValue; |
| 191 | unsigned char iConfiguration; |
| 192 | unsigned char bmAttributes; |
| 193 | unsigned char bMaxPower; |
| 194 | } __attribute__ ((packed)) configuration_descriptor_t; |
| 195 | |
| 196 | typedef struct { |
| 197 | unsigned char bLength; |
| 198 | unsigned char bDescriptorType; |
| 199 | unsigned char bInterfaceNumber; |
| 200 | unsigned char bAlternateSetting; |
| 201 | unsigned char bNumEndpoints; |
| 202 | unsigned char bInterfaceClass; |
| 203 | unsigned char bInterfaceSubClass; |
| 204 | unsigned char bInterfaceProtocol; |
| 205 | unsigned char iInterface; |
| 206 | } __attribute__ ((packed)) interface_descriptor_t; |
| 207 | |
| 208 | typedef struct { |
| 209 | unsigned char bLength; |
| 210 | unsigned char bDescriptorType; |
| 211 | unsigned char bEndpointAddress; |
| 212 | unsigned char bmAttributes; |
| 213 | unsigned short wMaxPacketSize; |
| 214 | unsigned char bInterval; |
| 215 | } __attribute__ ((packed)) endpoint_descriptor_t; |
| 216 | |
Stefan Reinauer | b56f2d0 | 2010-03-25 22:17:36 +0000 | [diff] [blame] | 217 | typedef struct { |
| 218 | unsigned char bLength; |
| 219 | unsigned char bDescriptorType; |
| 220 | unsigned short bcdHID; |
| 221 | unsigned char bCountryCode; |
| 222 | unsigned char bNumDescriptors; |
| 223 | unsigned char bReportDescriptorType; |
| 224 | unsigned short wReportDescriptorLength; |
| 225 | } __attribute__ ((packed)) hid_descriptor_t; |
| 226 | |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 227 | hci_t *new_controller (void); |
| 228 | void detach_controller (hci_t *controller); |
| 229 | void usb_poll (void); |
| 230 | void init_device_entry (hci_t *controller, int num); |
| 231 | |
| 232 | void set_feature (usbdev_t *dev, int endp, int feature, int rtype); |
| 233 | void get_status (usbdev_t *dev, int endp, int rtype, int len, void *data); |
Nico Huber | ba22e4c | 2012-11-23 11:34:20 +0100 | [diff] [blame] | 234 | void set_configuration (usbdev_t *dev); |
Nico Huber | 79e1f2f | 2012-06-01 09:50:11 +0200 | [diff] [blame] | 235 | int clear_feature (usbdev_t *dev, int endp, int feature, int rtype); |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 236 | int clear_stall (endpoint_t *ep); |
| 237 | |
| 238 | void usb_nop_init (usbdev_t *dev); |
| 239 | void usb_hub_init (usbdev_t *dev); |
| 240 | void usb_hid_init (usbdev_t *dev); |
| 241 | void usb_msc_init (usbdev_t *dev); |
Gabe Black | dc9e77f | 2013-02-01 20:19:27 -0800 | [diff] [blame] | 242 | void usb_generic_init (usbdev_t *dev); |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 243 | |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 244 | u8 *get_descriptor (usbdev_t *dev, unsigned char bmRequestType, |
| 245 | int descType, int descIdx, int langID); |
| 246 | |
| 247 | static inline unsigned char |
| 248 | gen_bmRequestType (dev_req_dir dir, dev_req_type type, dev_req_recp recp) |
| 249 | { |
| 250 | return (dir << 7) | (type << 5) | recp; |
| 251 | } |
| 252 | |
Patrick Georgi | 4727c07 | 2008-10-16 19:20:51 +0000 | [diff] [blame] | 253 | void usb_detach_device(hci_t *controller, int devno); |
Stefan Reinauer | b56f2d0 | 2010-03-25 22:17:36 +0000 | [diff] [blame] | 254 | int usb_attach_device(hci_t *controller, int hubaddress, int port, int speed); |
| 255 | |
| 256 | u32 usb_quirk_check(u16 vendor, u16 device); |
| 257 | int usb_interface_check(u16 vendor, u16 device); |
| 258 | |
| 259 | #define USB_QUIRK_MSC_FORCE_PROTO_SCSI (1 << 0) |
| 260 | #define USB_QUIRK_MSC_FORCE_PROTO_ATAPI (1 << 1) |
| 261 | #define USB_QUIRK_MSC_FORCE_PROTO_UFI (1 << 2) |
| 262 | #define USB_QUIRK_MSC_FORCE_PROTO_RBC (1 << 3) |
| 263 | #define USB_QUIRK_MSC_FORCE_TRANS_BBB (1 << 4) |
| 264 | #define USB_QUIRK_MSC_FORCE_TRANS_CBI (1 << 5) |
| 265 | #define USB_QUIRK_MSC_FORCE_TRANS_CBI_I (1 << 6) |
| 266 | #define USB_QUIRK_MSC_NO_TEST_UNIT_READY (1 << 7) |
| 267 | #define USB_QUIRK_MSC_SHORT_INQUIRY (1 << 8) |
| 268 | #define USB_QUIRK_TEST (1 << 31) |
| 269 | #define USB_QUIRK_NONE 0 |
| 270 | |
Gabe Black | 93ded59 | 2012-11-01 15:44:10 -0700 | [diff] [blame] | 271 | static inline void usb_debug(const char *fmt, ...) |
| 272 | { |
Stefan Reinauer | b56f2d0 | 2010-03-25 22:17:36 +0000 | [diff] [blame] | 273 | #ifdef USB_DEBUG |
Gabe Black | 93ded59 | 2012-11-01 15:44:10 -0700 | [diff] [blame] | 274 | va_list ap; |
| 275 | va_start(ap, fmt); |
| 276 | vprintf(fmt, ap); |
| 277 | va_end(ap); |
Stefan Reinauer | b56f2d0 | 2010-03-25 22:17:36 +0000 | [diff] [blame] | 278 | #endif |
Gabe Black | 93ded59 | 2012-11-01 15:44:10 -0700 | [diff] [blame] | 279 | } |
Stefan Reinauer | 5fe6e23 | 2009-07-31 11:39:55 +0000 | [diff] [blame] | 280 | |
Gabe Black | dc9e77f | 2013-02-01 20:19:27 -0800 | [diff] [blame] | 281 | /** |
| 282 | * To be implemented by libpayload-client. It's called by the USB stack |
| 283 | * when a new USB device is found which isn't claimed by a built in driver, |
| 284 | * so the client has the chance to know about it. |
| 285 | * |
| 286 | * @param dev descriptor for the USB device |
| 287 | */ |
| 288 | void __attribute__((weak)) usb_generic_create (usbdev_t *dev); |
| 289 | |
| 290 | /** |
| 291 | * To be implemented by libpayload-client. It's called by the USB stack |
| 292 | * when it finds out that a USB device is removed which wasn't claimed by a |
| 293 | * built in driver. |
| 294 | * |
| 295 | * @param dev descriptor for the USB device |
| 296 | */ |
| 297 | void __attribute__((weak)) usb_generic_remove (usbdev_t *dev); |
| 298 | |
Patrick Georgi | d21f68b | 2008-09-02 16:06:22 +0000 | [diff] [blame] | 299 | #endif |