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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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 .
+ */
+
+#ifndef GRUB_USB_H
+#define GRUB_USB_H 1
+
+#include
+#include
+
+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 .
+ */
+
+#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 .
+ */
+
+#ifndef GRUB_USBDESC_H
+#define GRUB_USBDESC_H 1
+
+#include
+#include
+
+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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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 .
+ */
+
+#include
+#include
+#include
+#include
+
+/* 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 .
+ */
+
+#include
+#include
+#include
+#include
+
+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 header file. */
#undef HAVE_UNISTD_H
+/* Define to 1 if you have the 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+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;
+}