Commit 9dba910e authored by Tejun Heo's avatar Tejun Heo Committed by Jesse Barnes

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: default avatarTejun Heo <tj@kernel.org>
Acked-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
Reviewed-by: default avatarGrant Grundler <grundler@parisc-linux.org>
Signed-off-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
parent 825c423a
...@@ -19,37 +19,98 @@ ...@@ -19,37 +19,98 @@
#include <linux/cpu.h> #include <linux/cpu.h>
#include "pci.h" #include "pci.h"
/*
* Dynamic device IDs are disabled for !CONFIG_HOTPLUG
*/
struct pci_dynid { struct pci_dynid {
struct list_head node; struct list_head node;
struct pci_device_id id; struct pci_device_id id;
}; };
#ifdef CONFIG_HOTPLUG /**
* pci_add_dynid - add a new PCI device ID to this driver and re-probe devices
* @drv: target pci driver
* @vendor: PCI vendor ID
* @device: PCI device ID
* @subvendor: PCI subvendor ID
* @subdevice: PCI subdevice ID
* @class: PCI class
* @class_mask: PCI class mask
* @driver_data: private driver data
*
* Adds a new dynamic pci device ID to this driver and causes the
* driver to probe for all devices again. @drv must have been
* registered prior to calling this function.
*
* CONTEXT:
* Does GFP_KERNEL allocation.
*
* RETURNS:
* 0 on success, -errno on failure.
*/
int pci_add_dynid(struct pci_driver *drv,
unsigned int vendor, unsigned int device,
unsigned int subvendor, unsigned int subdevice,
unsigned int class, unsigned int class_mask,
unsigned long driver_data)
{
struct pci_dynid *dynid;
int retval;
dynid = kzalloc(sizeof(*dynid), GFP_KERNEL);
if (!dynid)
return -ENOMEM;
dynid->id.vendor = vendor;
dynid->id.device = device;
dynid->id.subvendor = subvendor;
dynid->id.subdevice = subdevice;
dynid->id.class = class;
dynid->id.class_mask = class_mask;
dynid->id.driver_data = driver_data;
spin_lock(&drv->dynids.lock);
list_add_tail(&dynid->node, &drv->dynids.list);
spin_unlock(&drv->dynids.lock);
get_driver(&drv->driver);
retval = driver_attach(&drv->driver);
put_driver(&drv->driver);
return retval;
}
static void pci_free_dynids(struct pci_driver *drv)
{
struct pci_dynid *dynid, *n;
spin_lock(&drv->dynids.lock);
list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
list_del(&dynid->node);
kfree(dynid);
}
spin_unlock(&drv->dynids.lock);
}
/*
* Dynamic device ID manipulation via sysfs is disabled for !CONFIG_HOTPLUG
*/
#ifdef CONFIG_HOTPLUG
/** /**
* store_new_id - add a new PCI device ID to this driver and re-probe devices * store_new_id - sysfs frontend to pci_add_dynid()
* @driver: target device driver * @driver: target device driver
* @buf: buffer for scanning device ID data * @buf: buffer for scanning device ID data
* @count: input size * @count: input size
* *
* Adds a new dynamic pci device ID to this driver, * Allow PCI IDs to be added to an existing driver via sysfs.
* and causes the driver to probe for all devices again.
*/ */
static ssize_t static ssize_t
store_new_id(struct device_driver *driver, const char *buf, size_t count) store_new_id(struct device_driver *driver, const char *buf, size_t count)
{ {
struct pci_dynid *dynid;
struct pci_driver *pdrv = to_pci_driver(driver); struct pci_driver *pdrv = to_pci_driver(driver);
const struct pci_device_id *ids = pdrv->id_table; const struct pci_device_id *ids = pdrv->id_table;
__u32 vendor, device, subvendor=PCI_ANY_ID, __u32 vendor, device, subvendor=PCI_ANY_ID,
subdevice=PCI_ANY_ID, class=0, class_mask=0; subdevice=PCI_ANY_ID, class=0, class_mask=0;
unsigned long driver_data=0; unsigned long driver_data=0;
int fields=0; int fields=0;
int retval=0; int retval;
fields = sscanf(buf, "%x %x %x %x %x %x %lx", fields = sscanf(buf, "%x %x %x %x %x %x %lx",
&vendor, &device, &subvendor, &subdevice, &vendor, &device, &subvendor, &subdevice,
...@@ -72,27 +133,8 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count) ...@@ -72,27 +133,8 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
return retval; return retval;
} }
dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); retval = pci_add_dynid(pdrv, vendor, device, subvendor, subdevice,
if (!dynid) class, class_mask, driver_data);
return -ENOMEM;
dynid->id.vendor = vendor;
dynid->id.device = device;
dynid->id.subvendor = subvendor;
dynid->id.subdevice = subdevice;
dynid->id.class = class;
dynid->id.class_mask = class_mask;
dynid->id.driver_data = driver_data;
spin_lock(&pdrv->dynids.lock);
list_add_tail(&dynid->node, &pdrv->dynids.list);
spin_unlock(&pdrv->dynids.lock);
if (get_driver(&pdrv->driver)) {
retval = driver_attach(&pdrv->driver);
put_driver(&pdrv->driver);
}
if (retval) if (retval)
return retval; return retval;
return count; return count;
...@@ -145,19 +187,6 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count) ...@@ -145,19 +187,6 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count)
} }
static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id); static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
static void
pci_free_dynids(struct pci_driver *drv)
{
struct pci_dynid *dynid, *n;
spin_lock(&drv->dynids.lock);
list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
list_del(&dynid->node);
kfree(dynid);
}
spin_unlock(&drv->dynids.lock);
}
static int static int
pci_create_newid_file(struct pci_driver *drv) pci_create_newid_file(struct pci_driver *drv)
{ {
...@@ -186,7 +215,6 @@ static void pci_remove_removeid_file(struct pci_driver *drv) ...@@ -186,7 +215,6 @@ static void pci_remove_removeid_file(struct pci_driver *drv)
driver_remove_file(&drv->driver, &driver_attr_remove_id); driver_remove_file(&drv->driver, &driver_attr_remove_id);
} }
#else /* !CONFIG_HOTPLUG */ #else /* !CONFIG_HOTPLUG */
static inline void pci_free_dynids(struct pci_driver *drv) {}
static inline int pci_create_newid_file(struct pci_driver *drv) static inline int pci_create_newid_file(struct pci_driver *drv)
{ {
return 0; return 0;
...@@ -1106,6 +1134,7 @@ static int __init pci_driver_init(void) ...@@ -1106,6 +1134,7 @@ static int __init pci_driver_init(void)
postcore_initcall(pci_driver_init); postcore_initcall(pci_driver_init);
EXPORT_SYMBOL_GPL(pci_add_dynid);
EXPORT_SYMBOL(pci_match_id); EXPORT_SYMBOL(pci_match_id);
EXPORT_SYMBOL(__pci_register_driver); EXPORT_SYMBOL(__pci_register_driver);
EXPORT_SYMBOL(pci_unregister_driver); EXPORT_SYMBOL(pci_unregister_driver);
......
...@@ -796,6 +796,11 @@ int __must_check __pci_register_driver(struct pci_driver *, struct module *, ...@@ -796,6 +796,11 @@ int __must_check __pci_register_driver(struct pci_driver *, struct module *,
void pci_unregister_driver(struct pci_driver *dev); void pci_unregister_driver(struct pci_driver *dev);
void pci_remove_behind_bridge(struct pci_dev *dev); void pci_remove_behind_bridge(struct pci_dev *dev);
struct pci_driver *pci_dev_driver(const struct pci_dev *dev); struct pci_driver *pci_dev_driver(const struct pci_dev *dev);
int pci_add_dynid(struct pci_driver *drv,
unsigned int vendor, unsigned int device,
unsigned int subvendor, unsigned int subdevice,
unsigned int class, unsigned int class_mask,
unsigned long driver_data);
const struct pci_device_id *pci_match_id(const struct pci_device_id *ids, const struct pci_device_id *pci_match_id(const struct pci_device_id *ids,
struct pci_dev *dev); struct pci_dev *dev);
int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment