Re: Implement class_device_update_dev() function

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Kay,

> > > > > But userspace should also find out about this change, and this patch
> > > > > prevents that from happening.  What about just tearing down the class
> > > > > device and creating a new one?  That way userspace knows about the new
> > > > > linkage properly, and any device naming and permission issues can be
> > > > > handled anew?
> > > > 
> > > > This won't work for Bluetooth. We create the TTY and its class device
> > > > with tty_register_device() and then the device node is present. Then at
> > > > some point later we open that device and the Bluetooth connection gets
> > > > established. Only when the connection has been established we know the
> > > > device that represents it. So tearing down the class device and creating
> > > > a new one will screw up the application that is using this device node.
> > > > 
> > > > Would reissuing the uevent of the class device help here?
> > > 
> > > How about KOBJ_ONLINE/OFFLINE?
> > 
> > I am not that familiar with the internals of kobject. Can you give me an
> > example on how to do that?
> 
> Just send another event (but not add or remove), for the already created
> object. CPU hotplug uses ONLINE/OFFLINE, and we also use it to get
> notified when the device mapper table is set up (not upstream). Udev is
> able to update symlinks, or run actions on "online" events if asked
> for. 

the attached patch sends ONLINE when a device has been attached to a
class device and OFFLINE when it has been removed.

Regards

Marcel

[PATCH] Allow dynamically changing the device of a class device

This patch implements the class_device_update_dev() function which
allows to change the device of a class device after its creation.

Signed-off-by: Marcel Holtmann <[email protected]>

---
commit 97372f657bbb68ee1c4119bb21642fc700317ce3
tree bd46b493dd75b09515897eb0d12b2fd30a234e3c
parent 120bda20c6f64b32e8bfbdd7b34feafaa5f5332e
author Marcel Holtmann <[email protected]> Sat, 08 Jul 2006 15:24:15 +0200
committer Marcel Holtmann <[email protected]> Sat, 08 Jul 2006 15:24:15 +0200

 drivers/base/class.c   |   98 ++++++++++++++++++++++++++++++++++--------------
 include/linux/device.h |    1 
 2 files changed, 71 insertions(+), 28 deletions(-)

diff --git a/drivers/base/class.c b/drivers/base/class.c
index de89083..1789a6b 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -485,6 +485,48 @@ static void class_device_remove_groups(s
 	}
 }
 
