]> git.neil.brown.name Git - history.git/commitdiff
driver model: clean up interface handling.
authorPatrick Mochel <mochel@osdl.org>
Tue, 3 Dec 2002 06:40:21 +0000 (00:40 -0600)
committerPatrick Mochel <mochel@osdl.org>
Tue, 3 Dec 2002 06:40:21 +0000 (00:40 -0600)
- Make sure devices are added to an interface if the interface is registered
  e.g. as a module.
- Make sure interface data descriptors are inserted into the interface's list
  by adding a kobject to them and calling kobject_register.
- Create interface_remove_data() for explicit removal of data descriptor.
- Break common pieces into separate functions.
- Add lots of comments.
- Add class_list to struct device, so we can access the devices registered
  with the class.

drivers/base/base.h
drivers/base/class.c
drivers/base/core.c
drivers/base/intf.c
include/linux/device.h

index 0eaf5a2f0673bae61c25f332dccf3fb25893895f..ab31ff74207c9a973b968d519de20ae97d69b3c3 100644 (file)
@@ -14,8 +14,8 @@ extern void devclass_remove_device(struct device *);
 extern int devclass_add_driver(struct device_driver *);
 extern void devclass_remove_driver(struct device_driver *);
 
-extern int interface_add(struct device_class *, struct device *);
-extern void interface_remove(struct device_class *, struct device *);
+extern int interface_add_dev(struct device *);
+extern void interface_remove_dev(struct device *);
 
 
 #ifdef CONFIG_HOTPLUG
index fbc6326312d85dec8a18c6da962d835430113ca8..86c7775c42bdcd134e10442b79675714d5201da3 100644 (file)
@@ -2,7 +2,7 @@
  * class.c - basic device class management
  */
 
-#undef DEBUG
+#define DEBUG
 
 #include <linux/device.h>
 #include <linux/module.h>
@@ -98,7 +98,6 @@ void devclass_remove_file(struct device_class * cls, struct devclass_attribute *
 }
 
 
