[coreboot] libpayload: USB stack
Jordan Crouse
jordan.crouse at amd.com
Tue Sep 2 17:03:20 CEST 2008
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 at coresystems.de>
Acked-by: Jordan Crouse <jordan.crouse at 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:
> + * 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;
> +}
More information about the coreboot
mailing list