Re: [RFC] [PATCH] driver core: allow userspace to unbind drivers from devices.

Dmitry Torokhov
2004-11-16T23:27:03Z

[-- Attachment #1: Type: text/plain, Size: 606 bytes --]

On Mon, 15 Nov 2004 21:54:40 -0800, Greg KH <greg@kroah.com> wrote:
> On Tue, Nov 09, 2004 at 10:49:43PM -0500, Dmitry Torokhov wrote:
> > In the meantime, can I please have bind_mode patch applied? I believe
> > it is useful regardless of which bind/unbind solution will be adopted
> > and having them will allow me clean up serio bus implementaion quite a
> > bit.
> >
> > Pretty please...
>
> Care to resend it? I can't find it in my archives.
>

Here it is, against 2.6.10-rc2. Apologies for sending it as an attachment
but this interface will not let me put it inline without mangling.

--
Dmitry

[-- Attachment #2: bind_mode-attr.patch --]
[-- Type: application/octet-stream, Size: 10551 bytes --]

===================================================================

ChangeSet@1.1963, 2004-11-16 02:07:15-05:00, dtor_core@ameritech.net
Driver core: add "bind_mode" default device and driver attribute.
Calls to device_attach() and driver_attach() will not
bind device and driver if either one of them is in
"manual" bind mode. Manual binding is expected to be
handled by bus's drvctl()

echo -n "manual" > /sus/bus/serio/devices/serio2/bind_mode
echo -n "auto" > /sys/bus/serio/drivers/serio_raw/bind_mode

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

drivers/base/bus.c | 20 +++++++----
drivers/base/interface.c | 70 +++++++++++++++++++++++++++++++++++++---
drivers/input/serio/serio.c | 58 ++-------------------------------
drivers/input/serio/serio_raw.c | 4 +-
include/linux/device.h | 5 ++
include/linux/serio.h | 4 --
6 files changed, 89 insertions(+), 72 deletions(-)

===================================================================

diff -urN a/drivers/base/bus.c b/drivers/base/bus.c
--- a/drivers/base/bus.c 2004-11-16 02:20:53 -0500
+++ b/drivers/base/bus.c 2004-11-16 02:20:53 -0500
@@ -68,9 +68,12 @@
up(&drv->unload_sem);
}

+extern struct attribute * drv_default_attrs[];
+
static struct kobj_type ktype_driver = {
.sysfs_ops = &driver_sysfs_ops,
.release = driver_release,
+ .default_attrs = drv_default_attrs,
};

@@ -319,9 +322,12 @@
return 1;
}

