Hi there,
This patch adds USB capabilities to libpayload. It requires some memalign implementation (eg. the one I sent yesterday). Features: - UHCI controller driver - UHCI root hub driver - USB MSC (Mass Storage Class) driver - skeleton of a USB HID driver (requires better interrupt transfer handling, which is TODO) - skeleton of a USB hub driver (needs several blank spots filled in, eg. power management. Again: TODO)
OHCI and EHCI are not supported, though OHCI support should be rather easy as the stack provides reasonable abstractions (or so I hope). EHCI will probably be more complicated. Isochronous transfers (eg. webcams, audio stuff, ...) are not supported - they can be, but I doubt we'll have a reason for that in the boot environment.
The MSC driver was tested against a couple of USB flash drives, and should be reasonably tolerant by now. Though I probably underestimate the amount of bugs present in USB flash drives, so feedback is welcome.
Signed-off-by: Patrick Georgi patrick.georgi@coresystems.de
Regards, Patrick Georgi
Index: Config.in =================================================================== --- Config.in (revision 3557) +++ Config.in (working copy) @@ -132,5 +132,46 @@ bool "Support for PC speaker" default y
+config USB + bool "USB Support" + default n + +config USB_UHCI + bool "Support for USB UHCI controllers" + depends on USB + help + Select this option if you are going to use USB 1.1 on an Intel based + system. + +config USB_OHCI + bool "Support for USB OHCI controllers" + depends on USB + help + Select this option if you are going to use USB 1.1 on an AMD based + system. + NOTE: This option is not (fully) implemented yet + +config USB_EHCI + bool "Support for USB EHCI controllers" + depends on USB + help + Select this option if you want to use USB 2.0 + NOTE: This option is not (fully) implemented yet + +config USB_HID + bool "Support for USB keyboards (broken)" + depends on USB + default n + +config USB_HUB + bool "Support for USB hubs (broken)" + depends on USB + default n + +config USB_MSC + bool "Support for USB storage" + depends on USB + + endmenu
Index: Makefile =================================================================== --- Makefile (revision 3557) +++ Makefile (working copy) @@ -137,6 +137,7 @@ prepare: $(Q)mkdir -p $(obj)/util/kconfig/lxdialog $(Q)mkdir -p $(obj)/crypto $(obj)/curses $(obj)/drivers/video + $(Q)mkdir -p $(obj)/drivers/usb $(Q)mkdir -p $(obj)/i386 $(obj)/lib/$(ARCHDIR-y) $(obj)/libc $(Q)mkdir -p $(src)/lib/$(ARCHDIR-y)
Index: drivers/Makefile.inc =================================================================== --- drivers/Makefile.inc (revision 3557) +++ drivers/Makefile.inc (working copy) @@ -47,3 +47,14 @@ # Geode console drivers TARGETS-$(CONFIG_GEODE_VIDEO_CONSOLE) += drivers/video/geode.o TARGETS-$(CONFIG_GEODE_VIDEO_CONSOLE) += drivers/video/font8x16.o + +# USB stack +TARGETS-$(CONFIG_USB) += drivers/usb/usbinit.o +TARGETS-$(CONFIG_USB) += drivers/usb/usb.o +TARGETS-$(CONFIG_USB) += drivers/usb/usb_dev.o +TARGETS-$(CONFIG_USB_HUB) += drivers/usb/usbhub.o +TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci.o +TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci_rh.o +TARGETS-$(CONFIG_USB_HID) += drivers/usb/usbhid.o +TARGETS-$(CONFIG_USB_MSC) += drivers/usb/usbmsc.o + Index: drivers/usb/usb.h =================================================================== --- drivers/usb/usb.h (revision 0) +++ drivers/usb/usb.h (revision 0) @@ -0,0 +1,224 @@ +/* + * 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.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; + +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 = 0x2d, IN = 0x69, OUT = 0xe1 } pid_t; +typedef enum { CONTROL = 0, ISOCHRONOUS = 1, BULK = 2, INTERRUPT = 3 +} endpoint_type; + +typedef struct { + usbdev_t *dev; + int endpoint; + pid_t direction; + int toggle; + int maxpacketsize; + endpoint_type type; +} endpoint_t; + + +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 lowspeed; // 1 if lowspeed device + void *data; + u8 *descriptor; + u8 *configuration; + void (*init) (usbdev_t *dev); + void (*destroy) (usbdev_t *dev); + void (*poll) (usbdev_t *dev); +}; + +struct usbdev_hc { + struct usbdev_hc *next; + pcidev_t bus_address; + u32 reg_base; + usbdev_t devices[128]; // dev 0 is root hub, 127 is last addressable + void (*start) (hci_t *controller); + void (*stop) (hci_t *controller); + void (*reset) (hci_t *controller); + void (*shutdown) (hci_t *controller); + int (*packet) (usbdev_t *dev, int endp, int pid, int toggle, + int length, u8 *data); + int (*bulk) (endpoint_t *ep, int size, u8 *data, int finalize); + int (*control) (usbdev_t *dev, pid_t pid, int dr_length, + void *devreq, int data_length, u8 *data); + void *instance; +}; + +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; + +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); +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); + +int set_address (hci_t *controller, int lowspeed); + +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; +} + +#endif Index: drivers/usb/usbhid.c =================================================================== --- drivers/usb/usbhid.c (revision 0) +++ drivers/usb/usbhid.c (revision 0) @@ -0,0 +1,137 @@ +/* + * 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. + */ + +#include "usb.h" + +enum { hid_subclass_none = 0, hid_subclass_boot = 1 }; +enum { hid_proto_boot = 0, hid_proto_report = 1 }; +enum { hid_boot_proto_none = 0, hid_boot_proto_keyboard = + 1, hid_boot_proto_mouse = 2 +}; +static const char *boot_protos[3] = { "(none)", "keyboard", "mouse" }; +enum { GET_REPORT = 0x1, GET_IDLE = 0x2, GET_PROTOCOL = 0x3, SET_REPORT = + 0x9, SET_IDLE = 0xa, SET_PROTOCOL = 0xb +}; + +static void +usb_hid_destroy (usbdev_t *dev) +{ +} + +int keypress; +char keymap[256] = { + -1, -1, -1, -1, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '1', '2', + '3', '4', '5', '6', '7', '8', '9', '0', '\n', TERM_ESC, + TERM_BACKSPACE, TERM_TAB, ' ', '-', '=', '[', + ']', '\', -1, ';', ''', '`', ',', '.', '/', -1, -1, -1, -1, -1, -1, + -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, TERM_HOME, TERM_PPAGE, -1, + TERM_END, TERM_NPAGE, TERM_RIGHT, + TERM_LEFT, TERM_DOWN, TERM_UP, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + + +static void +usb_hid_poll (usbdev_t *dev) +{ + char buf[8]; + static int toggle = 0; + // hardcode to endpoint 1, 8 bytes + dev->controller->packet (dev, 1, IN, toggle, 8, buf); + toggle ^= 1; + // FIXME: manage buf[0]=special keys, too + keypress = keymap[buf[2]]; + if ((keypress == -1) && (buf[2] != 0)) { + printf ("%x %x %x %x %x %x %x %x\n", buf[0], buf[1], buf[2], + buf[3], buf[4], buf[5], buf[6], buf[7]); + } +} + +int (*oldhook) (void); + +int +hookfunc (void) +{ + int key; + if (oldhook != 0) + key = oldhook (); + if (key == -1) + key = keypress; + return key; +} + +void +usb_hid_init (usbdev_t *dev) +{ + + configuration_descriptor_t *cd = dev->configuration; + interface_descriptor_t *interface = ((char *) cd) + cd->bLength; + + if (interface->bInterfaceSubClass == hid_subclass_boot) { + printf (" supports boot interface..\n"); + printf (" it's a %s\n", + boot_protos[interface->bInterfaceProtocol]); + if (interface->bInterfaceProtocol == hid_boot_proto_keyboard) { + printf (" activating...\n"); + dev_req_t dr; + // set_protocol(hid_proto_boot) + dr.data_dir = host_to_device; + dr.req_type = class_type; + dr.req_recp = iface_recp; + dr.bRequest = SET_PROTOCOL; + dr.wValue = hid_proto_boot; + dr.wIndex = interface->bInterfaceNumber; + dr.wLength = 0; + dev->controller->control (dev, OUT, + sizeof (dev_req_t), &dr, 0, + 0); + + // only add here, because we only support boot-keyboard HID devices + // FIXME: make this a real console input driver instead, once the API is there + dev->destroy = usb_hid_destroy; + dev->poll = usb_hid_poll; + oldhook = getkey_hook; + getkey_hook = hookfunc; + } + } +} Index: drivers/usb/uhci_rh.c =================================================================== --- drivers/usb/uhci_rh.c (revision 0) +++ drivers/usb/uhci_rh.c (revision 0) @@ -0,0 +1,182 @@ +/* + * 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. + */ + +#include <libpayload.h> +#include "uhci.h" + +typedef struct { + int port[2]; +} rh_inst_t; + +#define RH_INST(dev) ((rh_inst_t*)(dev)->data) + +static void +uhci_rh_enable_port (usbdev_t *dev, int port) +{ + u16 value; + hci_t *controller = dev->controller; + if (port == 1) + port = PORTSC1; + else + port = PORTSC2; + uhci_reg_mask16 (controller, port, ~(1 << 12), 0); /* wakeup */ + + uhci_reg_mask16 (controller, port, ~0, 1 << 9); /* reset */ + mdelay (30); // >10ms + uhci_reg_mask16 (controller, port, ~(1 << 9), 0); + mdelay (1); // >5.3us per spec, <3ms because some devices make trouble + + uhci_reg_mask16 (controller, port, ~0, 1 << 2); /* enable */ + do { + value = uhci_reg_read16 (controller, port); + mdelay (1); + } while (((value & (1 << 2)) == 0) && (value & 0x01)); +} + +/* disable root hub */ +static void +uhci_rh_disable_port (usbdev_t *dev, int port) +{ + hci_t *controller = dev->controller; + port = PORTSC2; + if (port == 1) + port = PORTSC1; + uhci_reg_mask16 (controller, port, ~4, 0); + int value; + do { + value = uhci_reg_read16 (controller, port); + mdelay (1); + } while ((value & (1 << 2)) != 0); +} + +static void +uhci_rh_scanport (usbdev_t *dev, int port) +{ + int portsc, offset; + if (port == 1) { + portsc = PORTSC1; + offset = 0; + } else if (port == 2) { + portsc = PORTSC2; + offset = 1; + } else + return; + int devno = RH_INST (dev)->port[offset]; + if (devno != -1) { + dev->controller->devices[devno].destroy (&dev->controller-> + devices[devno]); + init_device_entry (dev->controller, devno); + RH_INST (dev)->port[offset] = -1; + } + uhci_reg_mask16 (dev->controller, portsc, ~0, (1 << 3) | (1 << 2)); // clear port state change, enable port + + if ((uhci_reg_read16 (dev->controller, portsc) & 1) != 0) { + int newdev; + usbdev_t *newdev_t; + // device attached + + uhci_rh_disable_port (dev, port); + uhci_rh_enable_port (dev, port); + + int lowspeed = + (uhci_reg_read16 (dev->controller, portsc) >> 8) & 1; + printf ("%sspeed device\n", (lowspeed == 1) ? "low" : "full"); + + newdev = set_address (dev->controller, lowspeed); + if (newdev == -1) + return; + newdev_t = &dev->controller->devices[newdev]; + RH_INST (dev)->port[offset] = newdev; + newdev_t->address = newdev; + newdev_t->hub = dev->address; + newdev_t->port = portsc; + // determine responsible driver + newdev_t->init (newdev_t); + } +} + +static int +uhci_rh_report_port_changes (usbdev_t *dev) +{ + int stored, real; + + stored = (RH_INST (dev)->port[0] == -1); + real = ((uhci_reg_read16 (dev->controller, PORTSC1) & 1) == 0); + if (stored != real) + return 1; + + stored = (RH_INST (dev)->port[1] == -1); + real = ((uhci_reg_read16 (dev->controller, PORTSC2) & 1) == 0); + if (stored != real) + return 2; + +// maybe detach+attach happened between two scans? + if ((uhci_reg_read16 (dev->controller, PORTSC1) & 2) > 0) + return 1; + if ((uhci_reg_read16 (dev->controller, PORTSC2) & 2) > 0) + return 2; + +// no change + return -1; +} + +static void +uhci_rh_destroy (usbdev_t *dev) +{ + uhci_rh_disable_port (dev, 1); + uhci_rh_disable_port (dev, 2); + free (RH_INST (dev)); +} + +static void +uhci_rh_poll (usbdev_t *dev) +{ + int port; + while ((port = uhci_rh_report_port_changes (dev)) != -1) + uhci_rh_scanport (dev, port); +} + +void +uhci_rh_init (usbdev_t *dev) +{ + dev->destroy = uhci_rh_destroy; + dev->poll = uhci_rh_poll; + + uhci_rh_enable_port (dev, 1); + uhci_rh_enable_port (dev, 2); + dev->data = malloc (sizeof (rh_inst_t)); + RH_INST (dev)->port[0] = -1; + RH_INST (dev)->port[1] = -1; + + /* we can set them here because a root hub _really_ shouldn't + appear elsewhere */ + dev->address = 0; + dev->hub = -1; + dev->port = -1; +} Index: drivers/usb/usbmsc.h =================================================================== --- drivers/usb/usbmsc.h (revision 0) +++ drivers/usb/usbmsc.h (revision 0) @@ -0,0 +1,47 @@ +/* + * 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 __USBMSC_H +#define __USBMSC_H +typedef struct { + unsigned int blocksize; + unsigned int numblocks; + endpoint_t *bulk_in; + endpoint_t *bulk_out; +} usbmsc_inst_t; + +#define MSC_INST(dev) ((usbmsc_inst_t*)(dev)->data) + +typedef enum { cbw_direction_data_in = 0x80, cbw_direction_data_out = 0 +} cbw_direction; + +int readwrite_blocks (usbdev_t *dev, int start, int n, cbw_direction dir, + u8 *buf); + +#endif Index: drivers/usb/TODO =================================================================== --- drivers/usb/TODO (revision 0) +++ drivers/usb/TODO (revision 0) @@ -0,0 +1,22 @@ +- handle error conditions +- handle disconnect more gracefully (ie. make calling layer aware that the device doesn't exist somehow) +- usbhub: + - proper client enumeration (esp. detach) + - change detection + - power management +- handle interrupts more cleverly: + create a new queue for the interrupt with a couple of TD sequences, + - each ending with "breadth first" flag + - linked as a chain + add that queue at the appropriate times in front of the default structure so the max latency is honored + - only one intr chain per framelist item, so it must be arranged appropriately + reads from usb device just look at "invalidated" tds and the results they got + handled tds get reactivated as a ring structure + - added as child of the oldest td + - queue header already dropped the td, so no issue there + + this setup ensures that: + - the max latency of the device is honored + - the client knows the right order of the data + - there is no need for an interrupt handler + - but must be polled at least max latency * num tds times -> more tds = less time pressure Index: drivers/usb/usb_dev.c =================================================================== --- drivers/usb/usb_dev.c (revision 0) +++ drivers/usb/usb_dev.c (revision 0) @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#include "usb.h" + +static void +usb_nop_destroy (usbdev_t *dev) +{ + if (dev->descriptor != 0) + free (dev->descriptor); + usb_nop_init (dev); + dev->address = -1; + dev->hub = -1; + dev->port = -1; +} + +static void +usb_nop_poll (usbdev_t *dev) +{ + return; +} + +void +usb_nop_init (usbdev_t *dev) +{ + dev->descriptor = 0; + dev->destroy = usb_nop_destroy; + dev->poll = usb_nop_poll; +} Index: drivers/usb/uhci.c =================================================================== --- drivers/usb/uhci.c (revision 0) +++ drivers/usb/uhci.c (revision 0) @@ -0,0 +1,507 @@ +/* + * 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. + */ + +#include "usb.h" +#include "uhci.h" +#include <arch/virtual.h> + +static void uhci_start (hci_t *controller); +static void uhci_stop (hci_t *controller); +static void uhci_reset (hci_t *controller); +static void uhci_shutdown (hci_t *controller); +static int uhci_packet (usbdev_t *dev, int endp, int pid, int toggle, + int length, u8 *data); +static int uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize); +static int uhci_control (usbdev_t *dev, pid_t dir, int drlen, void *devreq, + int dalen, u8 *data); + +#if 0 +/* dump uhci */ +static void +uhci_dump (hci_t *controller) +{ + printf ("dump:\nUSBCMD: %x\n", uhci_reg_read16 (controller, USBCMD)); + printf ("USBSTS: %x\n", uhci_reg_read16 (controller, USBSTS)); + printf ("USBINTR: %x\n", uhci_reg_read16 (controller, USBINTR)); + printf ("FRNUM: %x\n", uhci_reg_read16 (controller, FRNUM)); + printf ("FLBASEADD: %x\n", uhci_reg_read32 (controller, FLBASEADD)); + printf ("SOFMOD: %x\n", uhci_reg_read8 (controller, SOFMOD)); + printf ("PORTSC1: %x\n", uhci_reg_read16 (controller, PORTSC1)); + printf ("PORTSC2: %x\n", uhci_reg_read16 (controller, PORTSC2)); +} +#endif + +static void +td_dump (td_t *td) +{ + printf ("%x packet (at %lx) to %x.%x failed\n", td->pid, + virt_to_phys (td), td->dev_addr, td->endp); + printf ("td (counter at %x) returns: ", td->counter); + printf (" bitstuff err: %x, ", td->status_bitstuff_err); + printf (" CRC err: %x, ", td->status_crc_err); + printf (" NAK rcvd: %x, ", td->status_nakrcvd); + printf (" Babble: %x, ", td->status_babble); + printf (" Data Buffer err: %x, ", td->status_databuf_err); + printf (" Stalled: %x, ", td->status_stalled); + printf (" Active: %x\n", td->status_active); + if (td->status_babble) + printf (" Babble because of %s\n", + td->status_bitstuff_err ? "host" : "device"); + if (td->status_active) + printf (" still active - timeout?\n"); +} + +static void +uhci_reset (hci_t *controller) +{ + /* reset */ + uhci_reg_write16 (controller, USBCMD, 4); + mdelay (50); + uhci_reg_write16 (controller, USBCMD, 0); + mdelay (10); + uhci_reg_write16 (controller, USBCMD, 2); + while ((uhci_reg_read16 (controller, USBCMD) & 2) != 0) + mdelay (1); + + uhci_reg_write32 (controller, FLBASEADD, + (u32) virt_to_phys (UHCI_INST (controller)-> + framelistptr)); + //printf ("framelist at %p\n",UHCI_INST(controller)->framelistptr); + + /* disable irqs */ + uhci_reg_write16 (controller, USBINTR, 0); + + /* reset framelist index */ + uhci_reg_write16 (controller, FRNUM, 0); + + uhci_reg_mask16 (controller, USBCMD, ~0, 0xc0); // max packets, configure flag + + uhci_start (controller); +} + +hci_t * +uhci_init (pcidev_t addr) +{ + int i; + hci_t *controller = new_controller (); + + controller->instance = malloc (sizeof (uhci_t)); + controller->start = uhci_start; + controller->stop = uhci_stop; + controller->reset = uhci_reset; + controller->shutdown = uhci_shutdown; + controller->packet = uhci_packet; + controller->bulk = uhci_bulk; + controller->control = uhci_control; + UHCI_INST (controller)->roothub = &(controller->devices[0]); + + controller->bus_address = addr; + controller->reg_base = pci_read_config32 (controller->bus_address, 0x20) & ~1; /* ~1 clears the register type indicator that is set to 1 for IO space */ + + /* kill legacy support handler */ + uhci_stop (controller); + mdelay (1); + uhci_reg_write16 (controller, USBSTS, 0x3f); + pci_write_config32 (controller->bus_address, 0xc0, 0x8f00); + + UHCI_INST (controller)->framelistptr = memalign (0x1000, 1024 * sizeof (flistp_t *)); /* 4kb aligned to 4kb */ + memset (UHCI_INST (controller)->framelistptr, 0, + 1024 * sizeof (flistp_t)); + + UHCI_INST (controller)->qh_intr = memalign (16, sizeof (qh_t)); + UHCI_INST (controller)->qh_data = memalign (16, sizeof (qh_t)); + UHCI_INST (controller)->qh_last = memalign (16, sizeof (qh_t)); + + UHCI_INST (controller)->qh_intr->headlinkptr.ptr = + virt_to_phys (UHCI_INST (controller)->qh_data); + UHCI_INST (controller)->qh_intr->headlinkptr.queue_head = 1; + UHCI_INST (controller)->qh_intr->elementlinkptr.ptr = 0; + UHCI_INST (controller)->qh_intr->elementlinkptr.terminate = 1; + + UHCI_INST (controller)->qh_data->headlinkptr.ptr = + virt_to_phys (UHCI_INST (controller)->qh_last); + UHCI_INST (controller)->qh_data->headlinkptr.queue_head = 1; + UHCI_INST (controller)->qh_data->elementlinkptr.ptr = 0; + UHCI_INST (controller)->qh_data->elementlinkptr.terminate = 1; + + UHCI_INST (controller)->qh_last->headlinkptr.ptr = 0; + UHCI_INST (controller)->qh_last->headlinkptr.terminate = 1; + UHCI_INST (controller)->qh_last->elementlinkptr.ptr = 0; + UHCI_INST (controller)->qh_last->elementlinkptr.terminate = 1; + + for (i = 0; i < 1024; i++) { + UHCI_INST (controller)->framelistptr[i].ptr = + virt_to_phys (UHCI_INST (controller)->qh_intr); + UHCI_INST (controller)->framelistptr[i].terminate = 0; + UHCI_INST (controller)->framelistptr[i].queue_head = 1; + } + for (i = 1; i < 128; i++) { + init_device_entry (controller, i); + } + controller->devices[0].controller = controller; + controller->devices[0].init = uhci_rh_init; + controller->devices[0].init (&controller->devices[0]); + uhci_reset (controller); + return controller; +} + +static void +uhci_shutdown (hci_t *controller) +{ + if (controller == 0) + return; + detach_controller (controller); + UHCI_INST (controller)->roothub->destroy (UHCI_INST (controller)-> + roothub); + uhci_reg_mask16 (controller, USBCMD, 0, 0); // stop work + free (UHCI_INST (controller)->framelistptr); + free (UHCI_INST (controller)->qh_intr); + free (UHCI_INST (controller)->qh_data); + free (UHCI_INST (controller)->qh_last); + free (UHCI_INST (controller)); + free (controller); +} + +static void +uhci_start (hci_t *controller) +{ + uhci_reg_mask16 (controller, USBCMD, ~0, 1); // start work on schedule +} + +static void +uhci_stop (hci_t *controller) +{ + uhci_reg_mask16 (controller, USBCMD, ~1, 0); // stop work on schedule +} + +#define GET_TD(x) ((void*)(((unsigned int)(x))&~0xf)) + +static td_t * +wait_for_completed_qh (hci_t *controller, qh_t *qh) +{ + int timeout = 1000; /* max 30 ms. */ + void *current = GET_TD (qh->elementlinkptr.ptr); + while ((qh->elementlinkptr.terminate == 0) && (timeout-- > 0)) { + if (current != GET_TD (qh->elementlinkptr.ptr)) { + current = GET_TD (qh->elementlinkptr.ptr); + timeout = 1000; + } + uhci_reg_mask16 (controller, USBSTS, ~0, 0); // clear resettable registers + udelay (30); + } + return (GET_TD (qh->elementlinkptr.ptr) == + 0) ? 0 : GET_TD (phys_to_virt (qh->elementlinkptr.ptr)); +} + +static void +wait_for_completed_td (hci_t *controller, td_t *td) +{ + int timeout = 10000; + while ((td->status_active == 1) + && ((uhci_reg_read16 (controller, USBSTS) & 2) == 0) + && (timeout-- > 0)) { + uhci_reg_mask16 (controller, USBSTS, ~0, 0); // clear resettable registers + udelay (10); + } +} + +static int +maxlen (int size) +{ + return (size - 1) & 0x7ff; +} + +static int +min (int a, int b) +{ + if (a < b) + return a; + else + return b; +} + +static int +uhci_control (usbdev_t *dev, pid_t dir, int drlen, void *devreq, int dalen, + unsigned char *data) +{ + int endp = 0; /* this is control: always 0 */ + int mlen = dev->endpoints[0].maxpacketsize; + int count = (2 + (dalen + mlen - 1) / mlen); + unsigned short req = ((unsigned short *) devreq)[0]; + int i; + td_t *tds = memalign (16, sizeof (td_t) * count); + memset (tds, 0, sizeof (td_t) * count); + count--; /* to compensate for 0-indexed array */ + for (i = 0; i < count; i++) { + tds[i].ptr = virt_to_phys (&tds[i + 1]); + tds[i].depth_first = 1; + tds[i].terminate = 0; + } + tds[count].ptr = 0; + tds[count].depth_first = 1; + tds[count].terminate = 1; + + tds[0].pid = SETUP; + tds[0].dev_addr = dev->address; + tds[0].endp = endp; + tds[0].maxlen = maxlen (drlen); + tds[0].counter = 3; + tds[0].data_toggle = 0; + tds[0].lowspeed = dev->lowspeed; + tds[0].bufptr = virt_to_phys (devreq); + tds[0].status_active = 1; + + int toggle = 1; + for (i = 1; i < count; i++) { + tds[i].pid = dir; + tds[i].dev_addr = dev->address; + tds[i].endp = endp; + tds[i].maxlen = maxlen (min (mlen, dalen)); + tds[i].counter = 3; + tds[i].data_toggle = toggle; + tds[i].lowspeed = dev->lowspeed; + tds[i].bufptr = virt_to_phys (data); + tds[i].status_active = 1; + toggle ^= 1; + dalen -= mlen; + data += mlen; + } + + tds[count].pid = (dir == OUT) ? IN : OUT; + tds[count].dev_addr = dev->address; + tds[count].endp = endp; + tds[count].maxlen = maxlen (0); + tds[count].counter = 0; /* as per linux 2.4.10 */ + tds[count].data_toggle = 1; + tds[count].lowspeed = dev->lowspeed, tds[count].bufptr = 0; + tds[count].status_active = 1; + UHCI_INST (dev->controller)->qh_data->elementlinkptr.ptr = + virt_to_phys (tds); + UHCI_INST (dev->controller)->qh_data->elementlinkptr.queue_head = 0; + UHCI_INST (dev->controller)->qh_data->elementlinkptr.terminate = 0; + td_t *td = wait_for_completed_qh (dev->controller, + UHCI_INST (dev->controller)-> + qh_data); + int result; + if (td == 0) { + result = 0; + } else { + printf ("control packet, req %x\n", req); + td_dump (td); + result = 1; + } + free (tds); + return result; +} + +static int +uhci_packet (usbdev_t *dev, int endp, int pid, int toggle, int length, + unsigned char *data) +{ + static td_t *td = 0; + if (td == 0) + td = memalign (16, sizeof (td_t)); + + memset (td, 0, sizeof (td_t)); + td->ptr = 0; + td->terminate = 1; + td->queue_head = 0; + + td->pid = pid; + td->dev_addr = dev->address; + td->endp = endp & 0xf; + td->maxlen = maxlen (length); + if (pid == SETUP) + td->counter = 3; + else + td->counter = 0; + td->data_toggle = toggle & 1; + td->lowspeed = dev->lowspeed; + td->bufptr = virt_to_phys (data); + + td->status_active = 1; + + UHCI_INST (dev->controller)->qh_data->elementlinkptr.ptr = + virt_to_phys (td); + UHCI_INST (dev->controller)->qh_data->elementlinkptr.queue_head = 0; + UHCI_INST (dev->controller)->qh_data->elementlinkptr.terminate = 0; + wait_for_completed_td (dev->controller, td); + if ((td->status & 0x7f) == 0) { + //printf("successfully sent a %x packet to %x.%x\n",pid, dev->address,endp); + // success + return 0; + } else { + td_dump (td); + return 1; + } +} + +static td_t * +create_schedule (int numpackets) +{ + if (numpackets == 0) + return 0; + td_t *tds = memalign (16, sizeof (td_t) * numpackets); + memset (tds, 0, sizeof (td_t) * numpackets); + int i; + for (i = 0; i < numpackets; i++) { + tds[i].ptr = virt_to_phys (&tds[i + 1]); + tds[i].terminate = 0; + tds[i].queue_head = 0; + tds[i].depth_first = 1; + } + tds[numpackets - 1].ptr = 0; + tds[numpackets - 1].terminate = 1; + tds[numpackets - 1].queue_head = 0; + tds[numpackets - 1].depth_first = 0; + return tds; +} + +static void +fill_schedule (td_t *td, endpoint_t *ep, int length, unsigned char *data, + int *toggle) +{ + td->pid = ep->direction; + td->dev_addr = ep->dev->address; + td->endp = ep->endpoint & 0xf; + td->maxlen = maxlen (length); + if (ep->direction == SETUP) + td->counter = 3; + else + td->counter = 0; + td->data_toggle = *toggle & 1; + td->lowspeed = ep->dev->lowspeed; + td->bufptr = virt_to_phys (data); + + td->status_active = 1; + *toggle ^= 1; +} + +static int +run_schedule (usbdev_t *dev, td_t *td) +{ + UHCI_INST (dev->controller)->qh_data->elementlinkptr.ptr = + virt_to_phys (td); + UHCI_INST (dev->controller)->qh_data->elementlinkptr.queue_head = 0; + UHCI_INST (dev->controller)->qh_data->elementlinkptr.terminate = 0; + td = wait_for_completed_qh (dev->controller, + UHCI_INST (dev->controller)->qh_data); + if (td == 0) { + return 0; + } else { + td_dump (td); + return 1; + } +} + +/* finalize == 1: if data is of packet aligned size, add a zero length packet */ +static int +uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize) +{ + int maxpsize = ep->maxpacketsize; + if (maxpsize == 0) + fatal ("MaxPacketSize == 0!!!"); + int numpackets = (size + maxpsize - 1 + finalize) / maxpsize; + if (numpackets == 0) + return 0; + td_t *tds = create_schedule (numpackets); + int i = 0, toggle = ep->toggle; + while ((size > 0) || ((size == 0) && (finalize != 0))) { + fill_schedule (&tds[i], ep, min (size, maxpsize), data, + &toggle); + i++; + data += maxpsize; + size -= maxpsize; + } + if (run_schedule (ep->dev, tds) == 1) { + clear_stall (ep); + free (tds); + return 1; + } + ep->toggle = toggle; + free (tds); + return 0; +} + +void +uhci_reg_write32 (hci_t *ctrl, usbreg reg, u32 value) +{ + outl (value, ctrl->reg_base + reg); +} + +u32 +uhci_reg_read32 (hci_t *ctrl, usbreg reg) +{ + return inl (ctrl->reg_base + reg); +} + +void +uhci_reg_write16 (hci_t *ctrl, usbreg reg, u16 value) +{ + outw (value, ctrl->reg_base + reg); +} + +u16 +uhci_reg_read16 (hci_t *ctrl, usbreg reg) +{ + return inw (ctrl->reg_base + reg); +} + +void +uhci_reg_write8 (hci_t *ctrl, usbreg reg, u8 value) +{ + outb (value, ctrl->reg_base + reg); +} + +u8 +uhci_reg_read8 (hci_t *ctrl, usbreg reg) +{ + return inb (ctrl->reg_base + reg); +} + +void +uhci_reg_mask32 (hci_t *ctrl, usbreg reg, u32 andmask, u32 ormask) +{ + uhci_reg_write32 (ctrl, reg, + (uhci_reg_read32 (ctrl, reg) & andmask) | ormask); +} + +void +uhci_reg_mask16 (hci_t *ctrl, usbreg reg, u16 andmask, u16 ormask) +{ + uhci_reg_write16 (ctrl, reg, + (uhci_reg_read16 (ctrl, reg) & andmask) | ormask); +} + +void +uhci_reg_mask8 (hci_t *ctrl, usbreg reg, u8 andmask, u8 ormask) +{ + uhci_reg_write8 (ctrl, reg, + (uhci_reg_read8 (ctrl, reg) & andmask) | ormask); +} Index: drivers/usb/usbhub.c =================================================================== --- drivers/usb/usbhub.c (revision 0) +++ drivers/usb/usbhub.c (revision 0) @@ -0,0 +1,158 @@ +/* + * 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. + */ + +#include "usb.h" + +// assume that host_to_device is overwritten if necessary +#define DR_PORT gen_bmRequestType(host_to_device, class_type, other_recp) +#define PORT_RESET 0x4 +#define PORT_POWER 0x8 + +typedef struct { + int num_ports; + int *ports; + hub_descriptor_t *descriptor; +} usbhub_inst_t; + +#define HUB_INST(dev) ((usbhub_inst_t*)(dev)->data) + +static void +usb_hub_destroy (usbdev_t *dev) +{ + free (HUB_INST (dev)->ports); + free (HUB_INST (dev)->descriptor); + free (HUB_INST (dev)); +} + +static void +usb_hub_scanport (usbdev_t *dev, int port) +{ + int newdev; + unsigned short buf[2]; + usbdev_t *newdev_t; + + get_status (dev, port, DR_PORT, 4, buf); + int portstatus = ((buf[0] & 1) == 0); + int datastatus = (HUB_INST (dev)->ports[port] == -1); + if (portstatus == datastatus) + return; // no change - FIXME: read right fields for that test + + if (!datastatus) { + int devno = HUB_INST (dev)->ports[port]; + if (devno == -1) + fatal ("FATAL: illegal devno!\n"); + dev->controller->devices[devno].destroy (&dev->controller-> + devices[devno]); + init_device_entry (dev->controller, devno); + HUB_INST (dev)->ports[port] = -1; + return; + } + + set_feature (dev, port, PORT_RESET, DR_PORT); + mdelay (20); + + get_status (dev, port, DR_PORT, 4, buf); + int lowspeed = (buf[0] >> 9) & 1; + + newdev = set_address (dev->controller, lowspeed); + if (newdev == -1) + return; + newdev_t = &dev->controller->devices[newdev]; + + HUB_INST (dev)->ports[port] = newdev; + newdev_t->address = newdev; + newdev_t->hub = dev->address; + newdev_t->port = port; + // determine responsible driver + newdev_t->init (newdev_t); +} + +static int +usb_hub_report_port_changes (usbdev_t *dev) +{ + int port; + unsigned short buf[2]; + for (port = 1; port <= HUB_INST (dev)->num_ports; port++) { + get_status (dev, port, DR_PORT, 4, buf); + // FIXME: proper change detection + int portstatus = ((buf[0] & 1) == 0); + int datastatus = (HUB_INST (dev)->ports[port] == -1); + if (portstatus != datastatus) + return port; + } + +// no change + return -1; +} + +static void +usb_hub_enable_port (usbdev_t *dev, int port) +{ + set_feature (dev, port, PORT_POWER, DR_PORT); + mdelay (20); +} + +#if 0 +static void +usb_hub_disable_port (usbdev_t *dev, int port) +{ +} +#endif + +static void +usb_hub_poll (usbdev_t *dev) +{ + int port; + while ((port = usb_hub_report_port_changes (dev)) != -1) + usb_hub_scanport (dev, port); +} + +void +usb_hub_init (usbdev_t *dev) +{ + int i; + dev->destroy = usb_hub_destroy; + dev->poll = usb_hub_poll; + + dev->data = malloc (sizeof (usbhub_inst_t)); + + HUB_INST (dev)->descriptor = + (hub_descriptor_t *) get_descriptor (dev, + gen_bmRequestType + (device_to_host, + class_type, dev_recp), + 0x29, 0, 0); + HUB_INST (dev)->num_ports = HUB_INST (dev)->descriptor->bNbrPorts; + HUB_INST (dev)->ports = + malloc (sizeof (int) * (HUB_INST (dev)->num_ports + 1)); + for (i = 1; i <= HUB_INST (dev)->num_ports; i++) + HUB_INST (dev)->ports[i] = -1; + for (i = 1; i <= HUB_INST (dev)->num_ports; i++) + usb_hub_enable_port (dev, i); +} Index: drivers/usb/usb.c =================================================================== --- drivers/usb/usb.c (revision 0) +++ drivers/usb/usb.c (revision 0) @@ -0,0 +1,347 @@ +/* + * 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. + */ + +#include <config.h> +#include "usb.h" + +hci_t *usb_hcs = 0; + +hci_t * +new_controller () +{ + hci_t *controller = malloc (sizeof (hci_t)); + + /* atomic */ + controller->next = usb_hcs; + usb_hcs = controller; + /* atomic end */ + + return controller; +} + +void +detach_controller (hci_t *controller) +{ + if (controller == 0) + return; + if (usb_hcs == controller) { + usb_hcs = controller->next; + } else { + hci_t *it = usb_hcs; + while (it != 0) { + if (it->next == controller) { + it->next = controller->next; + return; + } + } + } +} + +void +usb_poll () +{ + if (usb_hcs == 0) + return; + hci_t *controller = usb_hcs; + while (controller != 0) { + int i; + for (i = 0; i < 128; i++) { + if (controller->devices[i].address != -1) { + controller->devices[i].poll (&controller-> + devices[i]); + } + } + controller = controller->next; + } +} + +void +init_device_entry (hci_t *controller, int i) +{ + controller->devices[i].controller = controller; + controller->devices[i].address = -1; + controller->devices[i].hub = -1; + controller->devices[i].port = -1; + controller->devices[i].init = usb_nop_init; + controller->devices[i].init (&controller->devices[i]); +} + +void +set_feature (usbdev_t *dev, int endp, int feature, int rtype) +{ + dev_req_t dr; + + dr.bmRequestType = rtype; + dr.data_dir = host_to_device; + dr.bRequest = SET_FEATURE; + dr.wValue = feature; + dr.wIndex = endp; + dr.wLength = 0; + dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0); +} + +void +get_status (usbdev_t *dev, int intf, int rtype, int len, void *data) +{ + dev_req_t dr; + + dr.bmRequestType = rtype; + dr.data_dir = device_to_host; + dr.bRequest = GET_STATUS; + dr.wValue = 0; + dr.wIndex = intf; + dr.wLength = len; + dev->controller->control (dev, IN, sizeof (dr), &dr, len, data); +} + +u8 * +get_descriptor (usbdev_t *dev, unsigned char bmRequestType, int descType, + int descIdx, int langID) +{ + u8 buf[8]; + u8 *result; + dev_req_t dr; + int size; + + dr.bmRequestType = bmRequestType; + dr.data_dir = device_to_host; // always like this for descriptors + dr.bRequest = GET_DESCRIPTOR; + dr.wValue = (descType << 8) | descIdx; + dr.wIndex = langID; + dr.wLength = 8; + if (dev->controller->control (dev, IN, sizeof (dr), &dr, 8, buf)) { + printf ("getting descriptor size (type %x) failed\n", + descType); + } + + if (descType == 1) { + device_descriptor_t *dd = (device_descriptor_t *) buf; + printf ("maxPacketSize0: %x\n", dd->bMaxPacketSize0); + if (dd->bMaxPacketSize0 != 0) + dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0; + } + + /* special case for configuration descriptors: they carry all their + subsequent descriptors with them, and keep the entire size at a + different location */ + size = buf[0]; + if (buf[1] == 2) { + int realsize = ((unsigned short *) (buf + 2))[0]; + size = realsize; + } + result = malloc (size); + memset (result, 0, size); + dr.wLength = size; + if (dev->controller-> + control (dev, IN, sizeof (dr), &dr, size, result)) { + printf ("getting descriptor (type %x, size %x) failed\n", + descType, size); + } + + return result; +} + +void +set_configuration (usbdev_t *dev) +{ + dev_req_t dr; + + dr.bmRequestType = 0; + dr.bRequest = SET_CONFIGURATION; + dr.wValue = dev->configuration[5]; + dr.wIndex = 0; + dr.wLength = 0; + dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0); +} + +int +clear_stall (endpoint_t *ep) +{ + usbdev_t *dev = ep->dev; + int endp = ep->endpoint; + dev_req_t dr; + + dr.bmRequestType = 0; + if (endp != 0) { + dr.req_recp = endp_recp; + } + dr.bRequest = CLEAR_FEATURE; + dr.wValue = ENDPOINT_HALT; + dr.wIndex = endp; + dr.wLength = 0; + dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0); + return 0; +} + +/* returns free address or -1 */ +static int +get_free_address (hci_t *controller) +{ + int i; + for (i = 1; i < 128; i++) { + if (controller->devices[i].address != i) + return i; + } + printf ("no free address found\n"); + return -1; // no free address +} + +int +set_address (hci_t *controller, int lowspeed) +{ + int adr = get_free_address (controller); // address to set + dev_req_t dr; + configuration_descriptor_t *cd; + device_descriptor_t *dd; + + memset (&dr, 0, sizeof (dr)); + dr.data_dir = host_to_device; + dr.req_type = standard_type; + dr.req_recp = dev_recp; + dr.bRequest = SET_ADDRESS; + dr.wValue = adr; + dr.wIndex = 0; + dr.wLength = 0; + + usbdev_t *dev = &controller->devices[adr]; + // dummy values for registering the address + dev->address = 0; + dev->lowspeed = lowspeed; + dev->endpoints[0].dev = dev; + dev->endpoints[0].endpoint = 0; + dev->endpoints[0].maxpacketsize = 8; + dev->endpoints[0].toggle = 0; + dev->endpoints[0].direction = SETUP; + mdelay (50); + if (dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0)) { + printf ("set_address failed\n"); + return -1; + } + mdelay (50); + dev->address = adr; + dev->descriptor = + get_descriptor (dev, + gen_bmRequestType (device_to_host, + standard_type, dev_recp), + 1, 0, 0); + dd = (device_descriptor_t *) dev->descriptor; + printf ("device version: %x.%x\n", dd->bcdUSB >> 8, + dd->bcdUSB & 0xff); + printf ("device has %x configurations\n", dd->bNumConfigurations); + if (dd->bNumConfigurations == 0) { + /* device isn't usable */ + printf ("no usable configuration!\n"); + dev->address = 0; + return -1; + } + dev->configuration = + get_descriptor (dev, + gen_bmRequestType (device_to_host, + standard_type, dev_recp), + 2, 0, 0); + cd = (configuration_descriptor_t *) dev->configuration; + set_configuration (dev); + interface_descriptor_t *interface = + (interface_descriptor_t *) (((char *) cd) + cd->bLength); + { + int i; + int num = cd->bNumInterfaces; + interface_descriptor_t *current = interface; + printf ("device has %x interfaces\n", num); + num = (num > 5) ? 5 : num; + for (i = 0; i < num; i++) { + int j; + printf (" #%x has %x endpoints, interface %x:%x, protocol %x\n", current->bInterfaceNumber, current->bNumEndpoints, current->bInterfaceClass, current->bInterfaceSubClass, current->bInterfaceProtocol); + endpoint_descriptor_t *endp = + (endpoint_descriptor_t *) (((char *) current) + + + current->bLength); + if (interface->bInterfaceClass == 0x3) + endp = (endpoint_descriptor_t *) (((char *) endp) + ((char *) endp)[0]); // ignore HID descriptor + memset (dev->endpoints, 0, sizeof (dev->endpoints)); + dev->num_endp = 1; // 0 always exists + dev->endpoints[0].dev = dev; + dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0; + dev->endpoints[0].direction = SETUP; + dev->endpoints[0].type = CONTROL; + for (j = 1; j <= current->bNumEndpoints; j++) { + static const char *transfertypes[4] = + { "control", "isochronous", "bulk", + "interrupt" + }; + printf (" #%x: Endpoint %x (%s), max packet size %x, type %s\n", j, endp->bEndpointAddress & 0x7f, ((endp->bEndpointAddress & 0x80) != 0) ? "in" : "out", endp->wMaxPacketSize, transfertypes[endp->bmAttributes]); + endpoint_t *ep = + &dev->endpoints[dev->num_endp++]; + ep->dev = dev; + ep->endpoint = endp->bEndpointAddress; + ep->toggle = 0; + ep->maxpacketsize = endp->wMaxPacketSize; + ep->direction = + ((endp->bEndpointAddress & 0x80) == + 0) ? OUT : IN; + ep->type = endp->bmAttributes; + endp = (endpoint_descriptor_t + *) (((char *) endp) + endp->bLength); + } + current = (interface_descriptor_t *) endp; + } + } + int class = dd->bDeviceClass; + if (class == 0) + class = interface->bInterfaceClass; + + enum { hid_device = 0x3, msc_device = 0x8, hub_device = 0x9 }; + + printf ("device of class %x found\n", class); + if (class == hub_device) { + printf ("hub found\n"); +#ifdef CONFIG_USB_HUB + controller->devices[adr].init = usb_hub_init; +#else + printf ("support not compiled in\n"); +#endif + } + if (class == hid_device) { + printf ("HID found\n"); +#ifdef CONFIG_USB_HID + controller->devices[adr].init = usb_hid_init; +#else + printf ("support not compiled in\n"); +#endif + } + if (class == msc_device) { + printf ("MSC found\n"); +#ifdef CONFIG_USB_MSC + controller->devices[adr].init = usb_msc_init; +#else + printf ("support not compiled in\n"); +#endif + } + return adr; +} Index: drivers/usb/usbdisk.h =================================================================== --- drivers/usb/usbdisk.h (revision 0) +++ drivers/usb/usbdisk.h (revision 0) @@ -0,0 +1,37 @@ +/* + * 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 __USBDISK_H +#define __USBDISK_H +#include "usb.h" + +void usbdisk_create (usbdev_t *dev); +void usbdisk_remove (usbdev_t *dev); + +#endif Index: drivers/usb/usbmsc.c =================================================================== --- drivers/usb/usbmsc.c (revision 0) +++ drivers/usb/usbmsc.c (revision 0) @@ -0,0 +1,389 @@ +/* + * 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. + */ + +#include <arch/endian.h> +#include "usb.h" +#include "usbmsc.h" +#include "usbdisk.h" + +enum { + msc_subclass_rbc = 0x1, + msc_subclass_mmc2 = 0x2, + msc_subclass_qic157 = 0x3, + msc_subclass_ufi = 0x4, + msc_subclass_sff8070i = 0x5, + msc_subclass_scsitrans = 0x6 +}; +static const char *msc_subclass_strings[7] = { + "(none)", + "RBC", + "MMC-2", + "QIC-157", + "UFI", + "SFF-8070i", + "SCSI transparent" +}; +enum { + msc_proto_cbi_wcomp = 0x0, + msc_proto_cbi_wocomp = 0x1, + msc_proto_bulk_only = 0x50 +}; +static const char *msc_protocol_strings[0x51] = { + "Control/Bulk/Interrupt protocol (with command completion interrupt)", + "Control/Bulk/Interrupt protocol (with no command completion interrupt)", + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "Bulk-Only Transport" +}; + + +static void +usb_msc_destroy (usbdev_t *dev) +{ + usbdisk_remove (dev); + free (dev->data); + dev->data = 0; +} + +static void +usb_msc_poll (usbdev_t *dev) +{ +} + +const int DEV_RESET = 0xff; +const int GET_MAX_LUN = 0xfe; + +const unsigned int cbw_signature = 0x43425355; +const unsigned int csw_signature = 0x53425355; + +typedef struct { + unsigned int dCBWSignature; + unsigned int dCBWTag; + unsigned int dCBWDataTransferLength; + unsigned char bmCBWFlags; + unsigned long bCBWLUN:4; + unsigned long:4; + unsigned long bCBWCBLength:5; + unsigned long:3; + unsigned char CBWCB[31 - 15]; +} __attribute__ ((packed)) + cbw_t; + + typedef struct { + unsigned int dCSWSignature; + unsigned int dCSWTag; + unsigned int dCSWDataResidue; + unsigned char bCSWStatus; + } __attribute__ ((packed)) + csw_t; + + static void + reset_transport (usbdev_t *dev) +{ + dev_req_t dr; + memset (&dr, 0, sizeof (dr)); + dr.bmRequestType = 0; + dr.data_dir = host_to_device; +#ifndef QEMU + dr.req_type = class_type; + dr.req_recp = iface_recp; +#endif + dr.bRequest = DEV_RESET; + dr.wValue = 0; + dr.wIndex = 0; + dr.wLength = 0; + dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0); + clear_stall (MSC_INST (dev)->bulk_in); + clear_stall (MSC_INST (dev)->bulk_out); +} + +/* device may stall this command, so beware! */ +static int +get_max_luns (usbdev_t *dev) +{ + unsigned char luns = 75; + dev_req_t dr; + dr.bmRequestType = 0; + dr.data_dir = device_to_host; +#ifndef QEMU + dr.req_type = class_type; + dr.req_recp = iface_recp; +#endif + dr.bRequest = GET_MAX_LUN; + dr.wValue = 0; + dr.wIndex = 0; + dr.wLength = 1; + if (dev->controller->control (dev, IN, sizeof (dr), &dr, 1, &luns)) { + luns = 0; // assume only 1 lun if req fails + } + return luns; +} + +int tag; +int lun = 0; + +static void +wrap_cbw (cbw_t *cbw, int datalen, cbw_direction dir, const u8 *cmd, + int cmdlen) +{ + memset (cbw, 0, sizeof (cbw_t)); + + cbw->dCBWSignature = cbw_signature; + cbw->dCBWTag = tag++; + cbw->bCBWLUN = lun; // static value per device + + cbw->dCBWDataTransferLength = datalen; + cbw->bmCBWFlags = dir; + memcpy (cbw->CBWCB, cmd, sizeof (cbw->CBWCB)); + cbw->bCBWCBLength = cmdlen; +} + +static void +get_csw (endpoint_t *ep, csw_t *csw) +{ + ep->dev->controller->bulk (ep, sizeof (csw_t), (u8 *) csw, 1); +} + +static int +execute_command (usbdev_t *dev, cbw_direction dir, const u8 *cb, int cblen, + u8 *buf, int buflen) +{ + cbw_t cbw; + csw_t csw; + + int always_succeed = 0; + if ((cb[0] == 0x1b) && (cb[4] == 1)) { //start command, always succeed + always_succeed = 1; + } + wrap_cbw (&cbw, buflen, dir, cb, cblen); + if (dev->controller-> + bulk (MSC_INST (dev)->bulk_out, sizeof (cbw), (u8 *) &cbw, 0)) { + clear_stall (MSC_INST (dev)->bulk_out); + return 1; + } + mdelay (10); + if (dir == cbw_direction_data_in) { + if (dev->controller-> + bulk (MSC_INST (dev)->bulk_in, buflen, buf, 0)) { + clear_stall (MSC_INST (dev)->bulk_in); + return 1; + } + } else { + if (dev->controller-> + bulk (MSC_INST (dev)->bulk_out, buflen, buf, 0)) { + clear_stall (MSC_INST (dev)->bulk_out); + return 1; + } + } + get_csw (MSC_INST (dev)->bulk_in, &csw); + if (always_succeed == 1) { + // return success, regardless of message + return 0; + } + if (csw.bCSWStatus == 2) { + // phase error, reset transport + reset_transport (dev); + return 1; + } + if (csw.bCSWStatus == 0) { + // no error, exit + return 0; + } + // error "check condition" or reserved error + return 1; +} + +typedef struct { + unsigned char command; //0 + unsigned char res1; //1 + unsigned int block; //2-5 + unsigned char res2; //6 + unsigned short numblocks; //7-8 + unsigned char res3; //9 - the block is 10 bytes long +} __attribute__ ((packed)) cmdblock_t; + +typedef struct { + unsigned char command; //0 + unsigned char res1; //1 + unsigned char res2; //2 + unsigned char res3; //3 + unsigned char lun; //4 + unsigned char res4; //5 +} __attribute__ ((packed)) cmdblock6_t; + + +/* returns success code (see execute_command) + buf must be preallocated to at least n*512 bytes! + limited to 2TB (READ(10) command) +*/ +int +readwrite_blocks (usbdev_t *dev, int start, int n, cbw_direction dir, u8 *buf) +{ + cmdblock_t cb; + memset (&cb, 0, sizeof (cb)); + if (dir == cbw_direction_data_in) { + // read + cb.command = 0x28; + } else { + // write + cb.command = 0x2a; + } + cb.block = ntohl (start); + cb.numblocks = ntohw (n); + return execute_command (dev, dir, (u8 *) &cb, sizeof (cb), buf, + n * 512); +} + +static int +test_unit_ready (usbdev_t *dev) +{ + cmdblock6_t cb; + memset (&cb, 0, sizeof (cb)); // full initialization for T-U-R + return execute_command (dev, cbw_direction_data_out, (u8 *) &cb, + sizeof (cb), 0, 0); +} + +static int +spin_up (usbdev_t *dev) +{ + cmdblock6_t cb; + memset (&cb, 0, sizeof (cb)); + cb.command = 0x1b; + cb.lun = 1; + return execute_command (dev, cbw_direction_data_out, (u8 *) &cb, + sizeof (cb), 0, 0); +} + +static void +read_capacity (usbdev_t *dev) +{ + cmdblock_t cb; + memset (&cb, 0, sizeof (cb)); + cb.command = 0x25; // read capacity + u8 buf[8]; + int count = 0; + while ((count++ < 20) + && + (execute_command + (dev, cbw_direction_data_in, (u8 *) &cb, sizeof (cb), buf, + 8) == 1)); + if (count >= 20) { + // still not successful, assume 2tb in 512byte sectors, which is just the same garbage as any other number, but probably reasonable. + printf ("assuming 2TB in 512byte sectors as READ CAPACITY didn't answer.\n"); + MSC_INST (dev)->numblocks = 0xffffffff; + MSC_INST (dev)->blocksize = 512; + } else { + MSC_INST (dev)->numblocks = ntohl (*(u32 *) buf) + 1; + MSC_INST (dev)->blocksize = ntohl (*(u32 *) (buf + 4)); + } + printf (" has %d blocks sized %db\n", MSC_INST (dev)->numblocks, + MSC_INST (dev)->blocksize); +} + +void +usb_msc_init (usbdev_t *dev) +{ + int i, timeout; + + dev->destroy = usb_msc_destroy; + dev->poll = usb_msc_poll; + + configuration_descriptor_t *cd = + (configuration_descriptor_t *) dev->configuration; + interface_descriptor_t *interface = + (interface_descriptor_t *) (((char *) cd) + cd->bLength); + + printf (" it uses %s command set\n", + msc_subclass_strings[interface->bInterfaceSubClass]); + printf (" it uses %s protocol\n", + msc_protocol_strings[interface->bInterfaceProtocol]); + + if ((interface->bInterfaceProtocol != 0x50) + || (interface->bInterfaceSubClass != 6)) { + /* Other protocols, such as ATAPI don't seem to be very popular. looks like ATAPI would be really easy to add, if necessary. */ + printf (" Only SCSI over Bulk is supported.\n"); + return; + } + + dev->data = malloc (sizeof (usbmsc_inst_t)); + MSC_INST (dev)->bulk_in = 0; + MSC_INST (dev)->bulk_out = 0; + + for (i = 1; i <= dev->num_endp; i++) { + if (dev->endpoints[i].endpoint == 0) + continue; + if (dev->endpoints[i].type != BULK) + continue; + if ((dev->endpoints[i].direction == IN) + && (MSC_INST (dev)->bulk_in == 0)) + MSC_INST (dev)->bulk_in = &dev->endpoints[i]; + if ((dev->endpoints[i].direction == OUT) + && (MSC_INST (dev)->bulk_out == 0)) + MSC_INST (dev)->bulk_out = &dev->endpoints[i]; + } + + if (MSC_INST (dev)->bulk_in == 0) + fatal ("couldn't find bulk-in endpoint"); + if (MSC_INST (dev)->bulk_out == 0) + fatal ("couldn't find bulk-out endpoint"); + printf (" using endpoint %x as in, %x as out\n", + MSC_INST (dev)->bulk_in->endpoint, + MSC_INST (dev)->bulk_out->endpoint); + + printf (" has %d luns\n", get_max_luns (dev) + 1); + + printf (" Waiting for device to become ready... "); + timeout = 10; + while (test_unit_ready (dev) && --timeout) { + mdelay (100); + printf ("."); + } + if (test_unit_ready (dev)) { + printf ("timeout. Device not ready. Still trying...\n"); + } else { + printf ("ok.\n"); + } + + printf (" spin up"); + for (i = 0; i < 30; i++) { + printf ("."); + if (!spin_up (dev)) { + printf (" OK."); + break; + } + mdelay (100); + } + printf ("\n"); + + read_capacity (dev); + usbdisk_create (dev); +} Index: drivers/usb/uhci.h =================================================================== --- drivers/usb/uhci.h (revision 0) +++ drivers/usb/uhci.h (revision 0) @@ -0,0 +1,124 @@ +/* + * 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 __UHCI_H +#define __UHCI_H + +#include <pci.h> +#include "usb.h" + +typedef union { + struct { + unsigned long terminate:1; + unsigned long queue_head:1; + unsigned long:2; + unsigned long ptr_part:28; + }; + u32 ptr; +} __attribute__ ((packed)) flistp_t; + +typedef struct { + union { + struct { + unsigned long terminate:1; + unsigned long queue_head:1; + unsigned long depth_first:1; + unsigned long:29; + } __attribute__ ((packed)); + u32 ptr; + } __attribute__ ((packed)); + + volatile unsigned long actlen:11; + volatile unsigned long:5; + union { + struct { + unsigned long:1; // bit 0 + unsigned long status_bitstuff_err:1; + unsigned long status_crc_err:1; + unsigned long status_nakrcvd:1; + unsigned long status_babble:1; + unsigned long status_databuf_err:1; + unsigned long status_stalled:1; + unsigned long status_active:1; // bit 7 + } __attribute__ ((packed)); + unsigned char status; + } __attribute__ ((packed)); + volatile unsigned long ioc:1; /* interrupt on complete */ + volatile unsigned long isochronous:1; + volatile unsigned long lowspeed:1; + volatile unsigned long counter:2; + volatile unsigned long shortpck:1; + volatile unsigned long:2; + + unsigned long pid:8; + unsigned long dev_addr:7; + unsigned long endp:4; + unsigned long data_toggle:1; + unsigned long:1; + unsigned long maxlen:11; + + u32 bufptr; + +} __attribute__ ((packed)) + td_t; + + typedef struct { + flistp_t headlinkptr; + volatile flistp_t elementlinkptr; + } __attribute__ ((packed)) + qh_t; + + typedef enum { USBCMD = 0, USBSTS = 2, USBINTR = 4, FRNUM = + 6, FLBASEADD = 8, SOFMOD = 0xc, PORTSC1 = 0x10, PORTSC2 = + 0x12 + } usbreg; + + void uhci_reg_write32 (hci_t *ctrl, usbreg reg, u32 value); + u32 uhci_reg_read32 (hci_t *ctrl, usbreg reg); + void uhci_reg_write16 (hci_t *ctrl, usbreg reg, u16 value); + u16 uhci_reg_read16 (hci_t *ctrl, usbreg reg); + void uhci_reg_write8 (hci_t *ctrl, usbreg reg, u8 value); + u8 uhci_reg_read8 (hci_t *ctrl, usbreg reg); + void uhci_reg_mask32 (hci_t *ctrl, usbreg reg, u32 andmask, u32 ormask); + void uhci_reg_mask16 (hci_t *ctrl, usbreg reg, u16 andmask, u16 ormask); + void uhci_reg_mask8 (hci_t *ctrl, usbreg reg, u8 andmask, u8 ormask); + + typedef struct uhci { + flistp_t *framelistptr; + qh_t *qh_intr, *qh_data, *qh_last; + usbdev_t *roothub; + } uhci_t; + +#define UHCI_INST(controller) ((uhci_t*)((controller)->instance)) + + hci_t *uhci_init (pcidev_t addr); + + void uhci_rh_init (usbdev_t *dev); + +#endif Index: drivers/usb/usbinit.c =================================================================== --- drivers/usb/usbinit.c (revision 0) +++ drivers/usb/usbinit.c (revision 0) @@ -0,0 +1,110 @@ +/* + * 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. + */ + +#include <config.h> +#include "usb.h" +#include "uhci.h" +#include "usbdisk.h" + +int +usb_controller_initialize (int bus, int dev, int func) +{ + u32 class; + u32 devclass; + u32 prog_if; + pcidev_t addr; + u32 pciid; + + addr = PCI_DEV (bus, dev, func); + class = pci_read_config32 (addr, 8); + pciid = pci_read_config32 (addr, 0); + + devclass = class >> 16; + prog_if = (class >> 8) & 0xff; + + /* enable busmaster */ +#define PCI_COMMAND 4 +#define PCI_COMMAND_MASTER 4 + pci_write_config32 (addr, PCI_COMMAND, + pci_read_config32 (addr, + PCI_COMMAND) | + PCI_COMMAND_MASTER); + + if (devclass == 0xc03) { + printf ("%02x:%02x.%x %04x:%04x.%d ", 0, dev, func, + pciid >> 16, pciid & 0xFFFF, func); + if (prog_if == 0) { + printf ("UHCI controller\n"); +#ifdef CONFIG_USB_UHCI + uhci_init (addr); + usb_poll (); + usb_poll (); +#else + printf ("Not supported.\n"); +#endif + } + if (prog_if == 0x10) { + printf ("OHCI controller\n"); +#ifdef CONFIG_USB_OHCI + // ohci_init(addr); +#else + printf ("Not supported.\n"); +#endif + + } + if (prog_if == 0x20) { + printf ("EHCI controller\n"); +#ifdef CONFIG_USB_EHCI + // ehci_init(addr); +#else + printf ("Not supported.\n"); +#endif + + } + } + + return 0; +} + +int +usb_initialize (void) +{ + int bus, dev, func; + for (bus = 0; bus < 256; bus++) + for (dev = 0; dev < 32; dev++) + for (func = 0; func < 8; func++) + usb_controller_initialize (bus, dev, func); + return 0; +} + +int +usb_exit (void) +{ + return 0; +}
On 02/09/08 11:06 +0200, Patrick Georgi wrote:
Hi there,
This patch adds USB capabilities to libpayload. It requires some memalign implementation (eg. the one I sent yesterday). Features:
- UHCI controller driver
- UHCI root hub driver
- USB MSC (Mass Storage Class) driver
- skeleton of a USB HID driver (requires better interrupt transfer
handling, which is TODO)
- skeleton of a USB hub driver (needs several blank spots filled in,
eg. power management. Again: TODO)
OHCI and EHCI are not supported, though OHCI support should be rather easy as the stack provides reasonable abstractions (or so I hope). EHCI will probably be more complicated. Isochronous transfers (eg. webcams, audio stuff, ...) are not supported
- they can be, but I doubt we'll have a reason for that in the boot
environment.
The MSC driver was tested against a couple of USB flash drives, and should be reasonably tolerant by now. Though I probably underestimate the amount of bugs present in USB flash drives, so feedback is welcome.
Signed-off-by: Patrick Georgi patrick.georgi@coresystems.de
Acked-by: Jordan Crouse jordan.crouse@amd.com
Lets get this in and then we can hack on it from there. As with the previous patch, if you can document the external (payload visible) functions, that would help a lot.
Now all we need is the one true USB 1.1 interface (OHCI, natch!). :)
Index: Config.in
--- Config.in (revision 3557) +++ Config.in (working copy) @@ -132,5 +132,46 @@ bool "Support for PC speaker" default y
+config USB
- bool "USB Support"
- default n
+config USB_UHCI
- bool "Support for USB UHCI controllers"
- depends on USB
- help
Select this option if you are going to use USB 1.1 on an Intel based
system.
+config USB_OHCI
- bool "Support for USB OHCI controllers"
- depends on USB
- help
Select this option if you are going to use USB 1.1 on an AMD based
system.
NOTE: This option is not (fully) implemented yet
+config USB_EHCI
- bool "Support for USB EHCI controllers"
- depends on USB
- help
Select this option if you want to use USB 2.0
NOTE: This option is not (fully) implemented yet
+config USB_HID
- bool "Support for USB keyboards (broken)"
- depends on USB
- default n
+config USB_HUB
- bool "Support for USB hubs (broken)"
- depends on USB
- default n
+config USB_MSC
- bool "Support for USB storage"
- depends on USB
endmenu
Index: Makefile
--- Makefile (revision 3557) +++ Makefile (working copy) @@ -137,6 +137,7 @@ prepare: $(Q)mkdir -p $(obj)/util/kconfig/lxdialog $(Q)mkdir -p $(obj)/crypto $(obj)/curses $(obj)/drivers/video
- $(Q)mkdir -p $(obj)/drivers/usb $(Q)mkdir -p $(obj)/i386 $(obj)/lib/$(ARCHDIR-y) $(obj)/libc $(Q)mkdir -p $(src)/lib/$(ARCHDIR-y)
Index: drivers/Makefile.inc
--- drivers/Makefile.inc (revision 3557) +++ drivers/Makefile.inc (working copy) @@ -47,3 +47,14 @@ # Geode console drivers TARGETS-$(CONFIG_GEODE_VIDEO_CONSOLE) += drivers/video/geode.o TARGETS-$(CONFIG_GEODE_VIDEO_CONSOLE) += drivers/video/font8x16.o
+# USB stack +TARGETS-$(CONFIG_USB) += drivers/usb/usbinit.o +TARGETS-$(CONFIG_USB) += drivers/usb/usb.o +TARGETS-$(CONFIG_USB) += drivers/usb/usb_dev.o +TARGETS-$(CONFIG_USB_HUB) += drivers/usb/usbhub.o +TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci.o +TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci_rh.o +TARGETS-$(CONFIG_USB_HID) += drivers/usb/usbhid.o +TARGETS-$(CONFIG_USB_MSC) += drivers/usb/usbmsc.o
Index: drivers/usb/usb.h
--- drivers/usb/usb.h (revision 0) +++ drivers/usb/usb.h (revision 0) @@ -0,0 +1,224 @@ +/*
- 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:
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 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.
- 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.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;
+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 = 0x2d, IN = 0x69, OUT = 0xe1 } pid_t; +typedef enum { CONTROL = 0, ISOCHRONOUS = 1, BULK = 2, INTERRUPT = 3 +} endpoint_type;
+typedef struct {
- usbdev_t *dev;
- int endpoint;
- pid_t direction;
- int toggle;
- int maxpacketsize;
- endpoint_type type;
+} endpoint_t;
+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 lowspeed; // 1 if lowspeed device
- void *data;
- u8 *descriptor;
- u8 *configuration;
- void (*init) (usbdev_t *dev);
- void (*destroy) (usbdev_t *dev);
- void (*poll) (usbdev_t *dev);
+};
+struct usbdev_hc {
- struct usbdev_hc *next;
- pcidev_t bus_address;
- u32 reg_base;
- usbdev_t devices[128]; // dev 0 is root hub, 127 is last addressable
- void (*start) (hci_t *controller);
- void (*stop) (hci_t *controller);
- void (*reset) (hci_t *controller);
- void (*shutdown) (hci_t *controller);
- int (*packet) (usbdev_t *dev, int endp, int pid, int toggle,
int length, u8 *data);
- int (*bulk) (endpoint_t *ep, int size, u8 *data, int finalize);
- int (*control) (usbdev_t *dev, pid_t pid, int dr_length,
void *devreq, int data_length, u8 *data);
- void *instance;
+};
+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;
+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); +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);
+int set_address (hci_t *controller, int lowspeed);
+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;
+}
+#endif Index: drivers/usb/usbhid.c =================================================================== --- drivers/usb/usbhid.c (revision 0) +++ drivers/usb/usbhid.c (revision 0) @@ -0,0 +1,137 @@ +/*
- 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:
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 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.
- 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.
- */
+#include "usb.h"
+enum { hid_subclass_none = 0, hid_subclass_boot = 1 }; +enum { hid_proto_boot = 0, hid_proto_report = 1 }; +enum { hid_boot_proto_none = 0, hid_boot_proto_keyboard =
1, hid_boot_proto_mouse = 2
+}; +static const char *boot_protos[3] = { "(none)", "keyboard", "mouse" }; +enum { GET_REPORT = 0x1, GET_IDLE = 0x2, GET_PROTOCOL = 0x3, SET_REPORT =
0x9, SET_IDLE = 0xa, SET_PROTOCOL = 0xb
+};
+static void +usb_hid_destroy (usbdev_t *dev) +{ +}
+int keypress; +char keymap[256] = {
- -1, -1, -1, -1, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
- 'l',
- 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
- '1', '2',
- '3', '4', '5', '6', '7', '8', '9', '0', '\n', TERM_ESC,
- TERM_BACKSPACE, TERM_TAB, ' ', '-', '=', '[',
- ']', '\', -1, ';', ''', '`', ',', '.', '/', -1, -1, -1, -1, -1, -1,
- -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, TERM_HOME, TERM_PPAGE, -1,
- TERM_END, TERM_NPAGE, TERM_RIGHT,
- TERM_LEFT, TERM_DOWN, TERM_UP, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+};
+static void +usb_hid_poll (usbdev_t *dev) +{
- char buf[8];
- static int toggle = 0;
- // hardcode to endpoint 1, 8 bytes
- dev->controller->packet (dev, 1, IN, toggle, 8, buf);
- toggle ^= 1;
- // FIXME: manage buf[0]=special keys, too
- keypress = keymap[buf[2]];
- if ((keypress == -1) && (buf[2] != 0)) {
printf ("%x %x %x %x %x %x %x %x\n", buf[0], buf[1], buf[2],
buf[3], buf[4], buf[5], buf[6], buf[7]);
- }
+}
+int (*oldhook) (void);
+int +hookfunc (void) +{
- int key;
- if (oldhook != 0)
key = oldhook ();
- if (key == -1)
key = keypress;
- return key;
+}
+void +usb_hid_init (usbdev_t *dev) +{
- configuration_descriptor_t *cd = dev->configuration;
- interface_descriptor_t *interface = ((char *) cd) + cd->bLength;
- if (interface->bInterfaceSubClass == hid_subclass_boot) {
printf (" supports boot interface..\n");
printf (" it's a %s\n",
boot_protos[interface->bInterfaceProtocol]);
if (interface->bInterfaceProtocol == hid_boot_proto_keyboard) {
printf (" activating...\n");
dev_req_t dr;
// set_protocol(hid_proto_boot)
dr.data_dir = host_to_device;
dr.req_type = class_type;
dr.req_recp = iface_recp;
dr.bRequest = SET_PROTOCOL;
dr.wValue = hid_proto_boot;
dr.wIndex = interface->bInterfaceNumber;
dr.wLength = 0;
dev->controller->control (dev, OUT,
sizeof (dev_req_t), &dr, 0,
0);
// only add here, because we only support boot-keyboard HID devices
// FIXME: make this a real console input driver instead, once the API is there
dev->destroy = usb_hid_destroy;
dev->poll = usb_hid_poll;
oldhook = getkey_hook;
getkey_hook = hookfunc;
}
- }
+} Index: drivers/usb/uhci_rh.c =================================================================== --- drivers/usb/uhci_rh.c (revision 0) +++ drivers/usb/uhci_rh.c (revision 0) @@ -0,0 +1,182 @@ +/*
- 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:
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 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.
- 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.
- */
+#include <libpayload.h> +#include "uhci.h"
+typedef struct {
- int port[2];
+} rh_inst_t;
+#define RH_INST(dev) ((rh_inst_t*)(dev)->data)
+static void +uhci_rh_enable_port (usbdev_t *dev, int port) +{
- u16 value;
- hci_t *controller = dev->controller;
- if (port == 1)
port = PORTSC1;
- else
port = PORTSC2;
- uhci_reg_mask16 (controller, port, ~(1 << 12), 0); /* wakeup */
- uhci_reg_mask16 (controller, port, ~0, 1 << 9); /* reset */
- mdelay (30); // >10ms
- uhci_reg_mask16 (controller, port, ~(1 << 9), 0);
- mdelay (1); // >5.3us per spec, <3ms because some devices make trouble
- uhci_reg_mask16 (controller, port, ~0, 1 << 2); /* enable */
- do {
value = uhci_reg_read16 (controller, port);
mdelay (1);
- } while (((value & (1 << 2)) == 0) && (value & 0x01));
+}
+/* disable root hub */ +static void +uhci_rh_disable_port (usbdev_t *dev, int port) +{
- hci_t *controller = dev->controller;
- port = PORTSC2;
- if (port == 1)
port = PORTSC1;
- uhci_reg_mask16 (controller, port, ~4, 0);
- int value;
- do {
value = uhci_reg_read16 (controller, port);
mdelay (1);
- } while ((value & (1 << 2)) != 0);
+}
+static void +uhci_rh_scanport (usbdev_t *dev, int port) +{
- int portsc, offset;
- if (port == 1) {
portsc = PORTSC1;
offset = 0;
- } else if (port == 2) {
portsc = PORTSC2;
offset = 1;
- } else
return;
- int devno = RH_INST (dev)->port[offset];
- if (devno != -1) {
dev->controller->devices[devno].destroy (&dev->controller->
devices[devno]);
init_device_entry (dev->controller, devno);
RH_INST (dev)->port[offset] = -1;
- }
- uhci_reg_mask16 (dev->controller, portsc, ~0, (1 << 3) | (1 << 2)); // clear port state change, enable port
- if ((uhci_reg_read16 (dev->controller, portsc) & 1) != 0) {
int newdev;
usbdev_t *newdev_t;
// device attached
uhci_rh_disable_port (dev, port);
uhci_rh_enable_port (dev, port);
int lowspeed =
(uhci_reg_read16 (dev->controller, portsc) >> 8) & 1;
printf ("%sspeed device\n", (lowspeed == 1) ? "low" : "full");
newdev = set_address (dev->controller, lowspeed);
if (newdev == -1)
return;
newdev_t = &dev->controller->devices[newdev];
RH_INST (dev)->port[offset] = newdev;
newdev_t->address = newdev;
newdev_t->hub = dev->address;
newdev_t->port = portsc;
// determine responsible driver
newdev_t->init (newdev_t);
- }
+}
+static int +uhci_rh_report_port_changes (usbdev_t *dev) +{
- int stored, real;
- stored = (RH_INST (dev)->port[0] == -1);
- real = ((uhci_reg_read16 (dev->controller, PORTSC1) & 1) == 0);
- if (stored != real)
return 1;
- stored = (RH_INST (dev)->port[1] == -1);
- real = ((uhci_reg_read16 (dev->controller, PORTSC2) & 1) == 0);
- if (stored != real)
return 2;
+// maybe detach+attach happened between two scans?
- if ((uhci_reg_read16 (dev->controller, PORTSC1) & 2) > 0)
return 1;
- if ((uhci_reg_read16 (dev->controller, PORTSC2) & 2) > 0)
return 2;
+// no change
- return -1;
+}
+static void +uhci_rh_destroy (usbdev_t *dev) +{
- uhci_rh_disable_port (dev, 1);
- uhci_rh_disable_port (dev, 2);
- free (RH_INST (dev));
+}
+static void +uhci_rh_poll (usbdev_t *dev) +{
- int port;
- while ((port = uhci_rh_report_port_changes (dev)) != -1)
uhci_rh_scanport (dev, port);
+}
+void +uhci_rh_init (usbdev_t *dev) +{
- dev->destroy = uhci_rh_destroy;
- dev->poll = uhci_rh_poll;
- uhci_rh_enable_port (dev, 1);
- uhci_rh_enable_port (dev, 2);
- dev->data = malloc (sizeof (rh_inst_t));
- RH_INST (dev)->port[0] = -1;
- RH_INST (dev)->port[1] = -1;
- /* we can set them here because a root hub _really_ shouldn't
appear elsewhere */
- dev->address = 0;
- dev->hub = -1;
- dev->port = -1;
+} Index: drivers/usb/usbmsc.h =================================================================== --- drivers/usb/usbmsc.h (revision 0) +++ drivers/usb/usbmsc.h (revision 0) @@ -0,0 +1,47 @@ +/*
- 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:
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 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.
- 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 __USBMSC_H +#define __USBMSC_H +typedef struct {
- unsigned int blocksize;
- unsigned int numblocks;
- endpoint_t *bulk_in;
- endpoint_t *bulk_out;
+} usbmsc_inst_t;
+#define MSC_INST(dev) ((usbmsc_inst_t*)(dev)->data)
+typedef enum { cbw_direction_data_in = 0x80, cbw_direction_data_out = 0 +} cbw_direction;
+int readwrite_blocks (usbdev_t *dev, int start, int n, cbw_direction dir,
u8 *buf);
+#endif Index: drivers/usb/TODO =================================================================== --- drivers/usb/TODO (revision 0) +++ drivers/usb/TODO (revision 0) @@ -0,0 +1,22 @@ +- handle error conditions +- handle disconnect more gracefully (ie. make calling layer aware that the device doesn't exist somehow) +- usbhub:
- proper client enumeration (esp. detach)
- change detection
- power management
+- handle interrupts more cleverly:
- create a new queue for the interrupt with a couple of TD sequences,
- each ending with "breadth first" flag
- linked as a chain
- add that queue at the appropriate times in front of the default structure so the max latency is honored
- only one intr chain per framelist item, so it must be arranged appropriately
- reads from usb device just look at "invalidated" tds and the results they got
- handled tds get reactivated as a ring structure
- added as child of the oldest td
- queue header already dropped the td, so no issue there
- this setup ensures that:
- the max latency of the device is honored
- the client knows the right order of the data
- there is no need for an interrupt handler
- but must be polled at least max latency * num tds times -> more tds = less time pressure
Index: drivers/usb/usb_dev.c
--- drivers/usb/usb_dev.c (revision 0) +++ drivers/usb/usb_dev.c (revision 0) @@ -0,0 +1,55 @@ +/*
- 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:
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 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.
- 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.
- */
+#include "usb.h"
+static void +usb_nop_destroy (usbdev_t *dev) +{
- if (dev->descriptor != 0)
free (dev->descriptor);
- usb_nop_init (dev);
- dev->address = -1;
- dev->hub = -1;
- dev->port = -1;
+}
+static void +usb_nop_poll (usbdev_t *dev) +{
- return;
+}
+void +usb_nop_init (usbdev_t *dev) +{
- dev->descriptor = 0;
- dev->destroy = usb_nop_destroy;
- dev->poll = usb_nop_poll;
+} Index: drivers/usb/uhci.c =================================================================== --- drivers/usb/uhci.c (revision 0) +++ drivers/usb/uhci.c (revision 0) @@ -0,0 +1,507 @@ +/*
- 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:
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 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.
- 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.
- */
+#include "usb.h" +#include "uhci.h" +#include <arch/virtual.h>
+static void uhci_start (hci_t *controller); +static void uhci_stop (hci_t *controller); +static void uhci_reset (hci_t *controller); +static void uhci_shutdown (hci_t *controller); +static int uhci_packet (usbdev_t *dev, int endp, int pid, int toggle,
int length, u8 *data);
+static int uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize); +static int uhci_control (usbdev_t *dev, pid_t dir, int drlen, void *devreq,
int dalen, u8 *data);
+#if 0 +/* dump uhci */ +static void +uhci_dump (hci_t *controller) +{
- printf ("dump:\nUSBCMD: %x\n", uhci_reg_read16 (controller, USBCMD));
- printf ("USBSTS: %x\n", uhci_reg_read16 (controller, USBSTS));
- printf ("USBINTR: %x\n", uhci_reg_read16 (controller, USBINTR));
- printf ("FRNUM: %x\n", uhci_reg_read16 (controller, FRNUM));
- printf ("FLBASEADD: %x\n", uhci_reg_read32 (controller, FLBASEADD));
- printf ("SOFMOD: %x\n", uhci_reg_read8 (controller, SOFMOD));
- printf ("PORTSC1: %x\n", uhci_reg_read16 (controller, PORTSC1));
- printf ("PORTSC2: %x\n", uhci_reg_read16 (controller, PORTSC2));
+} +#endif
+static void +td_dump (td_t *td) +{
- printf ("%x packet (at %lx) to %x.%x failed\n", td->pid,
virt_to_phys (td), td->dev_addr, td->endp);
- printf ("td (counter at %x) returns: ", td->counter);
- printf (" bitstuff err: %x, ", td->status_bitstuff_err);
- printf (" CRC err: %x, ", td->status_crc_err);
- printf (" NAK rcvd: %x, ", td->status_nakrcvd);
- printf (" Babble: %x, ", td->status_babble);
- printf (" Data Buffer err: %x, ", td->status_databuf_err);
- printf (" Stalled: %x, ", td->status_stalled);
- printf (" Active: %x\n", td->status_active);
- if (td->status_babble)
printf (" Babble because of %s\n",
td->status_bitstuff_err ? "host" : "device");
- if (td->status_active)
printf (" still active - timeout?\n");
+}
+static void +uhci_reset (hci_t *controller) +{
- /* reset */
- uhci_reg_write16 (controller, USBCMD, 4);
- mdelay (50);
- uhci_reg_write16 (controller, USBCMD, 0);
- mdelay (10);
- uhci_reg_write16 (controller, USBCMD, 2);
- while ((uhci_reg_read16 (controller, USBCMD) & 2) != 0)
mdelay (1);
- uhci_reg_write32 (controller, FLBASEADD,
(u32) virt_to_phys (UHCI_INST (controller)->
framelistptr));
- //printf ("framelist at %p\n",UHCI_INST(controller)->framelistptr);
- /* disable irqs */
- uhci_reg_write16 (controller, USBINTR, 0);
- /* reset framelist index */
- uhci_reg_write16 (controller, FRNUM, 0);
- uhci_reg_mask16 (controller, USBCMD, ~0, 0xc0); // max packets, configure flag
- uhci_start (controller);
+}
+hci_t * +uhci_init (pcidev_t addr) +{
- int i;
- hci_t *controller = new_controller ();
- controller->instance = malloc (sizeof (uhci_t));
- controller->start = uhci_start;
- controller->stop = uhci_stop;
- controller->reset = uhci_reset;
- controller->shutdown = uhci_shutdown;
- controller->packet = uhci_packet;
- controller->bulk = uhci_bulk;
- controller->control = uhci_control;
- UHCI_INST (controller)->roothub = &(controller->devices[0]);
- controller->bus_address = addr;
- controller->reg_base = pci_read_config32 (controller->bus_address, 0x20) & ~1; /* ~1 clears the register type indicator that is set to 1 for IO space */
- /* kill legacy support handler */
- uhci_stop (controller);
- mdelay (1);
- uhci_reg_write16 (controller, USBSTS, 0x3f);
- pci_write_config32 (controller->bus_address, 0xc0, 0x8f00);
- UHCI_INST (controller)->framelistptr = memalign (0x1000, 1024 * sizeof (flistp_t *)); /* 4kb aligned to 4kb */
- memset (UHCI_INST (controller)->framelistptr, 0,
1024 * sizeof (flistp_t));
- UHCI_INST (controller)->qh_intr = memalign (16, sizeof (qh_t));
- UHCI_INST (controller)->qh_data = memalign (16, sizeof (qh_t));
- UHCI_INST (controller)->qh_last = memalign (16, sizeof (qh_t));
- UHCI_INST (controller)->qh_intr->headlinkptr.ptr =
virt_to_phys (UHCI_INST (controller)->qh_data);
- UHCI_INST (controller)->qh_intr->headlinkptr.queue_head = 1;
- UHCI_INST (controller)->qh_intr->elementlinkptr.ptr = 0;
- UHCI_INST (controller)->qh_intr->elementlinkptr.terminate = 1;
- UHCI_INST (controller)->qh_data->headlinkptr.ptr =
virt_to_phys (UHCI_INST (controller)->qh_last);
- UHCI_INST (controller)->qh_data->headlinkptr.queue_head = 1;
- UHCI_INST (controller)->qh_data->elementlinkptr.ptr = 0;
- UHCI_INST (controller)->qh_data->elementlinkptr.terminate = 1;
- UHCI_INST (controller)->qh_last->headlinkptr.ptr = 0;
- UHCI_INST (controller)->qh_last->headlinkptr.terminate = 1;
- UHCI_INST (controller)->qh_last->elementlinkptr.ptr = 0;
- UHCI_INST (controller)->qh_last->elementlinkptr.terminate = 1;
- for (i = 0; i < 1024; i++) {
UHCI_INST (controller)->framelistptr[i].ptr =
virt_to_phys (UHCI_INST (controller)->qh_intr);
UHCI_INST (controller)->framelistptr[i].terminate = 0;
UHCI_INST (controller)->framelistptr[i].queue_head = 1;
- }
- for (i = 1; i < 128; i++) {
init_device_entry (controller, i);
- }
- controller->devices[0].controller = controller;
- controller->devices[0].init = uhci_rh_init;
- controller->devices[0].init (&controller->devices[0]);
- uhci_reset (controller);
- return controller;
+}
+static void +uhci_shutdown (hci_t *controller) +{
- if (controller == 0)
return;
- detach_controller (controller);
- UHCI_INST (controller)->roothub->destroy (UHCI_INST (controller)->
roothub);
- uhci_reg_mask16 (controller, USBCMD, 0, 0); // stop work
- free (UHCI_INST (controller)->framelistptr);
- free (UHCI_INST (controller)->qh_intr);
- free (UHCI_INST (controller)->qh_data);
- free (UHCI_INST (controller)->qh_last);
- free (UHCI_INST (controller));
- free (controller);
+}
+static void +uhci_start (hci_t *controller) +{
- uhci_reg_mask16 (controller, USBCMD, ~0, 1); // start work on schedule
+}
+static void +uhci_stop (hci_t *controller) +{
- uhci_reg_mask16 (controller, USBCMD, ~1, 0); // stop work on schedule
+}
+#define GET_TD(x) ((void*)(((unsigned int)(x))&~0xf))
+static td_t * +wait_for_completed_qh (hci_t *controller, qh_t *qh) +{
- int timeout = 1000; /* max 30 ms. */
- void *current = GET_TD (qh->elementlinkptr.ptr);
- while ((qh->elementlinkptr.terminate == 0) && (timeout-- > 0)) {
if (current != GET_TD (qh->elementlinkptr.ptr)) {
current = GET_TD (qh->elementlinkptr.ptr);
timeout = 1000;
}
uhci_reg_mask16 (controller, USBSTS, ~0, 0); // clear resettable registers
udelay (30);
- }
- return (GET_TD (qh->elementlinkptr.ptr) ==
0) ? 0 : GET_TD (phys_to_virt (qh->elementlinkptr.ptr));
+}
+static void +wait_for_completed_td (hci_t *controller, td_t *td) +{
- int timeout = 10000;
- while ((td->status_active == 1)
&& ((uhci_reg_read16 (controller, USBSTS) & 2) == 0)
&& (timeout-- > 0)) {
uhci_reg_mask16 (controller, USBSTS, ~0, 0); // clear resettable registers
udelay (10);
- }
+}
+static int +maxlen (int size) +{
- return (size - 1) & 0x7ff;
+}
+static int +min (int a, int b) +{
- if (a < b)
return a;
- else
return b;
+}
+static int +uhci_control (usbdev_t *dev, pid_t dir, int drlen, void *devreq, int dalen,
unsigned char *data)
+{
- int endp = 0; /* this is control: always 0 */
- int mlen = dev->endpoints[0].maxpacketsize;
- int count = (2 + (dalen + mlen - 1) / mlen);
- unsigned short req = ((unsigned short *) devreq)[0];
- int i;
- td_t *tds = memalign (16, sizeof (td_t) * count);
- memset (tds, 0, sizeof (td_t) * count);
- count--; /* to compensate for 0-indexed array */
- for (i = 0; i < count; i++) {
tds[i].ptr = virt_to_phys (&tds[i + 1]);
tds[i].depth_first = 1;
tds[i].terminate = 0;
- }
- tds[count].ptr = 0;
- tds[count].depth_first = 1;
- tds[count].terminate = 1;
- tds[0].pid = SETUP;
- tds[0].dev_addr = dev->address;
- tds[0].endp = endp;
- tds[0].maxlen = maxlen (drlen);
- tds[0].counter = 3;
- tds[0].data_toggle = 0;
- tds[0].lowspeed = dev->lowspeed;
- tds[0].bufptr = virt_to_phys (devreq);
- tds[0].status_active = 1;
- int toggle = 1;
- for (i = 1; i < count; i++) {
tds[i].pid = dir;
tds[i].dev_addr = dev->address;
tds[i].endp = endp;
tds[i].maxlen = maxlen (min (mlen, dalen));
tds[i].counter = 3;
tds[i].data_toggle = toggle;
tds[i].lowspeed = dev->lowspeed;
tds[i].bufptr = virt_to_phys (data);
tds[i].status_active = 1;
toggle ^= 1;
dalen -= mlen;
data += mlen;
- }
- tds[count].pid = (dir == OUT) ? IN : OUT;
- tds[count].dev_addr = dev->address;
- tds[count].endp = endp;
- tds[count].maxlen = maxlen (0);
- tds[count].counter = 0; /* as per linux 2.4.10 */
- tds[count].data_toggle = 1;
- tds[count].lowspeed = dev->lowspeed, tds[count].bufptr = 0;
- tds[count].status_active = 1;
- UHCI_INST (dev->controller)->qh_data->elementlinkptr.ptr =
virt_to_phys (tds);
- UHCI_INST (dev->controller)->qh_data->elementlinkptr.queue_head = 0;
- UHCI_INST (dev->controller)->qh_data->elementlinkptr.terminate = 0;
- td_t *td = wait_for_completed_qh (dev->controller,
UHCI_INST (dev->controller)->
qh_data);
- int result;
- if (td == 0) {
result = 0;
- } else {
printf ("control packet, req %x\n", req);
td_dump (td);
result = 1;
- }
- free (tds);
- return result;
+}
+static int +uhci_packet (usbdev_t *dev, int endp, int pid, int toggle, int length,
unsigned char *data)
+{
- static td_t *td = 0;
- if (td == 0)
td = memalign (16, sizeof (td_t));
- memset (td, 0, sizeof (td_t));
- td->ptr = 0;
- td->terminate = 1;
- td->queue_head = 0;
- td->pid = pid;
- td->dev_addr = dev->address;
- td->endp = endp & 0xf;
- td->maxlen = maxlen (length);
- if (pid == SETUP)
td->counter = 3;
- else
td->counter = 0;
- td->data_toggle = toggle & 1;
- td->lowspeed = dev->lowspeed;
- td->bufptr = virt_to_phys (data);
- td->status_active = 1;
- UHCI_INST (dev->controller)->qh_data->elementlinkptr.ptr =
virt_to_phys (td);
- UHCI_INST (dev->controller)->qh_data->elementlinkptr.queue_head = 0;
- UHCI_INST (dev->controller)->qh_data->elementlinkptr.terminate = 0;
- wait_for_completed_td (dev->controller, td);
- if ((td->status & 0x7f) == 0) {
//printf("successfully sent a %x packet to %x.%x\n",pid, dev->address,endp);
// success
return 0;
- } else {
td_dump (td);
return 1;
- }
+}
+static td_t * +create_schedule (int numpackets) +{
- if (numpackets == 0)
return 0;
- td_t *tds = memalign (16, sizeof (td_t) * numpackets);
- memset (tds, 0, sizeof (td_t) * numpackets);
- int i;
- for (i = 0; i < numpackets; i++) {
tds[i].ptr = virt_to_phys (&tds[i + 1]);
tds[i].terminate = 0;
tds[i].queue_head = 0;
tds[i].depth_first = 1;
- }
- tds[numpackets - 1].ptr = 0;
- tds[numpackets - 1].terminate = 1;
- tds[numpackets - 1].queue_head = 0;
- tds[numpackets - 1].depth_first = 0;
- return tds;
+}
+static void +fill_schedule (td_t *td, endpoint_t *ep, int length, unsigned char *data,
int *toggle)
+{
- td->pid = ep->direction;
- td->dev_addr = ep->dev->address;
- td->endp = ep->endpoint & 0xf;
- td->maxlen = maxlen (length);
- if (ep->direction == SETUP)
td->counter = 3;
- else
td->counter = 0;
- td->data_toggle = *toggle & 1;
- td->lowspeed = ep->dev->lowspeed;
- td->bufptr = virt_to_phys (data);
- td->status_active = 1;
- *toggle ^= 1;
+}
+static int +run_schedule (usbdev_t *dev, td_t *td) +{
- UHCI_INST (dev->controller)->qh_data->elementlinkptr.ptr =
virt_to_phys (td);
- UHCI_INST (dev->controller)->qh_data->elementlinkptr.queue_head = 0;
- UHCI_INST (dev->controller)->qh_data->elementlinkptr.terminate = 0;
- td = wait_for_completed_qh (dev->controller,
UHCI_INST (dev->controller)->qh_data);
- if (td == 0) {
return 0;
- } else {
td_dump (td);
return 1;
- }
+}
+/* finalize == 1: if data is of packet aligned size, add a zero length packet */ +static int +uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize) +{
- int maxpsize = ep->maxpacketsize;
- if (maxpsize == 0)
fatal ("MaxPacketSize == 0!!!");
- int numpackets = (size + maxpsize - 1 + finalize) / maxpsize;
- if (numpackets == 0)
return 0;
- td_t *tds = create_schedule (numpackets);
- int i = 0, toggle = ep->toggle;
- while ((size > 0) || ((size == 0) && (finalize != 0))) {
fill_schedule (&tds[i], ep, min (size, maxpsize), data,
&toggle);
i++;
data += maxpsize;
size -= maxpsize;
- }
- if (run_schedule (ep->dev, tds) == 1) {
clear_stall (ep);
free (tds);
return 1;
- }
- ep->toggle = toggle;
- free (tds);
- return 0;
+}
+void +uhci_reg_write32 (hci_t *ctrl, usbreg reg, u32 value) +{
- outl (value, ctrl->reg_base + reg);
+}
+u32 +uhci_reg_read32 (hci_t *ctrl, usbreg reg) +{
- return inl (ctrl->reg_base + reg);
+}
+void +uhci_reg_write16 (hci_t *ctrl, usbreg reg, u16 value) +{
- outw (value, ctrl->reg_base + reg);
+}
+u16 +uhci_reg_read16 (hci_t *ctrl, usbreg reg) +{
- return inw (ctrl->reg_base + reg);
+}
+void +uhci_reg_write8 (hci_t *ctrl, usbreg reg, u8 value) +{
- outb (value, ctrl->reg_base + reg);
+}
+u8 +uhci_reg_read8 (hci_t *ctrl, usbreg reg) +{
- return inb (ctrl->reg_base + reg);
+}
+void +uhci_reg_mask32 (hci_t *ctrl, usbreg reg, u32 andmask, u32 ormask) +{
- uhci_reg_write32 (ctrl, reg,
(uhci_reg_read32 (ctrl, reg) & andmask) | ormask);
+}
+void +uhci_reg_mask16 (hci_t *ctrl, usbreg reg, u16 andmask, u16 ormask) +{
- uhci_reg_write16 (ctrl, reg,
(uhci_reg_read16 (ctrl, reg) & andmask) | ormask);
+}
+void +uhci_reg_mask8 (hci_t *ctrl, usbreg reg, u8 andmask, u8 ormask) +{
- uhci_reg_write8 (ctrl, reg,
(uhci_reg_read8 (ctrl, reg) & andmask) | ormask);
+} Index: drivers/usb/usbhub.c =================================================================== --- drivers/usb/usbhub.c (revision 0) +++ drivers/usb/usbhub.c (revision 0) @@ -0,0 +1,158 @@ +/*
- 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:
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 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.
- 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.
- */
+#include "usb.h"
+// assume that host_to_device is overwritten if necessary +#define DR_PORT gen_bmRequestType(host_to_device, class_type, other_recp) +#define PORT_RESET 0x4 +#define PORT_POWER 0x8
+typedef struct {
- int num_ports;
- int *ports;
- hub_descriptor_t *descriptor;
+} usbhub_inst_t;
+#define HUB_INST(dev) ((usbhub_inst_t*)(dev)->data)
+static void +usb_hub_destroy (usbdev_t *dev) +{
- free (HUB_INST (dev)->ports);
- free (HUB_INST (dev)->descriptor);
- free (HUB_INST (dev));
+}
+static void +usb_hub_scanport (usbdev_t *dev, int port) +{
- int newdev;
- unsigned short buf[2];
- usbdev_t *newdev_t;
- get_status (dev, port, DR_PORT, 4, buf);
- int portstatus = ((buf[0] & 1) == 0);
- int datastatus = (HUB_INST (dev)->ports[port] == -1);
- if (portstatus == datastatus)
return; // no change - FIXME: read right fields for that test
- if (!datastatus) {
int devno = HUB_INST (dev)->ports[port];
if (devno == -1)
fatal ("FATAL: illegal devno!\n");
dev->controller->devices[devno].destroy (&dev->controller->
devices[devno]);
init_device_entry (dev->controller, devno);
HUB_INST (dev)->ports[port] = -1;
return;
- }
- set_feature (dev, port, PORT_RESET, DR_PORT);
- mdelay (20);
- get_status (dev, port, DR_PORT, 4, buf);
- int lowspeed = (buf[0] >> 9) & 1;
- newdev = set_address (dev->controller, lowspeed);
- if (newdev == -1)
return;
- newdev_t = &dev->controller->devices[newdev];
- HUB_INST (dev)->ports[port] = newdev;
- newdev_t->address = newdev;
- newdev_t->hub = dev->address;
- newdev_t->port = port;
- // determine responsible driver
- newdev_t->init (newdev_t);
+}
+static int +usb_hub_report_port_changes (usbdev_t *dev) +{
- int port;
- unsigned short buf[2];
- for (port = 1; port <= HUB_INST (dev)->num_ports; port++) {
get_status (dev, port, DR_PORT, 4, buf);
// FIXME: proper change detection
int portstatus = ((buf[0] & 1) == 0);
int datastatus = (HUB_INST (dev)->ports[port] == -1);
if (portstatus != datastatus)
return port;
- }
+// no change
- return -1;
+}
+static void +usb_hub_enable_port (usbdev_t *dev, int port) +{
- set_feature (dev, port, PORT_POWER, DR_PORT);
- mdelay (20);
+}
+#if 0 +static void +usb_hub_disable_port (usbdev_t *dev, int port) +{ +} +#endif
+static void +usb_hub_poll (usbdev_t *dev) +{
- int port;
- while ((port = usb_hub_report_port_changes (dev)) != -1)
usb_hub_scanport (dev, port);
+}
+void +usb_hub_init (usbdev_t *dev) +{
- int i;
- dev->destroy = usb_hub_destroy;
- dev->poll = usb_hub_poll;
- dev->data = malloc (sizeof (usbhub_inst_t));
- HUB_INST (dev)->descriptor =
(hub_descriptor_t *) get_descriptor (dev,
gen_bmRequestType
(device_to_host,
class_type, dev_recp),
0x29, 0, 0);
- HUB_INST (dev)->num_ports = HUB_INST (dev)->descriptor->bNbrPorts;
- HUB_INST (dev)->ports =
malloc (sizeof (int) * (HUB_INST (dev)->num_ports + 1));
- for (i = 1; i <= HUB_INST (dev)->num_ports; i++)
HUB_INST (dev)->ports[i] = -1;
- for (i = 1; i <= HUB_INST (dev)->num_ports; i++)
usb_hub_enable_port (dev, i);
+} Index: drivers/usb/usb.c =================================================================== --- drivers/usb/usb.c (revision 0) +++ drivers/usb/usb.c (revision 0) @@ -0,0 +1,347 @@ +/*
- 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:
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 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.
- 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.
- */
+#include <config.h> +#include "usb.h"
+hci_t *usb_hcs = 0;
+hci_t * +new_controller () +{
- hci_t *controller = malloc (sizeof (hci_t));
- /* atomic */
- controller->next = usb_hcs;
- usb_hcs = controller;
- /* atomic end */
- return controller;
+}
+void +detach_controller (hci_t *controller) +{
- if (controller == 0)
return;
- if (usb_hcs == controller) {
usb_hcs = controller->next;
- } else {
hci_t *it = usb_hcs;
while (it != 0) {
if (it->next == controller) {
it->next = controller->next;
return;
}
}
- }
+}
+void +usb_poll () +{
- if (usb_hcs == 0)
return;
- hci_t *controller = usb_hcs;
- while (controller != 0) {
int i;
for (i = 0; i < 128; i++) {
if (controller->devices[i].address != -1) {
controller->devices[i].poll (&controller->
devices[i]);
}
}
controller = controller->next;
- }
+}
+void +init_device_entry (hci_t *controller, int i) +{
- controller->devices[i].controller = controller;
- controller->devices[i].address = -1;
- controller->devices[i].hub = -1;
- controller->devices[i].port = -1;
- controller->devices[i].init = usb_nop_init;
- controller->devices[i].init (&controller->devices[i]);
+}
+void +set_feature (usbdev_t *dev, int endp, int feature, int rtype) +{
- dev_req_t dr;
- dr.bmRequestType = rtype;
- dr.data_dir = host_to_device;
- dr.bRequest = SET_FEATURE;
- dr.wValue = feature;
- dr.wIndex = endp;
- dr.wLength = 0;
- dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
+}
+void +get_status (usbdev_t *dev, int intf, int rtype, int len, void *data) +{
- dev_req_t dr;
- dr.bmRequestType = rtype;
- dr.data_dir = device_to_host;
- dr.bRequest = GET_STATUS;
- dr.wValue = 0;
- dr.wIndex = intf;
- dr.wLength = len;
- dev->controller->control (dev, IN, sizeof (dr), &dr, len, data);
+}
+u8 * +get_descriptor (usbdev_t *dev, unsigned char bmRequestType, int descType,
int descIdx, int langID)
+{
- u8 buf[8];
- u8 *result;
- dev_req_t dr;
- int size;
- dr.bmRequestType = bmRequestType;
- dr.data_dir = device_to_host; // always like this for descriptors
- dr.bRequest = GET_DESCRIPTOR;
- dr.wValue = (descType << 8) | descIdx;
- dr.wIndex = langID;
- dr.wLength = 8;
- if (dev->controller->control (dev, IN, sizeof (dr), &dr, 8, buf)) {
printf ("getting descriptor size (type %x) failed\n",
descType);
- }
- if (descType == 1) {
device_descriptor_t *dd = (device_descriptor_t *) buf;
printf ("maxPacketSize0: %x\n", dd->bMaxPacketSize0);
if (dd->bMaxPacketSize0 != 0)
dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0;
- }
- /* special case for configuration descriptors: they carry all their
subsequent descriptors with them, and keep the entire size at a
different location */
- size = buf[0];
- if (buf[1] == 2) {
int realsize = ((unsigned short *) (buf + 2))[0];
size = realsize;
- }
- result = malloc (size);
- memset (result, 0, size);
- dr.wLength = size;
- if (dev->controller->
control (dev, IN, sizeof (dr), &dr, size, result)) {
printf ("getting descriptor (type %x, size %x) failed\n",
descType, size);
- }
- return result;
+}
+void +set_configuration (usbdev_t *dev) +{
- dev_req_t dr;
- dr.bmRequestType = 0;
- dr.bRequest = SET_CONFIGURATION;
- dr.wValue = dev->configuration[5];
- dr.wIndex = 0;
- dr.wLength = 0;
- dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
+}
+int +clear_stall (endpoint_t *ep) +{
- usbdev_t *dev = ep->dev;
- int endp = ep->endpoint;
- dev_req_t dr;
- dr.bmRequestType = 0;
- if (endp != 0) {
dr.req_recp = endp_recp;
- }
- dr.bRequest = CLEAR_FEATURE;
- dr.wValue = ENDPOINT_HALT;
- dr.wIndex = endp;
- dr.wLength = 0;
- dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
- return 0;
+}
+/* returns free address or -1 */ +static int +get_free_address (hci_t *controller) +{
- int i;
- for (i = 1; i < 128; i++) {
if (controller->devices[i].address != i)
return i;
- }
- printf ("no free address found\n");
- return -1; // no free address
+}
+int +set_address (hci_t *controller, int lowspeed) +{
- int adr = get_free_address (controller); // address to set
- dev_req_t dr;
- configuration_descriptor_t *cd;
- device_descriptor_t *dd;
- memset (&dr, 0, sizeof (dr));
- dr.data_dir = host_to_device;
- dr.req_type = standard_type;
- dr.req_recp = dev_recp;
- dr.bRequest = SET_ADDRESS;
- dr.wValue = adr;
- dr.wIndex = 0;
- dr.wLength = 0;
- usbdev_t *dev = &controller->devices[adr];
- // dummy values for registering the address
- dev->address = 0;
- dev->lowspeed = lowspeed;
- dev->endpoints[0].dev = dev;
- dev->endpoints[0].endpoint = 0;
- dev->endpoints[0].maxpacketsize = 8;
- dev->endpoints[0].toggle = 0;
- dev->endpoints[0].direction = SETUP;
- mdelay (50);
- if (dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0)) {
printf ("set_address failed\n");
return -1;
- }
- mdelay (50);
- dev->address = adr;
- dev->descriptor =
get_descriptor (dev,
gen_bmRequestType (device_to_host,
standard_type, dev_recp),
1, 0, 0);
- dd = (device_descriptor_t *) dev->descriptor;
- printf ("device version: %x.%x\n", dd->bcdUSB >> 8,
dd->bcdUSB & 0xff);
- printf ("device has %x configurations\n", dd->bNumConfigurations);
- if (dd->bNumConfigurations == 0) {
/* device isn't usable */
printf ("no usable configuration!\n");
dev->address = 0;
return -1;
- }
- dev->configuration =
get_descriptor (dev,
gen_bmRequestType (device_to_host,
standard_type, dev_recp),
2, 0, 0);
- cd = (configuration_descriptor_t *) dev->configuration;
- set_configuration (dev);
- interface_descriptor_t *interface =
(interface_descriptor_t *) (((char *) cd) + cd->bLength);
- {
int i;
int num = cd->bNumInterfaces;
interface_descriptor_t *current = interface;
printf ("device has %x interfaces\n", num);
num = (num > 5) ? 5 : num;
for (i = 0; i < num; i++) {
int j;
printf (" #%x has %x endpoints, interface %x:%x, protocol %x\n", current->bInterfaceNumber, current->bNumEndpoints, current->bInterfaceClass, current->bInterfaceSubClass, current->bInterfaceProtocol);
endpoint_descriptor_t *endp =
(endpoint_descriptor_t *) (((char *) current)
+
current->bLength);
if (interface->bInterfaceClass == 0x3)
endp = (endpoint_descriptor_t *) (((char *) endp) + ((char *) endp)[0]); // ignore HID descriptor
memset (dev->endpoints, 0, sizeof (dev->endpoints));
dev->num_endp = 1; // 0 always exists
dev->endpoints[0].dev = dev;
dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0;
dev->endpoints[0].direction = SETUP;
dev->endpoints[0].type = CONTROL;
for (j = 1; j <= current->bNumEndpoints; j++) {
static const char *transfertypes[4] =
{ "control", "isochronous", "bulk",
"interrupt"
};
printf (" #%x: Endpoint %x (%s), max packet size %x, type %s\n", j, endp->bEndpointAddress & 0x7f, ((endp->bEndpointAddress & 0x80) != 0) ? "in" : "out", endp->wMaxPacketSize, transfertypes[endp->bmAttributes]);
endpoint_t *ep =
&dev->endpoints[dev->num_endp++];
ep->dev = dev;
ep->endpoint = endp->bEndpointAddress;
ep->toggle = 0;
ep->maxpacketsize = endp->wMaxPacketSize;
ep->direction =
((endp->bEndpointAddress & 0x80) ==
0) ? OUT : IN;
ep->type = endp->bmAttributes;
endp = (endpoint_descriptor_t
*) (((char *) endp) + endp->bLength);
}
current = (interface_descriptor_t *) endp;
}
- }
- int class = dd->bDeviceClass;
- if (class == 0)
class = interface->bInterfaceClass;
- enum { hid_device = 0x3, msc_device = 0x8, hub_device = 0x9 };
- printf ("device of class %x found\n", class);
- if (class == hub_device) {
printf ("hub found\n");
+#ifdef CONFIG_USB_HUB
controller->devices[adr].init = usb_hub_init;
+#else
printf ("support not compiled in\n");
+#endif
- }
- if (class == hid_device) {
printf ("HID found\n");
+#ifdef CONFIG_USB_HID
controller->devices[adr].init = usb_hid_init;
+#else
printf ("support not compiled in\n");
+#endif
- }
- if (class == msc_device) {
printf ("MSC found\n");
+#ifdef CONFIG_USB_MSC
controller->devices[adr].init = usb_msc_init;
+#else
printf ("support not compiled in\n");
+#endif
- }
- return adr;
+} Index: drivers/usb/usbdisk.h =================================================================== --- drivers/usb/usbdisk.h (revision 0) +++ drivers/usb/usbdisk.h (revision 0) @@ -0,0 +1,37 @@ +/*
- 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:
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 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.
- 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 __USBDISK_H +#define __USBDISK_H +#include "usb.h"
+void usbdisk_create (usbdev_t *dev); +void usbdisk_remove (usbdev_t *dev);
+#endif Index: drivers/usb/usbmsc.c =================================================================== --- drivers/usb/usbmsc.c (revision 0) +++ drivers/usb/usbmsc.c (revision 0) @@ -0,0 +1,389 @@ +/*
- 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:
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 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.
- 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.
- */
+#include <arch/endian.h> +#include "usb.h" +#include "usbmsc.h" +#include "usbdisk.h"
+enum {
- msc_subclass_rbc = 0x1,
- msc_subclass_mmc2 = 0x2,
- msc_subclass_qic157 = 0x3,
- msc_subclass_ufi = 0x4,
- msc_subclass_sff8070i = 0x5,
- msc_subclass_scsitrans = 0x6
+}; +static const char *msc_subclass_strings[7] = {
- "(none)",
- "RBC",
- "MMC-2",
- "QIC-157",
- "UFI",
- "SFF-8070i",
- "SCSI transparent"
+}; +enum {
- msc_proto_cbi_wcomp = 0x0,
- msc_proto_cbi_wocomp = 0x1,
- msc_proto_bulk_only = 0x50
+}; +static const char *msc_protocol_strings[0x51] = {
- "Control/Bulk/Interrupt protocol (with command completion interrupt)",
- "Control/Bulk/Interrupt protocol (with no command completion interrupt)",
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- "Bulk-Only Transport"
+};
+static void +usb_msc_destroy (usbdev_t *dev) +{
- usbdisk_remove (dev);
- free (dev->data);
- dev->data = 0;
+}
+static void +usb_msc_poll (usbdev_t *dev) +{ +}
+const int DEV_RESET = 0xff; +const int GET_MAX_LUN = 0xfe;
+const unsigned int cbw_signature = 0x43425355; +const unsigned int csw_signature = 0x53425355;
+typedef struct {
- unsigned int dCBWSignature;
- unsigned int dCBWTag;
- unsigned int dCBWDataTransferLength;
- unsigned char bmCBWFlags;
- unsigned long bCBWLUN:4;
- unsigned long:4;
- unsigned long bCBWCBLength:5;
- unsigned long:3;
- unsigned char CBWCB[31 - 15];
+} __attribute__ ((packed))
cbw_t;
typedef struct {
unsigned int dCSWSignature;
unsigned int dCSWTag;
unsigned int dCSWDataResidue;
unsigned char bCSWStatus;
} __attribute__ ((packed))
csw_t;
static void
reset_transport (usbdev_t *dev)
+{
- dev_req_t dr;
- memset (&dr, 0, sizeof (dr));
- dr.bmRequestType = 0;
- dr.data_dir = host_to_device;
+#ifndef QEMU
- dr.req_type = class_type;
- dr.req_recp = iface_recp;
+#endif
- dr.bRequest = DEV_RESET;
- dr.wValue = 0;
- dr.wIndex = 0;
- dr.wLength = 0;
- dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
- clear_stall (MSC_INST (dev)->bulk_in);
- clear_stall (MSC_INST (dev)->bulk_out);
+}
+/* device may stall this command, so beware! */ +static int +get_max_luns (usbdev_t *dev) +{
- unsigned char luns = 75;
- dev_req_t dr;
- dr.bmRequestType = 0;
- dr.data_dir = device_to_host;
+#ifndef QEMU
- dr.req_type = class_type;
- dr.req_recp = iface_recp;
+#endif
- dr.bRequest = GET_MAX_LUN;
- dr.wValue = 0;
- dr.wIndex = 0;
- dr.wLength = 1;
- if (dev->controller->control (dev, IN, sizeof (dr), &dr, 1, &luns)) {
luns = 0; // assume only 1 lun if req fails
- }
- return luns;
+}
+int tag; +int lun = 0;
+static void +wrap_cbw (cbw_t *cbw, int datalen, cbw_direction dir, const u8 *cmd,
int cmdlen)
+{
- memset (cbw, 0, sizeof (cbw_t));
- cbw->dCBWSignature = cbw_signature;
- cbw->dCBWTag = tag++;
- cbw->bCBWLUN = lun; // static value per device
- cbw->dCBWDataTransferLength = datalen;
- cbw->bmCBWFlags = dir;
- memcpy (cbw->CBWCB, cmd, sizeof (cbw->CBWCB));
- cbw->bCBWCBLength = cmdlen;
+}
+static void +get_csw (endpoint_t *ep, csw_t *csw) +{
- ep->dev->controller->bulk (ep, sizeof (csw_t), (u8 *) csw, 1);
+}
+static int +execute_command (usbdev_t *dev, cbw_direction dir, const u8 *cb, int cblen,
u8 *buf, int buflen)
+{
- cbw_t cbw;
- csw_t csw;
- int always_succeed = 0;
- if ((cb[0] == 0x1b) && (cb[4] == 1)) { //start command, always succeed
always_succeed = 1;
- }
- wrap_cbw (&cbw, buflen, dir, cb, cblen);
- if (dev->controller->
bulk (MSC_INST (dev)->bulk_out, sizeof (cbw), (u8 *) &cbw, 0)) {
clear_stall (MSC_INST (dev)->bulk_out);
return 1;
- }
- mdelay (10);
- if (dir == cbw_direction_data_in) {
if (dev->controller->
bulk (MSC_INST (dev)->bulk_in, buflen, buf, 0)) {
clear_stall (MSC_INST (dev)->bulk_in);
return 1;
}
- } else {
if (dev->controller->
bulk (MSC_INST (dev)->bulk_out, buflen, buf, 0)) {
clear_stall (MSC_INST (dev)->bulk_out);
return 1;
}
- }
- get_csw (MSC_INST (dev)->bulk_in, &csw);
- if (always_succeed == 1) {
// return success, regardless of message
return 0;
- }
- if (csw.bCSWStatus == 2) {
// phase error, reset transport
reset_transport (dev);
return 1;
- }
- if (csw.bCSWStatus == 0) {
// no error, exit
return 0;
- }
- // error "check condition" or reserved error
- return 1;
+}
+typedef struct {
- unsigned char command; //0
- unsigned char res1; //1
- unsigned int block; //2-5
- unsigned char res2; //6
- unsigned short numblocks; //7-8
- unsigned char res3; //9 - the block is 10 bytes long
+} __attribute__ ((packed)) cmdblock_t;
+typedef struct {
- unsigned char command; //0
- unsigned char res1; //1
- unsigned char res2; //2
- unsigned char res3; //3
- unsigned char lun; //4
- unsigned char res4; //5
+} __attribute__ ((packed)) cmdblock6_t;
+/* returns success code (see execute_command)
- buf must be preallocated to at least n*512 bytes!
- limited to 2TB (READ(10) command)
+*/ +int +readwrite_blocks (usbdev_t *dev, int start, int n, cbw_direction dir, u8 *buf) +{
- cmdblock_t cb;
- memset (&cb, 0, sizeof (cb));
- if (dir == cbw_direction_data_in) {
// read
cb.command = 0x28;
- } else {
// write
cb.command = 0x2a;
- }
- cb.block = ntohl (start);
- cb.numblocks = ntohw (n);
- return execute_command (dev, dir, (u8 *) &cb, sizeof (cb), buf,
n * 512);
+}
+static int +test_unit_ready (usbdev_t *dev) +{
- cmdblock6_t cb;
- memset (&cb, 0, sizeof (cb)); // full initialization for T-U-R
- return execute_command (dev, cbw_direction_data_out, (u8 *) &cb,
sizeof (cb), 0, 0);
+}
+static int +spin_up (usbdev_t *dev) +{
- cmdblock6_t cb;
- memset (&cb, 0, sizeof (cb));
- cb.command = 0x1b;
- cb.lun = 1;
- return execute_command (dev, cbw_direction_data_out, (u8 *) &cb,
sizeof (cb), 0, 0);
+}
+static void +read_capacity (usbdev_t *dev) +{
- cmdblock_t cb;
- memset (&cb, 0, sizeof (cb));
- cb.command = 0x25; // read capacity
- u8 buf[8];
- int count = 0;
- while ((count++ < 20)
&&
(execute_command
(dev, cbw_direction_data_in, (u8 *) &cb, sizeof (cb), buf,
8) == 1));
- if (count >= 20) {
// still not successful, assume 2tb in 512byte sectors, which is just the same garbage as any other number, but probably reasonable.
printf ("assuming 2TB in 512byte sectors as READ CAPACITY didn't answer.\n");
MSC_INST (dev)->numblocks = 0xffffffff;
MSC_INST (dev)->blocksize = 512;
- } else {
MSC_INST (dev)->numblocks = ntohl (*(u32 *) buf) + 1;
MSC_INST (dev)->blocksize = ntohl (*(u32 *) (buf + 4));
- }
- printf (" has %d blocks sized %db\n", MSC_INST (dev)->numblocks,
MSC_INST (dev)->blocksize);
+}
+void +usb_msc_init (usbdev_t *dev) +{
- int i, timeout;
- dev->destroy = usb_msc_destroy;
- dev->poll = usb_msc_poll;
- configuration_descriptor_t *cd =
(configuration_descriptor_t *) dev->configuration;
- interface_descriptor_t *interface =
(interface_descriptor_t *) (((char *) cd) + cd->bLength);
- printf (" it uses %s command set\n",
msc_subclass_strings[interface->bInterfaceSubClass]);
- printf (" it uses %s protocol\n",
msc_protocol_strings[interface->bInterfaceProtocol]);
- if ((interface->bInterfaceProtocol != 0x50)
|| (interface->bInterfaceSubClass != 6)) {
/* Other protocols, such as ATAPI don't seem to be very popular. looks like ATAPI would be really easy to add, if necessary. */
printf (" Only SCSI over Bulk is supported.\n");
return;
- }
- dev->data = malloc (sizeof (usbmsc_inst_t));
- MSC_INST (dev)->bulk_in = 0;
- MSC_INST (dev)->bulk_out = 0;
- for (i = 1; i <= dev->num_endp; i++) {
if (dev->endpoints[i].endpoint == 0)
continue;
if (dev->endpoints[i].type != BULK)
continue;
if ((dev->endpoints[i].direction == IN)
&& (MSC_INST (dev)->bulk_in == 0))
MSC_INST (dev)->bulk_in = &dev->endpoints[i];
if ((dev->endpoints[i].direction == OUT)
&& (MSC_INST (dev)->bulk_out == 0))
MSC_INST (dev)->bulk_out = &dev->endpoints[i];
- }
- if (MSC_INST (dev)->bulk_in == 0)
fatal ("couldn't find bulk-in endpoint");
- if (MSC_INST (dev)->bulk_out == 0)
fatal ("couldn't find bulk-out endpoint");
- printf (" using endpoint %x as in, %x as out\n",
MSC_INST (dev)->bulk_in->endpoint,
MSC_INST (dev)->bulk_out->endpoint);
- printf (" has %d luns\n", get_max_luns (dev) + 1);
- printf (" Waiting for device to become ready... ");
- timeout = 10;
- while (test_unit_ready (dev) && --timeout) {
mdelay (100);
printf (".");
- }
- if (test_unit_ready (dev)) {
printf ("timeout. Device not ready. Still trying...\n");
- } else {
printf ("ok.\n");
- }
- printf (" spin up");
- for (i = 0; i < 30; i++) {
printf (".");
if (!spin_up (dev)) {
printf (" OK.");
break;
}
mdelay (100);
- }
- printf ("\n");
- read_capacity (dev);
- usbdisk_create (dev);
+} Index: drivers/usb/uhci.h =================================================================== --- drivers/usb/uhci.h (revision 0) +++ drivers/usb/uhci.h (revision 0) @@ -0,0 +1,124 @@ +/*
- 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:
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 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.
- 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 __UHCI_H +#define __UHCI_H
+#include <pci.h> +#include "usb.h"
+typedef union {
- struct {
unsigned long terminate:1;
unsigned long queue_head:1;
unsigned long:2;
unsigned long ptr_part:28;
- };
- u32 ptr;
+} __attribute__ ((packed)) flistp_t;
+typedef struct {
- union {
struct {
unsigned long terminate:1;
unsigned long queue_head:1;
unsigned long depth_first:1;
unsigned long:29;
} __attribute__ ((packed));
u32 ptr;
- } __attribute__ ((packed));
- volatile unsigned long actlen:11;
- volatile unsigned long:5;
- union {
struct {
unsigned long:1; // bit 0
unsigned long status_bitstuff_err:1;
unsigned long status_crc_err:1;
unsigned long status_nakrcvd:1;
unsigned long status_babble:1;
unsigned long status_databuf_err:1;
unsigned long status_stalled:1;
unsigned long status_active:1; // bit 7
} __attribute__ ((packed));
unsigned char status;
- } __attribute__ ((packed));
- volatile unsigned long ioc:1; /* interrupt on complete */
- volatile unsigned long isochronous:1;
- volatile unsigned long lowspeed:1;
- volatile unsigned long counter:2;
- volatile unsigned long shortpck:1;
- volatile unsigned long:2;
- unsigned long pid:8;
- unsigned long dev_addr:7;
- unsigned long endp:4;
- unsigned long data_toggle:1;
- unsigned long:1;
- unsigned long maxlen:11;
- u32 bufptr;
+} __attribute__ ((packed))
td_t;
typedef struct {
flistp_t headlinkptr;
volatile flistp_t elementlinkptr;
} __attribute__ ((packed))
qh_t;
typedef enum { USBCMD = 0, USBSTS = 2, USBINTR = 4, FRNUM =
6, FLBASEADD = 8, SOFMOD = 0xc, PORTSC1 = 0x10, PORTSC2 =
0x12
} usbreg;
void uhci_reg_write32 (hci_t *ctrl, usbreg reg, u32 value);
u32 uhci_reg_read32 (hci_t *ctrl, usbreg reg);
void uhci_reg_write16 (hci_t *ctrl, usbreg reg, u16 value);
u16 uhci_reg_read16 (hci_t *ctrl, usbreg reg);
void uhci_reg_write8 (hci_t *ctrl, usbreg reg, u8 value);
u8 uhci_reg_read8 (hci_t *ctrl, usbreg reg);
void uhci_reg_mask32 (hci_t *ctrl, usbreg reg, u32 andmask, u32 ormask);
void uhci_reg_mask16 (hci_t *ctrl, usbreg reg, u16 andmask, u16 ormask);
void uhci_reg_mask8 (hci_t *ctrl, usbreg reg, u8 andmask, u8 ormask);
typedef struct uhci {
flistp_t *framelistptr;
qh_t *qh_intr, *qh_data, *qh_last;
usbdev_t *roothub;
} uhci_t;
+#define UHCI_INST(controller) ((uhci_t*)((controller)->instance))
hci_t *uhci_init (pcidev_t addr);
void uhci_rh_init (usbdev_t *dev);
+#endif Index: drivers/usb/usbinit.c =================================================================== --- drivers/usb/usbinit.c (revision 0) +++ drivers/usb/usbinit.c (revision 0) @@ -0,0 +1,110 @@ +/*
- 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:
- Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 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.
- 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.
- */
+#include <config.h> +#include "usb.h" +#include "uhci.h" +#include "usbdisk.h"
+int +usb_controller_initialize (int bus, int dev, int func) +{
- u32 class;
- u32 devclass;
- u32 prog_if;
- pcidev_t addr;
- u32 pciid;
- addr = PCI_DEV (bus, dev, func);
- class = pci_read_config32 (addr, 8);
- pciid = pci_read_config32 (addr, 0);
- devclass = class >> 16;
- prog_if = (class >> 8) & 0xff;
- /* enable busmaster */
+#define PCI_COMMAND 4 +#define PCI_COMMAND_MASTER 4
- pci_write_config32 (addr, PCI_COMMAND,
pci_read_config32 (addr,
PCI_COMMAND) |
PCI_COMMAND_MASTER);
- if (devclass == 0xc03) {
printf ("%02x:%02x.%x %04x:%04x.%d ", 0, dev, func,
pciid >> 16, pciid & 0xFFFF, func);
if (prog_if == 0) {
printf ("UHCI controller\n");
+#ifdef CONFIG_USB_UHCI
uhci_init (addr);
usb_poll ();
usb_poll ();
+#else
printf ("Not supported.\n");
+#endif
}
if (prog_if == 0x10) {
printf ("OHCI controller\n");
+#ifdef CONFIG_USB_OHCI
// ohci_init(addr);
+#else
printf ("Not supported.\n");
+#endif
}
if (prog_if == 0x20) {
printf ("EHCI controller\n");
+#ifdef CONFIG_USB_EHCI
// ehci_init(addr);
+#else
printf ("Not supported.\n");
+#endif
}
- }
- return 0;
+}
+int +usb_initialize (void) +{
- int bus, dev, func;
- for (bus = 0; bus < 256; bus++)
for (dev = 0; dev < 32; dev++)
for (func = 0; func < 8; func++)
usb_controller_initialize (bus, dev, func);
- return 0;
+}
+int +usb_exit (void) +{
- return 0;
+}
Jordan Crouse schrieb:
Signed-off-by: Patrick Georgi patrick.georgi@coresystems.de
Acked-by: Jordan Crouse jordan.crouse@amd.com
Thanks, r3560
On Tue, 02 Sep 2008 18:08:06 +0200, Patrick Georgi patrick@georgi-clan.de wrote:
Jordan Crouse schrieb:
Signed-off-by: Patrick Georgi patrick.georgi@coresystems.de
Acked-by: Jordan Crouse jordan.crouse@amd.com
Thanks, r3560
YAHOO!!! Sorry I wasn't much help Patrick, things have been crazy here, I haven't had any time for extra ciricular activities...