at v2.6.32 163 lines 3.8 kB view raw
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 "../pci.h" 23 24struct legacy_slot { 25 struct kobject kobj; 26 struct pci_dev *dev; 27 struct list_head list; 28}; 29 30static LIST_HEAD(legacy_list); 31 32static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr, 33 char *buf) 34{ 35 struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); 36 strcpy(buf, "1\n"); 37 return 2; 38} 39 40static void remove_callback(void *data) 41{ 42 pci_remove_bus_device((struct pci_dev *)data); 43} 44 45static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr, 46 const char *buf, size_t len) 47{ 48 struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); 49 unsigned long val; 50 51 if (strict_strtoul(buf, 0, &val) < 0) 52 return -EINVAL; 53 54 if (val) 55 pci_rescan_bus(slot->dev->bus); 56 else 57 sysfs_schedule_callback(&slot->dev->dev.kobj, remove_callback, 58 slot->dev, THIS_MODULE); 59 return len; 60} 61 62static struct attribute *legacy_attrs[] = { 63 &(struct attribute){ .name = "power", .mode = 0644 }, 64 NULL, 65}; 66 67static void legacy_release(struct kobject *kobj) 68{ 69 struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj); 70 71 pci_dev_put(slot->dev); 72 kfree(slot); 73} 74 75static struct kobj_type legacy_ktype = { 76 .sysfs_ops = &(struct sysfs_ops){ 77 .store = legacy_store, .show = legacy_show 78 }, 79 .release = &legacy_release, 80 .default_attrs = legacy_attrs, 81}; 82 83static int legacy_add_slot(struct pci_dev *pdev) 84{ 85 struct legacy_slot *slot = kzalloc(sizeof(*slot), GFP_KERNEL); 86 87 if (!slot) 88 return -ENOMEM; 89 90 if (kobject_init_and_add(&slot->kobj, &legacy_ktype, 91 &pci_slots_kset->kobj, "%s", 92 dev_name(&pdev->dev))) { 93 dev_warn(&pdev->dev, "Failed to created legacy fake slot\n"); 94 return -EINVAL; 95 } 96 slot->dev = pci_dev_get(pdev); 97 98 list_add(&slot->list, &legacy_list); 99 100 return 0; 101} 102 103static int legacy_notify(struct notifier_block *nb, 104 unsigned long action, void *data) 105{ 106 struct pci_dev *pdev = to_pci_dev(data); 107 108 if (action == BUS_NOTIFY_ADD_DEVICE) { 109 legacy_add_slot(pdev); 110 } else if (action == BUS_NOTIFY_DEL_DEVICE) { 111 struct legacy_slot *slot; 112 113 list_for_each_entry(slot, &legacy_list, list) 114 if (slot->dev == pdev) 115 goto found; 116 117 dev_warn(&pdev->dev, "Missing legacy fake slot?"); 118 return -ENODEV; 119found: 120 kobject_del(&slot->kobj); 121 list_del(&slot->list); 122 kobject_put(&slot->kobj); 123 } 124 125 return 0; 126} 127 128static struct notifier_block legacy_notifier = { 129 .notifier_call = legacy_notify 130}; 131 132static int __init init_legacy(void) 133{ 134 struct pci_dev *pdev = NULL; 135 136 /* Add existing devices */ 137 while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev))) 138 legacy_add_slot(pdev); 139 140 /* Be alerted of any new ones */ 141 bus_register_notifier(&pci_bus_type, &legacy_notifier); 142 return 0; 143} 144module_init(init_legacy); 145 146static void __exit remove_legacy(void) 147{ 148 struct legacy_slot *slot, *tmp; 149 150 bus_unregister_notifier(&pci_bus_type, &legacy_notifier); 151 152 list_for_each_entry_safe(slot, tmp, &legacy_list, list) { 153 list_del(&slot->list); 154 kobject_del(&slot->kobj); 155 kobject_put(&slot->kobj); 156 } 157} 158module_exit(remove_legacy); 159 160 161MODULE_AUTHOR("Trent Piepho <xyzzy@speakeasy.org>"); 162MODULE_DESCRIPTION("Legacy version of the fakephp interface"); 163MODULE_LICENSE("GPL");