|
@@ -133,6 +133,34 @@ static struct kobj_type ktype_bus = {
|
|
|
decl_subsys(bus, &ktype_bus, NULL);
|
|
|
|
|
|
|
|
|
+/* Manually detach a device from it's associated driver. */
|
|
|
+static int driver_helper(struct device *dev, void *data)
|
|
|
+{
|
|
|
+ const char *name = data;
|
|
|
+
|
|
|
+ if (strcmp(name, dev->bus_id) == 0)
|
|
|
+ return 1;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t driver_unbind(struct device_driver *drv,
|
|
|
+ const char *buf, size_t count)
|
|
|
+{
|
|
|
+ struct bus_type *bus = get_bus(drv->bus);
|
|
|
+ struct device *dev;
|
|
|
+ int err = -ENODEV;
|
|
|
+
|
|
|
+ dev = bus_find_device(bus, NULL, (void *)buf, driver_helper);
|
|
|
+ if ((dev) &&
|
|
|
+ (dev->driver == drv)) {
|
|
|
+ device_release_driver(dev);
|
|
|
+ err = count;
|
|
|
+ }
|
|
|
+ return err;
|
|
|
+}
|
|
|
+static DRIVER_ATTR(unbind, S_IWUSR, NULL, driver_unbind);
|
|
|
+
|
|
|
+
|
|
|
static struct device * next_device(struct klist_iter * i)
|
|
|
{
|
|
|
struct klist_node * n = klist_next(i);
|
|
@@ -396,6 +424,7 @@ int bus_add_driver(struct device_driver * drv)
|
|
|
module_add_driver(drv->owner, drv);
|
|
|
|
|
|
driver_add_attrs(bus, drv);
|
|
|
+ driver_create_file(drv, &driver_attr_unbind);
|
|
|
}
|
|
|
return error;
|
|
|
}
|
|
@@ -413,6 +442,7 @@ int bus_add_driver(struct device_driver * drv)
|
|
|
void bus_remove_driver(struct device_driver * drv)
|
|
|
{
|
|
|
if (drv->bus) {
|
|
|
+ driver_remove_file(drv, &driver_attr_unbind);
|
|
|
driver_remove_attrs(drv->bus, drv);
|
|
|
klist_remove(&drv->knode_bus);
|
|
|
pr_debug("bus %s: remove driver %s\n", drv->bus->name, drv->name);
|