grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC] USB Support


From: Marco Gerards
Subject: [RFC] USB Support
Date: Wed, 27 Aug 2008 21:56:22 +0200
User-agent: Gnus/5.110006 (No Gnus v0.6) Emacs/21.4 (gnu/linux)

Hi,

As many of you know, I am working on USB support.  I hope some of you
can have a look at this patch.  It is not finished yet, but I hope to
get some comments anyways so it can be committed sooner.  Please test
it, comment on the interfaces and generic structure of the code.  But
if you stop bugs or weird code, please tell me!

Features:

- UHCI support, tested on an Intel chipset and on QEmu

- Generic USB interfaces

- USB test command ("usb") that can be used to show device
  information, will be renamed and cleaned up.

- USB Mass Storage Support

- Broken OHCI support, it works in QEMu but I haven't tested on real
  hardware.  Please do not review ohci.c, it won't be committed but I
  included it now in case someone wants to test it anyways :-)

Some bugs that are still present:

- --enable-grub-emu-usb does not work properly.  grub_emu_SOURCES += ...
  doesn't seem to work properly.  I have no idea how to fix this, I do
  not want to learn ruby just to make such simple change.  Help is
  appreciated.

- Endianess fixes.  Unfortunately I didn't fix endianess everywhere
  and need to check this carefully... :-)  This will be done.

- I marked some weird or ugly things with XXX in my code, no need to
  check this.  Some might be even committed.

- There are some things I will have to check again.  Perhaps I should
  even review some parts of the standards to see if I indeed
  implemented this as described.  Initially I might have
  misinterpreted something which I do understand now.

- This code wants to be tested ;-)

--
Marco


