diff --git a/ChangeLog b/ChangeLog
index fd9b9e4..28ef356 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2009-07-15 Vladimir Serbinenko
+
+ Reset USB controller
+
+ * commands/usbreset.c: new file
+ * conf/i386-pc.rmk (pkglib_MODULES): add usbreset.mod
+ (usbreset_mod_SOURCES): new variable
+ (usbreset_mod_CFLAGS): likewise
+ (usbreset_mod_LFFLAGS): likewise
+
2009-07-15 Pavel Roskin
* include/grub/i386/pc/boot.h (GRUB_BOOT_MACHINE_BPB_END):
diff --git a/commands/usbreset.c b/commands/usbreset.c
new file mode 100644
index 0000000..7615e88
--- /dev/null
+++ b/commands/usbreset.c
@@ -0,0 +1,182 @@
+/* usbreset.c - reset usb controllers */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2009 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
+
+static grub_err_t
+preboot_ehci (int noreturn __attribute__ ((unused)))
+{
+ auto int NESTED_FUNC_ATTR find_card (int bus, int dev, int func,
+ grub_pci_id_t pciid);
+
+ int NESTED_FUNC_ATTR find_card (int bus, int dev, int func,
+ grub_pci_id_t pciid __attribute__ ((unused)))
+ {
+ grub_pci_address_t addr;
+ grub_uint32_t class;
+ grub_uint32_t subclass;
+ grub_uint32_t base;
+ grub_uint32_t eecp;
+
+ addr = grub_pci_make_address (bus, dev, func, 2);
+
+ class = grub_pci_read (addr);
+
+ subclass = (class >> 16) & 0xFF;
+ class >>= 24;
+
+ /* If this is not an EHCI controller, just return. */
+ if (class != 0x0c || subclass != 0x03 || func != 7)
+ return 0;
+
+ /* Determine IO base address. */
+ addr = grub_pci_make_address (bus, dev, func, 4);
+ base = grub_pci_read (addr);
+ base &= ~0xff;
+
+ eecp = *((grub_uint8_t *) (base + 9));
+
+ if (! eecp)
+ return 0;
+
+ addr = grub_pci_make_address (bus, dev, func, 0);
+ grub_pci_write_byte (addr + eecp + 3, 1);
+ grub_pci_write_byte (addr + eecp + 2, 0);
+ grub_pci_write_byte (addr + eecp + 4, 0);
+ grub_pci_write_byte (addr + eecp + 5, 0);
+
+ return 0;
+ }
+
+ grub_pci_iterate (find_card);
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+preboot_uhci (int noreturn __attribute__ ((unused)))
+{
+ auto int NESTED_FUNC_ATTR find_card (int bus, int dev, int func,
+ grub_pci_id_t pciid);
+
+ int NESTED_FUNC_ATTR find_card (int bus, int dev, int func,
+ grub_pci_id_t pciid __attribute__ ((unused)))
+ {
+ grub_pci_address_t addr;
+ grub_uint32_t class;
+ grub_uint32_t subclass;
+ grub_uint32_t base;
+
+ addr = grub_pci_make_address (bus, dev, func, 2);
+
+ class = grub_pci_read (addr);
+
+ subclass = (class >> 16) & 0xFF;
+ class >>= 24;
+
+ /* If this is not an EHCI controller, just return. */
+ if (class != 0x0c || subclass != 0x03 || func == 7)
+ return 0;
+
+ /* Determine IO base address. */
+ addr = grub_pci_make_address (bus, dev, func, 8);
+ base = grub_pci_read (addr);
+ base = (base >> 5) & 0x7ff;
+
+ addr = grub_pci_make_address (bus, dev, func, 0x30);
+
+ grub_pci_write_word (addr, 0x8f00);
+
+ grub_outw (base, 0x0002);
+ grub_millisleep (10);
+ grub_outw (base + 4, 0);
+ grub_millisleep (10);
+ grub_outw (base, 0);
+
+ return 0;
+ }
+
+ grub_pci_iterate (find_card);
+
+ return GRUB_ERR_NONE;
+}
+
+
+static grub_err_t
+preboot_rest (void)
+{
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_ehcireset (grub_command_t cmd __attribute__ ((unused)),
+ int argc __attribute__ ((unused)),
+ char *args[] __attribute__ ((unused)))
+{
+ void *preb_handle;
+ preb_handle
+ = grub_loader_register_preboot_hook (preboot_ehci, preboot_rest,
+ GRUB_LOADER_PREBOOT_HOOK_PRIO_CONSOLE);
+ if (! preb_handle)
+ return grub_errno;
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_uhcireset (grub_command_t cmd __attribute__ ((unused)),
+ int argc __attribute__ ((unused)),
+ char *args[] __attribute__ ((unused)))
+{
+ void *preb_handle;
+ preb_handle
+ = grub_loader_register_preboot_hook (preboot_uhci, preboot_rest,
+ GRUB_LOADER_PREBOOT_HOOK_PRIO_CONSOLE);
+ if (! preb_handle)
+ return grub_errno;
+ return GRUB_ERR_NONE;
+}
+
+static grub_command_t cmd_ehcireset, cmd_uhcireset;
+
+GRUB_MOD_INIT(usbreset)
+{
+ (void) mod; /* To stop warning. */
+ cmd_ehcireset = grub_register_command ("ehcireset",
+ grub_cmd_ehcireset,
+ "ehcireset",
+ "Reset EHCI controller");
+ cmd_uhcireset = grub_register_command ("uhcireset",
+ grub_cmd_uhcireset,
+ "uhcireset",
+ "Reset UHCI controller");
+}
+
+GRUB_MOD_FINI(usbreset)
+{
+ grub_unregister_command (cmd_ehcireset);
+ grub_unregister_command (cmd_uhcireset);
+}
+
diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk
index f1915b6..53d8510 100644
--- a/conf/i386-pc.rmk
+++ b/conf/i386-pc.rmk
@@ -189,7 +189,7 @@ pkglib_MODULES = biosdisk.mod chain.mod \
aout.mod bsd.mod pxe.mod pxecmd.mod datetime.mod date.mod \
datehook.mod lsmmap.mod ata_pthru.mod hdparm.mod \
usb.mod uhci.mod ohci.mod usbtest.mod usbms.mod usb_keyboard.mod \
- efiemu.mod mmap.mod acpi.mod drivemap.mod
+ efiemu.mod mmap.mod acpi.mod drivemap.mod usbreset.mod
# For boot.mod.
pkglib_MODULES += boot.mod
@@ -214,6 +214,11 @@ efiemu_mod_SOURCES = efiemu/main.c efiemu/i386/loadcore32.c \
efiemu_mod_CFLAGS = $(COMMON_CFLAGS)
efiemu_mod_LDFLAGS = $(COMMON_LDFLAGS)
+# For usbreset.mod.
+usbreset_mod_SOURCES = commands/usbreset.c
+usbreset_mod_CFLAGS = $(COMMON_CFLAGS)
+usbreset_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
# For acpi.mod.
acpi_mod_SOURCES = commands/acpi.c commands/i386/pc/acpi.c
acpi_mod_CFLAGS = $(COMMON_CFLAGS)