Linux Audio

Check our new training course

Embedded Linux training

Mar 10-20, 2025, special US time zones
Register
Loading...
Note: File does not exist in v4.17.
  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");