2008-08-27  Marco Gerards  <address@hidden>

        * Makefile.in (enable_grub_emu_usb): New variable.
        * conf/i386-pc.rmk (grub_emu_SOURCES): Add `disk/scsi.c'.
        (grub_emu_SOURCES) [grub_emu_SOURCES]: Add `disk/usbms.c',
        `util/usb.c', `bus/usb/usb.c' and `commands/usbtest.c'.
        (grub_emu_LDFLAGS): Add `$(LIBUSB)'.
        (pkglib_MODULES): Add `usb.mod', `uhci.mod', `ohci.mod',
        `usbtest.mod' and `usbms.mod'.
        (usb_mod_SOURCES, usb_mod_CFLAGS, usb_mod_LDFLAGS)
        (usbtest_mod_SOURCES, usbtest_mod_CFLAGS, usbtest_mod_LDFLAGS)
        (uhci_mod_SOURCES, uhci_mod_CFLAGS, uhci_mod_LDFLAGS,
        (ohci_mod_SOURCES, ohci_mod_CFLAGS, ohci_mod_LDFLAGS)
        (usbms_mod_SOURCES, usbms_mod_CFLAGS, usbms_mod_LDFLAGS): New
        variables.

        * disk/usbms.c: New file.

        * include/grub/usb.h: Likewise.

        * include/grub/usbtrans.h: Likewise.

        * include/grub/usbdesc.h: Likewise.

        * bus/usb/usbtrans.c: Likewise.

        * bus/usb/ohci.c: Likewise.

        * bus/usb/uhci.c: Likewise.

        * bus/usb/usbhub.c: Likewise.

        * bus/usb/usb.c: Likewise.

        * commands/usbtest.c: Likewise.

        * util/usb.c: Likewise.
        
        * include/grub/err.h (grub_err_t): Add `GRUB_ERR_IO'.

        * configure.ac: Test for libusb presence.
        
        * util/grub-emu.c (main) [HAVE_LIBUSB_H]: Call `grub_libusb_init'.


Index: Makefile.in
===================================================================
--- Makefile.in (revision 1830)
+++ Makefile.in (working copy)
@@ -92,6 +92,7 @@ UNIFONT_HEX = @UNIFONT_HEX@
 
 # Options.
 enable_grub_emu = @enable_grub_emu@
+enable_grub_emu_usb = @enable_grub_emu_usb@
 enable_grub_fstest = @enable_grub_fstest@
 enable_grub_pe2elf = @enable_grub_pe2elf@
 enable_lzo = @enable_lzo@
Index: conf/i386-pc.rmk
===================================================================
--- conf/i386-pc.rmk    (revision 1830)
+++ conf/i386-pc.rmk    (working copy)
@@ -119,7 +119,7 @@ grub_emu_SOURCES = commands/boot.c comma
        commands/search.c commands/blocklist.c commands/hexdump.c       \
        lib/hexdump.c commands/i386/pc/halt.c commands/reboot.c         \
        commands/i386/cpuid.c                                           \
-       disk/host.c disk/loopback.c                                     \
+       disk/host.c disk/loopback.c disk/scsi.c                         \
        fs/fshelp.c     \
        \
        io/gzio.c                                                       \
@@ -140,14 +140,21 @@ grub_emu_SOURCES = commands/boot.c comma
        fs/ufs.c fs/xfs.c fs/afs.c                                      \
        \
        util/console.c util/hostfs.c util/grub-emu.c util/misc.c        \
-       util/biosdisk.c util/getroot.c                                  \
+       util/biosdisk.c util/getroot.c                                  \
        util/i386/pc/misc.c                                             \
        \
        disk/raid.c disk/raid5_recover.c disk/raid6_recover.c           \
        disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c             \
+       \
        grub_emu_init.c
 
-grub_emu_LDFLAGS = $(LIBCURSES)
+ifeq ($(enable_grub_emu_usb), yes)
+grub_emu_SOURCES += disk/usbms.c util/usb.c bus/usb/usb.c      \
+                   commands/usbtest.c
+endif
+
+
+grub_emu_LDFLAGS = $(LIBCURSES) $(LIBUSB)
 
 # Scripts.
 sbin_SCRIPTS = grub-install
@@ -165,7 +172,8 @@ pkglib_MODULES = biosdisk.mod _chain.mod
        vbe.mod vbetest.mod vbeinfo.mod video.mod gfxterm.mod \
        videotest.mod play.mod bitmap.mod tga.mod cpuid.mod serial.mod  \
        ata.mod vga.mod memdisk.mod jpeg.mod png.mod pci.mod lspci.mod \
-       aout.mod _bsd.mod bsd.mod pxe.mod pxecmd.mod datetime.mod date.mod \
+       aout.mod _bsd.mod bsd.mod usb.mod uhci.mod ohci.mod usbtest.mod 
usbms.mod \
+       pxe.mod pxecmd.mod datetime.mod date.mod \
        datehook.mod
 
 # For biosdisk.mod.
@@ -333,6 +341,31 @@ bsd_mod_SOURCES = loader/i386/bsd_normal
 bsd_mod_CFLAGS = $(COMMON_CFLAGS)
 bsd_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+# For usb.mod
+usb_mod_SOURCES = bus/usb/usb.c bus/usb/usbtrans.c bus/usb/usbhub.c
+usb_mod_CFLAGS = $(COMMON_CFLAGS)
+usb_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For usbtest.mod
+usbtest_mod_SOURCES = commands/usbtest.c
+usbtest_mod_CFLAGS = $(COMMON_CFLAGS)
+usbtest_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For uhci.mod
+uhci_mod_SOURCES = bus/usb/uhci.c
+uhci_mod_CFLAGS = $(COMMON_CFLAGS)
+uhci_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For ohci.mod
+ohci_mod_SOURCES = bus/usb/ohci.c
+ohci_mod_CFLAGS = $(COMMON_CFLAGS)
+ohci_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For usbms.mod
+usbms_mod_SOURCES = disk/usbms.c
+usbms_mod_CFLAGS = $(COMMON_CFLAGS)
+usbms_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
 # For pxe.mod
 pxe_mod_SOURCES = fs/i386/pc/pxe.c
 pxe_mod_CFLAGS = $(COMMON_CFLAGS)
Index: disk/usbms.c
===================================================================
--- disk/usbms.c        (revision 0)
+++ disk/usbms.c        (revision 0)
@@ -0,0 +1,393 @@
+/* usbms.c - USB Mass Storage Support.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/usb.h>
+#include <grub/scsi.h>
+#include <grub/scsicmd.h>
+#include <grub/misc.h>
+
+#define GRUB_USBMS_DIRECTION_BIT       7
+
+/* The USB Mass Storage Command Block Wrapper.  */
+struct grub_usbms_cbw
+{
+  grub_uint32_t signature;
+  grub_uint32_t tag;
+  grub_uint32_t transfer_length;
+  grub_uint8_t flags;
+  grub_uint8_t lun;
+  grub_uint8_t length;
+  grub_uint8_t cbwcb[16];
+} __attribute__ ((packed));
+
+struct grub_usbms_csw
+{
+  grub_uint32_t signature;
+  grub_uint32_t tag;
+  grub_uint32_t residue;
+  grub_uint8_t status;
+} __attribute__ ((packed));
+
+struct grub_usbms_dev
+{
+  struct grub_usb_device *dev;
+
+  int luns;
+
+  int interface;
+  struct grub_usb_desc_endp *in;
+  struct grub_usb_desc_endp *out;
+
+  int in_maxsz;
+  int out_maxsz;
+
+  struct grub_usbms_dev *next;
+};
+typedef struct grub_usbms_dev *grub_usbms_dev_t;
+
+static grub_usbms_dev_t grub_usbms_dev_list;
+
+static int devcnt;
+
+static grub_err_t
+grub_usbms_reset (grub_usb_device_t dev, int interface)
+{
+  return grub_usb_control_msg (dev, 0x21, 255, 0, interface, 0, 0);
+}
+
+static void
+grub_usbms_finddevs (void)
+{
+  auto int usb_iterate (grub_usb_device_t dev);
+
+  int usb_iterate (grub_usb_device_t usbdev)
+    {
+      grub_usb_err_t err;
+      struct grub_usb_desc_device *descdev = &usbdev->descdev;
+      int i;
+
+      if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0)
+       return 0;
+
+      /* XXX: Just check configuration 0 for now.  */
+      for (i = 0; i < usbdev->config[0].descconf->numif; i++)
+       {
+         struct grub_usbms_dev *usbms;
+         struct grub_usb_desc_if *interf;
+         int j;
+         grub_uint8_t luns;
+
+         interf = usbdev->config[0].interf[i].descif;
+
+         /* If this is not a USB Mass Storage device with a supported
+            protocol, just skip it.  */
+         if (interf->class != GRUB_USB_CLASS_MASS_STORAGE
+             || interf->subclass != GRUB_USBMS_SUBCLASS_BULK
+             || interf->protocol != GRUB_USBMS_PROTOCOL_BULK)
+           {
+             continue;
+           }
+
+         devcnt++;
+         usbms = grub_malloc (sizeof (struct grub_usbms_dev));
+         if (! usbms)
+           return 1;
+
+         usbms->dev = usbdev;
+         usbms->interface = i;
+         usbms->in = NULL;
+         usbms->out = NULL;
+
+         /* Iterate over all endpoints of this interface, at least a
+            IN and OUT bulk endpoint are required.  */
+         for (j = 0; j < interf->endpointcnt; j++)
+           {
+             struct grub_usb_desc_endp *endp;
+             endp = &usbdev->config[0].interf[i].descendp[j];
+
+             if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2)
+               {
+                 /* Bulk IN endpoint.  */
+                 usbms->in = endp;
+                 grub_usb_clear_halt (usbdev, endp->endp_addr & 128);
+                 usbms->in_maxsz = endp->maxpacket;
+               }
+             else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2)
+               {
+                 /* Bulk OUT endpoint.  */
+                 usbms->out = endp;
+                 grub_usb_clear_halt (usbdev, endp->endp_addr & 128);
+                 usbms->out_maxsz = endp->maxpacket;
+               }
+           }
+
+         if (!usbms->in || !usbms->out)
+           {
+             grub_free (usbms);
+             return 0;
+           }
+
+         /* Query the amount of LUNs.  */
+         err = grub_usb_control_msg (usbdev, 0xA1, 254,
+                                     0, i, 1, (char *) &luns);
+         if (err)
+           {
+             /* In case of a stall, clear the stall.  */
+             if (err == GRUB_USB_ERR_STALL)
+               {
+                 grub_usb_clear_halt (usbdev, usbms->in->endp_addr & 3);
+                 grub_usb_clear_halt (usbdev, usbms->out->endp_addr & 3);
+               }
+
+             /* Just set the amount of LUNs to one.  */
+             grub_errno = GRUB_ERR_NONE;
+             usbms->luns = 1;
+           }
+         else
+           usbms->luns = luns;
+
+         /* XXX: Check the magic values, does this really make
+            sense?  */
+         grub_usb_control_msg (usbdev, (1 << 6) | 1, 255,
+                               0, i, 0, 0);
+
+         /* XXX: To make Qemu work?  */
+         if (usbms->luns == 0)
+           usbms->luns = 1;
+
+         usbms->next = grub_usbms_dev_list;
+         grub_usbms_dev_list = usbms;
+
+         /* XXX: Activate the first configuration.  */
+         grub_usb_set_configuration (usbdev, 1);
+
+         /* Bolk-Only Mass Storage Reset, after the reset commands
+            will be accepted.  */
+         grub_usbms_reset (usbdev, i);
+
+         return 0;
+       }
+
+      return 0;
+    }
+
+  grub_usb_iterate (usb_iterate);
+}
+
+
+
+static int
+grub_usbms_iterate (int (*hook) (const char *name, int luns))
+{
+  grub_usbms_dev_t p;
+  int cnt = 0;
+
+  for (p = grub_usbms_dev_list; p; p = p->next)
+    {
+      char devname[20];
+      grub_sprintf (devname, "usb%d", cnt);
+
+      if (hook (devname, p->luns))
+       return 1;
+      cnt++;
+    }
+
+  return 0;
+}
+
+static grub_err_t
+grub_usbms_tranfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
+                   grub_size_t size, char *buf, int read_write)
+{
+  struct grub_usbms_cbw cbw;
+  grub_usbms_dev_t dev = (grub_usbms_dev_t) scsi->data;
+  struct grub_usbms_csw status;
+  static grub_uint32_t tag = 0;
+  grub_usb_err_t err;
+  int retrycnt = 3;
+
+ retry:
+  if (retrycnt == 0)
+    return err;
+
+  /* Setup the request.  */
+  grub_memset (&cbw, 0, sizeof (cbw));
+  cbw.signature = grub_cpu_to_le32 (0x43425355);
+  cbw.tag = tag++;
+  cbw.transfer_length = grub_cpu_to_le32 (size);
+  cbw.flags = (!read_write) << GRUB_USBMS_DIRECTION_BIT;
+  cbw.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  cbw.length = cmdsize;
+  grub_memcpy (cbw.cbwcb, cmd, cmdsize);
+
+  /* Write the request.  */
+  err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr & 15,
+                            sizeof (cbw), (char *) &cbw);
+  if (err)
+    {
+      if (err == GRUB_USB_ERR_STALL)
+       {
+         grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
+         goto retry;
+       }
+      return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed");;
+    }
+
+  /* Read/write the data.  */
+  if (read_write == 0)
+    {
+      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15, size, buf);
+      grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL);
+      if (err)
+       {
+         if (err == GRUB_USB_ERR_STALL)
+           {
+             grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+             goto retry;
+           }
+         return grub_error (GRUB_ERR_READ_ERROR,
+                            "can't read from USB Mass Storage device");
+       }
+    }
+  else 
+    {
+      err = grub_usb_bulk_write (dev->dev, dev->in->endp_addr & 15, size, buf);
+      grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL);
+      if (err)
+       {
+         if (err == GRUB_USB_ERR_STALL)
+           {
+             grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
+             goto retry;
+           }
+         return grub_error (GRUB_ERR_WRITE_ERROR,
+                            "can't write to USB Mass Storage device");
+       }
+    }
+
+  /* Read the status.  */
+  err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15,
+                           sizeof (status), (char *) &status);
+  if (err)
+    {
+      if (err == GRUB_USB_ERR_STALL)
+       {
+         grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+         goto retry;
+       }
+      return grub_error (GRUB_ERR_READ_ERROR,
+                        "can't read status from USB Mass Storage device");
+    }
+
+  /* XXX: Magic and check this code.  */
+  if (status.status == 2)
+    {
+      /* XXX: Phase error, reset device.  */
+      grub_usbms_reset (dev->dev, dev->interface);
+      grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+      grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
+
+      retrycnt--;
+      if (retrycnt)
+       goto retry;
+    }
+
+  if (status.status)
+    return grub_error (GRUB_ERR_READ_ERROR,
+                      "error communication with USB Mass Storage device");
+
+  return GRUB_ERR_NONE;
+}
+
+
+static grub_err_t
+grub_usbms_read (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
+                grub_size_t size, char *buf)
+{
+  return grub_usbms_tranfer (scsi, cmdsize, cmd, size, buf, 0);
+}
+
+static grub_err_t
+grub_usbms_write (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
+                 grub_size_t size, char *buf)
+{
+  return grub_usbms_tranfer (scsi, cmdsize, cmd, size, buf, 1);
+}
+
+static grub_err_t
+grub_usbms_open (const char *name, struct grub_scsi *scsi)
+{
+  grub_usbms_dev_t p;
+  int devnum;
+  int i = 0;
+
+  if (grub_strncmp (name, "usb", 3))
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+                      "not a USB Mass Storage device");
+
+  devnum = grub_strtoul (name + 3, NULL, 10);
+  for (p = grub_usbms_dev_list; p; p = p->next)
+    {
+      /* Check if this is the devnumth device.  */
+      if (devnum == i)
+       {
+         scsi->data = p;
+         scsi->name = grub_strdup (name);
+         scsi->luns = p->luns;
+         if (! scsi->name)
+           return grub_errno;
+
+         return GRUB_ERR_NONE;
+       }
+
+      i++;
+    }
+
+  return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+                    "not a USB Mass Storage device");
+}
+
+static void
+grub_usbms_close (struct grub_scsi *scsi)
+{
+  grub_free (scsi->name);
+}
+
+static struct grub_scsi_dev grub_usbms_dev =
+  {
+    .name = "usb",
+    .iterate = grub_usbms_iterate,
+    .open = grub_usbms_open,
+    .close = grub_usbms_close,
+    .read = grub_usbms_read,
+    .write = grub_usbms_write
+  }; 
+
+GRUB_MOD_INIT(usbms)
+{
+  grub_usbms_finddevs ();
+  grub_scsi_dev_register (&grub_usbms_dev);
+}
+
+GRUB_MOD_FINI(usbms)
+{
+  grub_scsi_dev_unregister (&grub_usbms_dev);
+}
Index: configure.ac
===================================================================
--- configure.ac        (revision 1830)
+++ configure.ac        (working copy)
@@ -367,6 +367,9 @@ AC_ARG_ENABLE([mm-debug], 
 AC_ARG_ENABLE([grub-emu],
              [AS_HELP_STRING([--enable-grub-emu],
                              [build and install the `grub-emu' debugging 
utility])])
+AC_ARG_ENABLE([grub-emu-usb],
+             [AS_HELP_STRING([--enable-grub-emu-usb],
+                             [build and install the `grub-emu' debugging 
utility with USB support])])
 [if [ x"$enable_grub_emu" = xyes ]; then
   # Check for curses libraries.]
   AC_CHECK_LIB([ncurses], [wgetch], [LIBCURSES="-lncurses"],
@@ -379,8 +382,20 @@ AC_ARG_ENABLE([grub-emu],
     [AC_CHECK_HEADERS([ncurses.h], [],
       [AC_CHECK_HEADERS([curses.h], [],
        [AC_MSG_ERROR([(n)curses header files are required to build 
`grub-emu'])])])])
+
+  [if [ x"$enable_grub_emu_usb" = xyes ]; then
+    # Check for libusb libraries.]
+    AC_CHECK_LIB([usb], [usb_claim_interface], [LIBUSB="-lusb"],
+      [AC_MSG_ERROR([libusb libraries are required to build `grub-emu' with 
USB support])])
+    AC_SUBST([LIBUSB])
+
+    [# Check for headers.]
+    AC_CHECK_HEADERS([usb.h], [],
+      [AC_MSG_ERROR([libusb header file is required to build `grub-emu' with 
USB support])])
+  [fi]
 [fi]
 AC_SUBST([enable_grub_emu])
+AC_SUBST([enable_grub_emu_usb])
 
 AC_ARG_ENABLE([grub-fstest],
              [AS_HELP_STRING([--enable-grub-fstest],
Index: include/grub/usb.h
===================================================================
--- include/grub/usb.h  (revision 0)
+++ include/grub/usb.h  (revision 0)
@@ -0,0 +1,207 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef        GRUB_USB_H
+#define        GRUB_USB_H      1
+
+#include <grub/usbdesc.h>
+#include <grub/usbtrans.h>
+
+typedef struct grub_usb_device *grub_usb_device_t;
+typedef struct grub_usb_controller *grub_usb_controller_t;
+typedef struct grub_usb_controller_dev *grub_usb_controller_dev_t;
+
+typedef enum
+  {
+    GRUB_USB_ERR_NONE,
+    GRUB_USB_ERR_INTERNAL,
+    GRUB_USB_ERR_STALL,
+    GRUB_USB_ERR_DATA,
+    GRUB_USB_ERR_NAK,
+    GRUB_USB_ERR_BABBLE,
+    GRUB_USB_ERR_TIMEOUT,
+    GRUB_USB_ERR_BITSTUFF
+  } grub_usb_err_t;
+
+typedef enum
+  {
+    GRUB_USB_SPEED_NONE,
+    GRUB_USB_SPEED_LOW,
+    GRUB_USB_SPEED_FULL,
+    GRUB_USB_SPEED_HIGH
+  } grub_usb_speed_t;
+
+/* Call HOOK with each device, until HOOK returns non-zero.  */
+int grub_usb_iterate (int (*hook) (grub_usb_device_t dev));
+
+grub_usb_err_t grub_usb_device_initialize (grub_usb_device_t dev);
+
+grub_usb_err_t grub_usb_get_descriptor (grub_usb_device_t dev,
+                                       grub_uint8_t type, grub_uint8_t index,
+                                       grub_size_t size, char *data);
+
+struct grub_usb_desc_endp *
+grub_usb_get_endpdescriptor (grub_usb_device_t usbdev, int addr);
+
+grub_usb_err_t grub_usb_clear_halt (grub_usb_device_t dev, int endpoint);
+
+
+grub_usb_err_t grub_usb_set_configuration (grub_usb_device_t dev,
+                                          int configuration);
+
+grub_usb_err_t grub_usb_get_string (grub_usb_device_t dev, grub_uint8_t index,
+                                   int langid, char **string);
+
+void grub_usb_controller_dev_register (grub_usb_controller_dev_t usb);
+
+void grub_usb_controller_dev_unregister (grub_usb_controller_dev_t usb);
+
+int grub_usb_controller_iterate (int (*hook) (grub_usb_controller_t dev));
+
+
+grub_usb_err_t grub_usb_control_msg (grub_usb_device_t dev, grub_uint8_t 
reqtype,
+                                    grub_uint8_t request, grub_uint16_t value,
+                                    grub_uint16_t index, grub_size_t size,
+                                    char *data);
+
+grub_usb_err_t
+grub_usb_bulk_read (grub_usb_device_t dev,
+                   int endpoint, grub_size_t size, char *data);
+grub_usb_err_t
+grub_usb_bulk_write (grub_usb_device_t dev,
+                    int endpoint, grub_size_t size, char *data);
+
+grub_usb_err_t
+grub_usb_root_hub (grub_usb_controller_t controller);
+
+
+/* XXX: All handled by libusb for now.  */
+struct grub_usb_controller_dev
+{
+  /* The device name.  */
+  const char *name;
+
+  int (*iterate) (int (*hook) (grub_usb_controller_t dev));
+
+  grub_usb_err_t (*transfer) (grub_usb_controller_t dev,
+                             grub_usb_transfer_t transfer);
+
+  int (*hubports) (grub_usb_controller_t dev);
+
+  grub_err_t (*portstatus) (grub_usb_controller_t dev, unsigned int port,
+                           unsigned int enable);
+
+  grub_usb_speed_t (*detect_dev) (grub_usb_controller_t dev, int port);
+
+  /* The next host controller.  */
+  struct grub_usb_controller_dev *next;
+};
+
+struct grub_usb_controller
+{
+  /* The underlying USB Host Controller device.  */
+  grub_usb_controller_dev_t dev;
+
+  /* Data used by the USB Host Controller Driver.  */
+  void *data;
+};
+
+
+struct grub_usb_interface
+{
+  struct grub_usb_desc_if *descif;
+
+  struct grub_usb_desc_endp *descendp;
+};
+
+struct grub_usb_configuration
+{
+  /* Configuration descriptors .  */
+  struct grub_usb_desc_config *descconf;
+
+  /* Interfaces associated to this configuration.  */
+  struct grub_usb_interface interf[32];
+};
+
+struct grub_usb_device
+{
+  /* The device descriptor of this device.  */
+  struct grub_usb_desc_device descdev;
+
+  /* The controller the device is connected to.  */
+  struct grub_usb_controller controller;
+
+  /* Device configurations (after opening the device).  */
+  struct grub_usb_configuration config[8];
+
+  /* Device address.  */
+  int addr;
+
+  /* Device speed.  */
+  grub_usb_speed_t speed;
+
+  /* All desciptors are read if this is set to 1.  */
+  int initialized;
+
+  /* Data toggle values (used for bulk transfers only).  */
+  int toggle[16];
+
+  /* Device-specific data.  */
+  void *data;
+};
+
+
+
+typedef enum
+  {
+    GRUB_USB_CLASS_NOTHERE,
+    GRUB_USB_CLASS_AUDIO,
+    GRUB_USB_CLASS_COMMUNICATION,
+    GRUB_USB_CLASS_HID,
+    GRUB_USB_CLASS_XXX,
+    GRUB_USB_CLASS_PHYSICAL,
+    GRUB_USB_CLASS_IMAGE,
+    GRUB_USB_CLASS_PRINTER,
+    GRUB_USB_CLASS_MASS_STORAGE,
+    GRUB_USB_CLASS_HUB,
+    GRUB_USB_CLASS_DATA_INTERFACE,
+    GRUB_USB_CLASS_SMART_CARD,
+    GRUB_USB_CLASS_CONTENT_SECURITY,
+    GRUB_USB_CLASS_VIDEO
+  } grub_usb_classes_t;
+
+typedef enum
+  {
+    GRUB_USBMS_SUBCLASS_BULK = 0x06
+  } grub_usbms_subclass_t;
+
+typedef enum
+  {
+    GRUB_USBMS_PROTOCOL_BULK = 0x50
+  } grub_usbms_protocol_t;
+
+static inline struct grub_usb_desc_if *
+grub_usb_get_config_interface (struct grub_usb_desc_config *config)
+{
+  struct grub_usb_desc_if *interf;
+
+  interf = (struct grub_usb_desc_if *) (sizeof (*config) + (char *) config);
+  return interf;
+}
+
+#endif /* GRUB_USB_H */
Index: include/grub/err.h
===================================================================
--- include/grub/err.h  (revision 1830)
+++ include/grub/err.h  (working copy)
@@ -52,7 +52,8 @@ typedef enum
     GRUB_ERR_SYMLINK_LOOP,
     GRUB_ERR_BAD_GZIP_DATA,
     GRUB_ERR_MENU,
-    GRUB_ERR_TIMEOUT
+    GRUB_ERR_TIMEOUT,
+    GRUB_ERR_IO
   }
 grub_err_t;
 
Index: include/grub/usbtrans.h
===================================================================
--- include/grub/usbtrans.h     (revision 0)
+++ include/grub/usbtrans.h     (revision 0)
@@ -0,0 +1,107 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef        GRUB_USBTRANS_H
+#define        GRUB_USBTRANS_H 1
+
+typedef enum
+  {
+    GRUB_USB_TRANSFER_TYPE_IN,
+    GRUB_USB_TRANSFER_TYPE_OUT,
+    GRUB_USB_TRANSFER_TYPE_SETUP
+  } grub_transfer_type_t;
+
+typedef enum
+  {
+    GRUB_USB_TRANSACTION_TYPE_CONTROL,
+    GRUB_USB_TRANSACTION_TYPE_BULK
+  } grub_transaction_type_t;
+
+struct grub_usb_transaction
+{
+  int size;
+  int toggle;
+  grub_transfer_type_t pid;
+  char *data;
+};
+typedef struct grub_usb_transaction *grub_usb_transaction_t;
+
+struct grub_usb_transfer
+{
+  int devaddr;
+
+  int endpoint;
+
+  int size;
+
+  int transcnt;
+
+  int max;
+
+  grub_transaction_type_t type;
+
+  struct grub_usb_device *dev;
+
+  struct grub_usb_transaction *transactions;
+};
+typedef struct grub_usb_transfer *grub_usb_transfer_t;
+
+
+#define GRUB_USB_REQTYPE_IN            (1 << 7)
+#define GRUB_USB_REQTYPE_OUT           (0 << 7)
+#define GRUB_USB_REQTYPE_STANDARD      (0 << 5)
+#define GRUB_USB_REQTYPE_CLASS         (1 << 5)
+#define GRUB_USB_REQTYPE_VENDOR                (2 << 5)
+#define GRUB_USB_REQTYPE_TARGET_DEV    (0 << 0)
+#define GRUB_USB_REQTYPE_TARGET_INTERF (1 << 0)
+#define GRUB_USB_REQTYPE_TARGET_ENDP   (2 << 0)
+#define GRUB_USB_REQTYPE_TARGET_OTHER  (3 << 0)
+
+#define GRUB_USB_REQ_GET_STATUS                0x00
+#define GRUB_USB_REQ_CLEAR_FEATURE     0x01
+#define GRUB_USB_REQ_SET_FEATURE       0x03
+#define GRUB_USB_REQ_SET_ADDRESS       0x05
+#define GRUB_USB_REQ_GET_DESCRIPTOR    0x06
+#define GRUB_USB_REQ_SET_DESCRIPTOR    0x07
+#define GRUB_USB_REQ_GET_CONFIGURATION 0x08
+#define GRUB_USB_REQ_SET_CONFIGURATION 0x09
+#define GRUB_USB_REQ_GET_INTERFACE     0x0A
+#define GRUB_USB_REQ_SET_INTERFACE     0x0B
+#define GRUB_USB_REQ_SYNC_FRAME                0x0C
+
+#define GRUB_USB_REQ_HUB_GET_PORT_STATUS 0x00
+
+#define GRUB_USB_FEATURE_ENDP_HALT     0x01
+#define GRUB_USB_FEATURE_DEV_REMOTE_WU 0x02
+#define GRUB_USB_FEATURE_TEST_MODE     0x04
+
+#define GRUB_USB_HUB_STATUS_CONNECTED  (1 << 0)
+#define GRUB_USB_HUB_STATUS_LOWSPEED   (1 << 9)
+#define GRUB_USB_HUB_STATUS_HIGHSPEED  (1 << 10)
+
+struct grub_usb_packet_setup
+{
+  grub_uint8_t reqtype;
+  grub_uint8_t request;
+  grub_uint16_t value;
+  grub_uint16_t index;
+  grub_uint16_t length;
+} __attribute__((packed));
+
+
+#endif /* GRUB_USBTRANS_H */
Index: include/grub/usbdesc.h
===================================================================
--- include/grub/usbdesc.h      (revision 0)
+++ include/grub/usbdesc.h      (revision 0)
@@ -0,0 +1,119 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef        GRUB_USBDESC_H
+#define        GRUB_USBDESC_H  1
+
+#include <grub/types.h>
+#include <grub/symbol.h>
+
+typedef enum {
+  GRUB_USB_DESCRIPTOR_DEVICE = 1,
+  GRUB_USB_DESCRIPTOR_CONFIG,
+  GRUB_USB_DESCRIPTOR_STRING,
+  GRUB_USB_DESCRIPTOR_INTERFACE,
+  GRUB_USB_DESCRIPTOR_ENDPOINT,
+  GRUB_USB_DESCRIPTOR_HUB = 0x29
+} grub_usb_descriptor_t;
+
+struct grub_usb_desc_device
+{
+  grub_uint8_t length;
+  grub_uint8_t type;
+  grub_uint16_t usbrel;
+  grub_uint8_t class;
+  grub_uint8_t subclass;
+  grub_uint8_t protocol;
+  grub_uint8_t maxsize0;
+  grub_uint16_t vendorid;
+  grub_uint16_t prodid;
+  grub_uint16_t devrel;
+  grub_uint8_t strvendor;
+  grub_uint8_t strprod;
+  grub_uint8_t strserial;
+  grub_uint8_t configcnt;  
+} __attribute__ ((packed));
+
+struct grub_usb_desc_config
+{
+  grub_uint8_t length;
+  grub_uint8_t type;
+  grub_uint16_t totallen;
+  grub_uint8_t numif;
+  grub_uint8_t config;
+  grub_uint8_t strconfig;
+  grub_uint8_t attrib;
+  grub_uint8_t maxpower;
+} __attribute__ ((packed));
+
+#if 0
+struct grub_usb_desc_ifassosiation
+{
+  grub_uint8_t length;
+  grub_uint8_t type;
+  grub_uint8_t firstif;
+  grub_uint8_t ifcnt;
+  grub_uint8_t class;
+  grub_uint8_t subclass;
+  grub_uint8_t protocol;
+  grub_uint8_t function;
+} __attribute__ ((packed));
+#endif
+
+struct grub_usb_desc_if
+{
+  grub_uint8_t length;
+  grub_uint8_t type;
+  grub_uint8_t ifnum;
+  grub_uint8_t altsetting;
+  grub_uint8_t endpointcnt;
+  grub_uint8_t class;
+  grub_uint8_t subclass;
+  grub_uint8_t protocol;
+  grub_uint8_t strif;
+} __attribute__ ((packed));
+
+struct grub_usb_desc_endp
+{
+  grub_uint8_t length;
+  grub_uint8_t type;
+  grub_uint8_t endp_addr;
+  grub_uint8_t attrib;
+  grub_uint16_t maxpacket;
+  grub_uint8_t interval;
+} __attribute__ ((packed));
+
+struct grub_usb_desc_str
+{
+  grub_uint8_t length;
+  grub_uint8_t type;
+  grub_uint16_t str[0];
+} __attribute__ ((packed));
+
+struct grub_usb_usb_hubdesc
+{
+  grub_uint8_t length;
+  grub_uint8_t type;
+  grub_uint8_t portcnt;
+  grub_uint16_t characteristics;
+  grub_uint8_t pwdgood;
+  grub_uint8_t current;
+  /* Removable and power control bits follow.  */
+} __attribute__ ((packed));
+
+#endif /* GRUB_USBDESC_H */
Index: bus/usb/usbtrans.c
===================================================================
--- bus/usb/usbtrans.c  (revision 0)
+++ bus/usb/usbtrans.c  (revision 0)
@@ -0,0 +1,212 @@
+/* usbtrans.c - USB Transfers and Transactions.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/usb.h>
+#include <grub/usbtrans.h>
+
+grub_usb_err_t
+grub_usb_control_msg (grub_usb_device_t dev,
+                     grub_uint8_t reqtype,
+                     grub_uint8_t request,
+                     grub_uint16_t value,
+                     grub_uint16_t index,
+                     grub_size_t size, char *data)
+{
+  int i;
+  grub_usb_transfer_t transfer;
+  int datablocks;
+  struct grub_usb_packet_setup setupdata;
+  grub_usb_err_t err;
+  int max;
+
+  grub_dprintf ("usb",
+               "control: reqtype=0x%02x req=0x%02x val=0x%02x idx=0x%02x 
size=%d\n",
+               reqtype, request,  value, index, size);
+
+  /* Create a transfer.  */
+  transfer = grub_malloc (sizeof (struct grub_usb_transfer));
+  if (! transfer)
+    return grub_errno;
+
+  /* Determine the maximum packet size.  */
+  if (dev->initialized)
+    max = dev->descdev.maxsize0;
+  else
+    max = 64;
+
+  datablocks = (size + max - 1) / max;
+  
+  /* XXX: Discriminate between different types of control
+     messages.  */
+  transfer->transcnt = datablocks + 2;
+  transfer->size = size; /* XXX ? */
+  transfer->endpoint = 0;
+  transfer->devaddr = dev->addr;
+  transfer->type = GRUB_USB_TRANSACTION_TYPE_CONTROL;
+  transfer->max = max;
+  transfer->dev = dev;
+
+  /* Allocate an array of transfer data structures.  */
+  transfer->transactions = grub_malloc (transfer->transcnt
+                                       * sizeof (struct grub_usb_transfer));
+  if (! transfer->transactions)
+    {
+      grub_free (transfer);
+      return grub_errno;
+    }
+
+  /* Build a Setup packet.  XXX: Endianess.  */
+  setupdata.reqtype = reqtype;
+  setupdata.request = request;
+  setupdata.value = value;
+  setupdata.index = index;
+  setupdata.length = size;
+  transfer->transactions[0].size = sizeof (setupdata);
+  transfer->transactions[0].pid = GRUB_USB_TRANSFER_TYPE_SETUP;
+  transfer->transactions[0].data = (char *) &setupdata;
+  transfer->transactions[0].toggle = 0;
+
+  /* Now the data...  XXX: Is this the right way to transfer control
+     transfers?  */
+  for (i = 0; i < datablocks; i++)
+    {
+      grub_usb_transaction_t tr = &transfer->transactions[i + 1];
+
+      tr->size = (size > max) ? max : size;
+      /* Use the right most bit as the data toggle.  Simple and
+        effective.  */
+      tr->toggle = !(i & 1);
+      if (reqtype & 128)
+       tr->pid = GRUB_USB_TRANSFER_TYPE_IN;
+      else
+       tr->pid = GRUB_USB_TRANSFER_TYPE_OUT;
+      tr->data = &data[i * max];
+      size -= max;
+    }
+
+  /* End with an empty OUT transaction.  */
+  transfer->transactions[datablocks + 1].size = 0;
+  transfer->transactions[datablocks + 1].data = NULL;
+  if (reqtype & 128)
+    transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_OUT;
+  else
+    transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_IN;
+  
+  transfer->transactions[datablocks + 1].toggle = 1;
+
+  err = dev->controller.dev->transfer (&dev->controller, transfer);
+
+  grub_free (transfer->transactions);
+  grub_free (transfer);
+
+  return err;
+}
+
+static grub_usb_err_t
+grub_usb_bulk_readwrite (grub_usb_device_t dev,
+                        int endpoint, grub_size_t size, char *data,
+                        grub_transfer_type_t type)
+{
+  int i;
+  grub_usb_transfer_t transfer;
+  int datablocks;
+  unsigned int max;
+  grub_usb_err_t err;
+  int toggle = dev->toggle[endpoint];
+
+  /* Use the maximum packet size given in the endpoint descriptor.  */
+  if (dev->initialized)
+    {
+      struct grub_usb_desc_endp *endpdesc;
+      endpdesc = grub_usb_get_endpdescriptor (dev, 0);
+
+      if (endpdesc)
+       max = endpdesc->maxpacket;
+      else
+       max = 64;
+    }
+  else
+    max = 64;
+
+  /* Create a transfer.  */
+  transfer = grub_malloc (sizeof (struct grub_usb_transfer));
+  if (! transfer)
+    return grub_errno;
+
+  datablocks = ((size + max - 1) / max);
+  transfer->transcnt = datablocks;
+  transfer->size = size - 1;
+  transfer->endpoint = endpoint;
+  transfer->devaddr = dev->addr;
+  transfer->type = GRUB_USB_TRANSACTION_TYPE_BULK;
+  transfer->max = max;
+  transfer->dev = dev;
+
+  /* Allocate an array of transfer data structures.  */
+  transfer->transactions = grub_malloc (transfer->transcnt
+                                       * sizeof (struct grub_usb_transfer));
+  if (! transfer->transactions)
+    {
+      grub_free (transfer);
+      return grub_errno;
+    }
+
+  /* Set up all transfers.  */
+  for (i = 0; i < datablocks; i++)
+    {
+      grub_usb_transaction_t tr = &transfer->transactions[i];
+
+      tr->size = (size > max) ? max : size;
+      /* XXX: Use the right most bit as the data toggle.  Simple and
+        effective.  */
+      tr->toggle = toggle;
+      toggle = toggle ? 0 : 1;
+      tr->pid = type;
+      tr->data = &data[i * max];
+      size -= tr->size;
+    }
+ 
+  err = dev->controller.dev->transfer (&dev->controller, transfer);
+  grub_dprintf ("usb", "toggle=%d\n", toggle);
+  dev->toggle[endpoint] = toggle;
+
+  grub_free (transfer->transactions);
+  grub_free (transfer);
+
+  return err;
+}
+
+grub_usb_err_t
+grub_usb_bulk_write (grub_usb_device_t dev,
+                    int endpoint, grub_size_t size, char *data)
+{
+  return grub_usb_bulk_readwrite (dev, endpoint, size, data,
+                                 GRUB_USB_TRANSFER_TYPE_OUT);
+}
+
+grub_usb_err_t
+grub_usb_bulk_read (grub_usb_device_t dev,
+                   int endpoint, grub_size_t size, char *data)
+{
+  return grub_usb_bulk_readwrite (dev, endpoint, size, data,
+                                 GRUB_USB_TRANSFER_TYPE_IN);
+}
Index: bus/usb/ohci.c
===================================================================
--- bus/usb/ohci.c      (revision 0)
+++ bus/usb/ohci.c      (revision 0)
@@ -0,0 +1,608 @@
+/* ohci.c - OHCI Support.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/usb.h>
+#include <grub/usbtrans.h>
+#include <grub/misc.h>
+#include <grub/pci.h>
+#include <grub/cpu/pci.h>
+#include <grub/i386/io.h>
+#include <grub/time.h>
+
+struct grub_ohci_hcca
+{
+  /* Pointers to Interrupt Endpoint Descriptors.  Not used by
+     GRUB.  */
+  grub_uint32_t inttable[32];
+
+  /* Current frame number.  */
+  grub_uint16_t framenumber;
+
+  grub_uint16_t pad;
+
+  /* List of completed TDs.  */
+  grub_uint32_t donehead;
+
+  grub_uint8_t reserved[116];
+} __attribute__((packed));
+
+/* OHCI Endpoint Descriptor.  */
+struct grub_ohci_ed
+{
+  grub_uint32_t target;
+  grub_uint32_t td_tail;
+  grub_uint32_t td_head;
+  grub_uint32_t next_ed;
+} __attribute__((packed));
+
+struct grub_ohci_td
+{
+  /* Information used to construct the TOKEN packet.  */
+  grub_uint32_t token;
+
+  grub_uint32_t buffer;
+  grub_uint32_t next_td;
+  grub_uint32_t buffer_end;
+} __attribute__((packed));
+
+typedef struct grub_ohci_td *grub_ohci_td_t;
+typedef struct grub_ohci_ed *grub_ohci_ed_t;
+
+struct grub_ohci
+{
+  volatile grub_uint32_t *iobase;
+  volatile struct grub_ohci_hcca *hcca;
+  struct grub_ohci *next;
+};
+
+static struct grub_ohci *ohci;
+
+typedef enum
+{
+  GRUB_OHCI_REG_REVISION = 0x00,
+  GRUB_OHCI_REG_CONTROL,
+  GRUB_OHCI_REG_CMDSTATUS,
+  GRUB_OHCI_REG_INTSTATUS,
+  GRUB_OHCI_REG_INTENA,
+  GRUB_OHCI_REG_INTDIS,
+  GRUB_OHCI_REG_HCCA,
+  GRUB_OHCI_REG_PERIODIC,
+  GRUB_OHCI_REG_CONTROLHEAD,
+  GRUB_OHCI_REG_CONTROLCURR,
+  GRUB_OHCI_REG_BULKHEAD,
+  GRUB_OHCI_REG_BULKCURR,
+  GRUB_OHCI_REG_DONEHEAD,
+  GRUB_OHCI_REG_FRAME_INTERVAL,
+  GRUB_OHCI_REG_RHUBA = 18,
+  GRUB_OHCI_REG_RHUBPORT = 21
+} grub_ohci_reg_t;
+
+static grub_uint32_t
+grub_ohci_readreg32 (struct grub_ohci *o, grub_ohci_reg_t reg)
+{
+  return grub_le_to_cpu32 (*(o->iobase + reg));
+}
+
+static void
+grub_ohci_writereg32 (struct grub_ohci *o,
+                     grub_ohci_reg_t reg, grub_uint32_t val)
+{
+  *(o->iobase + reg) = grub_cpu_to_le32 (val);
+}
+
+
+
+/* Iterate over all PCI devices.  Determine if a device is an OHCI
+   controller.  If this is the case, initialize it.  */
+static int grub_ohci_pci_iter (int bus, int device, int func,
+                              grub_pci_id_t pciid __attribute__((unused)))
+{
+  grub_uint32_t class;
+  grub_uint32_t subclass;
+  int interf;
+  grub_uint32_t base;
+  grub_pci_address_t addr;
+  struct grub_ohci *o;
+  grub_uint32_t revision;
+  grub_uint32_t frame_interval;
+
+  addr = grub_pci_make_address (bus, device, func, 2);
+  class = grub_pci_read (addr);
+  addr = grub_pci_make_address (bus, device, func, 2);
+  class = grub_pci_read (addr);
+
+  interf = class & 0xFF;
+  subclass = (class >> 16) & 0xFF;
+  class >>= 24;
+
+  /* If this is not an OHCI controller, just return.  */
+  if (class != 0x0c || subclass != 0x03)
+    return 0;
+
+  /* Determine IO base address.  */
+  addr = grub_pci_make_address (bus, device, func, 4);
+  base = grub_pci_read (addr);
+
+#if 0
+  /* Stop if there is no IO space base address defined.  */
+  if (! (base & 1))
+    return 0;
+#endif
+
+  /* Allocate memory for the controller and register it.  */
+  o = grub_malloc (sizeof (*o));
+  if (! o)
+    return 1;
+
+  /* Link in the OHCI.  */
+  o->next = ohci;
+  ohci = o;
+  o->iobase = (grub_uint32_t *) base;
+
+  /* Reserve memory for the HCCA.  */
+  o->hcca = (struct grub_ohci_hcca *) grub_memalign (256, 256);
+
+  /* Check if the OHCI revision is actually 1.0 as supported.  */
+  revision = grub_ohci_readreg32 (o, GRUB_OHCI_REG_REVISION);
+  grub_dprintf ("ohci", "OHCI revision=0x%02x\n", revision & 0xFF);
+  if ((revision & 0xFF) != 0x10)
+    goto fail;
+
+  /* Backup the frame interval register.  */
+  frame_interval = grub_ohci_readreg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL);
+
+  /* Suspend the OHCI by issuing a reset.  */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic.
+                                                         */
+  grub_millisleep (1);
+  grub_dprintf ("ohci", "OHCI reset\n");
+
+  /* Restore the frame interval register.  */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL, frame_interval);
+
+  /* Setup the HCCA.  */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, (grub_uint32_t) o->hcca);
+  grub_dprintf ("ohci", "OHCI HCCA\n");
+ 
+  /* Enable the OHCI.  */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
+                       (2 << 6));
+  grub_dprintf ("ohci", "OHCI enable: 0x%02x\n",
+               (grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) >> 6) & 3);
+ 
+  return 0;
+
+ fail:
+  if (o)
+    grub_free ((void *) o->hcca);
+  grub_free (o);
+
+  return 1;
+}
+
+
+static void
+grub_ohci_inithw (void)
+{
+  grub_pci_iterate (grub_ohci_pci_iter);
+}
+
+
+
+static int
+grub_ohci_iterate (int (*hook) (grub_usb_controller_t dev))
+{
+  struct grub_ohci *o;
+  struct grub_usb_controller dev;
+
+  for (o = ohci; o; o = o->next)
+    {
+      dev.data = o;
+      if (hook (&dev))
+       return 1;
+    }
+
+  return 0;
+}
+
+static void
+grub_ohci_transaction (grub_ohci_td_t td, 
+                      grub_transfer_type_t type, unsigned int toggle,
+                      grub_size_t size, char *data)
+{
+  grub_uint32_t token;
+  grub_uint32_t buffer;
+  grub_uint32_t buffer_end;
+
+ grub_dprintf ("ohci", "OHCI transaction td=0x%02x type=%d, toggle=%d, 
size=%d\n",
+              td, type, toggle, size);
+ 
+  switch (type)
+    {
+    case GRUB_USB_TRANSFER_TYPE_SETUP:
+      token = 0 << 19;
+      break;
+    case GRUB_USB_TRANSFER_TYPE_IN:
+      token = 2 << 19;
+      break;
+    case GRUB_USB_TRANSFER_TYPE_OUT:
+      token = 1 << 19;
+      break;
+    default:
+      token = 0;
+      break;
+    }
+
+  /* Generate no interrupts.  */
+  token |= 7 << 21;
+
+  /* Set the token.  */
+  token |= toggle << 24;
+  token |= 1 << 25;
+
+  buffer = (grub_uint32_t) data;
+  buffer_end = buffer + size - 1;
+
+  td->token = grub_cpu_to_le32 (token);
+  td->buffer = grub_cpu_to_le32 (buffer);
+  td->next_td = 0;
+  td->buffer_end = grub_cpu_to_le32 (buffer_end);
+}
+
+static grub_usb_err_t
+grub_ohci_transfer (grub_usb_controller_t dev,
+                   grub_usb_transfer_t transfer)
+{
+  struct grub_ohci *o = (struct grub_ohci *) dev->data;
+  grub_ohci_ed_t ed;
+  grub_ohci_td_t td_list;
+  grub_uint32_t target;
+  grub_uint32_t td_tail;
+  grub_uint32_t td_head;
+  grub_uint32_t status;
+  grub_uint32_t control;
+  grub_usb_err_t err;
+  int i;
+
+  /* Allocate an Endpoint Descriptor.  */
+  ed = grub_memalign (16, sizeof (*ed));
+  if (! ed)
+    return GRUB_USB_ERR_INTERNAL;
+
+  td_list = grub_memalign (16, sizeof (*td_list) * (transfer->transcnt + 1));
+  if (! td_list)
+    {
+      grub_free ((void *) ed);
+      return GRUB_USB_ERR_INTERNAL;
+    }
+
+  grub_dprintf ("ohci", "alloc=0x%08x\n", td_list);
+
+  /* Setup all Transfer Descriptors.  */
+  for (i = 0; i < transfer->transcnt; i++)
+    {
+      grub_usb_transaction_t tr = &transfer->transactions[i];
+
+      grub_ohci_transaction (&td_list[i], tr->pid, tr->toggle,
+                            tr->size, tr->data); 
+
+      td_list[i].next_td = grub_cpu_to_le32 (&td_list[i + 1]);
+    }
+
+  /* Setup the Endpoint Descriptor.  */
+
+  /* Set the device address.  */
+  target = transfer->devaddr;
+
+  /* Set the endpoint.  */
+  target |= transfer->endpoint << 7;
+
+  /* Set the device speed.  */
+  target |= (transfer->dev->speed == GRUB_USB_SPEED_LOW) << 13;
+
+  /* Set the maximum packet size.  */
+  target |= transfer->max << 16;
+
+  td_head = (grub_uint32_t) td_list;
+
+  td_tail = (grub_uint32_t) &td_list[transfer->transcnt];
+
+  ed->target = grub_cpu_to_le32 (target);
+  ed->td_head = grub_cpu_to_le32 (td_head);
+  ed->td_tail = grub_cpu_to_le32 (td_tail);
+  ed->next_ed = grub_cpu_to_le32 (0);
+
+  grub_dprintf ("ohci", "program OHCI\n");
+
+  /* Program the OHCI to actually transfer.  */
+  switch (transfer->type)
+    {
+    case GRUB_USB_TRANSACTION_TYPE_BULK:
+      {
+       grub_dprintf ("ohci", "add to bulk list\n");
+
+       status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
+       control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+
+       /* Disable the Control and Bulk lists.  */
+       control &= ~(3 << 4);
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
+
+       /* Clear BulkListFilled.  */
+       status &= ~(1 << 2);
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
+
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, (grub_uint32_t) ed);
+
+       /* Enable the Bulk list.  */
+       control |= 1 << 5;
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
+
+       /* Set BulkListFilled.  */
+       status |= 1 << 2;
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
+
+       break;
+      }
+
+    case GRUB_USB_TRANSACTION_TYPE_CONTROL:
+      {
+       grub_dprintf ("ohci", "add to control list\n");
+       status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
+       control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+
+       /* Disable the Control and Bulk lists.  */
+       control &= ~(3 << 4);
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
+
+       /* Clear ControlListFilled.  */
+       status &= ~(1 << 1);
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
+
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD,
+                             (grub_uint32_t) ed);
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD+1,
+                             (grub_uint32_t) ed);
+
+       /* Enable the Control list.  */
+       control |= 1 << 4;
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
+
+       /* Set ControlListFilled.  */
+       status |= 1 << 1;
+       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
+       break;
+      }
+    }
+
+  grub_dprintf ("ohci", "wait for completion\n");
+  grub_dprintf ("ohci", "control=0x%02x status=0x%02x\n",
+               grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL),
+               grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS));
+
+  /* Wait until the transfer is completed or STALLs.  */
+  while ((ed->td_head & ~0xf) != (ed->td_tail & ~0xf))
+    {
+      grub_cpu_idle ();
+
+      grub_dprintf ("ohci", "head=0x%02x tail=0x%02x\n", ed->td_head, 
ed->td_tail);
+
+      /* Detected a STALL.  */
+      if (ed->td_head & 1)
+       break;
+    }
+
+  grub_dprintf ("ohci", "complete\n");
+
+/*   if (ed->td_head & 1) */
+/*     err = GRUB_USB_ERR_STALL; */
+/*   else if (ed->td */
+
+
+  if (ed->td_head & 1)
+    {
+      grub_uint8_t errcode;
+      grub_ohci_td_t tderr;
+
+      tderr = (grub_ohci_td_t) grub_ohci_readreg32 (o,
+                                                   GRUB_OHCI_REG_DONEHEAD);
+      errcode = tderr->token >> 28;
+
+      switch (errcode)
+       {
+       case 0:
+         /* XXX: Should not happen!  */
+         grub_error (GRUB_ERR_IO, "OHCI without reporting the reason");
+         err = GRUB_USB_ERR_INTERNAL;
+         break;
+
+       case 1:
+         /* XXX: CRC error.  */
+         err = GRUB_USB_ERR_TIMEOUT;
+         break;
+
+       case 2:
+         err = GRUB_USB_ERR_BITSTUFF;
+         break;
+
+       case 3:
+         /* XXX: Data Toggle error.  */
+         err = GRUB_USB_ERR_DATA;
+         break;
+
+       case 4:
+         err = GRUB_USB_ERR_STALL;
+         break;
+
+       case 5:
+         /* XXX: Not responding.  */
+         err = GRUB_USB_ERR_TIMEOUT;
+         break;
+
+       case 6:
+         /* XXX: PID Check bits failed.  */
+         err = GRUB_USB_ERR_BABBLE;
+         break;
+
+       case 7:
+         /* XXX: PID unexpected failed.  */
+         err = GRUB_USB_ERR_BABBLE;
+         break;
+
+       case 8:
+         /* XXX: Data overrun error.  */
+         err = GRUB_USB_ERR_DATA;
+         break;
+
+       case 9:
+         /* XXX: Data underrun error.  */
+         err = GRUB_USB_ERR_DATA;
+         break;
+
+       case 10:
+         /* XXX: Reserved.  */
+         err = GRUB_USB_ERR_NAK;
+         break;
+
+       case 11:
+         /* XXX: Reserved.  */
+         err = GRUB_USB_ERR_NAK;
+         break;
+
+       case 12:
+         /* XXX: Buffer overrun.  */
+         err = GRUB_USB_ERR_DATA;
+         break;
+
+       case 13:
+         /* XXX: Buffer underrun.  */
+         err = GRUB_USB_ERR_DATA;
+         break;
+
+       default:
+         err = GRUB_USB_ERR_NAK;
+         break;
+       }
+    }
+  else
+    err = GRUB_USB_ERR_NONE;
+
+  /* Disable the Control and Bulk lists.  */
+  control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+  control &= ~(3 << 4);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
+
+  /* Clear BulkListFilled and ControlListFilled.  */
+  status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
+  status &= ~((1 << 2) | (1 << 3));
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
+
+  /* XXX */
+  grub_free (td_list);
+  grub_free (ed);
+
+  return err;
+}
+
+static grub_err_t
+grub_ohci_portstatus (grub_usb_controller_t dev,
+                     unsigned int port, unsigned int enable)
+{
+   struct grub_ohci *o = (struct grub_ohci *) dev->data;
+   grub_uint32_t status;
+
+   /* Reset the port.  */
+   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
+   status |= (1 << 4); /* XXX: Magic.  */
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
+   grub_millisleep (100);
+
+   /* End the reset signaling.  */
+   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
+   status |= (1 << 20); /* XXX: Magic.  */
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
+   grub_millisleep (10);
+
+   /* Enable the port.  */
+   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
+   status |= (enable << 1); /* XXX: Magic.  */
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
+
+   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
+   grub_dprintf ("ohci", "portstatus=0x%02x\n", status);
+
+   return GRUB_ERR_NONE;
+}
+
+static grub_usb_speed_t
+grub_ohci_detect_dev (grub_usb_controller_t dev, int port)
+{
+   struct grub_ohci *o = (struct grub_ohci *) dev->data;
+   grub_uint32_t status;
+
+   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
+
+   grub_dprintf ("ohci", "detect_dev status=0x%02x\n", status);
+
+   if (! (status & 1))
+     return GRUB_USB_SPEED_NONE;
+   else if (status & (1 << 9))
+     return GRUB_USB_SPEED_LOW;
+   else
+     return GRUB_USB_SPEED_FULL;
+}
+
+static int
+grub_ohci_hubports (grub_usb_controller_t dev)
+{
+  struct grub_ohci *o = (struct grub_ohci *) dev->data;
+  grub_uint32_t portinfo;
+
+  portinfo = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA);
+
+  grub_dprintf ("ohci", "root hub ports=%d\n", portinfo & 0xFF);
+
+  /* The root hub has exactly two ports.  */
+  return portinfo & 0xFF;
+}
+
+
+
+static struct grub_usb_controller_dev usb_controller =
+{
+  .name = "ohci",
+  .iterate = grub_ohci_iterate,
+  .transfer = grub_ohci_transfer,
+  .hubports = grub_ohci_hubports,
+  .portstatus = grub_ohci_portstatus,
+  .detect_dev = grub_ohci_detect_dev
+};
+
+GRUB_MOD_INIT(ohci)
+{
+  grub_ohci_inithw ();
+  grub_usb_controller_dev_register (&usb_controller);
+}
+
+GRUB_MOD_FINI(ohci)
+{
+  grub_usb_controller_dev_unregister (&usb_controller);  
+}
Index: bus/usb/uhci.c
===================================================================
--- bus/usb/uhci.c      (revision 0)
+++ bus/usb/uhci.c      (revision 0)
@@ -0,0 +1,675 @@
+/* uhci.c - UHCI Support.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/usb.h>
+#include <grub/usbtrans.h>
+#include <grub/pci.h>
+#include <grub/cpu/pci.h>
+#include <grub/i386/io.h>
+#include <grub/time.h>
+
+#define GRUB_UHCI_IOMASK       (0x7FF << 5)
+
+typedef enum
+  {
+    GRUB_UHCI_REG_USBCMD = 0x00,
+    GRUB_UHCI_REG_FLBASEADD = 0x08,
+    GRUB_UHCI_REG_PORTSC1 = 0x10,
+    GRUB_UHCI_REG_PORTSC2 = 0x12
+  } grub_uhci_reg_t;
+
+#define GRUB_UHCI_LINK_TERMINATE       1
+#define GRUB_UHCI_LINK_QUEUE_HEAD      2
+
+
+/* UHCI Queue Head.  */
+struct grub_uhci_qh
+{
+  /* Queue head link pointer which points to the next queue head.  */
+  grub_uint32_t linkptr;
+
+  /* Queue element link pointer which points to the first data object
+     within the queue.  */
+  grub_uint32_t elinkptr;
+
+  /* Queue heads are aligned on 16 bytes, pad so a queue head is 16
+     bytes so we can store many in a 4K page.  */
+  grub_uint8_t pad[8];
+} __attribute__ ((packed));
+
+/* UHCI Tranfer Descriptor.  */
+struct grub_uhci_td
+{
+  /* Pointer to the next TD in the list.  */
+  grub_uint32_t linkptr;
+
+  /* Control and status bits.  */
+  grub_uint32_t ctrl_status;
+
+  /* All information required to transfer the Token packet.  */
+  grub_uint32_t token;
+
+  /* A pointer to the data buffer, UHCI requires this pointer to be 32
+     bits.  */
+  grub_uint32_t buffer;
+
+  /* Another linkptr that is not overwritten by the Host Controller.
+     This is GRUB specific.  */
+  grub_uint32_t linkptr2;
+
+  /* 3 additional 32 bits words reserved for the Host Controller Driver.  */
+  grub_uint32_t data[3];
+} __attribute__ ((packed));
+
+typedef volatile struct grub_uhci_td *grub_uhci_td_t;
+typedef volatile struct grub_uhci_qh *grub_uhci_qh_t;
+
+struct grub_uhci
+{
+  int iobase;
+  grub_uint32_t *framelist;
+
+  /* 256 Queue Heads.  */
+  grub_uhci_qh_t qh;
+
+  /* 256 Transfer Descriptors.  */
+  grub_uhci_td_t td;
+
+  /* Free Transfer Descriptors.  */
+  grub_uhci_td_t tdfree;
+
+  struct grub_uhci *next;
+};
+
+static struct grub_uhci *uhci;
+
+static grub_uint16_t
+grub_uhci_readreg16 (struct grub_uhci *u, grub_uhci_reg_t reg)
+{
+  return grub_inw (u->iobase + reg);
+}
+
+#if 0
+static grub_uint32_t
+grub_uhci_readreg32 (struct grub_uhci *u, grub_uhci_reg_t reg)
+{
+  return grub_inl (u->iobase + reg);
+}
+#endif
+
+static void
+grub_uhci_writereg16 (struct grub_uhci *u,
+                     grub_uhci_reg_t reg, grub_uint16_t val)
+{
+  grub_outw (val, u->iobase + reg);
+}
+
+static void
+grub_uhci_writereg32 (struct grub_uhci *u,
+                   grub_uhci_reg_t reg, grub_uint32_t val)
+{
+  grub_outl (val, u->iobase + reg);
+}
+
+static grub_err_t
+grub_uhci_portstatus (grub_usb_controller_t dev,
+                     unsigned int port, unsigned int enable);
+
+
+/* Iterate over all PCI devices.  Determine if a device is an UHCI
+   controller.  If this is the case, initialize it.  */
+static int grub_uhci_pci_iter (int bus, int device, int func,
+                              grub_pci_id_t pciid __attribute__((unused)))
+{
+  grub_uint32_t class;
+  grub_uint32_t subclass;
+  grub_uint32_t base;
+  grub_uint32_t fp;
+  grub_pci_address_t addr;
+  struct grub_uhci *u;
+  int i;
+
+  addr = grub_pci_make_address (bus, device, func, 2);
+  class = grub_pci_read (addr);
+  addr = grub_pci_make_address (bus, device, func, 2);
+  class = grub_pci_read (addr);
+
+  subclass = (class >> 16) & 0xFF;
+  class >>= 24;
+
+  /* If this is not an UHCI controller, just return.  */
+  if (class != 0x0c || subclass != 0x03)
+    return 0;
+
+  /* Determine IO base address.  */
+  addr = grub_pci_make_address (bus, device, func, 8);
+  base = grub_pci_read (addr);
+  /* Stop if there is no IO space base address defined.  */
+  if (! (base & 1))
+    return 0;
+
+  /* Allocate memory for the controller and register it.  */
+  u = grub_malloc (sizeof (*u));
+  if (! u)
+    return 1;
+
+  u->next = uhci;
+  uhci = u;
+  u->iobase = base & GRUB_UHCI_IOMASK;
+  u->framelist = 0;
+  u->qh = 0;
+  u->td = 0;
+  grub_dprintf ("uhci", "class=0x%02x 0x%02x base=0x%x\n",
+               class, subclass, u->iobase);
+
+  /* Reserve a page for the frame list.  */
+  u->framelist = grub_memalign (4096, 4096);
+  if (! u->framelist)
+    goto fail;
+
+  /* The framelist pointer of UHCI is only 32 bits, make sure this
+     code works on on 64 bits architectures.  */
+#if GRUB_CPU_SIZEOF_VOID_P == 8
+  if ((grub_uint64_t) u->framelist >> 32)
+    {
+      grub_error (GRUB_ERR_OUT_OF_MEMORY,
+                 "allocated frame list memory not <4GB");
+      goto fail;
+    }
+#endif
+
+  /* The QH pointer of UHCI is only 32 bits, make sure this
+     code works on on 64 bits architectures.  */
+  u->qh = (grub_uhci_qh_t) grub_memalign (4096, 4096);
+  if (! u->qh)
+    goto fail;
+
+#if GRUB_CPU_SIZEOF_VOID_P == 8
+  if ((grub_uint64_t) u->qh >> 32)
+    {
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, "allocated QH memory not <4GB");
+      goto fail;
+    }
+#endif
+
+  /* The TD pointer of UHCI is only 32 bits, make sure this
+     code works on on 64 bits architectures.  */
+  u->td = (grub_uhci_td_t) grub_memalign (4096, 4096*2);
+  if (! u->td)
+    goto fail;
+
+#if GRUB_CPU_SIZEOF_VOID_P == 8
+  if ((grub_uint64_t) u->td >> 32)
+    {
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, "allocated TD memory not <4GB");
+      goto fail;
+    }
+#endif
+
+  /* Link all Transfer Descriptors in a list of available Transfer
+     Descriptors.  */
+  for (i = 0; i < 256; i++)
+    u->td[i].linkptr = (grub_uint32_t) &u->td[i + 1];
+  u->td[255 - 1].linkptr = 0;
+  u->tdfree = u->td;
+
+  /* Make sure UHCI is disabled!  */
+  grub_uhci_writereg16 (u, GRUB_UHCI_REG_USBCMD, 0);
+
+  /* Setup the frame list pointers.  Since no isochronous transfers
+     are and will be supported, they all point to the (same!) queue
+     head.  */
+  fp = (grub_uint32_t) u->qh & (~15);
+  /* Mark this as a queue head.  */
+  fp |= 2;
+  for (i = 0; i < 1024; i++)
+    u->framelist[i] = fp;
+  /* Program the framelist address into the UHCI controller.  */
+  grub_uhci_writereg32 (u, GRUB_UHCI_REG_FLBASEADD,
+                       (grub_uint32_t) u->framelist);
+
+  /* Make the Queue Heads point to eachother.  */
+  for (i = 0; i < 256; i++)
+    {
+      /* Point to the next QH.  */
+      u->qh[i].linkptr = (grub_uint32_t) (&u->qh[i + 1]) & (~15);
+
+      /* This is a QH.  */
+      u->qh[i].linkptr |= GRUB_UHCI_LINK_QUEUE_HEAD;
+
+      /* For the moment, do not point to a Transfer Descriptor.  These
+        are set at transfer time, so just terminate it.  */
+      u->qh[i].elinkptr = 1;
+    }
+
+  /* The last Queue Head should terminate.  256 are too many QHs so
+     just use 50.  */
+  u->qh[50 - 1].linkptr = 1;
+
+  /* Enable UHCI again.  */
+  grub_uhci_writereg16 (u, GRUB_UHCI_REG_USBCMD, 1 | (1 << 7));
+
+  /* UHCI is initialized and ready for transfers.  */
+  grub_dprintf ("uhci", "UHCI initialized\n");
+
+
+#if 0
+  {
+    int i;
+    for (i = 0; i < 10; i++)
+      {
+       grub_uint16_t frnum;
+
+       frnum = grub_uhci_readreg16 (u, 6);
+       grub_dprintf ("uhci", "Framenum=%d\n", frnum);
+       grub_millisleep (100);
+      }
+  }
+#endif
+
+  return 0;
+
+ fail:
+  if (u)
+    {
+      grub_free ((void *) u->qh);
+      grub_free (u->framelist);
+    }
+  grub_free (u);
+
+  return 1;
+}
+
+static void
+grub_uhci_inithw (void)
+{
+  grub_pci_iterate (grub_uhci_pci_iter);
+}
+
+static grub_uhci_td_t
+grub_alloc_td (struct grub_uhci *u)
+{
+  grub_uhci_td_t ret;
+
+  /* Check if there is a Transfer Descriptor available.  */
+  if (! u->tdfree)
+    return NULL;
+
+  ret = u->tdfree;
+  u->tdfree = (grub_uhci_td_t) u->tdfree->linkptr;
+
+  return ret;
+}
+
+static void
+grub_free_td (struct grub_uhci *u, grub_uhci_td_t td)
+{
+  td->linkptr = (grub_uint32_t) u->tdfree;
+  u->tdfree = td;
+}
+
+static void
+grub_free_queue (struct grub_uhci *u, grub_uhci_td_t td)
+{
+  /* Free the TDs in this queue.  */
+  while (td)
+    {
+      grub_uhci_td_t tdprev;
+
+      /* Unlink the queue.  */
+      tdprev = td;
+      td = (grub_uhci_td_t) td->linkptr2;
+
+      /* Free the TD.  */
+      grub_free_td (u, tdprev);
+    }
+}
+
+static grub_uhci_qh_t
+grub_alloc_qh (struct grub_uhci *u,
+              grub_transaction_type_t tr __attribute__((unused)))
+{
+  int i;
+  grub_uhci_qh_t qh;
+
+  /* Look for a Queue Head for this transfer.  Skip the first QH if
+     this is a Interrupt Transfer.  */
+#if 0
+  if (tr == GRUB_USB_TRANSACTION_TYPE_INTERRUPT)
+    i = 0;
+  else
+#endif
+    i = 1;
+
+  for (; i < 255; i++)
+    {
+      if (u->qh[i].elinkptr & 1)
+       break;
+    }
+  qh = &u->qh[i];
+  if (! (qh->elinkptr & 1))
+    {
+      grub_error (GRUB_ERR_OUT_OF_MEMORY,
+                 "no free queue heads available");
+      return NULL;
+    }
+
+  return qh;
+}
+
+static grub_uhci_td_t
+grub_uhci_transaction (struct grub_uhci *u, unsigned int endp,
+                      grub_transfer_type_t type, unsigned int addr,
+                      unsigned int toggle, grub_size_t size,
+                      char *data)
+{
+  grub_uhci_td_t td;
+  static const unsigned int tf[] = { 0x69, 0xE1, 0x2D };
+
+  /* XXX: Check if data is <4GB.  If it isn't, just copy stuff around.
+     This is only relevant for 64 bits architectures.  */
+
+  /* Grab a free Transfer Descriptor and initialize it.  */
+  td = grub_alloc_td (u);
+  if (! td)
+    {
+      grub_error (GRUB_ERR_OUT_OF_MEMORY,
+                 "no transfer descriptors available for UHCI transfer");
+      return 0;
+    }
+
+  grub_dprintf ("uhci",
+               "transaction: endp=%d, type=%d, addr=%d, toggle=%d, size=%d 
data=%p td=%p\n",
+               endp, type, addr, toggle, size, data, td);
+
+  /* Don't point to any TD, just terminate.  */
+  td->linkptr = 1;
+
+  /* Active!  Only retry a transfer 3 times.  */
+  td->ctrl_status = (1 << 23) | (3 << 27);
+
+  /* If zero bytes are transmitted, size is 0x7FF.  Otherwise size is
+     size-1.  */
+  if (size == 0)
+    size = 0x7FF;
+  else
+    size = size - 1;
+
+  /* Setup whatever is required for the token packet.  */
+  td->token = ((size << 21) | (toggle << 19) | (endp << 15)
+              | (addr << 8) | tf[type]);
+
+  td->buffer = (grub_uint32_t) data;
+
+  return td;
+}
+
+static grub_usb_err_t
+grub_uhci_transfer (grub_usb_controller_t dev,
+                   grub_usb_transfer_t transfer)
+{
+  struct grub_uhci *u = (struct grub_uhci *) dev->data;
+  grub_uhci_qh_t qh;
+  grub_uhci_td_t td;
+  grub_uhci_td_t td_first = NULL;
+  grub_uhci_td_t td_prev = NULL;
+  grub_usb_err_t err = GRUB_USB_ERR_NONE;
+  int i;
+
+  /* Allocate a queue head for the transfer queue.  */
+  qh = grub_alloc_qh (u, GRUB_USB_TRANSACTION_TYPE_CONTROL);
+  if (! qh)
+    return grub_errno;
+
+  for (i = 0; i < transfer->transcnt; i++)
+    {
+      grub_usb_transaction_t tr = &transfer->transactions[i];
+
+      td = grub_uhci_transaction (u, transfer->endpoint, tr->pid,
+                                 transfer->devaddr, tr->toggle,
+                                 tr->size, tr->data); 
+      if (! td)
+       {
+         /* Terminate and free.  */
+         td_prev->linkptr2 = 0;
+         td_prev->linkptr = 1;
+
+         if (td_first)
+           grub_free_queue (u, td_first);
+
+         return GRUB_USB_ERR_INTERNAL;
+       }
+
+      if (! td_first)
+       td_first = td;
+      else
+       {
+         td_prev->linkptr2 = (grub_uint32_t) td;
+         td_prev->linkptr = (grub_uint32_t) td;
+         td_prev->linkptr |= 4;
+       }
+      td_prev = td;
+    }
+  td_prev->linkptr2 = 0;
+  td_prev->linkptr = 1;
+
+  grub_dprintf ("uhci", "setup transaction %d\n", transfer->type);
+
+  /* Link it into the queue and terminate.  Now the transaction can
+     take place.  */
+  qh->elinkptr = (grub_uint32_t) td_first;
+
+  grub_dprintf ("uhci", "initiate transaction\n");
+
+  /* Wait until either the transaction completed or an error
+     occured.  */
+  for (;;)
+    {
+      grub_uhci_td_t errtd;
+
+      errtd = (grub_uhci_td_t) (qh->elinkptr & ~0x0f);
+
+      grub_dprintf ("uhci", ">t status=0x%02x data=0x%02x td=%p\n",
+                   errtd->ctrl_status, errtd->buffer & (~15), errtd);
+
+      /* Check if the transaction completed.  */
+      if (qh->elinkptr & 1)
+       break;
+
+      grub_dprintf ("uhci", "t status=0x%02x\n", errtd->ctrl_status);
+
+      /* Check if the TD is not longer active.  */
+      if (! (errtd->ctrl_status & (1 << 23)))
+       {
+         grub_dprintf ("uhci", ">>t status=0x%02x\n", errtd->ctrl_status);
+
+         /* Check if the endpoint is stalled.  */
+         if (errtd->ctrl_status & (1 << 22))
+           err = GRUB_USB_ERR_STALL;
+
+         /* Check if an error related to the data buffer occured.  */
+         if (errtd->ctrl_status & (1 << 21))
+           err = GRUB_USB_ERR_DATA;
+
+         /* Check if a babble error occured.  */
+         if (errtd->ctrl_status & (1 << 20))
+           err = GRUB_USB_ERR_BABBLE;
+ 
+         /* Check if a NAK occured.  */
+         if (errtd->ctrl_status & (1 << 19))
+           err = GRUB_USB_ERR_NAK;
+
+         /* Check if a timeout occured.  */
+         if (errtd->ctrl_status & (1 << 18))
+           err = GRUB_USB_ERR_TIMEOUT;
+
+         /* Check if a bitstuff error occured.  */
+         if (errtd->ctrl_status & (1 << 17))
+           err = GRUB_USB_ERR_BITSTUFF;
+
+         if (err)
+           goto fail;
+
+         /* Fall through, no errors occured, so the QH might be
+            updated.  */
+         grub_dprintf ("uhci", "transaction fallthrough\n");
+       }
+    }
+
+  grub_dprintf ("uhci", "transaction complete\n");
+
+ fail:
+
+  grub_dprintf ("uhci", "transaction failed\n");
+
+  /* Place the QH back in the free list and deallocate the associated
+     TDs.  */
+  qh->elinkptr = 1;
+  grub_free_queue (u, td_first);
+
+  return err;
+}
+
+static int
+grub_uhci_iterate (int (*hook) (grub_usb_controller_t dev))
+{
+  struct grub_uhci *u;
+  struct grub_usb_controller dev;
+
+  for (u = uhci; u; u = u->next)
+    {
+      dev.data = u;
+      if (hook (&dev))
+       return 1;
+    }
+
+  return 0;
+}
+
+static grub_err_t
+grub_uhci_portstatus (grub_usb_controller_t dev,
+                     unsigned int port, unsigned int enable)
+{
+  struct grub_uhci *u = (struct grub_uhci *) dev->data;
+  int reg;
+  unsigned int status;
+
+  grub_dprintf ("uhci", "enable=%d port=%d\n", enable, port);
+
+  if (port == 0)
+    reg = GRUB_UHCI_REG_PORTSC1;
+  else if (port == 1)
+    reg = GRUB_UHCI_REG_PORTSC2;
+  else
+    return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                      "UHCI Root Hub port does not exist");
+
+  status = grub_uhci_readreg16 (u, reg);
+  grub_dprintf ("uhci", "detect=0x%02x\n", status);
+
+  /* Reset the port.  */
+  grub_uhci_writereg16 (u, reg, enable << 9);
+
+  /* Wait for the reset to complete.  XXX: How long exactly?  */
+  grub_millisleep (10);
+  status = grub_uhci_readreg16 (u, reg);
+  grub_uhci_writereg16 (u, reg, status & ~(1 << 9));
+  grub_dprintf ("uhci", "reset completed\n");
+
+  /* Enable the port.  */
+  grub_uhci_writereg16 (u, reg, enable << 2);
+  grub_millisleep (10);
+
+  grub_dprintf ("uhci", "waiting for the port to be enabled\n");
+
+  while (! (grub_uhci_readreg16 (u, reg) & (1 << 2)));
+
+  status = grub_uhci_readreg16 (u, reg);
+  grub_dprintf ("uhci", ">3detect=0x%02x\n", status);
+
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_usb_speed_t
+grub_uhci_detect_dev (grub_usb_controller_t dev, int port)
+{
+  struct grub_uhci *u = (struct grub_uhci *) dev->data;
+  int reg;
+  unsigned int status;
+
+  if (port == 0)
+    reg = GRUB_UHCI_REG_PORTSC1;
+  else if (port == 1)
+    reg = GRUB_UHCI_REG_PORTSC2;
+  else
+    return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                      "UHCI Root Hub port does not exist");
+
+  status = grub_uhci_readreg16 (u, reg);
+
+  grub_dprintf ("uhci", "detect=0x%02x port=%d\n", status, port);
+
+  if (! (status & 1))
+    return GRUB_USB_SPEED_NONE;
+  else if (status & (1 << 8))
+    return GRUB_USB_SPEED_LOW;
+  else
+    return GRUB_USB_SPEED_FULL;
+}
+
+static int
+grub_uhci_hubports (grub_usb_controller_t dev __attribute__((unused)))
+{
+  /* The root hub has exactly two ports.  */
+  return 2;
+}
+
+
+static struct grub_usb_controller_dev usb_controller =
+{
+  .name = "uhci",
+  .iterate = grub_uhci_iterate,
+  .transfer = grub_uhci_transfer,
+  .hubports = grub_uhci_hubports,
+  .portstatus = grub_uhci_portstatus,
+  .detect_dev = grub_uhci_detect_dev
+};
+
+GRUB_MOD_INIT(uhci)
+{
+  grub_uhci_inithw ();
+  grub_usb_controller_dev_register (&usb_controller);
+  grub_dprintf ("uhci", "registed\n");
+}
+
+GRUB_MOD_FINI(uhci)
+{
+  struct grub_uhci *u;
+
+  /* Disable all UHCI controllers.  */
+  for (u = uhci; u; u = u->next)
+    grub_uhci_writereg16 (u, GRUB_UHCI_REG_USBCMD, 0);
+
+  /* Unregister the controller.  */
+  grub_usb_controller_dev_unregister (&usb_controller);
+}
Index: bus/usb/usbhub.c
===================================================================
--- bus/usb/usbhub.c    (revision 0)
+++ bus/usb/usbhub.c    (revision 0)
@@ -0,0 +1,193 @@
+/* usb.c - USB Hub Support.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/usb.h>
+#include <grub/misc.h>
+
+/* USB Supports 127 devices, with device 0 as special case.  */
+static struct grub_usb_device *grub_usb_devs[128];
+
+/* Add a device that currently has device number 0 and resides on
+   CONTROLLER, the Hub reported that the device speed is SPEED.  */
+static grub_usb_device_t
+grub_usb_hub_add_dev (grub_usb_controller_t controller, grub_usb_speed_t speed)
+{
+  grub_usb_device_t dev;
+  int i;
+
+  dev = grub_malloc (sizeof (struct grub_usb_device));
+  if (! dev)
+    return NULL;
+
+  dev->controller = *controller;
+  dev->addr = 0;
+  dev->initialized = 0;
+  dev->speed = speed;
+
+  grub_usb_device_initialize (dev);
+
+  /* Assign a new address to the device.  */
+  for (i = 1; i < 128; i++)
+    {
+      if (! grub_usb_devs[i])
+       break;
+    }
+  if (grub_usb_devs[i])
+    {
+      grub_error (GRUB_ERR_IO, "Can't assign address to USB device");
+      return NULL;
+    }
+
+  grub_usb_control_msg (dev,
+                       (GRUB_USB_REQTYPE_OUT
+                        | GRUB_USB_REQTYPE_STANDARD
+                        | GRUB_USB_REQTYPE_TARGET_DEV),
+                       GRUB_USB_REQ_SET_ADDRESS,
+                       i, 0, 0, NULL);
+  dev->addr = i;
+  dev->initialized = 1;
+  grub_usb_devs[i] = dev;
+
+  return dev;
+}
+
+
+static grub_err_t
+grub_usb_add_hub (grub_usb_device_t dev)
+{
+  struct grub_usb_usb_hubdesc hubdesc;
+  grub_err_t err;
+  int i;
+
+  grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
+                             | GRUB_USB_REQTYPE_CLASS
+                             | GRUB_USB_REQTYPE_TARGET_DEV),
+                       GRUB_USB_REQ_GET_DESCRIPTOR,
+                       (GRUB_USB_DESCRIPTOR_HUB << 8) | 0,
+                       0, sizeof (hubdesc), (char *) &hubdesc);
+
+  /* Iterate over the Hub ports.  */
+  for (i = 1; i <= hubdesc.portcnt; i++)
+    {
+      grub_uint32_t status;
+
+      /* Get the port status.  */
+      err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
+                                       | GRUB_USB_REQTYPE_CLASS
+                                       | GRUB_USB_REQTYPE_TARGET_OTHER),
+                                 GRUB_USB_REQ_HUB_GET_PORT_STATUS,
+                                 0, i, sizeof (status), (char *) &status);
+
+      /* Just ignore the device if the Hub does not report the
+        status.  */
+      if (err)
+       continue;
+
+      /* If connected, reset and enable the port.  */
+      if (status & GRUB_USB_HUB_STATUS_CONNECTED)
+       {
+         grub_usb_speed_t speed;
+
+         /* Determine the device speed.  */
+         if (status & GRUB_USB_HUB_STATUS_LOWSPEED)
+           speed = GRUB_USB_SPEED_LOW;
+         else
+           {
+             if (status & GRUB_USB_HUB_STATUS_HIGHSPEED)
+               speed = GRUB_USB_SPEED_HIGH;
+             else
+               speed = GRUB_USB_SPEED_FULL;
+           }
+
+         /* A device is actually connected to this port, not enable
+            the port.  XXX: Why 0x03?  According to some docs it
+            should be 0x0.  Check the specification!  */
+         err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
+                                           | GRUB_USB_REQTYPE_CLASS
+                                           | GRUB_USB_REQTYPE_TARGET_OTHER),
+                                     0x3, 0x4, i, 0, 0);
+
+         /* If the Hub does not cooperate for this port, just skip
+            the port.  */
+         if (err)
+           continue;
+
+         /* Add the device and assign a device address to it.  */
+         grub_usb_hub_add_dev (&dev->controller, speed);
+       }
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+grub_usb_err_t
+grub_usb_root_hub (grub_usb_controller_t controller)
+{
+  grub_err_t err;
+  int ports;
+  int i;
+
+  /* Query the number of ports the root Hub has.  */
+  ports = controller->dev->hubports (controller);
+
+  for (i = 0; i < ports; i++)
+    {
+      grub_usb_speed_t speed = controller->dev->detect_dev (controller, i);
+
+      if (speed != GRUB_USB_SPEED_NONE)
+       {
+         grub_usb_device_t dev;
+
+         /* Enable the port.  */
+         err = controller->dev->portstatus (controller, i, 1);
+         if (err)
+           continue;
+
+         /* Enable the port and create a device.  */
+         dev = grub_usb_hub_add_dev (controller, speed);
+         if (! dev)
+           continue;
+
+         /* If the device is a Hub, scan it for more devices.  */
+         if (dev->descdev.class == 0x09)
+           grub_usb_add_hub (dev);
+       }
+    }
+
+  return GRUB_USB_ERR_NONE;
+}
+
+int
+grub_usb_iterate (int (*hook) (grub_usb_device_t dev))
+{
+  int i;
+
+  for (i = 0; i < 128; i++)
+    {
+      if (grub_usb_devs[i])
+       {
+         if (hook (grub_usb_devs[i]))
+             return 1;
+       }
+    }
+
+  return 0;
+}
Index: bus/usb/usb.c
===================================================================
--- bus/usb/usb.c       (revision 0)
+++ bus/usb/usb.c       (revision 0)
@@ -0,0 +1,263 @@
+/* usb.c - Generic USB interfaces.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/usb.h>
+#include <grub/misc.h>
+
+static grub_usb_controller_dev_t grub_usb_list;
+
+void
+grub_usb_controller_dev_register (grub_usb_controller_dev_t usb)
+{
+  auto int iterate_hook (grub_usb_controller_t dev);
+
+  /* Iterate over all controllers found by the driver.  */
+  int iterate_hook (grub_usb_controller_t dev)
+    {
+      dev->dev = usb;
+
+      /* Enable the ports of the USB Root Hub.  */
+      grub_usb_root_hub (dev);
+
+      return 0;
+    }
+
+  usb->next = grub_usb_list;
+  grub_usb_list = usb;
+
+  if (usb->iterate)
+    usb->iterate (iterate_hook);
+}
+
+void
+grub_usb_controller_dev_unregister (grub_usb_controller_dev_t usb)
+{
+  grub_usb_controller_dev_t *p, q;
+
+  for (p = &grub_usb_list, q = *p; q; p = &(q->next), q = q->next)
+    if (q == usb)
+      {
+       *p = q->next;
+       break;
+      }
+}
+
+#if 0
+int
+grub_usb_controller_iterate (int (*hook) (grub_usb_controller_t dev))
+{
+  grub_usb_controller_dev_t p;
+
+  auto int iterate_hook (grub_usb_controller_t dev);
+
+  int iterate_hook (grub_usb_controller_t dev)
+    {
+      dev->dev = p;
+      if (hook (dev))
+       return 1;
+      return 0;
+    }
+
+  /* Iterate over all controller drivers.  */
+  for (p = grub_usb_list; p; p = p->next)
+    {
+      /* Iterate over the busses of the controllers.  XXX: Actually, a
+        hub driver should do this.  */
+      if (p->iterate (iterate_hook))
+       return 1;
+    }
+
+  return 0;
+}
+#endif
+
+
+grub_usb_err_t
+grub_usb_clear_halt (grub_usb_device_t dev, int endpoint)
+{
+  dev->toggle[endpoint] = 0;
+  return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
+                                    | GRUB_USB_REQTYPE_STANDARD
+                                    | GRUB_USB_REQTYPE_TARGET_ENDP),
+                              GRUB_USB_REQ_CLEAR_FEATURE,
+                              GRUB_USB_FEATURE_ENDP_HALT,
+                              endpoint, 0, 0);
+}
+
+grub_usb_err_t
+grub_usb_set_configuration (grub_usb_device_t dev, int configuration)
+{
+  int i;
+
+  for (i = 0; i < 16; i++)
+    dev->toggle[i] = 0;
+
+  return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
+                                    | GRUB_USB_REQTYPE_STANDARD
+                                    | GRUB_USB_REQTYPE_TARGET_DEV),
+                              GRUB_USB_REQ_SET_CONFIGURATION, configuration,
+                              0, 0, NULL);
+}
+
+grub_usb_err_t
+grub_usb_get_descriptor (grub_usb_device_t dev,
+                        grub_uint8_t type, grub_uint8_t index,
+                        grub_size_t size, char *data)
+{
+  return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
+                                    | GRUB_USB_REQTYPE_STANDARD
+                                    | GRUB_USB_REQTYPE_TARGET_DEV),
+                              GRUB_USB_REQ_GET_DESCRIPTOR,
+                              (type << 8) | index,
+                              0, size, data);
+}
+
+struct grub_usb_desc_endp *
+grub_usb_get_endpdescriptor (grub_usb_device_t usbdev, int addr)
+{
+  int i;
+
+  for (i = 0; i < usbdev->config[0].descconf->numif; i++)
+    {
+      struct grub_usb_desc_if *interf;
+      int j;
+
+      interf = usbdev->config[0].interf[i].descif;
+
+      for (j = 0; j < interf->endpointcnt; j++)
+       {
+         struct grub_usb_desc_endp *endp;
+         endp = &usbdev->config[0].interf[i].descendp[j];
+
+         if (endp->endp_addr == addr)
+           return endp;
+       }
+    }
+
+  return NULL;
+}
+
+grub_usb_err_t
+grub_usb_get_string (grub_usb_device_t dev, grub_uint8_t index, int langid,
+                    char **string)
+{
+  struct grub_usb_desc_str descstr;
+  struct grub_usb_desc_str *descstrp;
+  grub_usb_err_t err;
+
+  /* Only get the length.  */
+  err = grub_usb_control_msg (dev, 1 << 7,
+                             0x06, (3 << 8) | index,
+                             langid, 1, (char *) &descstr);
+  if (err)
+    return err;
+
+  descstrp = grub_malloc (descstr.length);
+  if (! descstrp)
+    return GRUB_USB_ERR_INTERNAL;
+  err = grub_usb_control_msg (dev, 1 << 7,
+                             0x06, (3 << 8) | index,
+                             langid, descstr.length, (char *) descstrp);
+
+  *string = grub_malloc (descstr.length / 2);
+  if (! *string)
+    {
+      grub_free (descstrp);
+      return GRUB_USB_ERR_INTERNAL;
+    }
+
+  grub_utf16_to_utf8 ((grub_uint8_t *) *string, descstrp->str, 
descstrp->length / 2 - 1);
+  (*string)[descstr.length / 2 - 1] = '\0';
+  grub_free (descstrp);
+
+  return GRUB_USB_ERR_NONE;
+}
+
+grub_usb_err_t
+grub_usb_device_initialize (grub_usb_device_t dev)
+{
+  struct grub_usb_desc_device *descdev;
+  struct grub_usb_desc_config config;
+  grub_usb_err_t err;
+  int i;
+
+  err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE,
+                                0, sizeof (struct grub_usb_desc_device),
+                                (char *) &dev->descdev);
+  if (err)
+    return err;
+  descdev = &dev->descdev;
+
+  for (i = 0; i < 8; i++)
+    dev->config[i].descconf = NULL;
+
+  for (i = 0; i < descdev->configcnt; i++)
+    {
+      int pos;
+      int currif;
+      char *data;
+
+      /* First just read the first 4 bytes of the configuration
+        descriptor, after that it is known how many bytes really have
+        to be read.  */
+      err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_CONFIG, i, 4,
+                                    (char *) &config);
+
+      data = grub_malloc (config.totallen);
+      if (! data)
+       {
+         err = GRUB_USB_ERR_INTERNAL;
+         goto fail;
+       }
+
+      dev->config[i].descconf = (struct grub_usb_desc_config *) data;
+      err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_CONFIG, i,
+                                    config.totallen, data);
+      if (err)
+       goto fail;
+
+      /* Skip the configuration descriptor.  */
+      pos = sizeof (struct grub_usb_desc_config);
+      
+      /* Read all interfaces.  */
+      for (currif = 0; currif < dev->config[i].descconf->numif; currif++)
+       {
+         dev->config[i].interf[currif].descif
+           = (struct grub_usb_desc_if *) &data[pos];
+         pos += sizeof (struct grub_usb_desc_if);
+
+         /* Point to the first endpoint.  */
+         dev->config[i].interf[currif].descendp
+           = (struct grub_usb_desc_endp *) &data[pos];
+         pos += (sizeof (struct grub_usb_desc_endp)
+                 * dev->config[i].interf[currif].descif->endpointcnt);
+       }
+    }
+
+  return GRUB_USB_ERR_NONE;
+
+ fail:
+
+  for (i = 0; i < 8; i++)
+    grub_free (dev->config[i].descconf);
+
+  return err;
+}
Index: config.h.in
===================================================================
--- config.h.in (revision 1830)
+++ config.h.in (working copy)
@@ -73,6 +73,9 @@
 /* Define to 1 if you have the <unistd.h> header file. */
 #undef HAVE_UNISTD_H
 
+/* Define to 1 if you have the <usb.h> header file. */
+#undef HAVE_USB_H
+
 /* Define to 1 if you enable memory manager debugging. */
 #undef MM_DEBUG
 
Index: commands/usbtest.c
===================================================================
--- commands/usbtest.c  (revision 0)
+++ commands/usbtest.c  (revision 0)
@@ -0,0 +1,160 @@
+/* usbtest.c - test module for USB */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/normal.h>
+#include <grub/usb.h>
+
+static const char *usb_classes[] =
+  {
+    "",
+    "Audio",
+    "Communication Interface",
+    "HID",
+    "",
+    "Physical",
+    "Image",
+    "Printer",
+    "Mass Storage",
+    "Hub",
+    "Data Interface",
+    "Smart Card",
+    "Content Security",
+    "Video"
+  };
+
+static const char *usb_endp_type[] =
+  {
+    "Control",
+    "Isochronous",
+    "Bulk",
+    "Interrupt"
+  };
+
+static const char *usb_devspeed[] = 
+  {
+    "",
+    "Low",
+    "Full",
+    "High"
+  };
+
+static void
+usb_print_str (const char *description, grub_usb_device_t dev, int idx)
+{
+  char *name;
+  /* XXX: LANGID  */
+
+  if (! idx)
+    return;
+
+  grub_usb_get_string (dev, idx, 0x0409, &name);
+  grub_printf ("%s: `%s'\n", description, name);
+  grub_free (name);
+}
+
+static int
+usb_iterate (grub_usb_device_t dev)
+{
+  struct grub_usb_desc_device *descdev;
+  int i;
+
+  descdev = &dev->descdev;
+
+  usb_print_str ("Product", dev, descdev->strprod);
+  usb_print_str ("Vendor", dev, descdev->strvendor);
+  usb_print_str ("Serial", dev, descdev->strserial);
+  
+  if (descdev->class > 0 && descdev->class <= 0x0E)
+    grub_printf ("Class: (0x%02x) %s, Subclass: 0x%02x, Protocol: 0x%02x\n",
+                descdev->class, usb_classes[descdev->class],
+                descdev->subclass, descdev->protocol);
+  grub_printf ("USB version %d.%d, VendorID: 0x%02x, ProductID: 0x%02x, #conf: 
%d\n",
+              descdev->usbrel >> 8, (descdev->usbrel >> 4) & 0x0F,
+              descdev->vendorid, descdev->prodid, descdev->configcnt);
+
+  grub_printf ("%s speed device\n", usb_devspeed[dev->speed]);
+
+  for (i = 0; i < descdev->configcnt; i++)
+    {
+      struct grub_usb_desc_config *config;
+
+      config = dev->config[i].descconf;
+      usb_print_str ("Configuration:", dev, config->strconfig);
+    }
+
+  for (i = 0; i < dev->config[0].descconf->numif; i++)
+    {
+      int j;
+      struct grub_usb_desc_if *interf;
+      interf = dev->config[0].interf[i].descif;
+
+      grub_printf ("Interface #%d: #Endpoints: %d   ",
+                  i, interf->endpointcnt);
+      if (interf->class > 0 && interf->class <= 0x0E)
+       grub_printf ("Class: (0x%02x) %s, Subclass: 0x%02x, Protocol: 0x%02x\n",
+                    interf->class, usb_classes[interf->class],
+                    interf->subclass, interf->protocol);
+
+      usb_print_str ("Interface", dev, interf->strif);
+
+      for (j = 0; j < interf->endpointcnt; j++)
+       {
+         struct grub_usb_desc_endp *endp;
+         endp = &dev->config[0].interf[i].descendp[j];
+
+         grub_printf ("Endpoint #%d: %s, max packed size: %d, transfer type: 
%s, latency: %d\n",
+                      endp->endp_addr & 15,
+                      (endp->endp_addr & 128) ? "IN" : "OUT",
+                      endp->maxpacket, usb_endp_type[endp->attrib & 3],
+                      endp->interval);
+       }
+    }
+
+  grub_printf("\n");
+
+  return 0;
+}
+
+static grub_err_t
+grub_cmd_usbtest (struct grub_arg_list *state __attribute__ ((unused)),
+                 int argc __attribute__ ((unused)),
+                 char **args __attribute__ ((unused)))
+{
+  grub_printf ("USB devices:\n\n");
+  grub_usb_iterate (usb_iterate);
+
+  return 0;
+}
+
+GRUB_MOD_INIT(usbtest)
+{
+  (void)mod;                   /* To stop warning. */
+  grub_register_command ("usb", grub_cmd_usbtest, GRUB_COMMAND_FLAG_BOTH,
+                        "usb", "Test USB support", 0);
+}
+
+GRUB_MOD_FINI(usbtest)
+{
+  grub_unregister_command ("usb");
+}
Index: util/grub-emu.c
===================================================================
--- util/grub-emu.c     (revision 1830)
+++ util/grub-emu.c     (working copy)
@@ -187,6 +187,10 @@ main (int argc, char *argv[])
   /* XXX: This is a bit unportable.  */
   grub_util_biosdisk_init (dev_map);
 
+#if HAVE_USB_H
+  grub_libusb_init ();
+#endif
+
   grub_init_all ();
 
   /* Make sure that there is a root device.  */
Index: util/usb.c
===================================================================
--- util/usb.c  (revision 0)
+++ util/usb.c  (revision 0)
@@ -0,0 +1,191 @@
+/*  usb.c -- libusb USB support for GRUB.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <usb.h>
+#include <grub/usb.h>
+#include <grub/dl.h>
+
+
+static struct grub_usb_controller_dev usb_controller =
+{
+  .name = "libusb"
+};
+
+static struct grub_usb_device *grub_usb_devs[128];
+
+struct usb_bus *busses;
+
+static grub_err_t
+grub_libusb_devices (void)
+
+{
+  struct usb_bus *bus;
+  int last = 0;
+
+  busses = usb_get_busses();
+
+  for (bus = busses; bus; bus = bus->next)
+    {
+      struct usb_device *usbdev;
+      struct grub_usb_device *dev;
+
+      for (usbdev = bus->devices; usbdev; usbdev = usbdev->next)
+       {
+         struct usb_device_descriptor *desc = &usbdev->descriptor;
+
+         if (! desc->bcdUSB)
+           continue;
+
+         dev = grub_malloc (sizeof (*dev));
+         if (! dev)
+           return grub_errno;
+
+         dev->data = usbdev;
+
+         /* Fill in all descriptors.  */
+         grub_usb_device_initialize (dev);
+
+         /* Register the device.  */
+         grub_usb_devs[last++] = dev;
+       }
+    }
+
+  return GRUB_USB_ERR_NONE;
+}
+
+grub_err_t
+grub_libusb_init (void)
+{
+  usb_init();
+  usb_find_busses();
+  usb_find_devices();
+
+  if (grub_libusb_devices ())
+    return grub_errno;
+
+  grub_usb_controller_dev_register (&usb_controller);  
+
+  return 0;
+}
+
+grub_err_t
+grub_libusb_fini (void)
+{
+  return 0;
+}
+
+
+int
+grub_usb_iterate (int (*hook) (grub_usb_device_t dev))
+{
+  int i;
+
+  for (i = 0; i < 128; i++)
+    {
+      if (grub_usb_devs[i])
+       {
+         if (hook (grub_usb_devs[i]))
+             return 1;
+       }
+    }
+
+  return 0;
+}
+
+grub_usb_err_t
+grub_usb_root_hub (grub_usb_controller_t controller __attribute__((unused)))
+{
+  return GRUB_USB_ERR_NONE;
+}
+
+grub_usb_err_t
+grub_usb_control_msg (grub_usb_device_t dev, grub_uint8_t reqtype,
+                     grub_uint8_t request, grub_uint16_t value,
+                     grub_uint16_t index, grub_size_t size, char *data)
+{
+  usb_dev_handle *devh;
+  struct usb_device *d = dev->data;
+
+  devh = usb_open (d);
+  if (usb_control_msg (devh, reqtype, request,
+                      value, index, data, size, 20) < 0)
+    {
+      usb_close (devh);
+      return GRUB_USB_ERR_STALL;
+    }
+
+  usb_close (devh);
+
+  return GRUB_USB_ERR_NONE;
+}
+
+grub_usb_err_t
+grub_usb_bulk_read (grub_usb_device_t dev,
+                   int endpoint, grub_size_t size, char *data)
+{
+  usb_dev_handle *devh;
+  struct usb_device *d = dev->data;
+
+  devh = usb_open (d);
+  if (usb_claim_interface (devh, 0) < 1)
+    {
+      usb_close (devh);
+      return GRUB_USB_ERR_STALL;
+    }
+
+  if (usb_bulk_read (devh, endpoint, data, size, 20) < 1)
+    {
+      usb_close (devh);
+      return GRUB_USB_ERR_STALL;
+    }
+
+  usb_release_interface (devh, 0);
+  usb_close (devh);
+
+  return GRUB_USB_ERR_NONE;
+}
+
+grub_usb_err_t
+grub_usb_bulk_write (grub_usb_device_t dev,
+                    int endpoint, grub_size_t size, char *data)
+{
+  usb_dev_handle *devh;
+  struct usb_device *d = dev->data;
+
+  devh = usb_open (d);
+  if (usb_claim_interface (devh, 0) < 0)
+    goto fail;
+
+  if (usb_bulk_write (devh, endpoint, data, size, 20) < 0)
+    goto fail;
+
+  if (usb_release_interface (devh, 0) < 0)
+    goto fail;
+
+  usb_close (devh);
+
+  return GRUB_USB_ERR_NONE;
+
+ fail:
+  usb_close (devh);
+  return GRUB_USB_ERR_STALL;
+}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]