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)