linux/drivers/pci/hotplug/fakephp.c
<<
>>
Prefs
   1/* Works like the fakephp driver used to, except a little better.
   2 *
   3 * - It's possible to remove devices with subordinate busses.
   4 * - New PCI devices that appear via any method, not just a fakephp triggered
   5 *   rescan, will be noticed.
   6 * - Devices that are removed via any method, not just a fakephp triggered
   7 *   removal, will also be noticed.
   8 *
   9 * Uses nothing from the pci-hotplug subsystem.
  10 *
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/kernel.h>
  15#include <linux/types.h>
  16#include <linux/list.h>
  17#include <linux/kobject.h>
  18#include <linux/sysfs.h>
  19#include <linux/init.h>
  20#include <linux/pci.h>
  21#include <linux/device.h>
  22#include <linux/slab.h>
  23#include "../pci.h"
  24
  25struct legacy_slot {
  26        struct kobject          kobj;
  27        struct pci_dev          *dev;
  28        struct list_head        list;
  29};
  30
  31static LIST_HEAD(legacy_list);
  32
  33static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr,
  34                           char *buf)
  35{
  36        struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
  37        strcpy(buf, "1\n");
  38        return 2;
  39}
  40
  41static void remove_callback(void *data)
  42{
  43        pci_remove_bus_device((struct pci_dev *)data);
  44}
  45
  46static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr,
  47                            const char *buf, size_t len)
  48{
  49        struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
  50        unsigned long val;
  51
  52        if (strict_strtoul(buf, 0, &val) < 0)
  53                return -EINVAL;
  54
  55        if (val)
  56                pci_rescan_bus(slot->dev->bus);
  57        else
  58                sysfs_schedule_callback(&slot->dev->dev.kobj, remove_callback,
  59                                        slot->dev, THIS_MODULE);
  60        return len;
  61}
  62
  63static struct attribute *legacy_attrs[] = {
  64        &(struct attribute){ .name = "power", .mode = 0644 },
  65        NULL,
  66};
  67
  68static void legacy_release(struct kobject *kobj)
  69{
  70        struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
  71
  72        pci_dev_put(slot->dev);
  73        kfree(slot);
  74}
  75
  76static struct kobj_type legacy_ktype = {
  77        .sysfs_ops = &(const struct sysfs_ops){
  78                .store = legacy_store, .show = legacy_show
  79        },
  80        .release = &legacy_release,
  81        .default_attrs = legacy_attrs,
  82};
  83
  84static int legacy_add_slot(struct pci_dev *pdev)
  85{
  86        struct legacy_slot *slot = kzalloc(sizeof(*slot), GFP_KERNEL);
  87
  88        if (!slot)
  89                return -ENOMEM;
  90
  91        if (kobject_init_and_add(&slot->kobj, &legacy_ktype,
  92                                 &pci_slots_kset->kobj, "%s",
  93                                 dev_name(&pdev->dev))) {
  94                dev_warn(&pdev->dev, "Failed to created legacy fake slot\n");
  95                return -EINVAL;
  96        }
  97        slot->dev = pci_dev_get(pdev);
  98
  99        list_add(&slot->list, &legacy_list);
 100
 101        return 0;
 102}
 103
 104static int legacy_notify(struct notifier_block *nb,
 105                         unsigned long action, void *data)
 106{
 107        struct pci_dev *pdev = to_pci_dev(data);
 108
 109        if (action == BUS_NOTIFY_ADD_DEVICE) {
 110                legacy_add_slot(pdev);
 111        } else if (action == BUS_NOTIFY_DEL_DEVICE) {
 112                struct legacy_slot *slot;
 113
 114                list_for_each_entry(slot, &legacy_list, list)
 115                        if (slot->dev == pdev)
 116                                goto found;
 117
 118                dev_warn(&pdev->dev, "Missing legacy fake slot?");
 119                return -ENODEV;
 120found:
 121                kobject_del(&slot->kobj);
 122                list_del(&slot->list);
 123                kobject_put(&slot->kobj);
 124        }
 125
 126        return 0;
 127}
 128
 129static struct notifier_block legacy_notifier = {
 130        .notifier_call = legacy_notify
 131};
 132
 133static int __init init_legacy(void)
 134{
 135        struct pci_dev *pdev = NULL;
 136
 137        /* Add existing devices */
 138        for_each_pci_dev(pdev)
 139                legacy_add_slot(pdev);
 140
 141        /* Be alerted of any new ones */
 142        bus_register_notifier(&pci_bus_type, &legacy_notifier);
 143        return 0;
 144}
 145module_init(init_legacy);
 146
 147static void __exit remove_legacy(void)
 148{
 149        struct legacy_slot *slot, *tmp;
 150
 151        bus_unregister_notifier(&pci_bus_type, &legacy_notifier);
 152
 153        list_for_each_entry_safe(slot, tmp, &legacy_list, list) {
 154                list_del(&slot->list);
 155                kobject_del(&slot->kobj);
 156                kobject_put(&slot->kobj);
 157        }
 158}
 159module_exit(remove_legacy);
 160
 161
 162MODULE_AUTHOR("Trent Piepho <xyzzy@speakeasy.org>");
 163MODULE_DESCRIPTION("Legacy version of the fakephp interface");
 164MODULE_LICENSE("GPL");
 165