Commit 6f614c99 authored by Patrick Mochel's avatar Patrick Mochel

driver model: clean up interface handling.

- 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. 
parent 94d24994
......@@ -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
......
......@@ -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);
......
......@@ -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);
}
......
......@@ -2,7 +2,7 @@
* intf.c - class-specific interface management
*/
#undef DEBUG
#define DEBUG
#include <linux/device.h>
#include <linux/module.h>
......@@ -10,98 +10,267 @@
#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);
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment