diff options
| -rw-r--r-- | drivers/base/base.h | 1 | ||||
| -rw-r--r-- | drivers/base/bus.c | 117 | ||||
| -rw-r--r-- | drivers/base/core.c | 2 | ||||
| -rw-r--r-- | drivers/base/dd.c | 2 | ||||
| -rw-r--r-- | drivers/base/driver.c | 35 | ||||
| -rw-r--r-- | include/linux/device.h | 7 | 
6 files changed, 143 insertions, 21 deletions
| diff --git a/drivers/base/base.h b/drivers/base/base.h index 645f62692920..783752b68a9a 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h | |||
| @@ -5,6 +5,7 @@ extern int bus_add_driver(struct device_driver *); | |||
| 5 | extern void bus_remove_driver(struct device_driver *); | 5 | extern void bus_remove_driver(struct device_driver *); | 
| 6 | 6 | ||
| 7 | extern void driver_detach(struct device_driver * drv); | 7 | extern void driver_detach(struct device_driver * drv); | 
| 8 | extern int driver_probe_device(struct device_driver *, struct device *); | ||
| 8 | 9 | ||
| 9 | static inline struct class_device *to_class_dev(struct kobject *obj) | 10 | static inline struct class_device *to_class_dev(struct kobject *obj) | 
| 10 | { | 11 | { | 
| diff --git a/drivers/base/bus.c b/drivers/base/bus.c index c3fac7fd555e..96fe2f956754 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c | |||
| @@ -133,6 +133,58 @@ static struct kobj_type ktype_bus = { | |||
| 133 | decl_subsys(bus, &ktype_bus, NULL); | 133 | decl_subsys(bus, &ktype_bus, NULL); | 
| 134 | 134 | ||
| 135 | 135 | ||
| 136 | /* Manually detach a device from it's associated driver. */ | ||
| 137 | static int driver_helper(struct device *dev, void *data) | ||
| 138 | { | ||
| 139 | const char *name = data; | ||
| 140 | |||
| 141 | if (strcmp(name, dev->bus_id) == 0) | ||
| 142 | return 1; | ||
| 143 | return 0; | ||
| 144 | } | ||
| 145 | |||
| 146 | static ssize_t driver_unbind(struct device_driver *drv, | ||
| 147 | const char *buf, size_t count) | ||
| 148 | { | ||
| 149 | struct bus_type *bus = get_bus(drv->bus); | ||
| 150 | struct device *dev; | ||
| 151 | int err = -ENODEV; | ||
| 152 | |||
| 153 | dev = bus_find_device(bus, NULL, (void *)buf, driver_helper); | ||
| 154 | if ((dev) && | ||
| 155 | (dev->driver == drv)) { | ||
| 156 | device_release_driver(dev); | ||
| 157 | err = count; | ||
| 158 | } | ||
| 159 | return err; | ||
| 160 | } | ||
| 161 | static DRIVER_ATTR(unbind, S_IWUSR, NULL, driver_unbind); | ||
| 162 | |||
| 163 | /* | ||
| 164 | * Manually attach a device to a driver. | ||
| 165 | * Note: the driver must want to bind to the device, | ||
| 166 | * it is not possible to override the driver's id table. | ||
| 167 | */ | ||
| 168 | static ssize_t driver_bind(struct device_driver *drv, | ||
| 169 | const char *buf, size_t count) | ||
| 170 | { | ||
| 171 | struct bus_type *bus = get_bus(drv->bus); | ||
| 172 | struct device *dev; | ||
| 173 | int err = -ENODEV; | ||
| 174 | |||
| 175 | dev = bus_find_device(bus, NULL, (void *)buf, driver_helper); | ||
| 176 | if ((dev) && | ||
| 177 | (dev->driver == NULL)) { | ||
| 178 | down(&dev->sem); | ||
| 179 | err = driver_probe_device(drv, dev); | ||
| 180 | up(&dev->sem); | ||
| 181 | put_device(dev); | ||
| 182 | } | ||
| 183 | return err; | ||
| 184 | } | ||
| 185 | static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind); | ||
| 186 | |||
| 187 | |||
| 136 | static struct device * next_device(struct klist_iter * i) | 188 | static struct device * next_device(struct klist_iter * i) | 
| 137 | { | 189 | { | 
| 138 | struct klist_node * n = klist_next(i); | 190 | struct klist_node * n = klist_next(i); | 
| @@ -177,6 +229,39 @@ int bus_for_each_dev(struct bus_type * bus, struct device * start, | |||
| 177 | return error; | 229 | return error; | 
| 178 | } | 230 | } | 
| 179 | 231 | ||
| 232 | /** | ||
| 233 | * bus_find_device - device iterator for locating a particular device. | ||
| 234 | * @bus: bus type | ||
| 235 | * @start: Device to begin with | ||
| 236 | * @data: Data to pass to match function | ||
| 237 | * @match: Callback function to check device | ||
| 238 | * | ||
| 239 | * This is similar to the bus_for_each_dev() function above, but it | ||
| 240 | * returns a reference to a device that is 'found' for later use, as | ||
| 241 | * determined by the @match callback. | ||
| 242 | * | ||
| 243 | * The callback should return 0 if the device doesn't match and non-zero | ||
| 244 | * if it does. If the callback returns non-zero, this function will | ||
| 245 | * return to the caller and not iterate over any more devices. | ||
| 246 | */ | ||
| 247 | struct device * bus_find_device(struct bus_type *bus, | ||
| 248 | struct device *start, void *data, | ||
| 249 | int (*match)(struct device *, void *)) | ||
| 250 | { | ||
| 251 | struct klist_iter i; | ||
| 252 | struct device *dev; | ||
| 253 | |||
| 254 | if (!bus) | ||
| 255 | return NULL; | ||
| 256 | |||
| 257 | klist_iter_init_node(&bus->klist_devices, &i, | ||
| 258 | (start ? &start->knode_bus : NULL)); | ||
| 259 | while ((dev = next_device(&i))) | ||
| 260 | if (match(dev, data) && get_device(dev)) | ||
| 261 | break; | ||
| 262 | klist_iter_exit(&i); | ||
| 263 | return dev; | ||
| 264 | } | ||
| 180 | 265 | ||
| 181 | 266 | ||
| 182 | static struct device_driver * next_driver(struct klist_iter * i) | 267 | static struct device_driver * next_driver(struct klist_iter * i) | 
| @@ -363,6 +448,8 @@ int bus_add_driver(struct device_driver * drv) | |||
| 363 | module_add_driver(drv->owner, drv); | 448 | module_add_driver(drv->owner, drv); | 
| 364 | 449 | ||
| 365 | driver_add_attrs(bus, drv); | 450 | driver_add_attrs(bus, drv); | 
| 451 | driver_create_file(drv, &driver_attr_unbind); | ||
| 452 | driver_create_file(drv, &driver_attr_bind); | ||
| 366 | } | 453 | } | 
| 367 | return error; | 454 | return error; | 
| 368 | } | 455 | } | 
| @@ -380,6 +467,8 @@ int bus_add_driver(struct device_driver * drv) | |||
| 380 | void bus_remove_driver(struct device_driver * drv) | 467 | void bus_remove_driver(struct device_driver * drv) | 
| 381 | { | 468 | { | 
| 382 | if (drv->bus) { | 469 | if (drv->bus) { | 
| 470 | driver_remove_file(drv, &driver_attr_bind); | ||
| 471 | driver_remove_file(drv, &driver_attr_unbind); | ||
| 383 | driver_remove_attrs(drv->bus, drv); | 472 | driver_remove_attrs(drv->bus, drv); | 
| 384 | klist_remove(&drv->knode_bus); | 473 | klist_remove(&drv->knode_bus); | 
| 385 | pr_debug("bus %s: remove driver %s\n", drv->bus->name, drv->name); | 474 | pr_debug("bus %s: remove driver %s\n", drv->bus->name, drv->name); | 
| @@ -394,31 +483,22 @@ void bus_remove_driver(struct device_driver * drv) | |||
| 394 | /* Helper for bus_rescan_devices's iter */ | 483 | /* Helper for bus_rescan_devices's iter */ | 
| 395 | static int bus_rescan_devices_helper(struct device *dev, void *data) | 484 | static int bus_rescan_devices_helper(struct device *dev, void *data) | 
| 396 | { | 485 | { | 
| 397 | int *count = data; | 486 | if (!dev->driver) | 
| 398 | 487 | device_attach(dev); | |
| 399 | if (!dev->driver && (device_attach(dev) > 0)) | ||
| 400 | (*count)++; | ||
| 401 | |||
| 402 | return 0; | 488 | return 0; | 
| 403 | } | 489 | } | 
| 404 | 490 | ||
| 405 | |||
| 406 | /** | 491 | /** | 
| 407 | * bus_rescan_devices - rescan devices on the bus for possible drivers | 492 | * bus_rescan_devices - rescan devices on the bus for possible drivers | 
| 408 | * @bus: the bus to scan. | 493 | * @bus: the bus to scan. | 
| 409 | * | 494 | * | 
| 410 | * This function will look for devices on the bus with no driver | 495 | * This function will look for devices on the bus with no driver | 
| 411 | * attached and rescan it against existing drivers to see if it | 496 | * attached and rescan it against existing drivers to see if it matches | 
| 412 | * matches any. Calls device_attach(). Returns the number of devices | 497 | * any by calling device_attach() for the unbound devices. | 
| 413 | * that were sucessfully bound to a driver. | ||
| 414 | */ | 498 | */ | 
| 415 | int bus_rescan_devices(struct bus_type * bus) | 499 | void bus_rescan_devices(struct bus_type * bus) | 
| 416 | { | 500 | { | 
| 417 | int count = 0; | 501 | bus_for_each_dev(bus, NULL, NULL, bus_rescan_devices_helper); | 
| 418 | |||
| 419 | bus_for_each_dev(bus, NULL, &count, bus_rescan_devices_helper); | ||
| 420 | |||
| 421 | return count; | ||
| 422 | } | 502 | } | 
| 423 | 503 | ||
| 424 | 504 | ||
| @@ -557,6 +637,7 @@ int __init buses_init(void) | |||
| 557 | 637 | ||
| 558 | 638 | ||
| 559 | EXPORT_SYMBOL_GPL(bus_for_each_dev); | 639 | EXPORT_SYMBOL_GPL(bus_for_each_dev); | 
| 640 | EXPORT_SYMBOL_GPL(bus_find_device); | ||
| 560 | EXPORT_SYMBOL_GPL(bus_for_each_drv); | 641 | EXPORT_SYMBOL_GPL(bus_for_each_drv); | 
| 561 | 642 | ||
| 562 | EXPORT_SYMBOL_GPL(bus_add_device); | 643 | EXPORT_SYMBOL_GPL(bus_add_device); | 
| diff --git a/drivers/base/core.c b/drivers/base/core.c index 86d79755fbfb..efe03a024a5b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
| @@ -333,7 +333,7 @@ void device_del(struct device * dev) | |||
| 333 | struct device * parent = dev->parent; | 333 | struct device * parent = dev->parent; | 
| 334 | 334 | ||
| 335 | if (parent) | 335 | if (parent) | 
| 336 | klist_remove(&dev->knode_parent); | 336 | klist_del(&dev->knode_parent); | 
| 337 | 337 | ||
| 338 | /* Notify the platform of the removal, in case they | 338 | /* Notify the platform of the removal, in case they | 
| 339 | * need to do anything... | 339 | * need to do anything... | 
| diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 6db3a789c54f..16323f9cbff0 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c | |||
| @@ -65,7 +65,7 @@ void device_bind_driver(struct device * dev) | |||
| 65 | * | 65 | * | 
| 66 | * This function must be called with @dev->sem held. | 66 | * This function must be called with @dev->sem held. | 
| 67 | */ | 67 | */ | 
| 68 | static int driver_probe_device(struct device_driver * drv, struct device * dev) | 68 | int driver_probe_device(struct device_driver * drv, struct device * dev) | 
| 69 | { | 69 | { | 
| 70 | int ret = 0; | 70 | int ret = 0; | 
| 71 | 71 | ||
| diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 1b645886e9eb..291c5954a3af 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c | |||
| @@ -56,6 +56,41 @@ EXPORT_SYMBOL_GPL(driver_for_each_device); | |||
| 56 | 56 | ||
| 57 | 57 | ||
| 58 | /** | 58 | /** | 
| 59 | * driver_find_device - device iterator for locating a particular device. | ||
| 60 | * @driver: The device's driver | ||
| 61 | * @start: Device to begin with | ||
| 62 | * @data: Data to pass to match function | ||
| 63 | * @match: Callback function to check device | ||
| 64 | * | ||
| 65 | * This is similar to the driver_for_each_device() function above, but | ||
| 66 | * it returns a reference to a device that is 'found' for later use, as | ||
| 67 | * determined by the @match callback. | ||
| 68 | * | ||
| 69 | * The callback should return 0 if the device doesn't match and non-zero | ||
| 70 | * if it does. If the callback returns non-zero, this function will | ||
| 71 | * return to the caller and not iterate over any more devices. | ||
| 72 | */ | ||
| 73 | struct device * driver_find_device(struct device_driver *drv, | ||
| 74 | struct device * start, void * data, | ||
| 75 | int (*match)(struct device *, void *)) | ||
| 76 | { | ||
| 77 | struct klist_iter i; | ||
| 78 | struct device *dev; | ||
| 79 | |||
| 80 | if (!drv) | ||
| 81 | return NULL; | ||
| 82 | |||
| 83 | klist_iter_init_node(&drv->klist_devices, &i, | ||
| 84 | (start ? &start->knode_driver : NULL)); | ||
| 85 | while ((dev = next_device(&i))) | ||
| 86 | if (match(dev, data) && get_device(dev)) | ||
| 87 | break; | ||
| 88 | klist_iter_exit(&i); | ||
| 89 | return dev; | ||
| 90 | } | ||
| 91 | EXPORT_SYMBOL_GPL(driver_find_device); | ||
| 92 | |||
| 93 | /** | ||
| 59 | * driver_create_file - create sysfs file for driver. | 94 | * driver_create_file - create sysfs file for driver. | 
| 60 | * @drv: driver. | 95 | * @drv: driver. | 
| 61 | * @attr: driver attribute descriptor. | 96 | * @attr: driver attribute descriptor. | 
| diff --git a/include/linux/device.h b/include/linux/device.h index 7b781a72b293..f378c846e6d5 100644 --- a/include/linux/device.h +++ b/include/linux/device.h | |||
| @@ -69,7 +69,7 @@ struct bus_type { | |||
| 69 | extern int bus_register(struct bus_type * bus); | 69 | extern int bus_register(struct bus_type * bus); | 
| 70 | extern void bus_unregister(struct bus_type * bus); | 70 | extern void bus_unregister(struct bus_type * bus); | 
| 71 | 71 | ||
| 72 | extern int bus_rescan_devices(struct bus_type * bus); | 72 | extern void bus_rescan_devices(struct bus_type * bus); | 
| 73 | 73 | ||
| 74 | extern struct bus_type * get_bus(struct bus_type * bus); | 74 | extern struct bus_type * get_bus(struct bus_type * bus); | 
| 75 | extern void put_bus(struct bus_type * bus); | 75 | extern void put_bus(struct bus_type * bus); | 
| @@ -80,6 +80,8 @@ extern struct bus_type * find_bus(char * name); | |||
| 80 | 80 | ||
| 81 | int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data, | 81 | int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data, | 
| 82 | int (*fn)(struct device *, void *)); | 82 | int (*fn)(struct device *, void *)); | 
| 83 | struct device * bus_find_device(struct bus_type *bus, struct device *start, | ||
| 84 | void *data, int (*match)(struct device *, void *)); | ||
| 83 | 85 | ||
| 84 | int bus_for_each_drv(struct bus_type * bus, struct device_driver * start, | 86 | int bus_for_each_drv(struct bus_type * bus, struct device_driver * start, | 
| 85 | void * data, int (*fn)(struct device_driver *, void *)); | 87 | void * data, int (*fn)(struct device_driver *, void *)); | 
| @@ -142,6 +144,9 @@ extern void driver_remove_file(struct device_driver *, struct driver_attribute * | |||
| 142 | 144 | ||
| 143 | extern int driver_for_each_device(struct device_driver * drv, struct device * start, | 145 | extern int driver_for_each_device(struct device_driver * drv, struct device * start, | 
| 144 | void * data, int (*fn)(struct device *, void *)); | 146 | void * data, int (*fn)(struct device *, void *)); | 
| 147 | struct device * driver_find_device(struct device_driver *drv, | ||
| 148 | struct device *start, void *data, | ||
| 149 | int (*match)(struct device *, void *)); | ||
| 145 | 150 | ||
| 146 | 151 | ||
| 147 | /* | 152 | /* | 
