Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  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 
 24 struct legacy_slot {
 25         struct kobject          kobj;
 26         struct pci_dev          *dev;
 27         struct list_head        list;
 28 };
 29 
 30 static LIST_HEAD(legacy_list);
 31 
 32 static 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 
 40 static void remove_callback(void *data)
 41 {
 42         pci_remove_bus_device((struct pci_dev *)data);
 43 }
 44 
 45 static 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 
 62 static struct attribute *legacy_attrs[] = {
 63         &(struct attribute){ .name = "power", .mode = 0644 },
 64         NULL,
 65 };
 66 
 67 static 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 
 75 static 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 
 83 static 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 
103 static 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;
119 found:
120                 kobject_del(&slot->kobj);
121                 list_del(&slot->list);
122                 kobject_put(&slot->kobj);
123         }
124 
125         return 0;
126 }
127 
128 static struct notifier_block legacy_notifier = {
129         .notifier_call = legacy_notify
130 };
131 
132 static 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 }
144 module_init(init_legacy);
145 
146 static 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 }
158 module_exit(remove_legacy);
159 
160 
161 MODULE_AUTHOR("Trent Piepho <xyzzy@speakeasy.org>");
162 MODULE_DESCRIPTION("Legacy version of the fakephp interface");
163 MODULE_LICENSE("GPL");
164 
  This page was automatically generated by the LXR engine.