Commit 59d6f107 authored by Patrick Mochel's avatar Patrick Mochel

Introduce struct device_interface.

Device interfaces are the logical interfaces of device classes that correlate
directly to userspace interfaces, like device nodes. 

Device interfaces are registered with the class they belong to. As devices
are added to the class, they are added to each interface registered with
the class. The interface is responsible for determining whether the device
supports the interface or not. 

The interface is responsible for allocating and initializing a struct 
intf_data and calling interface_add_data() to add it to the device's list
of interfaces it belongs to. This list will be iterated over when the device
is removed from the class (instead of all possible interfaces for a class).
This structure should probably be embedded in whatever per-device data 
structure the interface is allocating anyway.

Devices are enumerated within the interface. This happens in interface_add_data()
and the enumerated value is stored in the struct intf_data for that device. 

Interfaces get a directory in driverfs under their class's directory. Each
time a device is added to the interface, a symlink is created in that 
directory that points to the device's directory in the physical hierarchy.
The name of this symlink is the interface-enumerated value of the device.
parent 78fc20f7
# Makefile for the Linux device tree # Makefile for the Linux device tree
obj-y := core.o sys.o interface.o power.o bus.o \ obj-y := core.o sys.o interface.o power.o bus.o \
driver.o class.o driver.o class.o intf.o
obj-y += fs/ obj-y += fs/
export-objs := core.o power.o sys.o bus.o driver.o class.o export-objs := core.o power.o sys.o bus.o driver.o \
class.o intf.o
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
...@@ -35,5 +35,18 @@ extern void devclass_drv_unlink(struct device_driver *); ...@@ -35,5 +35,18 @@ extern void devclass_drv_unlink(struct device_driver *);
extern int devclass_dev_link(struct device_class *, struct device *); extern int devclass_dev_link(struct device_class *, struct device *);
extern void devclass_dev_unlink(struct device_class *, struct device *); extern void devclass_dev_unlink(struct device_class *, struct device *);
extern int devclass_add_device(struct device *);
extern void devclass_remove_device(struct device *);
extern int intf_make_dir(struct device_interface *);
extern void intf_remove_dir(struct device_interface *);
extern int intf_dev_link(struct intf_data *);
extern void intf_dev_unlink(struct intf_data *);
extern int interface_add(struct device_class *, struct device *);
extern void interface_remove(struct device_class *, struct device *);
extern int driver_attach(struct device_driver * drv); extern int driver_attach(struct device_driver * drv);
extern void driver_detach(struct device_driver * drv); extern void driver_detach(struct device_driver * drv);
...@@ -58,10 +58,12 @@ int devclass_add_device(struct device * dev) ...@@ -58,10 +58,12 @@ int devclass_add_device(struct device * dev)
if (cls) { if (cls) {
pr_debug("adding device '%s' to class '%s'\n", pr_debug("adding device '%s' to class '%s'\n",
dev->name,cls->name); dev->name,cls->name);
if (cls->add_device) if (cls->add_device)
error = cls->add_device(dev); error = cls->add_device(dev);
if (!error) if (!error) {
enum_device(cls,dev); enum_device(cls,dev);
interface_add(cls,dev);
}
} }
return error; return error;
} }
...@@ -72,6 +74,7 @@ void devclass_remove_device(struct device * dev) ...@@ -72,6 +74,7 @@ void devclass_remove_device(struct device * dev)
if (cls) { if (cls) {
pr_debug("removing device '%s' from class '%s'\n", pr_debug("removing device '%s' from class '%s'\n",
dev->name,cls->name); dev->name,cls->name);
interface_remove(cls,dev);
unenum_device(cls,dev); unenum_device(cls,dev);
if (cls->remove_device) if (cls->remove_device)
cls->remove_device(dev); cls->remove_device(dev);
...@@ -81,6 +84,7 @@ void devclass_remove_device(struct device * dev) ...@@ -81,6 +84,7 @@ void devclass_remove_device(struct device * dev)
int devclass_register(struct device_class * cls) int devclass_register(struct device_class * cls)
{ {
INIT_LIST_HEAD(&cls->drivers); INIT_LIST_HEAD(&cls->drivers);
INIT_LIST_HEAD(&cls->intf_list);
pr_debug("registering device class '%s'\n",cls->name); pr_debug("registering device class '%s'\n",cls->name);
......
...@@ -54,7 +54,8 @@ static int found_match(struct device * dev, struct device_driver * drv) ...@@ -54,7 +54,8 @@ static int found_match(struct device * dev, struct device_driver * drv)
spin_lock(&device_lock); spin_lock(&device_lock);
list_add_tail(&dev->driver_list,&drv->devices); list_add_tail(&dev->driver_list,&drv->devices);
spin_unlock(&device_lock); spin_unlock(&device_lock);
devclass_add_device(dev);
goto Done; goto Done;
ProbeFailed: ProbeFailed:
...@@ -99,6 +100,7 @@ static void device_detach(struct device * dev) ...@@ -99,6 +100,7 @@ static void device_detach(struct device * dev)
struct device_driver * drv; struct device_driver * drv;
if (dev->driver) { if (dev->driver) {
devclass_remove_device(dev);
spin_lock(&device_lock); spin_lock(&device_lock);
drv = dev->driver; drv = dev->driver;
dev->driver = NULL; dev->driver = NULL;
...@@ -172,6 +174,7 @@ int device_register(struct device *dev) ...@@ -172,6 +174,7 @@ int device_register(struct device *dev)
INIT_LIST_HEAD(&dev->g_list); INIT_LIST_HEAD(&dev->g_list);
INIT_LIST_HEAD(&dev->driver_list); INIT_LIST_HEAD(&dev->driver_list);
INIT_LIST_HEAD(&dev->bus_list); INIT_LIST_HEAD(&dev->bus_list);
INIT_LIST_HEAD(&dev->intf_list);
spin_lock_init(&dev->lock); spin_lock_init(&dev->lock);
atomic_set(&dev->refcount,2); atomic_set(&dev->refcount,2);
......
obj-y := device.o bus.o driver.o class.o obj-y := device.o bus.o driver.o class.o intf.o
export-objs := device.o bus.o driver.o class.o export-objs := device.o bus.o driver.o class.o intf.o
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
/*
* intf.c - driverfs glue for device interfaces
*/
#include <linux/device.h>
#include <linux/slab.h>
#include "fs.h"
/**
* intf_dev_link - symlink from interface's directory to device's directory
*
*/
int intf_dev_link(struct intf_data * data)
{
char linkname[16];
char * path;
int length;
int error;
length = get_devpath_length(data->dev);
length += strlen("../../../root");
if (length > PATH_MAX)
return -ENAMETOOLONG;
if (!(path = kmalloc(length,GFP_KERNEL)))
return -ENOMEM;
memset(path,0,length);
strcpy(path,"../../../root");
fill_devpath(data->dev,path,length);
snprintf(linkname,16,"%u",data->intf_num);
error = driverfs_create_symlink(&data->intf->dir,linkname,path);
kfree(path);
return error;
}
void intf_dev_unlink(struct intf_data * data)
{
char linkname[16];
snprintf(linkname,16,"%u",data->intf_num);
driverfs_remove_file(&data->intf->dir,linkname);
}
void intf_remove_dir(struct device_interface * intf)
{
driverfs_remove_dir(&intf->dir);
}
int intf_make_dir(struct device_interface * intf)
{
intf->dir.name = intf->name;
return device_create_dir(&intf->dir,&intf->devclass->dir);
}
/*
* intf.c - class-specific interface management
*/
#define DEBUG 1
#include <linux/device.h>
#include <linux/module.h>
#include "base.h"
#define to_intf(node) container_of(node,struct device_interface,node)
int interface_register(struct device_interface * intf)
{
struct device_class * cls = intf->devclass;
if (cls) {
pr_debug("register interface '%s' with class '%s\n",
intf->name,cls->name);
intf_make_dir(intf);
spin_lock(&device_lock);
list_add_tail(&intf->node,&cls->intf_list);
spin_unlock(&device_lock);
return 0;
}
return -EINVAL;
}
void interface_unregister(struct device_interface * intf)
{
pr_debug("unregistering interface '%s' from class '%s'\n",
intf->name,intf->devclass->name);
spin_lock(&device_lock);
list_del_init(&intf->node);
spin_unlock(&device_lock);
intf_remove_dir(intf);
}
int interface_add(struct device_class * cls, struct device * dev)
{
struct list_head * node;
int error = 0;
pr_debug("adding '%s' to %s class interfaces\n",dev->name,cls->name);
list_for_each(node,&cls->intf_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);
}
}
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);
spin_lock(&device_lock);
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);
spin_unlock(&device_lock);
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);
spin_lock(&device_lock);
}
spin_unlock(&device_lock);
pr_debug("\n");
}
int interface_add_data(struct intf_data * data)
{
spin_lock(&device_lock);
list_add_tail(&data->node,&data->dev->intf_list);
data->intf_num = ++data->intf->devnum;
spin_unlock(&device_lock);
intf_dev_link(data);
return 0;
}
EXPORT_SYMBOL(interface_register);
EXPORT_SYMBOL(interface_unregister);
...@@ -173,6 +173,7 @@ struct device_class { ...@@ -173,6 +173,7 @@ struct device_class {
struct list_head node; struct list_head node;
struct list_head drivers; struct list_head drivers;
struct list_head intf_list;
struct driver_dir_entry dir; struct driver_dir_entry dir;
struct driver_dir_entry driver_dir; struct driver_dir_entry driver_dir;
...@@ -203,6 +204,56 @@ extern int devclass_create_file(struct device_class *, struct devclass_attribute ...@@ -203,6 +204,56 @@ extern int devclass_create_file(struct device_class *, struct devclass_attribute
extern void devclass_remove_file(struct device_class *, struct devclass_attribute *); extern void devclass_remove_file(struct device_class *, struct devclass_attribute *);
/*
* device interfaces
* These are the logical interfaces of device classes.
* These entities map directly to specific userspace interfaces, like
* device nodes.
* Interfaces are registered with the device class they belong to. When
* a device is registered with the class, each interface's add_device
* callback is called. It is up to the interface to decide whether or not
* it supports the device.
*/
struct intf_data;
struct device_interface {
char * name;
struct device_class * devclass;
struct list_head node;
struct list_head devices;
struct driver_dir_entry dir;
u32 devnum;
int (*add_device) (struct device *);
int (*remove_device) (struct intf_data *);
};
extern int interface_register(struct device_interface *);
extern void interface_unregister(struct device_interface *);
/*
* intf_data - per-device data for an interface
* Each interface typically has a per-device data structure
* that it allocates. It should embed one of these structures
* in that structure and call interface_add_data() to add it
* to the device's list.
* That will also enumerate the device within the 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;
};
extern int interface_add_data(struct intf_data *);
struct device { struct device {
struct list_head g_list; /* node in depth-first order list */ struct list_head g_list; /* node in depth-first order list */
...@@ -210,6 +261,7 @@ struct device { ...@@ -210,6 +261,7 @@ struct device {
struct list_head bus_list; /* node in bus's list */ struct list_head bus_list; /* node in bus's list */
struct list_head driver_list; struct list_head driver_list;
struct list_head children; struct list_head children;
struct list_head intf_list;
struct device * parent; struct device * parent;
char name[DEVICE_NAME_SIZE]; /* descriptive ascii string */ char name[DEVICE_NAME_SIZE]; /* descriptive ascii string */
......
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