- if (bus->match) {
- list_for_each(entry, &bus->drivers.list) {
- struct device_driver * drv = to_drv(entry);
+ if (dev->manual_bind || !bus->match)
+ return 0;
+
+ list_for_each(entry, &bus->drivers.list) {
+ struct device_driver * drv = to_drv(entry);
+ if (!drv->manual_bind) {
error = driver_probe_device(drv, dev);
if (!error)
/* success, driver matched */
@@ -329,8 +335,8 @@
if (error != -ENODEV)
/* driver matched but the probe failed */
printk(KERN_WARNING
- "%s: probe of %s failed with error %d\n",
- drv->name, dev->bus_id, error);
+ "%s: probe of %s failed with error %d\n",
+ drv->name, dev->bus_id, error);
}
}

@@ -356,12 +362,12 @@
struct list_head * entry;
int error;

- if (!bus->match)
+ if (drv->manual_bind || !bus->match)
return;

list_for_each(entry, &bus->devices.list) {
struct device * dev = container_of(entry, struct device, bus_list);
- if (!dev->driver) {
+ if (!dev->driver && !dev->manual_bind) {
error = driver_probe_device(drv, dev);
if (error && (error != -ENODEV))
/* driver matched but the probe failed */
diff -urN a/drivers/base/interface.c b/drivers/base/interface.c
--- a/drivers/base/interface.c 2004-11-16 02:20:53 -0500
+++ b/drivers/base/interface.c 2004-11-16 02:20:53 -0500
@@ -1,6 +1,6 @@
/*
* drivers/base/interface.c - common driverfs interface that's exported to
- * the world for all devices.
+ * the world for all devices and drivers.
*
* Copyright (c) 2002-3 Patrick Mochel
* Copyright (c) 2002-3 Open Source Development Labs
@@ -14,6 +14,40 @@
#include <linux/stat.h>
#include <linux/string.h>

+
+static int parse_bind_mode(const char * buf, size_t count, unsigned int * manual_bind)
+{
+ int retval = count;
+
+ if (!strncmp(buf, "manual", count)) {
+ *manual_bind = 1;
+ } else if (!strncmp(buf, "auto", count)) {
+ *manual_bind = 0;
+ } else {
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+/**
+ * bind_mode - controls the binding mode for the device.
+ *
+ * When set to "auto" driver core will try to automatically bind the
+ * device once appropriate driver becomes available. When bind mode
+ * is "manual" user intervention is required.
+ */
+
+static ssize_t dev_bind_mode_show(struct device * dev, char * buf)
+{
+ return sprintf(buf, "%s\n", dev->manual_bind ? "manual" : "auto");
+}
+
+static ssize_t dev_bind_mode_store(struct device * dev, const char * buf, size_t count)
+{
+ return parse_bind_mode(buf, count, &dev->manual_bind);
+}
+
+
/**
* detach_state - control the default power state for the device.
*
@@ -27,12 +61,12 @@
* driver's suspend method.
*/

-static ssize_t detach_show(struct device * dev, char * buf)
+static ssize_t dev_detach_show(struct device * dev, char * buf)
{
return sprintf(buf, "%u\n", dev->detach_state);
}

-static ssize_t detach_store(struct device * dev, const char * buf, size_t n)
+static ssize_t dev_detach_store(struct device * dev, const char * buf, size_t n)
{
u32 state;
state = simple_strtoul(buf, NULL, 10);
@@ -42,10 +76,36 @@
return n;
}

-static DEVICE_ATTR(detach_state, 0644, detach_show, detach_store);
-
+static DEVICE_ATTR(bind_mode, 0644, dev_bind_mode_show, dev_bind_mode_store);
+static DEVICE_ATTR(detach_state, 0644, dev_detach_show, dev_detach_store);

struct attribute * dev_default_attrs[] = {
+ &dev_attr_bind_mode.attr,
&dev_attr_detach_state.attr,
NULL,
};
+
+/**
+ * bind_mode - controls the binding mode for the driver.
+ *
+ * When set to "auto" driver core will try to automatically bind the
+ * driver once appropriate device becomes available. When bind mode
+ * is "manual" user intervention is required.
+ */
+
+static ssize_t drv_bind_mode_show(struct device_driver * drv, char * buf)
+{
+ return sprintf(buf, "%s\n", drv->manual_bind ? "manual" : "auto");
+}
+
+static ssize_t drv_bind_mode_store(struct device_driver * drv, const char * buf, size_t count)
+{
+ return parse_bind_mode(buf, count, &drv->manual_bind);
+}
+
+static DRIVER_ATTR(bind_mode, 0644, drv_bind_mode_show, drv_bind_mode_store);
+
+struct attribute * drv_default_attrs[] = {
+ &driver_attr_bind_mode.attr,
+ NULL,
+};
diff -urN a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
--- a/drivers/input/serio/serio.c 2004-11-16 02:20:53 -0500
+++ b/drivers/input/serio/serio.c 2004-11-16 02:20:53 -0500
@@ -92,7 +92,7 @@
struct serio_driver *drv;

list_for_each_entry(drv, &serio_driver_list, node)
- if (!drv->manual_bind)
+ if (!drv->driver.manual_bind)
if (serio_bind_driver(serio, drv))
break;
}
@@ -277,33 +277,9 @@
return retval;
}

-static ssize_t serio_show_bind_mode(struct device *dev, char *buf)
-{
- struct serio *serio = to_serio_port(dev);
- return sprintf(buf, "%s\n", serio->manual_bind ? "manual" : "auto");
-}
-
-static ssize_t serio_set_bind_mode(struct device *dev, const char *buf, size_t count)
-{
- struct serio *serio = to_serio_port(dev);
- int retval;
-
- retval = count;
- if (!strncmp(buf, "manual", count)) {
- serio->manual_bind = 1;
- } else if (!strncmp(buf, "auto", count)) {
- serio->manual_bind = 0;
- } else {
- retval = -EINVAL;
- }
-
- return retval;
-}
-
static struct device_attribute serio_device_attrs[] = {
__ATTR(description, S_IRUGO, serio_show_description, NULL),
__ATTR(drvctl, S_IWUSR, NULL, serio_rebind_driver),
- __ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode),
__ATTR_NULL
};

@@ -371,7 +347,7 @@

if (drv)
serio_bind_driver(serio, drv);
- else if (!serio->manual_bind)
+ else if (!serio->dev.manual_bind)
serio_find_driver(serio);

/* Ok, now bind children, if any */
@@ -383,7 +359,7 @@

serio_create_port(serio);

- if (!serio->manual_bind) {
+ if (!serio->dev.manual_bind) {
/*
* With children we just _prefer_ passed in driver,
* but we will try other options in case preferred
@@ -505,34 +481,8 @@
return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)");
}

-static ssize_t serio_driver_show_bind_mode(struct device_driver *drv, char *buf)
-{
- struct serio_driver *serio_drv = to_serio_driver(drv);
- return sprintf(buf, "%s\n", serio_drv->manual_bind ? "manual" : "auto");
-}
-
-static ssize_t serio_driver_set_bind_mode(struct device_driver *drv, const char *buf, size_t count)
-{
- struct serio_driver *serio_drv = to_serio_driver(drv);
- int retval;
-
- retval = count;
- if (!strncmp(buf, "manual", count)) {
- serio_drv->manual_bind = 1;
- } else if (!strncmp(buf, "auto", count)) {
- serio_drv->manual_bind = 0;
- } else {
- retval = -EINVAL;
- }
-
- return retval;
-}
-
-
static struct driver_attribute serio_driver_attrs[] = {
__ATTR(description, S_IRUGO, serio_driver_show_description, NULL),
- __ATTR(bind_mode, S_IWUSR | S_IRUGO,
- serio_driver_show_bind_mode, serio_driver_set_bind_mode),
__ATTR_NULL
};

@@ -547,7 +497,7 @@
drv->driver.bus = &serio_bus;
driver_register(&drv->driver);

- if (drv->manual_bind)
+ if (drv->driver.manual_bind)
goto out;

start_over:
diff -urN a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
--- a/drivers/input/serio/serio_raw.c 2004-11-16 02:20:53 -0500
+++ b/drivers/input/serio/serio_raw.c 2004-11-16 02:20:53 -0500
@@ -365,14 +365,14 @@

static struct serio_driver serio_raw_drv = {
.driver = {
- .name = "serio_raw",
+ .name = "serio_raw",
+ .manual_bind = 1,
},
.description = DRIVER_DESC,
.interrupt = serio_raw_interrupt,
.connect = serio_raw_connect,
.reconnect = serio_raw_reconnect,
.disconnect = serio_raw_disconnect,
- .manual_bind = 1,
};

int __init serio_raw_init(void)
diff -urN a/include/linux/device.h b/include/linux/device.h
--- a/include/linux/device.h 2004-11-16 02:20:53 -0500
+++ b/include/linux/device.h 2004-11-16 02:20:53 -0500
@@ -102,6 +102,8 @@
char * name;
struct bus_type * bus;

+ unsigned int manual_bind;
+
struct semaphore unload_sem;
struct kobject kobj;
struct list_head devices;
@@ -264,6 +266,9 @@
struct bus_type * bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this
device */
+ unsigned int manual_bind; /* indicates whether the core will
+ try to find a driver for the
+ device automatically */
void *driver_data; /* data private to the driver */
void *platform_data; /* Platform specific data (e.g. ACPI,
BIOS data relevant to device) */
diff -urN a/include/linux/serio.h b/include/linux/serio.h
--- a/include/linux/serio.h 2004-11-16 02:20:53 -0500
+++ b/include/linux/serio.h 2004-11-16 02:20:53 -0500
@@ -27,8 +27,6 @@
char name[32];
char phys[32];

- unsigned int manual_bind;
-
unsigned short idbus;
unsigned short idvendor;
unsigned short idproduct;
@@ -57,8 +55,6 @@
void *private;
char *description;

- unsigned int manual_bind;
-
void (*write_wakeup)(struct serio *);
irqreturn_t (*interrupt)(struct serio *, unsigned char,
unsigned int, struct pt_regs *);