123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- /* 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_stop_and_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");
|