+static int class_device_add_dev(struct class_device *class_dev)
+{
+	char *class_name = NULL;
+	int error = 0;
+
+	if (class_dev->dev) {
+		class_name = make_class_name(class_dev->class->name,
+					     &class_dev->kobj);
+		error = sysfs_create_link(&class_dev->kobj,
+					  &class_dev->dev->kobj, "device");
+		if (error)
+			goto out;
+
+		error = sysfs_create_link(&class_dev->dev->kobj,
+					  &class_dev->kobj, class_name);
+		if (error) {
+			sysfs_remove_link(&class_dev->kobj, "device");
+			goto out;
+		}
+
+		kobject_uevent(&class_dev->kobj, KOBJ_ONLINE);
+	}
+
+ out:
+	kfree(class_name);
+	return error;
+}
+
+static void class_device_remove_dev(struct class_device *class_dev)
+{
+	char *class_name;
+
+	if (class_dev->dev) {
+		class_name = make_class_name(class_dev->class->name,
+					     &class_dev->kobj);
+		sysfs_remove_link(&class_dev->kobj, "device");
+		sysfs_remove_link(&class_dev->dev->kobj, class_name);
+		kobject_uevent(&class_dev->kobj, KOBJ_OFFLINE);
+		kfree(class_name);
+	}
+}
+
 static ssize_t show_dev(struct class_device *class_dev, char *buf)
 {
 	return print_dev_t(buf, class_dev->devt);
@@ -526,7 +568,6 @@ int class_device_add(struct class_device
 	struct class *parent_class = NULL;
 	struct class_device *parent_class_dev = NULL;
 	struct class_interface *class_intf;
-	char *class_name = NULL;
 	int error = -EINVAL;
 
 	class_dev = class_device_get(class_dev);
@@ -593,22 +634,13 @@ int class_device_add(struct class_device
 	if (error)
 		goto out5;
 
-	if (class_dev->dev) {
-		class_name = make_class_name(class_dev->class->name,
-					     &class_dev->kobj);
-		error = sysfs_create_link(&class_dev->kobj,
-					  &class_dev->dev->kobj, "device");
-		if (error)
-			goto out6;
-		error = sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj,
-					  class_name);
-		if (error)
-			goto out7;
-	}
+	error = class_device_add_dev(class_dev);
+	if (error)
+		goto out6;
 
 	error = class_device_add_groups(class_dev);
 	if (error)
-		goto out8;
+		goto out7;
 
 	kobject_uevent(&class_dev->kobj, KOBJ_ADD);
 
@@ -623,12 +655,8 @@ int class_device_add(struct class_device
 
 	goto out1;
 
- out8:
-	if (class_dev->dev)
-		sysfs_remove_link(&class_dev->kobj, class_name);
  out7:
-	if (class_dev->dev)
-		sysfs_remove_link(&class_dev->kobj, "device");
+	class_device_remove_dev(class_dev);
  out6:
 	class_device_remove_attrs(class_dev);
  out5:
@@ -644,7 +672,6 @@ int class_device_add(struct class_device
 	class_put(parent_class);
  out1:
 	class_device_put(class_dev);
-	kfree(class_name);
 	return error;
 }
 
@@ -720,7 +747,6 @@ void class_device_del(struct class_devic
 	struct class *parent_class = class_dev->class;
 	struct class_device *parent_device = class_dev->parent;
 	struct class_interface *class_intf;
-	char *class_name = NULL;
 
 	if (parent_class) {
 		down(&parent_class->sem);
@@ -731,12 +757,7 @@ void class_device_del(struct class_devic
 		up(&parent_class->sem);
 	}
 
-	if (class_dev->dev) {
-		class_name = make_class_name(class_dev->class->name,
-					     &class_dev->kobj);
-		sysfs_remove_link(&class_dev->kobj, "device");
-		sysfs_remove_link(&class_dev->dev->kobj, class_name);
-	}
+	class_device_remove_dev(class_dev);
 	sysfs_remove_link(&class_dev->kobj, "subsystem");
 	class_device_remove_file(class_dev, &class_dev->uevent_attr);
 	if (class_dev->devt_attr)
@@ -749,7 +770,6 @@ void class_device_del(struct class_devic
 
 	class_device_put(parent_device);
 	class_put(parent_class);
-	kfree(class_name);
 }
 
 void class_device_unregister(struct class_device *class_dev)
@@ -821,6 +841,27 @@ int class_device_rename(struct class_dev
 	return error;
 }
 
+int class_device_update_dev(struct class_device *class_dev, struct device *dev)
+{
+	int error = 0;
+
+	class_dev = class_device_get(class_dev);
+	if (!class_dev)
+		return -EINVAL;
+
+	if (class_dev->dev != dev) {
+		class_device_remove_dev(class_dev);
+
+		class_dev->dev = dev;
+
+		error = class_device_add_dev(class_dev);
+	}
+
+	class_device_put(class_dev);
+
+	return error;
+}
+
 struct class_device * class_device_get(struct class_device *class_dev)
 {
 	if (class_dev)
@@ -911,6 +952,7 @@ EXPORT_SYMBOL_GPL(class_device_get);
 EXPORT_SYMBOL_GPL(class_device_put);
 EXPORT_SYMBOL_GPL(class_device_create);
 EXPORT_SYMBOL_GPL(class_device_destroy);
+EXPORT_SYMBOL_GPL(class_device_update_dev);
 EXPORT_SYMBOL_GPL(class_device_create_file);
 EXPORT_SYMBOL_GPL(class_device_remove_file);
 EXPORT_SYMBOL_GPL(class_device_create_bin_file);
diff --git a/include/linux/device.h b/include/linux/device.h
index 1e5f30d..d76072b 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -249,6 +249,7 @@ extern int class_device_add(struct class
 extern void class_device_del(struct class_device *);
 
 extern int class_device_rename(struct class_device *, char *);
+extern int class_device_update_dev(struct class_device *, struct device *);
 
 extern struct class_device * class_device_get(struct class_device *);
 extern void class_device_put(struct class_device *);

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux