Driver core: fix "driver" symlink timing
Create the "driver" link before the child device may be created by the probing logic. This makes it possible for userspace (udev), to determine the driver property of the parent device, at the time the child device is created. Signed-off-by: Kay Sievers <kay.sievers@novell.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Родитель
116af37820
Коммит
1901fb2604
|
@ -26,6 +26,50 @@
|
||||||
#define to_drv(node) container_of(node, struct device_driver, kobj.entry)
|
#define to_drv(node) container_of(node, struct device_driver, kobj.entry)
|
||||||
|
|
||||||
|
|
||||||
|
static void driver_bound(struct device *dev)
|
||||||
|
{
|
||||||
|
if (klist_node_attached(&dev->knode_driver)) {
|
||||||
|
printk(KERN_WARNING "%s: device %s already bound\n",
|
||||||
|
__FUNCTION__, kobject_name(&dev->kobj));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_debug("bound device '%s' to driver '%s'\n",
|
||||||
|
dev->bus_id, dev->driver->name);
|
||||||
|
|
||||||
|
if (dev->bus)
|
||||||
|
blocking_notifier_call_chain(&dev->bus->bus_notifier,
|
||||||
|
BUS_NOTIFY_BOUND_DRIVER, dev);
|
||||||
|
|
||||||
|
klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int driver_sysfs_add(struct device *dev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj,
|
||||||
|
kobject_name(&dev->kobj));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void driver_sysfs_remove(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_driver *drv = dev->driver;
|
||||||
|
|
||||||
|
if (drv) {
|
||||||
|
sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj));
|
||||||
|
sysfs_remove_link(&dev->kobj, "driver");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* device_bind_driver - bind a driver to one device.
|
* device_bind_driver - bind a driver to one device.
|
||||||
* @dev: device.
|
* @dev: device.
|
||||||
|
@ -42,32 +86,8 @@
|
||||||
*/
|
*/
|
||||||
int device_bind_driver(struct device *dev)
|
int device_bind_driver(struct device *dev)
|
||||||
{
|
{
|
||||||
int ret;
|
driver_bound(dev);
|
||||||
|
return driver_sysfs_add(dev);
|
||||||
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);
|
|
||||||
|
|
||||||
if (dev->bus)
|
|
||||||
blocking_notifier_call_chain(&dev->bus->bus_notifier,
|
|
||||||
BUS_NOTIFY_BOUND_DRIVER, dev);
|
|
||||||
|
|
||||||
klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices);
|
|
||||||
ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj,
|
|
||||||
kobject_name(&dev->kobj));
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct stupid_thread_structure {
|
struct stupid_thread_structure {
|
||||||
|
@ -90,30 +110,32 @@ static int really_probe(void *void_data)
|
||||||
drv->bus->name, drv->name, dev->bus_id);
|
drv->bus->name, drv->name, dev->bus_id);
|
||||||
|
|
||||||
dev->driver = drv;
|
dev->driver = drv;
|
||||||
|
if (driver_sysfs_add(dev)) {
|
||||||
|
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
|
||||||
|
__FUNCTION__, dev->bus_id);
|
||||||
|
goto probe_failed;
|
||||||
|
}
|
||||||
|
|
||||||
if (dev->bus->probe) {
|
if (dev->bus->probe) {
|
||||||
ret = dev->bus->probe(dev);
|
ret = dev->bus->probe(dev);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev->driver = NULL;
|
|
||||||
goto probe_failed;
|
goto probe_failed;
|
||||||
}
|
|
||||||
} else if (drv->probe) {
|
} else if (drv->probe) {
|
||||||
ret = drv->probe(dev);
|
ret = drv->probe(dev);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev->driver = NULL;
|
|
||||||
goto probe_failed;
|
goto probe_failed;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (device_bind_driver(dev)) {
|
driver_bound(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;
|
ret = 1;
|
||||||
pr_debug("%s: Bound Device %s to Driver %s\n",
|
pr_debug("%s: Bound Device %s to Driver %s\n",
|
||||||
drv->bus->name, dev->bus_id, drv->name);
|
drv->bus->name, dev->bus_id, drv->name);
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
probe_failed:
|
probe_failed:
|
||||||
|
driver_sysfs_remove(dev);
|
||||||
|
dev->driver = NULL;
|
||||||
|
|
||||||
if (ret == -ENODEV || ret == -ENXIO) {
|
if (ret == -ENODEV || ret == -ENXIO) {
|
||||||
/* Driver matched, but didn't support device
|
/* Driver matched, but didn't support device
|
||||||
* or device not found.
|
* or device not found.
|
||||||
|
@ -289,7 +311,7 @@ static void __device_release_driver(struct device * dev)
|
||||||
drv = dev->driver;
|
drv = dev->driver;
|
||||||
if (drv) {
|
if (drv) {
|
||||||
get_driver(drv);
|
get_driver(drv);
|
||||||
sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj));
|
driver_sysfs_remove(dev);
|
||||||
sysfs_remove_link(&dev->kobj, "driver");
|
sysfs_remove_link(&dev->kobj, "driver");
|
||||||
klist_remove(&dev->knode_driver);
|
klist_remove(&dev->knode_driver);
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче