Commit 44729bba authored by Patrick Mochel's avatar Patrick Mochel

Merge osdl.org:/home/mochel/src/kernel/devel/linux-2.5-sync

into osdl.org:/home/mochel/src/kernel/devel/linux-2.5-linus
parents 1138d8a0 4220f3d3
...@@ -17,3 +17,6 @@ extern int device_make_dir(struct device * dev); ...@@ -17,3 +17,6 @@ extern int device_make_dir(struct device * dev);
extern void device_remove_dir(struct device * dev); extern void device_remove_dir(struct device * dev);
extern int device_bus_link(struct device * dev); extern int device_bus_link(struct device * dev);
extern int driver_bind(struct device_driver * drv);
extern void driver_unbind(struct device_driver * drv);
...@@ -22,6 +22,91 @@ static struct driver_dir_entry bus_dir = { ...@@ -22,6 +22,91 @@ static struct driver_dir_entry bus_dir = {
mode: (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO), mode: (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO),
}; };
/**
* bus_for_each_dev - walk list of devices and do something to each
* @bus: bus in question
* @data: data for the callback
* @callback: caller-defined action to perform on each device
*
* Why do we do this? So we can guarantee proper locking and reference
* counting on devices as we touch each one.
*
* Algorithm:
* Take the bus lock and get the first node in the list. We increment
* the reference count and unlock the bus. If we have a device from a
* previous iteration, we decrement the reference count.
* After we call the callback, we get the next node in the list and loop.
* At the end, if @dev is not null, we still have it pinned, so we need
* to let it go.
*/
int bus_for_each_dev(struct bus_type * bus, void * data,
int (*callback)(struct device * dev, void * data))
{
struct device * next;
struct device * dev = NULL;
struct list_head * node;
int error = 0;
get_bus(bus);
read_lock(&bus->lock);
node = bus->devices.next;
while (node != &bus->devices) {
next = list_entry(node,struct device,bus_list);
get_device(next);
read_unlock(&bus->lock);
if (dev)
put_device(dev);
dev = next;
if ((error = callback(dev,data))) {
put_device(dev);
break;
}
read_lock(&bus->lock);
node = dev->bus_list.next;
}
read_unlock(&bus->lock);
if (dev)
put_device(dev);
put_bus(bus);
return error;
}
int bus_for_each_drv(struct bus_type * bus, void * data,
int (*callback)(struct device_driver * drv, void * data))
{
struct device_driver * next;
struct device_driver * drv = NULL;
struct list_head * node;
int error = 0;
/* pin bus in memory */
get_bus(bus);
read_lock(&bus->lock);
node = bus->drivers.next;
while (node != &bus->drivers) {
next = list_entry(node,struct device_driver,bus_list);
get_driver(next);
read_unlock(&bus->lock);
if (drv)
put_driver(drv);
drv = next;
if ((error = callback(drv,data))) {
put_driver(drv);
break;
}
read_lock(&bus->lock);
node = drv->bus_list.next;
}
read_unlock(&bus->lock);
if (drv)
put_driver(drv);
put_bus(bus);
return error;
}
/** /**
* bus_add_device - add device to bus * bus_add_device - add device to bus
* @dev: device being added * @dev: device being added
...@@ -119,6 +204,8 @@ static int __init bus_init(void) ...@@ -119,6 +204,8 @@ static int __init bus_init(void)
core_initcall(bus_init); core_initcall(bus_init);
EXPORT_SYMBOL(bus_for_each_dev);
EXPORT_SYMBOL(bus_for_each_drv);
EXPORT_SYMBOL(bus_add_device); EXPORT_SYMBOL(bus_add_device);
EXPORT_SYMBOL(bus_remove_device); EXPORT_SYMBOL(bus_remove_device);
EXPORT_SYMBOL(bus_register); EXPORT_SYMBOL(bus_register);
......
...@@ -23,6 +23,120 @@ int (*platform_notify_remove)(struct device * dev) = NULL; ...@@ -23,6 +23,120 @@ int (*platform_notify_remove)(struct device * dev) = NULL;
spinlock_t device_lock = SPIN_LOCK_UNLOCKED; spinlock_t device_lock = SPIN_LOCK_UNLOCKED;
/**
* found_match - do actual binding of device to driver
* @dev: device
* @drv: driver
*
* We're here because the bus's bind callback returned success for this
* pair. We call the driver's probe callback to verify they're really a
* match made in heaven.
*
* In the future, we may want to notify userspace of the binding. (But,
* we might not want to do it here).
*
* We may also want to create a symlink in the driver's directory to the
* device's physical directory.
*/
static int found_match(struct device * dev, struct device_driver * drv)
{
int error = 0;
dev->driver = get_driver(drv);
if (drv->probe)
if (drv->probe(dev))
goto ProbeFailed;
pr_debug("bound device '%s' to driver '%s'\n",
dev->bus_id,drv->name);
write_lock(&drv->lock);
list_add_tail(&dev->driver_list,&drv->devices);
write_unlock(&drv->lock);
goto Done;
ProbeFailed:
put_driver(drv);
dev->driver = NULL;
Done:
return error;
}
/**
* bind_device - try to associated device with a driver
* @drv: current driver to try
* @data: device in disguise
*
* This function is used as a callback to bus_for_each_drv.
* It calls the bus's ::bind callback to check if the driver supports
* the device. If so, it calls the found_match() function above to
* take care of all the details.
*/
static int do_device_bind(struct device_driver * drv, void * data)
{
struct device * dev = (struct device *)data;
int error = 0;
if (!dev->driver) {
if (drv->bus->bind && drv->bus->bind(dev,drv))
error = found_match(dev,drv);
}
return error;
}
static int device_bind(struct device * dev)
{
int error = 0;
if (dev->bus)
error = bus_for_each_drv(dev->bus,dev,do_device_bind);
return error;
}
static void device_unbind(struct device * dev)
{
/* unbind from driver */
if (dev->driver && dev->driver->remove)
dev->driver->remove(dev,REMOVE_NOTIFY);
}
static int do_driver_bind(struct device * dev, void * data)
{
struct device_driver * drv = (struct device_driver *)data;
int error = 0;
if (!dev->driver) {
if (dev->bus->bind && dev->bus->bind(dev,drv))
error = found_match(dev,drv);
}
return error;
}
int driver_bind(struct device_driver * drv)
{
return bus_for_each_dev(drv->bus,drv,do_driver_bind);
}
static int do_driver_unbind(struct device * dev, void * data)
{
struct device_driver * drv = (struct device_driver *)data;
lock_device(dev);
if (dev->driver == drv) {
dev->driver = NULL;
unlock_device(dev);
if (drv->remove)
drv->remove(dev,REMOVE_NOTIFY);
} else
unlock_device(dev);
return 0;
}
void driver_unbind(struct device_driver * drv)
{
// driver_for_each_dev(drv,drv,do_driver_unbind);
}
/** /**
* device_register - register a device * device_register - register a device
* @dev: pointer to the device structure * @dev: pointer to the device structure
...@@ -72,6 +186,9 @@ int device_register(struct device *dev) ...@@ -72,6 +186,9 @@ int device_register(struct device *dev)
bus_add_device(dev); bus_add_device(dev);
/* bind to driver */
device_bind(dev);
/* notify platform of device entry */ /* notify platform of device entry */
if (platform_notify) if (platform_notify)
platform_notify(dev); platform_notify(dev);
...@@ -104,15 +221,9 @@ void put_device(struct device * dev) ...@@ -104,15 +221,9 @@ void put_device(struct device * dev)
if (platform_notify_remove) if (platform_notify_remove)
platform_notify_remove(dev); platform_notify_remove(dev);
device_unbind(dev);
bus_remove_device(dev); bus_remove_device(dev);
/* Tell the driver to clean up after itself.
* Note that we likely didn't allocate the device,
* so this is the driver's chance to free that up...
*/
if (dev->driver && dev->driver->remove)
dev->driver->remove(dev,REMOVE_FREE_RESOURCES);
/* remove the driverfs directory */ /* remove the driverfs directory */
device_remove_dir(dev); device_remove_dir(dev);
......
...@@ -38,6 +38,7 @@ int driver_register(struct device_driver * drv) ...@@ -38,6 +38,7 @@ int driver_register(struct device_driver * drv)
list_add(&drv->bus_list,&drv->bus->drivers); list_add(&drv->bus_list,&drv->bus->drivers);
write_unlock(&drv->bus->lock); write_unlock(&drv->bus->lock);
driver_make_dir(drv); driver_make_dir(drv);
driver_bind(drv);
put_driver(drv); put_driver(drv);
return 0; return 0;
} }
...@@ -55,6 +56,7 @@ void put_driver(struct device_driver * drv) ...@@ -55,6 +56,7 @@ void put_driver(struct device_driver * drv)
if (drv->bus) { if (drv->bus) {
pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name); pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name);
driver_unbind(drv);
write_lock(&drv->bus->lock); write_lock(&drv->bus->lock);
list_del_init(&drv->bus_list); list_del_init(&drv->bus_list);
write_unlock(&drv->bus->lock); write_unlock(&drv->bus->lock);
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/kmod.h> /* for hotplug_path */ #include <linux/kmod.h> /* for hotplug_path */
extern int pci_announce_device(struct pci_driver *drv, struct pci_dev *dev);
#ifndef FALSE #ifndef FALSE
#define FALSE (0) #define FALSE (0)
#define TRUE (!FALSE) #define TRUE (!FALSE)
...@@ -48,28 +46,6 @@ run_sbin_hotplug(struct pci_dev *pdev, int insert) ...@@ -48,28 +46,6 @@ run_sbin_hotplug(struct pci_dev *pdev, int insert)
call_usermodehelper (argv [0], argv, envp); call_usermodehelper (argv [0], argv, envp);
} }
/**
* pci_announce_device_to_drivers - tell the drivers a new device has appeared
* @dev: the device that has shown up
*
* Notifys the drivers that a new device has appeared, and also notifys
* userspace through /sbin/hotplug.
*/
void
pci_announce_device_to_drivers(struct pci_dev *dev)
{
struct list_head *ln;
for(ln=pci_bus_type.drivers.next; ln != &pci_bus_type.drivers; ln=ln->next) {
struct pci_driver *drv = list_entry(ln, struct pci_driver, node);
if (drv->remove && pci_announce_device(drv, dev))
break;
}
/* notify userspace of new hotplug device */
run_sbin_hotplug(dev, TRUE);
}
/** /**
* pci_insert_device - insert a hotplug device * pci_insert_device - insert a hotplug device
* @dev: the device to insert * @dev: the device to insert
...@@ -85,7 +61,8 @@ pci_insert_device(struct pci_dev *dev, struct pci_bus *bus) ...@@ -85,7 +61,8 @@ pci_insert_device(struct pci_dev *dev, struct pci_bus *bus)
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
pci_proc_attach_device(dev); pci_proc_attach_device(dev);
#endif #endif
pci_announce_device_to_drivers(dev); /* notify userspace of new hotplug device */
run_sbin_hotplug(dev, TRUE);
} }
static void static void
...@@ -110,11 +87,7 @@ pci_free_resources(struct pci_dev *dev) ...@@ -110,11 +87,7 @@ pci_free_resources(struct pci_dev *dev)
void void
pci_remove_device(struct pci_dev *dev) pci_remove_device(struct pci_dev *dev)
{ {
if (dev->driver) { put_device(&dev->dev);
if (dev->driver->remove)
dev->driver->remove(dev);
dev->driver = NULL;
}
list_del(&dev->bus_list); list_del(&dev->bus_list);
list_del(&dev->global_list); list_del(&dev->global_list);
pci_free_resources(dev); pci_free_resources(dev);
...@@ -128,4 +101,3 @@ pci_remove_device(struct pci_dev *dev) ...@@ -128,4 +101,3 @@ pci_remove_device(struct pci_dev *dev)
EXPORT_SYMBOL(pci_insert_device); EXPORT_SYMBOL(pci_insert_device);
EXPORT_SYMBOL(pci_remove_device); EXPORT_SYMBOL(pci_remove_device);
EXPORT_SYMBOL(pci_announce_device_to_drivers);
...@@ -10,56 +10,6 @@ ...@@ -10,56 +10,6 @@
* Registration of PCI drivers and handling of hot-pluggable devices. * Registration of PCI drivers and handling of hot-pluggable devices.
*/ */
/**
* 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
*
* 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(const struct pci_device_id *ids, const struct pci_dev *dev)
{
while (ids->vendor || ids->subvendor || ids->class_mask) {
if ((ids->vendor == PCI_ANY_ID || ids->vendor == dev->vendor) &&
(ids->device == PCI_ANY_ID || ids->device == dev->device) &&
(ids->subvendor == PCI_ANY_ID || ids->subvendor == dev->subsystem_vendor) &&
(ids->subdevice == PCI_ANY_ID || ids->subdevice == dev->subsystem_device) &&
!((ids->class ^ dev->class) & ids->class_mask))
return ids;
ids++;
}
return NULL;
}
int
pci_announce_device(struct pci_driver *drv, struct pci_dev *dev)
{
const struct pci_device_id *id;
int ret = 0;
if (drv->id_table) {
id = pci_match_device(drv->id_table, dev);
if (!id) {
ret = 0;
goto out;
}
} else
id = NULL;
dev_probe_lock();
if (drv->probe(dev, id) >= 0) {
dev->driver = drv;
ret = 1;
}
dev_probe_unlock();
out:
return ret;
}
static int pci_device_probe(struct device * dev) static int pci_device_probe(struct device * dev)
{ {
int error = 0; int error = 0;
...@@ -68,8 +18,7 @@ static int pci_device_probe(struct device * dev) ...@@ -68,8 +18,7 @@ static int pci_device_probe(struct device * dev)
struct pci_dev * pci_dev = list_entry(dev,struct pci_dev,dev); struct pci_dev * pci_dev = list_entry(dev,struct pci_dev,dev);
if (drv->probe) if (drv->probe)
error = drv->probe(pci_dev,NULL); error = drv->probe(pci_dev,drv->id_table);
printk("%s: returning %d\n",__FUNCTION__,error);
return error > 0 ? 0 : -ENODEV; return error > 0 ? 0 : -ENODEV;
} }
...@@ -123,7 +72,6 @@ int ...@@ -123,7 +72,6 @@ int
pci_register_driver(struct pci_driver *drv) pci_register_driver(struct pci_driver *drv)
{ {
int count = 0; int count = 0;
struct pci_dev * dev;
/* initialize common driver fields */ /* initialize common driver fields */
drv->driver.name = drv->name; drv->driver.name = drv->name;
...@@ -135,11 +83,6 @@ pci_register_driver(struct pci_driver *drv) ...@@ -135,11 +83,6 @@ pci_register_driver(struct pci_driver *drv)
/* register with core */ /* register with core */
count = driver_register(&drv->driver); count = driver_register(&drv->driver);
pci_for_each_dev(dev) {
if (!pci_dev_driver(dev))
pci_announce_device(drv, dev);
}
return count ? count : 1; return count ? count : 1;
} }
...@@ -156,20 +99,6 @@ pci_register_driver(struct pci_driver *drv) ...@@ -156,20 +99,6 @@ pci_register_driver(struct pci_driver *drv)
void void
pci_unregister_driver(struct pci_driver *drv) pci_unregister_driver(struct pci_driver *drv)
{ {
list_t * node;
node = drv->driver.devices.next;
while (node != &drv->driver.devices) {
struct device * dev = list_entry(node,struct device,driver_list);
struct pci_dev * pci_dev = list_entry(dev,struct pci_dev,dev);
if (drv->remove)
drv->remove(pci_dev);
pci_dev->driver = NULL;
dev->driver = NULL;
list_del_init(&dev->driver_list);
}
put_driver(&drv->driver); put_driver(&drv->driver);
} }
...@@ -198,8 +127,39 @@ pci_dev_driver(const struct pci_dev *dev) ...@@ -198,8 +127,39 @@ pci_dev_driver(const struct pci_dev *dev)
return NULL; return NULL;
} }
/**
* pci_bus_bind - 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
*
* 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.
*/
static int pci_bus_bind(struct device * dev, struct device_driver * drv)
{
struct pci_dev * pci_dev = list_entry(dev, struct pci_dev, dev);
struct pci_driver * pci_drv = list_entry(drv,struct pci_driver,driver);
const struct pci_device_id * ids = pci_drv->id_table;
if (!ids)
return 0;
while (ids->vendor || ids->subvendor || ids->class_mask) {
if ((ids->vendor == PCI_ANY_ID || ids->vendor == pci_dev->vendor) &&
(ids->device == PCI_ANY_ID || ids->device == pci_dev->device) &&
(ids->subvendor == PCI_ANY_ID || ids->subvendor == pci_dev->subsystem_vendor) &&
(ids->subdevice == PCI_ANY_ID || ids->subdevice == pci_dev->subsystem_device) &&
!((ids->class ^ pci_dev->class) & ids->class_mask))
return 1;
ids++;
}
return 0;
}
struct bus_type pci_bus_type = { struct bus_type pci_bus_type = {
name: "pci", name: "pci",
bind: pci_bus_bind,
}; };
static int __init pci_driver_init(void) static int __init pci_driver_init(void)
...@@ -209,7 +169,6 @@ static int __init pci_driver_init(void) ...@@ -209,7 +169,6 @@ static int __init pci_driver_init(void)
subsys_initcall(pci_driver_init); subsys_initcall(pci_driver_init);
EXPORT_SYMBOL(pci_match_device);
EXPORT_SYMBOL(pci_register_driver); EXPORT_SYMBOL(pci_register_driver);
EXPORT_SYMBOL(pci_unregister_driver); EXPORT_SYMBOL(pci_unregister_driver);
EXPORT_SYMBOL(pci_dev_driver); EXPORT_SYMBOL(pci_dev_driver);
...@@ -54,7 +54,7 @@ enum { ...@@ -54,7 +54,7 @@ enum {
}; };
struct device; struct device;
struct device_driver;
struct bus_type { struct bus_type {
char * name; char * name;
...@@ -68,6 +68,8 @@ struct bus_type { ...@@ -68,6 +68,8 @@ struct bus_type {
struct driver_dir_entry dir; struct driver_dir_entry dir;
struct driver_dir_entry device_dir; struct driver_dir_entry device_dir;
struct driver_dir_entry driver_dir; struct driver_dir_entry driver_dir;
int (*bind) (struct device * dev, struct device_driver * drv);
}; };
...@@ -82,6 +84,11 @@ static inline struct bus_type * get_bus(struct bus_type * bus) ...@@ -82,6 +84,11 @@ static inline struct bus_type * get_bus(struct bus_type * bus)
extern void put_bus(struct bus_type * bus); extern void put_bus(struct bus_type * bus);
extern int bus_for_each_dev(struct bus_type * bus, void * data,
int (*callback)(struct device * dev, void * data));
extern int bus_for_each_drv(struct bus_type * bus, void * data,
int (*callback)(struct device_driver * drv, void * data));
struct device_driver { struct device_driver {
char * name; char * name;
......
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