-
 int devclass_add_driver(struct device_driver * drv)
 {
        struct device_class * cls = get_devclass(drv->devclass);
@@ -172,9 +171,11 @@ int devclass_add_device(struct device * dev)
                                error = cls->add_device(dev);
                        if (!error) {
                                enum_device(cls,dev);
-                               interface_add(cls,dev);
+                               interface_add_dev(dev);
                        }
 
+                       list_add_tail(&dev->class_list,&cls->devices);
+
                        /* notify userspace (call /sbin/hotplug) */
                        class_hotplug (dev, "add");
 
@@ -196,9 +197,11 @@ void devclass_remove_device(struct device * dev)
                        down_write(&cls->subsys.rwsem);
                        pr_debug("device class %s: removing device %s\n",
                                 cls->name,dev->name);
-                       interface_remove(cls,dev);
+                       interface_remove_dev(dev);
                        unenum_device(cls,dev);
 
+                       list_del(&dev->class_list);
+
                        /* notify userspace (call /sbin/hotplug) */
                        class_hotplug (dev, "remove");
 
@@ -224,6 +227,7 @@ void put_devclass(struct device_class * cls)
 int devclass_register(struct device_class * cls)
 {
        INIT_LIST_HEAD(&cls->drivers);
+       INIT_LIST_HEAD(&cls->devices);
 
        pr_debug("device class '%s': registering\n",cls->name);
        strncpy(cls->subsys.kobj.name,cls->name,KOBJ_NAME_LEN);
index e6504bef4b5da8178f29d3a2695b8158d9d6f6f9..a5ab807d9f49a3b837886012356bd044eba2efd4 100644 (file)
@@ -144,6 +144,7 @@ void device_initialize(struct device *dev)
        INIT_LIST_HEAD(&dev->children);
        INIT_LIST_HEAD(&dev->driver_list);
        INIT_LIST_HEAD(&dev->bus_list);
+       INIT_LIST_HEAD(&dev->class_list);
        INIT_LIST_HEAD(&dev->intf_list);
 }
 
index d31ad38bf3d2eeaea379d3eaff48ac9b820c1a1f..63c5a4d12cd43ea41c480513dc97c9289a18debe 100644 (file)
@@ -2,7 +2,7 @@
  * intf.c - class-specific interface management
  */
 
-#undef DEBUG
+#define DEBUG
 
 #include <linux/device.h>
 #include <linux/module.h>
 #include "base.h"
 
 
-#define to_intf(node) container_of(node,struct device_interface,kobj.entry)
+#define to_intf(node) container_of(node,struct device_interface,subsys.kobj.entry)
+
+#define to_data(e) container_of(e,struct intf_data,kobj.entry)
+
+#define intf_from_data(d) container_of(d->kobj.subsys,struct device_interface, subsys);
+
 
 /**
- * intf_dev_link - symlink from interface's directory to device's directory
+ *     intf_dev_link - create sysfs symlink for interface.
+ *     @data:  interface data descriptor.
  *
+ *     Create a symlink 'phys' in the interface's directory to 
  */
+
 static int intf_dev_link(struct intf_data * data)
 {
-       char    linkname[16];
-
-       snprintf(linkname,16,"%u",data->intf_num);
-       return sysfs_create_link(&data->intf->kobj,&data->dev->kobj,linkname);
+       char    name[16];
+       snprintf(name,16,"%d",data->intf_num);
+       return sysfs_create_link(&data->intf->subsys.kobj,&data->dev->kobj,name);
 }
 
+/**
+ *     intf_dev_unlink - remove symlink for interface.
+ *     @intf:  interface data descriptor.
+ *
+ */
+
 static void intf_dev_unlink(struct intf_data * data)
 {
-       char    linkname[16];
-       snprintf(linkname,16,"%u",data->intf_num);
-       sysfs_remove_link(&data->intf->kobj,linkname);
+       char    name[16];
+       snprintf(name,16,"%d",data->intf_num);
+       sysfs_remove_link(&data->intf->subsys.kobj,name);
+}
+
+
+/**
+ *     interface_add_data - attach data descriptor
+ *     @data:  interface data descriptor.
+ *
+ *     This attaches the per-instance interface object to the
+ *     interface (by registering its kobject) and the device
+ *     itself (by inserting it into the device's list).
+ *
+ *     Note that there is no explicit protection done in this
+ *     function. This should be called from the interface's 
+ *     add_device() method, which is called under the protection
+ *     of the class's rwsem.
+ */
+
+int interface_add_data(struct intf_data * data)
+{
+       struct device_interface * intf = intf_from_data(data);
+
+       data->intf_num = data->intf->devnum++;
+       data->kobj.subsys = &intf->subsys;
+       kobject_register(&data->kobj);
+
+       list_add_tail(&data->dev_entry,&data->dev->intf_list);
+       intf_dev_link(data);
+       return 0;
+}
+
+
+/**
+ *     interface_remove_data - detach data descriptor.
+ *     @data:  interface data descriptor.
+ *
+ *     This detaches the per-instance data descriptor by removing 
+ *     it from the device's list and unregistering the kobject from
+ *     the subsystem.
+ */
+
+void interface_remove_data(struct intf_data * data)
+{
+       intf_dev_unlink(data);
+       list_del_init(&data->dev_entry);
+       kobject_unregister(&data->kobj);
 }
 
 
+/**
+ *     add - attach device to interface
+ *     @intf:  interface.
+ *     @dev:   device.
+ *
+ *     This is just a simple helper. Check the interface's interface
+ *     helper and call it. This is called when adding an interface
+ *     the class's devices, or a device to the class's interfaces.
+ */
+
+static int add(struct device_interface * intf, struct device * dev)
+{
+       int error = 0;
+
+       if (intf->add_device) 
+               error = intf->add_device(dev);
+       pr_debug(" -> %s (%d)\n",dev->bus_id,error);
+       return error;
+}
+
+/**
+ *     del - detach device from interface.
+ *     @data:  interface data descriptor.
+ *
+ *     Another simple helper. Remove the data descriptor from 
+ *     the device and the interface, then call the interface's 
+ *     remove_device() method.
+ */
+
+static void del(struct intf_data * data)
+{
+       struct device_interface * intf = intf_from_data(data);
+
+       pr_debug(" -> %s ",data->intf->name);
+       interface_remove_data(data);
+       if (intf->remove_device)
+               intf->remove_device(data);
+}
+
+#define to_dev(entry) container_of(entry,struct device,class_list)
+
+
+/**
+ *     add_intf - add class's devices to interface.
+ *     @intf:  interface.
+ *
+ *     Loop over the devices registered with the class, and call
+ *     the interface's add_device() method for each.
+ *
+ *     On an error, we won't break, but we will print debugging info.
+ */
+static void add_intf(struct device_interface * intf)
+{
+       struct device_class * cls = intf->devclass;
+       struct list_head * entry;
+
+       down_write(&cls->subsys.rwsem);
+       list_for_each(entry,&cls->devices)
+               add(intf,to_dev(entry));
+       up_write(&cls->subsys.rwsem);
+}
+
+/**
+ *     interface_register - register an interface with a device class.
+ *     @intf:  interface.
+ *
+ *     An interface may be loaded after drivers and devices have been
+ *     added to the class. So, we must add each device already known to
+ *     the class to the interface as its registered.
+ */
+
 int interface_register(struct device_interface * intf)
 {
        struct device_class * cls = get_devclass(intf->devclass);
-       int error = 0;
 
        if (cls) {
                pr_debug("register interface '%s' with class '%s'\n",
                         intf->name,cls->name);
-               strncpy(intf->kobj.name,intf->name,KOBJ_NAME_LEN);
-               intf->kobj.subsys = &cls->subsys;
-               kobject_register(&intf->kobj);
-       } else
-               error = -EINVAL;
-       return error;
+
+               strncpy(intf->subsys.kobj.name,intf->name,KOBJ_NAME_LEN);
+               intf->subsys.kobj.subsys = &cls->subsys;
+               subsystem_register(&intf->subsys);
+               add_intf(intf);
+       }
+       return 0;
 }
 
+
+/**
+ *     del_intf - remove devices from interface.
+ *     @intf:  interface being unloaded.
+ *
+ *     This loops over the devices registered with a class and 
+ *     calls the interface's remove_device() method for each.
+ *     This is called when an interface is being unregistered.
+ */
+
+static void del_intf(struct device_interface * intf)
+{
+       struct list_head * entry;
+
+       down_write(&intf->devclass->subsys.rwsem);
+       list_for_each(entry,&intf->subsys.list) {
+               struct intf_data * data = to_data(entry);
+               del(data);
+       }
+       up_write(&intf->devclass->subsys.rwsem);
+}
+
+/**
+ *     interface_unregister - remove interface from class.
+ *     @intf:  interface.
+ *
+ *     This is called when an interface in unloaded, giving it a
+ *     chance to remove itself from devicse that have been added to 
+ *     it.
+ */
+
 void interface_unregister(struct device_interface * intf)
 {
-       pr_debug("unregistering interface '%s' from class '%s'\n",
-                intf->name,intf->devclass->name);
-       kobject_unregister(&intf->kobj);
+       struct device_class * cls = intf->devclass;
+       if (cls) {
+               pr_debug("unregistering interface '%s' from class '%s'\n",
+                        intf->name,cls->name);
+               del_intf(intf);
+               subsystem_unregister(&intf->subsys);
+               put_devclass(cls);
+       }
 }
 
-int interface_add(struct device_class * cls, struct device * dev)
+
+/**
+ *     interface_add_dev - add device to interfaces.
+ *     @dev:   device.
+ *
+ *     This is a helper for the class driver core. When a 
+ *     device is being added to a class, this is called to add
+ *     the device to all the interfaces in the class.
+ *
+ *     The operation is simple enough: loop over the interfaces
+ *     and call add() [above] for each. The class rwsem is assumed
+ *     to be held.
+ */
+
+int interface_add_dev(struct device * dev)
 {
+       struct device_class * cls = dev->driver->devclass;
        struct list_head * node;
-       int error = 0;
 
-       pr_debug("adding '%s' to %s class interfaces\n",dev->name,cls->name);
+       pr_debug("interfaces: adding device %s\n",dev->name);
 
        list_for_each(node,&cls->subsys.list) {
                struct device_interface * intf = to_intf(node);
-               if (intf->add_device) {
-                       error = intf->add_device(dev);
-                       if (error)
-                               pr_debug("%s:%s: adding '%s' failed: %d\n",
-                                        cls->name,intf->name,dev->name,error);
-               }
-               
+               add(intf,dev);
        }
        return 0;
 }
 
-void interface_remove(struct device_class * cls, struct device * dev)
-{
-       struct list_head * node;
-       struct list_head * next;
 
-       pr_debug("remove '%s' from %s class interfaces: ",dev->name,cls->name);
+/**
+ *     interface_remove_dev - remove device from interfaces.
+ *     @dev:   device.
+ *
+ *     This is another helper for the class driver core, and called
+ *     when the device is being removed from the class. 
+ *     
+ *     We iterate over the list of interface data descriptors attached
+ *     to the device, and call del() [above] for each. Again, the 
+ *     class's rwsem is assumed to be held during this.
+ */
 
-       list_for_each_safe(node,next,&dev->intf_list) {
-               struct intf_data * intf_data = container_of(node,struct intf_data,node);
-               list_del_init(&intf_data->node);
+void interface_remove_dev(struct device * dev)
+{
+       struct list_head * entry, * next;
 
-               intf_dev_unlink(intf_data);
-               pr_debug("%s ",intf_data->intf->name);
-               if (intf_data->intf->remove_device)
-                       intf_data->intf->remove_device(intf_data);
-       }
-       pr_debug("\n");
-}
+       pr_debug("interfaces: removing device %s\n",dev->name);
 
-int interface_add_data(struct intf_data * data)
-{
-       down_write(&data->intf->devclass->subsys.rwsem);
-       list_add_tail(&data->node,&data->dev->intf_list);
-       data->intf_num = data->intf->devnum++;
-       intf_dev_link(data);
-       up_write(&data->intf->devclass->subsys.rwsem);
-       return 0;
+       list_for_each_safe(entry,next,&dev->intf_list) {
+               struct intf_data * intf_data = to_data(entry);
+               del(intf_data);
+       }
 }
 
 EXPORT_SYMBOL(interface_register);
index c1918942c4cd8db2b3e255c86f0f2b2b43585f8b..48612267bd65fe2132a1c3aaa9d2713ed468eb64 100644 (file)
@@ -168,6 +168,7 @@ struct device_class {
        struct subsystem        devsubsys;
        struct subsystem        drvsubsys;
        struct list_head        drivers;
+       struct list_head        devices;
 
        int     (*add_device)(struct device *);
        void    (*remove_device)(struct device *);
@@ -216,9 +217,7 @@ struct device_interface {
        char                    * name;
        struct device_class     * devclass;
 
-       struct kobject          kobj;
-       struct list_head        devices;
-
+       struct subsystem        subsys;
        u32                     devnum;
 
        int (*add_device)       (struct device *);
@@ -239,10 +238,11 @@ extern void interface_unregister(struct device_interface *);
  * and create a driverfs symlink for it.
  */
 struct intf_data {
-       struct list_head        node;
        struct device_interface * intf;
        struct device           * dev;
        u32                     intf_num;
+       struct list_head        dev_entry;
+       struct kobject          kobj;
 };
 
 extern int interface_add_data(struct intf_data *);
@@ -252,6 +252,7 @@ extern int interface_add_data(struct intf_data *);
 struct device {
        struct list_head node;          /* node in sibling list */
        struct list_head bus_list;      /* node in bus's list */
+       struct list_head class_list;
        struct list_head driver_list;
        struct list_head children;
        struct list_head intf_list;