diff options
author | Tejun Heo <tj@kernel.org> | 2009-09-03 02:26:36 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-09-09 16:43:58 -0400 |
commit | 9dba910e9de2c4aa15ec1286f10052c107ef48ca (patch) | |
tree | 4870eccc95e406ae8d490136f5d65953203418c8 | |
parent | 825c423a35a80a8fd66398a3f9bde7f0b0187a76 (diff) |
PCI: separate out pci_add_dynid()
Separate out pci_add_dynid() from store_new_id() and export it so that
in-kernel code can add PCI IDs dynamically. As the function will be
available regardless of HOTPLUG, put it and pull pci_free_dynids()
outside of CONFIG_HOTPLUG.
This will be used by pci-stub to initialize initial IDs via module
param.
While at it, remove bogus get_driver() failure check.
Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Reviewed-by: Grant Grundler <grundler@parisc-linux.org>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
-rw-r--r-- | drivers/pci/pci-driver.c | 119 | ||||
-rw-r--r-- | include/linux/pci.h | 5 |
2 files changed, 79 insertions, 45 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index f99bc7f089f1..c66dc4341fa0 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c | |||
@@ -19,37 +19,98 @@ | |||
19 | #include <linux/cpu.h> | 19 | #include <linux/cpu.h> |
20 | #include "pci.h" | 20 | #include "pci.h" |
21 | 21 | ||
22 | /* | ||
23 | * Dynamic device IDs are disabled for !CONFIG_HOTPLUG | ||
24 | */ | ||
25 | |||
26 | struct pci_dynid { | 22 | struct pci_dynid { |
27 | struct list_head node; | 23 | struct list_head node; |
28 | struct pci_device_id id; | 24 | struct pci_device_id id; |
29 | }; | 25 | }; |
30 | 26 | ||
31 | #ifdef CONFIG_HOTPLUG | 27 | /** |
28 | * pci_add_dynid - add a new PCI device ID to this driver and re-probe devices | ||
29 | * @drv: target pci driver | ||
30 | * @vendor: PCI vendor ID | ||
31 | * @device: PCI device ID | ||
32 | * @subvendor: PCI subvendor ID | ||
33 | * @subdevice: PCI subdevice ID | ||
34 | * @class: PCI class | ||
35 | * @class_mask: PCI class mask | ||
36 | * @driver_data: private driver data | ||
37 | * | ||
38 | * Adds a new dynamic pci device ID to this driver and causes the | ||
39 | * driver to probe for all devices again. @drv must have been | ||
40 | * registered prior to calling this function. | ||
41 | * | ||
42 | * CONTEXT: | ||
43 | * Does GFP_KERNEL allocation. | ||
44 | * | ||
45 | * RETURNS: | ||
46 | * 0 on success, -errno on failure. | ||
47 | */ | ||
48 | int pci_add_dynid(struct pci_driver *drv, | ||
49 | unsigned int vendor, unsigned int device, | ||
50 | unsigned int subvendor, unsigned int subdevice, | ||
51 | unsigned int class, unsigned int class_mask, | ||
52 | unsigned long driver_data) | ||
53 | { | ||
54 | struct pci_dynid *dynid; | ||
55 | int retval; | ||
56 | |||
57 | dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); | ||
58 | if (!dynid) | ||
59 | return -ENOMEM; | ||
60 | |||
61 | dynid->id.vendor = vendor; | ||
62 | dynid->id.device = device; | ||
63 | dynid->id.subvendor = subvendor; | ||
64 | dynid->id.subdevice = subdevice; | ||
65 | dynid->id.class = class; | ||
66 | dynid->id.class_mask = class_mask; | ||
67 | dynid->id.driver_data = driver_data; | ||
32 | 68 | ||
69 | spin_lock(&drv->dynids.lock); | ||
70 | list_add_tail(&dynid->node, &drv->dynids.list); | ||
71 | spin_unlock(&drv->dynids.lock); | ||
72 | |||
73 | get_driver(&drv->driver); | ||
74 | retval = driver_attach(&drv->driver); | ||
75 | put_driver(&drv->driver); | ||
76 | |||
77 | return retval; | ||
78 | } | ||
79 | |||
80 | static void pci_free_dynids(struct pci_driver *drv) | ||
81 | { | ||
82 | struct pci_dynid *dynid, *n; | ||
83 | |||
84 | spin_lock(&drv->dynids.lock); | ||
85 | list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { | ||
86 | list_del(&dynid->node); | ||
87 | kfree(dynid); | ||
88 | } | ||
89 | spin_unlock(&drv->dynids.lock); | ||
90 | } | ||
91 | |||
92 | /* | ||
93 | * Dynamic device ID manipulation via sysfs is disabled for !CONFIG_HOTPLUG | ||
94 | */ | ||
95 | #ifdef CONFIG_HOTPLUG | ||
33 | /** | 96 | /** |
34 | * store_new_id - add a new PCI device ID to this driver and re-probe devices | 97 | * store_new_id - sysfs frontend to pci_add_dynid() |
35 | * @driver: target device driver | 98 | * @driver: target device driver |
36 | * @buf: buffer for scanning device ID data | 99 | * @buf: buffer for scanning device ID data |
37 | * @count: input size | 100 | * @count: input size |
38 | * | 101 | * |
39 | * Adds a new dynamic pci device ID to this driver, | 102 | * Allow PCI IDs to be added to an existing driver via sysfs. |
40 | * and causes the driver to probe for all devices again. | ||
41 | */ | 103 | */ |
42 | static ssize_t | 104 | static ssize_t |
43 | store_new_id(struct device_driver *driver, const char *buf, size_t count) | 105 | store_new_id(struct device_driver *driver, const char *buf, size_t count) |
44 | { | 106 | { |
45 | struct pci_dynid *dynid; | ||
46 | struct pci_driver *pdrv = to_pci_driver(driver); | 107 | struct pci_driver *pdrv = to_pci_driver(driver); |
47 | const struct pci_device_id *ids = pdrv->id_table; | 108 | const struct pci_device_id *ids = pdrv->id_table; |
48 | __u32 vendor, device, subvendor=PCI_ANY_ID, | 109 | __u32 vendor, device, subvendor=PCI_ANY_ID, |
49 | subdevice=PCI_ANY_ID, class=0, class_mask=0; | 110 | subdevice=PCI_ANY_ID, class=0, class_mask=0; |
50 | unsigned long driver_data=0; | 111 | unsigned long driver_data=0; |
51 | int fields=0; | 112 | int fields=0; |
52 | int retval=0; | 113 | int retval; |
53 | 114 | ||
54 | fields = sscanf(buf, "%x %x %x %x %x %x %lx", | 115 | fields = sscanf(buf, "%x %x %x %x %x %x %lx", |
55 | &vendor, &device, &subvendor, &subdevice, | 116 | &vendor, &device, &subvendor, &subdevice, |
@@ -72,27 +133,8 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count) | |||
72 | return retval; | 133 | return retval; |
73 | } | 134 | } |
74 | 135 | ||
75 | dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); | 136 | retval = pci_add_dynid(pdrv, vendor, device, subvendor, subdevice, |
76 | if (!dynid) | 137 | class, class_mask, driver_data); |
77 | return -ENOMEM; | ||
78 | |||
79 | dynid->id.vendor = vendor; | ||
80 | dynid->id.device = device; | ||
81 | dynid->id.subvendor = subvendor; | ||
82 | dynid->id.subdevice = subdevice; | ||
83 | dynid->id.class = class; | ||
84 | dynid->id.class_mask = class_mask; | ||
85 | dynid->id.driver_data = driver_data; | ||
86 | |||
87 | spin_lock(&pdrv->dynids.lock); | ||
88 | list_add_tail(&dynid->node, &pdrv->dynids.list); | ||
89 | spin_unlock(&pdrv->dynids.lock); | ||
90 | |||
91 | if (get_driver(&pdrv->driver)) { | ||
92 | retval = driver_attach(&pdrv->driver); | ||
93 | put_driver(&pdrv->driver); | ||
94 | } | ||
95 | |||
96 | if (retval) | 138 | if (retval) |
97 | return retval; | 139 | return retval; |
98 | return count; | 140 | return count; |
@@ -145,19 +187,6 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count) | |||
145 | } | 187 | } |
146 | static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id); | 188 | static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id); |
147 | 189 | ||
148 | static void | ||
149 | pci_free_dynids(struct pci_driver *drv) | ||
150 | { | ||
151 | struct pci_dynid *dynid, *n; | ||
152 | |||
153 | spin_lock(&drv->dynids.lock); | ||
154 | list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { | ||
155 | list_del(&dynid->node); | ||
156 | kfree(dynid); | ||
157 | } | ||
158 | spin_unlock(&drv->dynids.lock); | ||
159 | } | ||
160 | |||
161 | static int | 190 | static int |
162 | pci_create_newid_file(struct pci_driver *drv) | 191 | pci_create_newid_file(struct pci_driver *drv) |
163 | { | 192 | { |
@@ -186,7 +215,6 @@ static void pci_remove_removeid_file(struct pci_driver *drv) | |||
186 | driver_remove_file(&drv->driver, &driver_attr_remove_id); | 215 | driver_remove_file(&drv->driver, &driver_attr_remove_id); |
187 | } | 216 | } |
188 | #else /* !CONFIG_HOTPLUG */ | 217 | #else /* !CONFIG_HOTPLUG */ |
189 | static inline void pci_free_dynids(struct pci_driver *drv) {} | ||
190 | static inline int pci_create_newid_file(struct pci_driver *drv) | 218 | static inline int pci_create_newid_file(struct pci_driver *drv) |
191 | { | 219 | { |
192 | return 0; | 220 | return 0; |
@@ -1106,6 +1134,7 @@ static int __init pci_driver_init(void) | |||
1106 | 1134 | ||
1107 | postcore_initcall(pci_driver_init); | 1135 | postcore_initcall(pci_driver_init); |
1108 | 1136 | ||
1137 | EXPORT_SYMBOL_GPL(pci_add_dynid); | ||
1109 | EXPORT_SYMBOL(pci_match_id); | 1138 | EXPORT_SYMBOL(pci_match_id); |
1110 | EXPORT_SYMBOL(__pci_register_driver); | 1139 | EXPORT_SYMBOL(__pci_register_driver); |
1111 | EXPORT_SYMBOL(pci_unregister_driver); | 1140 | EXPORT_SYMBOL(pci_unregister_driver); |
diff --git a/include/linux/pci.h b/include/linux/pci.h index da90217a7b0e..d75668317705 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -796,6 +796,11 @@ int __must_check __pci_register_driver(struct pci_driver *, struct module *, | |||
796 | void pci_unregister_driver(struct pci_driver *dev); | 796 | void pci_unregister_driver(struct pci_driver *dev); |
797 | void pci_remove_behind_bridge(struct pci_dev *dev); | 797 | void pci_remove_behind_bridge(struct pci_dev *dev); |
798 | struct pci_driver *pci_dev_driver(const struct pci_dev *dev); | 798 | struct pci_driver *pci_dev_driver(const struct pci_dev *dev); |
799 | int pci_add_dynid(struct pci_driver *drv, | ||
800 | unsigned int vendor, unsigned int device, | ||
801 | unsigned int subvendor, unsigned int subdevice, | ||
802 | unsigned int class, unsigned int class_mask, | ||
803 | unsigned long driver_data); | ||
799 | const struct pci_device_id *pci_match_id(const struct pci_device_id *ids, | 804 | const struct pci_device_id *pci_match_id(const struct pci_device_id *ids, |
800 | struct pci_dev *dev); | 805 | struct pci_dev *dev); |
801 | int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, | 806 | int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, |