This patch adds USB capabilities to libpayload. It requires some
authorPatrick Georgi <patrick.georgi@coresystems.de>
Tue, 2 Sep 2008 16:06:22 +0000 (16:06 +0000)
committerPatrick Georgi <patrick.georgi@coresystems.de>
Tue, 2 Sep 2008 16:06:22 +0000 (16:06 +0000)
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. But 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>
git-svn-id: svn://svn.coreboot.org/coreboot/trunk@3560 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1

16 files changed:
payloads/libpayload/Config.in
payloads/libpayload/Makefile
payloads/libpayload/drivers/Makefile.inc
payloads/libpayload/drivers/usb/TODO [new file with mode: 0644]
payloads/libpayload/drivers/usb/uhci.c [new file with mode: 0644]
payloads/libpayload/drivers/usb/uhci.h [new file with mode: 0644]
payloads/libpayload/drivers/usb/uhci_rh.c [new file with mode: 0644]
payloads/libpayload/drivers/usb/usb.c [new file with mode: 0644]
payloads/libpayload/drivers/usb/usb.h [new file with mode: 0644]
payloads/libpayload/drivers/usb/usb_dev.c [new file with mode: 0644]
payloads/libpayload/drivers/usb/usbdisk.h [new file with mode: 0644]
payloads/libpayload/drivers/usb/usbhid.c [new file with mode: 0644]
payloads/libpayload/drivers/usb/usbhub.c [new file with mode: 0644]
payloads/libpayload/drivers/usb/usbinit.c [new file with mode: 0644]
payloads/libpayload/drivers/usb/usbmsc.c [new file with mode: 0644]
payloads/libpayload/drivers/usb/usbmsc.h [new file with mode: 0644]

index 97bf78d649d938de9211d04eb09d97c2f66b0028..61567003f06f289ea10105ed0d73ec7d6e071e25 100644 (file)
@@ -132,5 +132,46 @@ config SPEAKER
        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 65d7d3d5a6adde4ade3536a09ec84f870bef54ed..9254de7ad1bfeb8c0eca1cfc484c75da7b07f89f 100644 (file)
@@ -137,6 +137,7 @@ install: lib
 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 854082c511a97016980ec6b37fb93f029035fe7f..cad208021622d2cfc3a5b51bde0be72cef7041ac 100644 (file)
@@ -47,3 +47,14 @@ TARGETS-$(CONFIG_VGA_VIDEO_CONSOLE) += drivers/video/vga.o
 # 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
+
diff --git a/payloads/libpayload/drivers/usb/TODO b/payloads/libpayload/drivers/usb/TODO
new file mode 100644 (file)
index 0000000..1dfc9d4
--- /dev/null
@@ -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
diff --git a/payloads/libpayload/drivers/usb/uhci.c b/payloads/libpayload/drivers/usb/uhci.c
new file mode 100644 (file)
index 0000000..a558db8
--- /dev/null
@@ -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);
+}
diff --git a/payloads/libpayload/drivers/usb/uhci.h b/payloads/libpayload/drivers/usb/uhci.h
new file mode 100644 (file)
index 0000000..f5727c4
--- /dev/null
@@ -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
diff --git a/payloads/libpayload/drivers/usb/uhci_rh.c b/payloads/libpayload/drivers/usb/uhci_rh.c
new file mode 100644 (file)
index 0000000..e703e53
--- /dev/null
@@ -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;
+}
diff --git a/payloads/libpayload/drivers/usb/usb.c b/payloads/libpayload/drivers/usb/usb.c
new file mode 100644 (file)
index 0000000..fc4de87
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+ * 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;
+                       }
+               }
+       }
+}
+
+/**
+ * Polls all hubs on all USB controllers, to find out about device changes
+ */
+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;
+}
diff --git a/payloads/libpayload/drivers/usb/usb.h b/payloads/libpayload/drivers/usb/usb.h
new file mode 100644 (file)
index 0000000..1500b75
--- /dev/null
@@ -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
diff --git a/payloads/libpayload/drivers/usb/usb_dev.c b/payloads/libpayload/drivers/usb/usb_dev.c
new file mode 100644 (file)
index 0000000..0c6a34c
--- /dev/null
@@ -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;
+}
diff --git a/payloads/libpayload/drivers/usb/usbdisk.h b/payloads/libpayload/drivers/usb/usbdisk.h
new file mode 100644 (file)
index 0000000..3ac2463
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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"
+
+/**
+ * To be implemented by libpayload-client. It's called by the USB stack
+ * when a new USB storage device is found, so the client has the chance
+ * to know about it.
+ *
+ * @param dev descriptor for the USB storage device
+ */
+void usbdisk_create (usbdev_t *dev);
+
+/**
+ * To be implemented by libpayload-client. It's called by the USB stack
+ * when it finds out that a USB storage device is removed.
+ *
+ * @param dev descriptor for the USB storage device
+ */
+void usbdisk_remove (usbdev_t *dev);
+
+#endif
diff --git a/payloads/libpayload/drivers/usb/usbhid.c b/payloads/libpayload/drivers/usb/usbhid.c
new file mode 100644 (file)
index 0000000..414cc07
--- /dev/null
@@ -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;
+               }
+       }
+}
diff --git a/payloads/libpayload/drivers/usb/usbhub.c b/payloads/libpayload/drivers/usb/usbhub.c
new file mode 100644 (file)
index 0000000..04c04c3
--- /dev/null
@@ -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);
+}
diff --git a/payloads/libpayload/drivers/usb/usbinit.c b/payloads/libpayload/drivers/usb/usbinit.c
new file mode 100644 (file)
index 0000000..fe13837
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * 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"
+
+/**
+ * Initializes USB controller attached to PCI
+ *
+ * @param bus PCI bus number
+ * @param dev PCI device id at bus
+ * @param func function id of the controller
+ */
+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;
+}
+
+/**
+ * Initialize all USB controllers attached to PCI.
+ */
+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;
+}
diff --git a/payloads/libpayload/drivers/usb/usbmsc.c b/payloads/libpayload/drivers/usb/usbmsc.c
new file mode 100644 (file)
index 0000000..cbef585
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * 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;
+
+
+/**
+ * Reads or writes a number of sequential blocks on a USB storage device.
+ * As it uses the READ(10) SCSI-2 command, it's limited to storage devices
+ * of at most 2TB. It assumes sectors of 512 bytes.
+ *
+ * @param dev device to access
+ * @param start first sector to access
+ * @param n number of sectors to access
+ * @param dir direction of access: cbw_direction_data_in == read, cbw_direction_data_out == write
+ * @param buf buffer to read into or write from. Must be at least n*512 bytes
+ * @return 0 on success, 1 on failure
+ */
+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);
+}
diff --git a/payloads/libpayload/drivers/usb/usbmsc.h b/payloads/libpayload/drivers/usb/usbmsc.h
new file mode 100644 (file)
index 0000000..e180d3e
--- /dev/null
@@ -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