blob: 6151389fd90304d1b02b364150432d8d3e17bb34 [file] [log] [blame]
Trent Piepho83dbf662009-03-20 14:56:46 -06001/* 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>
Stephen Rothwell7bb2cb32009-03-26 18:34:33 +110021#include <linux/device.h>
Trent Piepho83dbf662009-03-20 14:56:46 -060022#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",
Stephen Rothwell7bb2cb32009-03-26 18:34:33 +110092 dev_name(&pdev->dev))) {
Trent Piepho83dbf662009-03-20 14:56:46 -060093 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");