/* Works like the fakephp driver used to, except a little better. * * - It's possible to remove devices with subordinate busses. * - New PCI devices that appear via any method, not just a fakephp triggered * rescan, will be noticed. * - Devices that are removed via any method, not just a fakephp triggered * removal, will also be noticed. * * Uses nothing from the pci-hotplug subsystem. * */ #include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/list.h>#include <linux/kobject.h>#include <linux/sysfs.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/device.h>#include <linux/slab.h>#include "../pci.h" struct legacy_slot { struct kobject kobj; struct pci_dev *dev; struct list_head list;}; static LIST_HEAD(legacy_list); static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr, char *buf){ struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); strcpy(buf, "1\n"); return 2;} static void remove_callback(void *data){ pci_remove_bus_device((struct pci_dev *)data);} static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len){ struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); unsigned long val; if (strict_strtoul(buf, 0, &val) < 0) return -EINVAL; if (val) pci_rescan_bus(slot->dev->bus); else sysfs_schedule_callback(&slot->dev->dev.kobj, remove_callback, slot->dev, THIS_MODULE); return len;} static struct attribute *legacy_attrs[] = { &(struct attribute){ .name = "power", .mode = 0644 }, NULL,}; static void legacy_release(struct kobject *kobj){ struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); pci_dev_put(slot->dev); kfree(slot);} static struct kobj_type legacy_ktype = { .sysfs_ops = &(const struct sysfs_ops){ .store = legacy_store, .show = legacy_show }, .release = &legacy_release, .default_attrs = legacy_attrs,}; static int legacy_add_slot(struct pci_dev *pdev){ struct legacy_slot *slot = kzalloc(sizeof(*slot), GFP_KERNEL); if (!slot) return -ENOMEM; if (kobject_init_and_add(&slot->kobj, &legacy_ktype, &pci_slots_kset->kobj, "%s", dev_name(&pdev->dev))) { dev_warn(&pdev->dev, "Failed to created legacy fake slot\n"); return -EINVAL; } slot->dev = pci_dev_get(pdev); list_add(&slot->list, &legacy_list); return 0;} static int legacy_notify(struct notifier_block *nb, unsigned long action, void *data){ struct pci_dev *pdev = to_pci_dev(data); if (action == BUS_NOTIFY_ADD_DEVICE) { legacy_add_slot(pdev); } else if (action == BUS_NOTIFY_DEL_DEVICE) { struct legacy_slot *slot; list_for_each_entry(slot, &legacy_list, list) if (slot->dev == pdev) goto found; dev_warn(&pdev->dev, "Missing legacy fake slot?"); return -ENODEV;found: kobject_del(&slot->kobj); list_del(&slot->list); kobject_put(&slot->kobj); } return 0;} static struct notifier_block legacy_notifier = { .notifier_call = legacy_notify}; static int __init init_legacy(void){ struct pci_dev *pdev = NULL; /* Add existing devices */ for_each_pci_dev(pdev) legacy_add_slot(pdev); /* Be alerted of any new ones */ bus_register_notifier(&pci_bus_type, &legacy_notifier); return 0;}module_init(init_legacy); static void __exit remove_legacy(void){ struct legacy_slot *slot, *tmp; bus_unregister_notifier(&pci_bus_type, &legacy_notifier); list_for_each_entry_safe(slot, tmp, &legacy_list, list) { list_del(&slot->list); kobject_del(&slot->kobj); kobject_put(&slot->kobj); }}module_exit(remove_legacy); MODULE_AUTHOR("Trent Piepho <xyzzy@speakeasy.org>");MODULE_DESCRIPTION("Legacy version of the fakephp interface");MODULE_LICENSE("GPL");
/* Works like the fakephp driver used to, except a little better.
*
* - It's possible to remove devices with subordinate busses.
* - New PCI devices that appear via any method, not just a fakephp triggered
* rescan, will be noticed.
* - Devices that are removed via any method, not just a fakephp triggered
* removal, will also be noticed.
*
* Uses nothing from the pci-hotplug subsystem.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/slab.h>
#include "../pci.h"
struct legacy_slot {
struct kobject kobj;
struct pci_dev *dev;
struct list_head list;
};
static LIST_HEAD(legacy_list);
static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
strcpy(buf, "1\n");
return 2;
}
static void remove_callback(void *data)
{
pci_remove_bus_device((struct pci_dev *)data);
}
static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t len)
{
struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
unsigned long val;
if (strict_strtoul(buf, 0, &val) < 0)
return -EINVAL;
if (val)
pci_rescan_bus(slot->dev->bus);
else
sysfs_schedule_callback(&slot->dev->dev.kobj, remove_callback,
slot->dev, THIS_MODULE);
return len;
}
static struct attribute *legacy_attrs[] = {
&(struct attribute){ .name = "power", .mode = 0644 },
NULL,
};
static void legacy_release(struct kobject *kobj)
{
struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
pci_dev_put(slot->dev);
kfree(slot);
}
static struct kobj_type legacy_ktype = {
.sysfs_ops = &(const struct sysfs_ops){
.store = legacy_store, .show = legacy_show
},
.release = &legacy_release,
.default_attrs = legacy_attrs,
};
static int legacy_add_slot(struct pci_dev *pdev)
{
struct legacy_slot *slot = kzalloc(sizeof(*slot), GFP_KERNEL);
if (!slot)
return -ENOMEM;
if (kobject_init_and_add(&slot->kobj, &legacy_ktype,
&pci_slots_kset->kobj, "%s",
dev_name(&pdev->dev))) {
dev_warn(&pdev->dev, "Failed to created legacy fake slot\n");
return -EINVAL;
}
slot->dev = pci_dev_get(pdev);
list_add(&slot->list, &legacy_list);
return 0;
}
static int legacy_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct pci_dev *pdev = to_pci_dev(data);
if (action == BUS_NOTIFY_ADD_DEVICE) {
legacy_add_slot(pdev);
} else if (action == BUS_NOTIFY_DEL_DEVICE) {
struct legacy_slot *slot;
list_for_each_entry(slot, &legacy_list, list)
if (slot->dev == pdev)
goto found;
dev_warn(&pdev->dev, "Missing legacy fake slot?");
return -ENODEV;
found:
kobject_del(&slot->kobj);
list_del(&slot->list);
kobject_put(&slot->kobj);
}
return 0;
}
static struct notifier_block legacy_notifier = {
.notifier_call = legacy_notify
};
static int __init init_legacy(void)
{
struct pci_dev *pdev = NULL;
/* Add existing devices */
for_each_pci_dev(pdev)
legacy_add_slot(pdev);
/* Be alerted of any new ones */
bus_register_notifier(&pci_bus_type, &legacy_notifier);
return 0;
}
module_init(init_legacy);
static void __exit remove_legacy(void)
{
struct legacy_slot *slot, *tmp;
bus_unregister_notifier(&pci_bus_type, &legacy_notifier);
list_for_each_entry_safe(slot, tmp, &legacy_list, list) {
list_del(&slot->list);
kobject_del(&slot->kobj);
kobject_put(&slot->kobj);
}
}
module_exit(remove_legacy);
MODULE_AUTHOR("Trent Piepho <xyzzy@speakeasy.org>");
MODULE_DESCRIPTION("Legacy version of the fakephp interface");
MODULE_LICENSE("GPL");