diff -Naurp linux-2.6.13/drivers/pci/hotplug/dummyphp.c linux-2.6.13-eike/drivers/pci/hotplug/dummyphp.c --- linux-2.6.13/drivers/pci/hotplug/dummyphp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.13-eike/drivers/pci/hotplug/dummyphp.c 2005-08-31 09:34:58.000000000 +0200 @@ -0,0 +1,547 @@ +/* + * Dummy PCI Hot Plug Controller Driver + * + * Copyright (c) 2003-2005 Rolf Eike Beer + * + * Based on code from: + * Vladimir Kondratiev + * Greg Kroah-Hartman + * + * All rights reserved. + * + * This program 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, version 2 of the License. + * + * Send feedback to + */ + +/* + * This driver will "emulate" removing PCI devices from the system. If + * the "power" file is written to with "0" then the specified PCI device + * will be completely removed from the kernel. + * + * WARNING, this does NOT turn off the power to the PCI device. This is + * a "logical" removal, not a physical or electrical removal. + * + * Use this module at your own risk, you have been warned! + */ +#include +#include +#include +#include +#include +#include +#include +#include "pci_hotplug.h" +#include "../pci.h" + +#define dbg(format, arg...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "%s: " format, \ + MY_NAME , ## arg); \ + } while (0) +#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg) +#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg) + +/* name size which is used for entries in sysfs */ +#define SLOT_NAME_SIZE 11 /* {DOMAIN}:{BUS}:{DEV} */ + +struct dummy_slot { + struct list_head node; + struct pci_bus *bus; + int devfn; + struct pci_dev *dev; + struct hotplug_slot *slot; + char name[SLOT_NAME_SIZE]; + struct hotplug_slot_info info; + int addlock; +}; + +static int debug; +static int showall; +static LIST_HEAD(slot_list); +static spinlock_t listlock = SPIN_LOCK_UNLOCKED; + +#define MY_NAME "dummyphp" +#define DRIVER_DESC "Dummy PCI Hot Plug Controller Driver" + +MODULE_AUTHOR("Rolf Eike Beer "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debugging mode"); +module_param(showall, bool, 0644); +MODULE_PARM_DESC(showall, "Show all PCI slots even if there is no device in it"); + +static int enable_slot(struct hotplug_slot *slot); +static int disable_slot(struct hotplug_slot *slot); +static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value); +static int enable_bus(struct hotplug_slot *slot); + +static struct hotplug_slot_ops dummy_hotplug_slot_ops = { + .owner = THIS_MODULE, + .enable_slot = enable_slot, + .disable_slot = disable_slot, + .get_adapter_status = get_adapter_status +}; + +static struct hotplug_slot_ops dummy_hotplug_bus_ops = { + .owner = THIS_MODULE, + .enable_slot = enable_bus +}; + +/** + * dummy_release - free the memory of a slot + * @slot: slot pointer to remove + */ +static void +dummy_release(struct hotplug_slot *slot) +{ + struct dummy_slot *dslot = slot->private; + + list_del(&dslot->node); + kfree(slot); + pci_dev_put(dslot->dev); + kfree(dslot); +} + +/** + * get_adapter_status - look if adapter is present in slot + * @slot: slot to test value + * @value: status of the adapter + */ +static int +get_adapter_status(struct hotplug_slot *slot, u8 *value) +{ + struct dummy_slot *dslot = slot->private; + + *value = pci_scan_slot(dslot->bus, dslot->devfn); + dslot->info.adapter_status = *value; + + return 0; +} + +static struct dummy_slot * +dslot_for_dev(const struct pci_dev *dev) +{ + struct list_head *sl; + + list_for_each(sl, &slot_list) { + struct dummy_slot *dslot = list_entry(sl, struct dummy_slot, node); + + if ((dslot->bus == dev->bus) && (dslot->devfn == dev->devfn) && (dslot->name[1] != 'u')) + return dslot; + } + return NULL; +} + +static struct dummy_slot * +dslot_for_bus(const struct pci_bus *bus) +{ + struct list_head *sl; + + list_for_each(sl, &slot_list) { + struct dummy_slot *dslot = list_entry(sl, struct dummy_slot, node); + + if ((dslot->bus == bus) && (dslot->name[1] = 'u')) + return dslot; + } + return NULL; +} + +static void* +kzalloc(size_t size, unsigned int __nocast flags) +{ + void *p; + + p = kmalloc(size, flags); + if (p) + memset(p, 0, size); + return p; +} + +/** + * add_dev_slot - add a new "hotplug" slot + * @dev: a struct pci_dev describing this slot (regardless if + * there is actually a device in this slot or not) + * + * calling this on a already registered slot will cause no error + */ +static int +add_dev_slot(const struct pci_dev *dev, const int force) +{ + struct dummy_slot *dslot; + struct hotplug_slot *slot; + int retval = -ENOMEM; + + /* scan list of slots if this one is already present. */ + if (dslot_for_dev(dev)) + return 0; + + dslot = kzalloc(sizeof(*dslot), GFP_KERNEL); + if (!dslot) + goto error; + + dslot->bus = dev->bus; + dslot->devfn = dev->devfn; + + dslot->dev = pci_get_slot(dslot->bus, dslot->devfn); + + if (!force && !dslot->dev) { + retval = 0; + goto error_dslot; + } + + slot = kzalloc(sizeof(*slot), GFP_KERNEL); + if (!slot) + goto error_dslot; + + slot->info = &dslot->info; + + dslot->info.max_bus_speed = PCI_SPEED_UNKNOWN; + dslot->info.cur_bus_speed = PCI_SPEED_UNKNOWN; + + slot->name = dslot->name; + dslot->info.power_status = (dslot->dev != NULL); + dslot->info.adapter_status = dslot->info.power_status; + + slot->ops = &dummy_hotplug_slot_ops; + slot->release = &dummy_release; + slot->private = dslot; + + snprintf(slot->name, SLOT_NAME_SIZE, "%04x:%02x:%02x", + pci_domain_nr(dev->bus), dslot->bus->number, + PCI_SLOT(dslot->devfn)); + + retval = pci_hp_register(slot); + if (retval) { + err("pci_hp_register failed with error %d\n", retval); + goto error_slot; + } + + dslot->slot = slot; + list_add(&dslot->node, &slot_list); + + return retval; + +error_slot: + kfree(slot); +error_dslot: + pci_dev_put(dslot->dev); + kfree(dslot); +error: + return retval; +} + +/** + * add_bus_slot - add a new "hotplug" slot for a bus + * @bus: a struct pci_bus describing this slot + * + * calling this on a already registered bus will cause no error + */ +static int +add_bus_slot(struct pci_bus *bus) +{ + struct dummy_slot *dslot; + struct hotplug_slot *slot; + int retval = -ENOMEM; + + /* scan list of slots if this one is already present. */ + if (dslot_for_bus(bus)) + return 0; + + dslot = kzalloc(sizeof(*dslot), GFP_KERNEL); + if (!dslot) + goto error; + + dslot->bus = bus; + + slot = kzalloc(sizeof(*slot), GFP_KERNEL); + if (!slot) + goto error_dslot; + + slot->info = &dslot->info; + + dslot->info.max_bus_speed = PCI_SPEED_UNKNOWN; + dslot->info.cur_bus_speed = PCI_SPEED_UNKNOWN; + + slot->name = dslot->name; + dslot->info.power_status = 1; + dslot->info.adapter_status = 1; + + slot->ops = &dummy_hotplug_bus_ops; + slot->release = &dummy_release; + slot->private = dslot; + + snprintf(slot->name, SLOT_NAME_SIZE, "bus%04x:%02x", + pci_domain_nr(bus), bus->number); + + retval = pci_hp_register(slot); + if (retval) { + err("pci_hp_register failed with error %d\n", retval); + goto error_slot; + } + + dslot->slot = slot; + list_add(&dslot->node, &slot_list); + + return retval; + +error_slot: + kfree(slot); +error_dslot: + kfree(dslot); +error: + return retval; +} + +static int scan_pci_buses(struct list_head *list); + +/** + * scan_pci_bus - add an entry for every slot on this bus + * @bus: bus to scan + */ +static int +scan_pci_bus(struct pci_bus *bus) +{ + int retval; + struct pci_dev dev; + + if ( (retval = add_bus_slot(bus)) ) + return retval; + + dev.bus = bus; + dev.sysdata = bus->sysdata; + + for (dev.devfn = 0; dev.devfn < 0x100; dev.devfn += 8) { + if ( (retval = add_dev_slot(&dev, showall)) ) + return retval; + } + return scan_pci_buses(&bus->children); +} + +/** + * scan_pci_buses - scan this bus and all child buses for slots + * @list: list of buses to scan + */ +static int +scan_pci_buses(struct list_head *list) +{ + int retval = 0; + const struct list_head *l, *n; + + list_for_each_safe(l, n, list) { + struct pci_bus *b = pci_bus_b(l); + + /* scan the list of slots to see if this + * bus is already registered */ + if (!dslot_for_bus(b)) { + if ( (retval = scan_pci_bus(b)) ) + break; + } + } + return retval; +} + +/** + * enable_bus - scan bus for devices and enable all + * @hotplug_slot: slot to enable + */ +static int +enable_bus(struct hotplug_slot *hotplug_slot) +{ + struct dummy_slot *dslot = hotplug_slot->private; + struct pci_dev dev; + int r = 0; + + dbg("%s - bus = %s\n", __FUNCTION__, hotplug_slot->name); + + dev.bus = dslot->bus; + spin_lock(&listlock); + + for (dev.devfn = 8; dev.devfn < 0x100; dev.devfn += 8) { + struct dummy_slot *ds; + + if ( !(ds = dslot_for_dev(&dev)) && + pci_scan_slot(dslot->bus, dev.devfn)) { + r = add_dev_slot(&dev, 1); + ds = dslot_for_dev(&dev); + } + + if (!r && ds) { + /* tell enable_slot that we have the listlock */ + ds->addlock = 1; + r = enable_slot(ds->slot); + ds->addlock = 0; + } + + if (r) + break; + } + + spin_unlock(&listlock); + return r; +} + +/** + * enable_slot - power on and enable a slot + * @hotplug_slot: slot to enable + */ +static int +enable_slot(struct hotplug_slot *hotplug_slot) +{ + struct dummy_slot *dslot = hotplug_slot->private; + int num, result = -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + /* exit if device in slot is already active */ + if (dslot->dev != NULL) + return 0; + + dslot->dev = pci_get_slot(dslot->bus, dslot->devfn); + + /* Still NULL? Well, then scan for it! */ + if (dslot->dev == NULL) { + num = pci_scan_slot(dslot->bus, dslot->devfn); + if (!num) { + dbg("INFO: enable_slot called on empty slot\n"); + return result; + } + + pci_bus_assign_resources(dslot->bus); + pci_bus_add_devices(dslot->bus); + + dslot->dev = pci_get_slot(dslot->bus, dslot->devfn); + if (dslot->dev == NULL) { + WARN_ON(1); + return result; + } + } + + if (dslot->dev->multifunction) { + /* scan all functions of this device to find sub-buses */ + for (num = 1; num < 8; num++) { + struct pci_dev *d; + + d = pci_get_slot(dslot->bus, dslot->devfn + num); + if (d && d->subordinate) { + if (!dslot->addlock) + spin_lock(&listlock); + result = scan_pci_bus(d->subordinate); + if (!dslot->addlock) + spin_unlock(&listlock); + if (result) + return result; + } + } + } + if (dslot->dev->subordinate) { + result = scan_pci_bus(dslot->dev->subordinate); + } else { + result = 0; + } + + return result; +} + +/** + * disable_subordinate - remove hotplug slot entry of all devices on this bus + * @bus: PCI bus to remove (including all children) + */ +static void +disable_subordinate(struct pci_bus *bus) +{ + struct list_head *tmp; + + list_for_each(tmp, &slot_list) { + struct dummy_slot *dslot = list_entry(tmp, struct dummy_slot, node); + + if (dslot->bus == bus) { + if (dslot->dev != NULL) + if (dslot->dev->subordinate) + /* FIXME: look if this bus can be reached through a + different bridge. If yes, don't disable it */ + disable_subordinate(dslot->dev->subordinate); + /* no need to disable the devices itself, this will be + done by pci_remove_bus_device */ + pci_hp_deregister(dslot->slot); + } + } +} + +/** + * disable_slot - disable any adapter in this slot + * @slot: slot to disable + */ +static int +disable_slot(struct hotplug_slot *slot) +{ + struct dummy_slot *dslot = slot->private; + struct pci_dev *dev = dslot->dev; + int i; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, slot->name); + + if (!dev) { + dbg("no device in slot, exiting\n"); + return -ENODEV; + } + + /* check if this is a PCI bridge and remove devices on + sub-buses first */ + if (dev->subordinate) { + spin_lock(&listlock); + disable_subordinate(dev->subordinate); + spin_unlock(&listlock); + } + + dslot->dev = NULL; + pci_dev_put(dev); + + /* remove the device(s) from the PCI core */ + info("slot %s removed\n", pci_name(dev)); + + for (i = 0; i < 8; i++) { + dev = pci_get_slot(dslot->bus, dslot->devfn + i); + if (dev) + pci_remove_bus_device(dev); + } + + dslot->info.power_status = 0; + + return 0; +} + +static int __init +dummyphp_init(void) +{ + int r; + + info(DRIVER_DESC "\n"); + + spin_lock(&listlock); + r = scan_pci_buses(&pci_root_buses); + spin_unlock(&listlock); + + return r; +} + +static void __exit +dummyphp_exit(void) +{ + struct list_head *tmp, *next; + struct dummy_slot *dslot; + + spin_lock(&listlock); + list_for_each_safe(tmp, next, &slot_list) { + dslot = list_entry(tmp, struct dummy_slot, node); + pci_hp_deregister(dslot->slot); + } + spin_unlock(&listlock); +} + +module_init(dummyphp_init); +module_exit(dummyphp_exit); diff -Naurp linux-2.6.13/drivers/pci/hotplug/Kconfig linux-2.6.13-eike/drivers/pci/hotplug/Kconfig --- linux-2.6.13/drivers/pci/hotplug/Kconfig 2005-08-29 01:41:01.000000000 +0200 +++ linux-2.6.13-eike/drivers/pci/hotplug/Kconfig 2005-08-31 09:34:47.000000000 +0200 @@ -19,6 +19,34 @@ config HOTPLUG_PCI When in doubt, say N. +config HOTPLUG_PCI_DUMMY + tristate "Dummy PCI Hotplug driver" + depends on HOTPLUG_PCI + help + Say Y here if you want to use the dummy PCI hotplug driver. It can + be used to simulate PCI hotplug events even if your system is not + PCI hotplug capable. + + This driver will "emulate" removing PCI devices from the system. + If the "power" file is written to with "0" then the specified PCI + device will be completely removed from the kernel. Writing "1" to + the power file will bring the device back. + + Be careful: it claims all PCI slots in the system with a device in it. + There may be side effects on other hotplug drivers, so do not use + another hotplug driver at the same time. + + WARNING, this does NOT turn off the power to the PCI device. + This is a "logical" removal, not a physical or electrical + removal. + + Use this module at your own risk. You have been warned! + + To compile this driver as a module, choose M here: the module + will be called dummyphp. + + When in doubt, say N. + config HOTPLUG_PCI_FAKE tristate "Fake PCI Hotplug driver" depends on HOTPLUG_PCI diff -Naurp linux-2.6.13/drivers/pci/hotplug/Makefile linux-2.6.13-eike/drivers/pci/hotplug/Makefile --- linux-2.6.13/drivers/pci/hotplug/Makefile 2005-08-29 01:41:01.000000000 +0200 +++ linux-2.6.13-eike/drivers/pci/hotplug/Makefile 2005-08-31 09:34:47.000000000 +0200 @@ -4,6 +4,7 @@ obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o +obj-$(CONFIG_HOTPLUG_PCI_DUMMY) += dummyphp.o obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o