/*
* drivers/pci/pci-driver.c
*
+ * (C) Copyright 2002-2004, 2007 Greg Kroah-Hartman <greg@kroah.com>
+ * (C) Copyright 2007 Novell Inc.
+ *
+ * Released under the GPL v2 only.
+ *
*/
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/mempolicy.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
#include "pci.h"
/*
- * Registration of PCI drivers and handling of hot-pluggable devices.
- */
-
-/*
* Dynamic device IDs are disabled for !CONFIG_HOTPLUG
*/
#ifdef CONFIG_HOTPLUG
/**
- * store_new_id
+ * store_new_id - add a new PCI device ID to this driver and re-probe devices
+ * @driver: target device driver
+ * @buf: buffer for scanning device ID data
+ * @count: input size
*
* Adds a new dynamic pci device ID to this driver,
* and causes the driver to probe for all devices again.
*/
-static inline ssize_t
+static ssize_t
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);
- __u32 vendor=PCI_ANY_ID, device=PCI_ANY_ID, subvendor=PCI_ANY_ID,
+ __u32 vendor, device, subvendor=PCI_ANY_ID,
subdevice=PCI_ANY_ID, class=0, class_mask=0;
unsigned long driver_data=0;
int fields=0;
+ int retval = 0;
fields = sscanf(buf, "%x %x %x %x %x %x %lux",
&vendor, &device, &subvendor, &subdevice,
&class, &class_mask, &driver_data);
- if (fields < 0)
+ if (fields < 2)
return -EINVAL;
- dynid = kmalloc(sizeof(*dynid), GFP_KERNEL);
+ dynid = kzalloc(sizeof(*dynid), GFP_KERNEL);
if (!dynid)
return -ENOMEM;
- memset(dynid, 0, sizeof(*dynid));
- INIT_LIST_HEAD(&dynid->node);
dynid->id.vendor = vendor;
dynid->id.device = device;
dynid->id.subvendor = subvendor;
driver_data : 0UL;
spin_lock(&pdrv->dynids.lock);
- list_add_tail(&pdrv->dynids.list, &dynid->node);
+ list_add_tail(&dynid->node, &pdrv->dynids.list);
spin_unlock(&pdrv->dynids.lock);
if (get_driver(&pdrv->driver)) {
- driver_attach(&pdrv->driver);
+ retval = driver_attach(&pdrv->driver);
put_driver(&pdrv->driver);
}
+ if (retval)
+ return retval;
return count;
}
static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
{
int error = 0;
if (drv->probe != NULL)
- error = sysfs_create_file(&drv->driver.kobj,
- &driver_attr_new_id.attr);
+ error = driver_create_file(&drv->driver, &driver_attr_new_id);
return error;
}
+static void pci_remove_newid_file(struct pci_driver *drv)
+{
+ driver_remove_file(&drv->driver, &driver_attr_new_id);
+}
#else /* !CONFIG_HOTPLUG */
static inline void pci_free_dynids(struct pci_driver *drv) {}
static inline int pci_create_newid_file(struct pci_driver *drv)
{
return 0;
}
+static inline void pci_remove_newid_file(struct pci_driver *drv) {}
#endif
/**
* system is in its list of supported devices. Returns the matching
* pci_device_id structure or %NULL if there is no match.
*
- * Depreciated, don't use this as it will not catch any dynamic ids
+ * Deprecated, don't use this as it will not catch any dynamic ids
* that a driver might want to check for.
*/
const struct pci_device_id *pci_match_id(const struct pci_device_id *ids,
}
/**
- * pci_match_device - Tell if a PCI device structure has a matching
- * PCI device id structure
- * @ids: array of PCI device id structures to search in
- * @dev: the PCI device structure to match against
+ * pci_match_device - Tell if a PCI device structure has a matching PCI device id structure
* @drv: the PCI driver to match against
+ * @dev: the PCI device structure to match against
*
* Used by a driver to check whether a PCI device present in the
* system is in its list of supported devices. Returns the matching
* pci_device_id structure or %NULL if there is no match.
*/
-const struct pci_device_id *pci_match_device(struct pci_driver *drv,
- struct pci_dev *dev)
+static const struct pci_device_id *pci_match_device(struct pci_driver *drv,
+ struct pci_dev *dev)
{
- const struct pci_device_id *id;
struct pci_dynid *dynid;
- id = pci_match_id(drv->id_table, dev);
- if (id)
- return id;
-
- /* static ids didn't match, lets look at the dynamic ones */
+ /* Look at the dynamic ids first, before the static ones */
spin_lock(&drv->dynids.lock);
list_for_each_entry(dynid, &drv->dynids.list, node) {
if (pci_match_one_device(&dynid->id, dev)) {
}
}
spin_unlock(&drv->dynids.lock);
- return NULL;
+
+ return pci_match_id(drv->id_table, dev);
}
static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,
set_cpus_allowed(current, node_to_cpumask(node));
/* And set default memory allocation policy */
oldpol = current->mempolicy;
- current->mempolicy = &default_policy;
- mpol_get(current->mempolicy);
+ current->mempolicy = NULL; /* fall back to system default policy */
#endif
error = drv->probe(dev, id);
#ifdef CONFIG_NUMA
set_cpus_allowed(current, oldmask);
- mpol_free(current->mempolicy);
current->mempolicy = oldpol;
#endif
return error;
/**
* __pci_device_probe()
+ * @drv: driver to call to check if it wants the PCI device
+ * @pci_dev: PCI device being probed
*
- * returns 0 on success, else error.
+ * returns 0 on success, else error.
* side-effect: pci_dev->driver is set to drv when drv claims pci_dev.
*/
static int
}
/*
+ * If the device is still on, set the power state as "unknown",
+ * since it might change by the next time we load the driver.
+ */
+ if (pci_dev->current_state == PCI_D0)
+ pci_dev->current_state = PCI_UNKNOWN;
+
+ /*
* We would love to complain here if pci_dev->is_enabled is set, that
* the driver should have called pci_disable_device(), but the
* unfortunate fact is there are too many odd BIOS and bridge setups
struct pci_driver * drv = pci_dev->driver;
int i = 0;
- if (drv && drv->suspend)
+ if (drv && drv->suspend) {
i = drv->suspend(pci_dev, state);
- else
+ suspend_report_result(drv->suspend, i);
+ } else {
pci_save_state(pci_dev);
+ /*
+ * mark its power state as "unknown", since we don't know if
+ * e.g. the BIOS will change its device state when we suspend.
+ */
+ if (pci_dev->current_state == PCI_D0)
+ pci_dev->current_state = PCI_UNKNOWN;
+ }
return i;
}
+static int pci_device_suspend_late(struct device * dev, pm_message_t state)
+{
+ struct pci_dev * pci_dev = to_pci_dev(dev);
+ struct pci_driver * drv = pci_dev->driver;
+ int i = 0;
+
+ if (drv && drv->suspend_late) {
+ i = drv->suspend_late(pci_dev, state);
+ suspend_report_result(drv->suspend_late, i);
+ }
+ return i;
+}
/*
* Default resume method for devices that have no driver provided resume,
* or not even a driver at all.
*/
-static void pci_default_resume(struct pci_dev *pci_dev)
+static int pci_default_resume(struct pci_dev *pci_dev)
{
- int retval;
+ int retval = 0;
/* restore the PCI config space */
pci_restore_state(pci_dev);
/* if the device was enabled before suspend, reenable */
- if (pci_dev->is_enabled)
- retval = pci_enable_device(pci_dev);
+ retval = pci_reenable_device(pci_dev);
/* if the device was busmaster before the suspend, make it busmaster again */
if (pci_dev->is_busmaster)
pci_set_master(pci_dev);
+
+ return retval;
}
static int pci_device_resume(struct device * dev)
{
+ int error;
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
if (drv && drv->resume)
- drv->resume(pci_dev);
+ error = drv->resume(pci_dev);
else
- pci_default_resume(pci_dev);
- return 0;
-}
-
-static void pci_device_shutdown(struct device *dev)
-{
- struct pci_dev *pci_dev = to_pci_dev(dev);
- struct pci_driver *drv = pci_dev->driver;
-
- if (drv && drv->shutdown)
- drv->shutdown(pci_dev);
+ error = pci_default_resume(pci_dev);
+ return error;
}
-#define kobj_to_pci_driver(obj) container_of(obj, struct device_driver, kobj)
-#define attr_to_driver_attribute(obj) container_of(obj, struct driver_attribute, attr)
-
-static ssize_t
-pci_driver_attr_show(struct kobject * kobj, struct attribute *attr, char *buf)
+static int pci_device_resume_early(struct device * dev)
{
- struct device_driver *driver = kobj_to_pci_driver(kobj);
- struct driver_attribute *dattr = attr_to_driver_attribute(attr);
- ssize_t ret;
-
- if (!get_driver(driver))
- return -ENODEV;
+ int error = 0;
+ struct pci_dev * pci_dev = to_pci_dev(dev);
+ struct pci_driver * drv = pci_dev->driver;
- ret = dattr->show ? dattr->show(driver, buf) : -EIO;
+ pci_fixup_device(pci_fixup_resume, pci_dev);
- put_driver(driver);
- return ret;
+ if (drv && drv->resume_early)
+ error = drv->resume_early(pci_dev);
+ return error;
}
-static ssize_t
-pci_driver_attr_store(struct kobject * kobj, struct attribute *attr,
- const char *buf, size_t count)
+static void pci_device_shutdown(struct device *dev)
{
- struct device_driver *driver = kobj_to_pci_driver(kobj);
- struct driver_attribute *dattr = attr_to_driver_attribute(attr);
- ssize_t ret;
-
- if (!get_driver(driver))
- return -ENODEV;
-
- ret = dattr->store ? dattr->store(driver, buf, count) : -EIO;
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
- put_driver(driver);
- return ret;
+ if (drv && drv->shutdown)
+ drv->shutdown(pci_dev);
}
-static struct sysfs_ops pci_driver_sysfs_ops = {
- .show = pci_driver_attr_show,
- .store = pci_driver_attr_store,
-};
-static struct kobj_type pci_driver_kobj_type = {
- .sysfs_ops = &pci_driver_sysfs_ops,
-};
-
/**
- * pci_register_driver - register a new pci driver
+ * __pci_register_driver - register a new pci driver
* @drv: the driver structure to register
+ * @owner: owner module of drv
+ * @mod_name: module name string
*
* Adds the driver structure to the list of registered drivers.
* Returns a negative value on error, otherwise 0.
* If no error occurred, the driver remains registered even if
* no device was claimed during registration.
*/
-int pci_register_driver(struct pci_driver *drv)
+int __pci_register_driver(struct pci_driver *drv, struct module *owner,
+ const char *mod_name)
{
int error;
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &pci_bus_type;
- drv->driver.probe = pci_device_probe;
- drv->driver.remove = pci_device_remove;
- /* FIXME, once all of the existing PCI drivers have been fixed to set
- * the pci shutdown function, this test can go away. */
- if (!drv->driver.shutdown)
- drv->driver.shutdown = pci_device_shutdown;
- else
- printk(KERN_WARNING "Warning: PCI driver %s has a struct "
- "device_driver shutdown method, please update!\n",
- drv->name);
- drv->driver.owner = drv->owner;
- drv->driver.kobj.ktype = &pci_driver_kobj_type;
+ drv->driver.owner = owner;
+ drv->driver.mod_name = mod_name;
spin_lock_init(&drv->dynids.lock);
INIT_LIST_HEAD(&drv->dynids.list);
/* register with core */
error = driver_register(&drv->driver);
+ if (error)
+ return error;
- if (!error)
- error = pci_create_newid_file(drv);
+ error = pci_create_newid_file(drv);
+ if (error)
+ driver_unregister(&drv->driver);
return error;
}
void
pci_unregister_driver(struct pci_driver *drv)
{
+ pci_remove_newid_file(drv);
driver_unregister(&drv->driver);
pci_free_dynids(drv);
}
/**
* pci_bus_match - Tell if a PCI device structure has a matching PCI device id structure
- * @ids: array of PCI device id structures to search in
* @dev: the PCI device structure to match against
+ * @drv: the device driver to search for matching PCI device id structures
*
* Used by a driver to check whether a PCI device present in the
- * system is in its list of supported devices.Returns the matching
+ * system is in its list of supported devices. Returns the matching
* pci_device_id structure or %NULL if there is no match.
*/
static int pci_bus_match(struct device *dev, struct device_driver *drv)
}
#ifndef CONFIG_HOTPLUG
-int pci_hotplug (struct device *dev, char **envp, int num_envp,
- char *buffer, int buffer_size)
+int pci_uevent(struct device *dev, struct kobj_uevent_env *env)
{
return -ENODEV;
}
struct bus_type pci_bus_type = {
.name = "pci",
.match = pci_bus_match,
- .hotplug = pci_hotplug,
+ .uevent = pci_uevent,
+ .probe = pci_device_probe,
+ .remove = pci_device_remove,
.suspend = pci_device_suspend,
+ .suspend_late = pci_device_suspend_late,
+ .resume_early = pci_device_resume_early,
.resume = pci_device_resume,
+ .shutdown = pci_device_shutdown,
.dev_attrs = pci_dev_attrs,
};
postcore_initcall(pci_driver_init);
EXPORT_SYMBOL(pci_match_id);
-EXPORT_SYMBOL(pci_match_device);
-EXPORT_SYMBOL(pci_register_driver);
+EXPORT_SYMBOL(__pci_register_driver);
EXPORT_SYMBOL(pci_unregister_driver);
EXPORT_SYMBOL(pci_dev_driver);
EXPORT_SYMBOL(pci_bus_type);