X-Git-Url: http://git.rot13.org/?a=blobdiff_plain;f=drivers%2Fbase%2Fdd.c;h=b5f43c3e44fa2a5c9d5bf23898ea57472e7b38ae;hb=d1ad4ea331e78a5ff90eda7718da31bcbc1a9c38;hp=3b419c9a1e7e6d407a8b3a5ad26440534c499a7b;hpb=2b08c8d0468866f86da97f836c6ac14338cb81a9;p=powerpc.git diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 3b419c9a1e..b5f43c3e44 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -17,6 +17,7 @@ #include #include +#include #include "base.h" #include "power/power.h" @@ -38,59 +39,73 @@ * * This function must be called with @dev->sem held. */ -void device_bind_driver(struct device * dev) +int device_bind_driver(struct device *dev) { - if (klist_node_attached(&dev->knode_driver)) - return; + int ret; + + if (klist_node_attached(&dev->knode_driver)) { + printk(KERN_WARNING "%s: device %s already bound\n", + __FUNCTION__, kobject_name(&dev->kobj)); + return 0; + } pr_debug("bound device '%s' to driver '%s'\n", dev->bus_id, dev->driver->name); klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices); - sysfs_create_link(&dev->driver->kobj, &dev->kobj, + ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj, kobject_name(&dev->kobj)); - sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver"); + if (ret == 0) { + ret = sysfs_create_link(&dev->kobj, &dev->driver->kobj, + "driver"); + if (ret) + sysfs_remove_link(&dev->driver->kobj, + kobject_name(&dev->kobj)); + } + return ret; } -/** - * driver_probe_device - attempt to bind device & driver. - * @drv: driver. - * @dev: device. - * - * First, we call the bus's match function, if one present, which - * should compare the device IDs the driver supports with the - * device IDs of the device. Note we don't do this ourselves - * because we don't know the format of the ID structures, nor what - * is to be considered a match and what is not. - * - * This function returns 1 if a match is found, an error if one - * occurs (that is not -ENODEV or -ENXIO), and 0 otherwise. - * - * This function must be called with @dev->sem held. - */ -int driver_probe_device(struct device_driver * drv, struct device * dev) +struct stupid_thread_structure { + struct device_driver *drv; + struct device *dev; +}; + +static atomic_t probe_count = ATOMIC_INIT(0); +static int really_probe(void *void_data) { + struct stupid_thread_structure *data = void_data; + struct device_driver *drv = data->drv; + struct device *dev = data->dev; int ret = 0; - if (drv->bus->match && !drv->bus->match(dev, drv)) - goto Done; + atomic_inc(&probe_count); + pr_debug("%s: Probing driver %s with device %s\n", + drv->bus->name, drv->name, dev->bus_id); - pr_debug("%s: Matched Device %s with Driver %s\n", - drv->bus->name, dev->bus_id, drv->name); dev->driver = drv; - if (drv->probe) { + if (dev->bus->probe) { + ret = dev->bus->probe(dev); + if (ret) { + dev->driver = NULL; + goto probe_failed; + } + } else if (drv->probe) { ret = drv->probe(dev); if (ret) { dev->driver = NULL; - goto ProbeFailed; + goto probe_failed; } } - device_bind_driver(dev); + if (device_bind_driver(dev)) { + printk(KERN_ERR "%s: device_bind_driver(%s) failed\n", + __FUNCTION__, dev->bus_id); + /* How does undo a ->probe? We're screwed. */ + } ret = 1; pr_debug("%s: Bound Device %s to Driver %s\n", drv->bus->name, dev->bus_id, drv->name); - goto Done; + goto done; - ProbeFailed: +probe_failed: if (ret == -ENODEV || ret == -ENXIO) { /* Driver matched, but didn't support device * or device not found. @@ -103,7 +118,71 @@ int driver_probe_device(struct device_driver * drv, struct device * dev) "%s: probe of %s failed with error %d\n", drv->name, dev->bus_id, ret); } - Done: +done: + kfree(data); + atomic_dec(&probe_count); + return ret; +} + +/** + * driver_probe_done + * Determine if the probe sequence is finished or not. + * + * Should somehow figure out how to use a semaphore, not an atomic variable... + */ +int driver_probe_done(void) +{ + pr_debug("%s: probe_count = %d\n", __FUNCTION__, + atomic_read(&probe_count)); + if (atomic_read(&probe_count)) + return -EBUSY; + return 0; +} + +/** + * driver_probe_device - attempt to bind device & driver together + * @drv: driver to bind a device to + * @dev: device to try to bind to the driver + * + * First, we call the bus's match function, if one present, which should + * compare the device IDs the driver supports with the device IDs of the + * device. Note we don't do this ourselves because we don't know the + * format of the ID structures, nor what is to be considered a match and + * what is not. + * + * This function returns 1 if a match is found, an error if one occurs + * (that is not -ENODEV or -ENXIO), and 0 otherwise. + * + * This function must be called with @dev->sem held. When called for a + * USB interface, @dev->parent->sem must be held as well. + */ +int driver_probe_device(struct device_driver * drv, struct device * dev) +{ + struct stupid_thread_structure *data; + struct task_struct *probe_task; + int ret = 0; + + if (!device_is_registered(dev)) + return -ENODEV; + if (drv->bus->match && !drv->bus->match(dev, drv)) + goto done; + + pr_debug("%s: Matched Device %s with Driver %s\n", + drv->bus->name, dev->bus_id, drv->name); + + data = kmalloc(sizeof(*data), GFP_KERNEL); + data->drv = drv; + data->dev = dev; + + if (drv->multithread_probe) { + probe_task = kthread_run(really_probe, data, + "probe-%s", dev->bus_id); + if (IS_ERR(probe_task)) + ret = PTR_ERR(probe_task); + } else + ret = really_probe(data); + +done: return ret; } @@ -123,6 +202,8 @@ static int __device_attach(struct device_driver * drv, void * data) * * Returns 1 if the device was bound to a driver; * 0 if no matching device was found; error code otherwise. + * + * When called for a USB interface, @dev->parent->sem must be held. */ int device_attach(struct device * dev) { @@ -130,8 +211,9 @@ int device_attach(struct device * dev) down(&dev->sem); if (dev->driver) { - device_bind_driver(dev); - ret = 1; + ret = device_bind_driver(dev); + if (ret == 0) + ret = 1; } else ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); up(&dev->sem); @@ -152,10 +234,14 @@ static int __driver_attach(struct device * dev, void * data) * is an error. */ + if (dev->parent) /* Needed for USB */ + down(&dev->parent->sem); down(&dev->sem); if (!dev->driver) driver_probe_device(drv, dev); up(&dev->sem); + if (dev->parent) + up(&dev->parent->sem); return 0; } @@ -169,9 +255,9 @@ static int __driver_attach(struct device * dev, void * data) * returns 0 and the @dev->driver is set, we've found a * compatible pair. */ -void driver_attach(struct device_driver * drv) +int driver_attach(struct device_driver * drv) { - bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); + return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); } /** @@ -181,6 +267,8 @@ void driver_attach(struct device_driver * drv) * Manually detach device from driver. * * __device_release_driver() must be called with @dev->sem held. + * When called for a USB interface, @dev->parent->sem must be held + * as well. */ static void __device_release_driver(struct device * dev) @@ -194,7 +282,9 @@ static void __device_release_driver(struct device * dev) sysfs_remove_link(&dev->kobj, "driver"); klist_remove(&dev->knode_driver); - if (drv->remove) + if (dev->bus && dev->bus->remove) + dev->bus->remove(dev); + else if (drv->remove) drv->remove(dev); dev->driver = NULL; put_driver(drv); @@ -233,10 +323,14 @@ void driver_detach(struct device_driver * drv) get_device(dev); spin_unlock(&drv->klist_devices.k_lock); + if (dev->parent) /* Needed for USB */ + down(&dev->parent->sem); down(&dev->sem); if (dev->driver == drv) __device_release_driver(dev); up(&dev->sem); + if (dev->parent) + up(&dev->parent->sem); put_device(dev); } }