Commit 37724ebd authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://ldm.bkbits.net/linux-2.5-cls

into penguin.transmeta.com:/home/penguin/torvalds/repositories/kernel/linux
parents ebf6f305 59d6f107
# 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 driver.o class.o intf.o
obj-y += fs/ obj-y += fs/
export-objs := core.o power.o sys.o bus.o driver.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
...@@ -26,5 +26,27 @@ extern void driver_remove_dir(struct device_driver * drv); ...@@ -26,5 +26,27 @@ extern void driver_remove_dir(struct device_driver * drv);
extern int device_bus_link(struct device * dev); extern int device_bus_link(struct device * dev);
extern void device_remove_symlink(struct driver_dir_entry * dir, const char * name); extern void device_remove_symlink(struct driver_dir_entry * dir, const char * name);
extern int devclass_make_dir(struct device_class *);
extern void devclass_remove_dir(struct device_class *);
extern int devclass_drv_link(struct device_driver *);
extern void devclass_drv_unlink(struct device_driver *);
extern int devclass_dev_link(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);
/*
* class.c - basic device class management
*/
#include <linux/device.h>
#include <linux/module.h>
#include "base.h"
static LIST_HEAD(class_list);
int devclass_add_driver(struct device_driver * drv)
{
if (drv->devclass) {
pr_debug("Registering driver %s:%s with class %s\n",
drv->bus->name,drv->name,drv->devclass->name);
spin_lock(&device_lock);
list_add_tail(&drv->class_list,&drv->devclass->drivers);
spin_unlock(&device_lock);
devclass_drv_link(drv);
}
return 0;
}
void devclass_remove_driver(struct device_driver * drv)
{
if (drv->devclass) {
pr_debug("Removing driver %s:%s:%s\n",
drv->devclass->name,drv->bus->name,drv->name);
spin_lock(&device_lock);
list_del_init(&drv->class_list);
spin_unlock(&device_lock);
devclass_drv_unlink(drv);
}
}
static void enum_device(struct device_class * cls, struct device * dev)
{
u32 val;
spin_lock(&device_lock);
val = cls->devnum++;
write_unlock(&device_lock);
dev->class_num = val;
devclass_dev_link(cls,dev);
}
static void unenum_device(struct device_class * cls, struct device * dev)
{
devclass_dev_unlink(cls,dev);
dev->class_num = 0;
}
int devclass_add_device(struct device * dev)
{
struct device_class * cls = dev->driver->devclass;
int error = 0;
if (cls) {
pr_debug("adding device '%s' to class '%s'\n",
dev->name,cls->name);
if (cls->add_device)
error = cls->add_device(dev);
if (!error) {
enum_device(cls,dev);
interface_add(cls,dev);
}
}
return error;
}
void devclass_remove_device(struct device * dev)
{
struct device_class * cls = dev->driver->devclass;
if (cls) {
pr_debug("removing device '%s' from class '%s'\n",
dev->name,cls->name);
interface_remove(cls,dev);
unenum_device(cls,dev);
if (cls->remove_device)
cls->remove_device(dev);
}
}
int devclass_register(struct device_class * cls)
{
INIT_LIST_HEAD(&cls->drivers);
INIT_LIST_HEAD(&cls->intf_list);
pr_debug("registering device class '%s'\n",cls->name);
spin_lock(&device_lock);
list_add_tail(&cls->node,&class_list);
spin_unlock(&device_lock);
devclass_make_dir(cls);
return 0;
}
void devclass_unregister(struct device_class * cls)
{
pr_debug("unregistering device class '%s'\n",cls->name);
devclass_remove_dir(cls);
spin_lock(&device_lock);
list_del_init(&class_list);
spin_unlock(&device_lock);
}
EXPORT_SYMBOL(devclass_register);
EXPORT_SYMBOL(devclass_unregister);
...@@ -54,6 +54,7 @@ static int found_match(struct device * dev, struct device_driver * drv) ...@@ -54,6 +54,7 @@ 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;
...@@ -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 obj-y := device.o bus.o driver.o class.o intf.o
export-objs := device.o bus.o driver.o export-objs := device.o bus.o driver.o class.o intf.o
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
/*
* class.c - driverfs bindings for device classes.
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/err.h>
#include "fs.h"
static struct driver_dir_entry class_dir;
#define to_class_attr(_attr) container_of(_attr,struct devclass_attribute,attr)
#define to_class(d) container_of(d,struct device_class,dir)
static ssize_t
devclass_attr_show(struct driver_dir_entry * dir, struct attribute * attr,
char * buf, size_t count, loff_t off)
{
struct devclass_attribute * class_attr = to_class_attr(attr);
struct device_class * dc = to_class(dir);
ssize_t ret = 0;
if (class_attr->show)
ret = class_attr->show(dc,buf,count,off);
return ret;
}
static ssize_t
devclass_attr_store(struct driver_dir_entry * dir, struct attribute * attr,
const char * buf, size_t count, loff_t off)
{
struct devclass_attribute * class_attr = to_class_attr(attr);
struct device_class * dc = to_class(dir);
ssize_t ret = 0;
if (class_attr->store)
ret = class_attr->store(dc,buf,count,off);
return ret;
}
static struct driverfs_ops devclass_attr_ops = {
show: devclass_attr_show,
store: devclass_attr_store,
};
int devclass_create_file(struct device_class * dc, struct devclass_attribute * attr)
{
int error;
if (dc) {
error = driverfs_create_file(&attr->attr,&dc->dir);
} else
error = -EINVAL;
return error;
}
void devclass_remove_file(struct device_class * dc, struct devclass_attribute * attr)
{
if (dc)
driverfs_remove_file(&dc->dir,attr->attr.name);
}
/**
* devclass_dev_link - create symlink to device's directory
* @cls - device class we're a part of
* @dev - device we're linking to
*
* Create a symlink in the class's devices/ directory to @dev's
* directory in the physical hierarchy. The name is the device's
* class-enumerated value (struct device::class_num). We're
* creating:
* class/<class name>/devices/<link name> ->
* root/<path to device>/<device's dir>
* So, the link looks like:
* ../../../root/<path to device>/
*/
int devclass_dev_link(struct device_class * cls, struct device * dev)
{
char linkname[16];
char * path;
int length;
int error;
length = get_devpath_length(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(dev,path,length);
snprintf(linkname,16,"%u",dev->class_num);
error = driverfs_create_symlink(&cls->device_dir,linkname,path);
kfree(path);
return error;
}
void devclass_dev_unlink(struct device_class * cls, struct device * dev)
{
char linkname[16];
snprintf(linkname,16,"%u",dev->class_num);
driverfs_remove_file(&cls->device_dir,linkname);
}
/**
* devclass_drv_link - create symlink to driver's directory
* @drv: driver we're linking up
*
* Create a symlink in the class's drivers/ directory to @drv's
* directory (in the bus's directory). It's name is <bus>:<driver>
* to prevent naming conflicts.
*
* We're creating
* class/<class name>/drivers/<link name> ->
* bus/<bus name>/drivers/<driver name>/
* So, the link looks like:
* ../../../bus/<bus name>/drivers/<driver name>
*/
int devclass_drv_link(struct device_driver * drv)
{
char * name;
char * path;
int namelen;
int pathlen;
int error = 0;
namelen = strlen(drv->name) + strlen(drv->bus->name) + 2;
name = kmalloc(namelen,GFP_KERNEL);
if (!name)
return -ENOMEM;
snprintf(name,namelen,"%s:%s",drv->bus->name,drv->name);
pathlen = strlen("../../../bus/") +
strlen(drv->bus->name) +
strlen("/drivers/") +
strlen(drv->name) + 1;
if (!(path = kmalloc(pathlen,GFP_KERNEL))) {
error = -ENOMEM;
goto Done;
}
snprintf(path,pathlen,"%s%s%s%s",
"../../../bus/",
drv->bus->name,
"/drivers/",
drv->name);
error = driverfs_create_symlink(&drv->devclass->driver_dir,name,path);
Done:
kfree(name);
kfree(path);
return error;
}
void devclass_drv_unlink(struct device_driver * drv)
{
char * name;
int length;
length = strlen(drv->name) + strlen(drv->bus->name) + 2;
if ((name = kmalloc(length,GFP_KERNEL))) {
driverfs_remove_file(&drv->devclass->driver_dir,name);
kfree(name);
}
}
void devclass_remove_dir(struct device_class * dc)
{
driverfs_remove_dir(&dc->device_dir);
driverfs_remove_dir(&dc->driver_dir);
driverfs_remove_dir(&dc->dir);
}
int devclass_make_dir(struct device_class * dc)
{
int error;
dc->dir.name = dc->name;
dc->dir.ops = &devclass_attr_ops;
error = device_create_dir(&dc->dir,&class_dir);
if (!error) {
dc->driver_dir.name = "drivers";
error = device_create_dir(&dc->driver_dir,&dc->dir);
if (!error) {
dc->device_dir.name = "devices";
error = device_create_dir(&dc->device_dir,&dc->dir);
}
if (error)
driverfs_remove_dir(&dc->dir);
}
return error;
}
static struct driver_dir_entry class_dir = {
name: "class",
mode: (S_IRWXU | S_IRUGO | S_IXUGO),
};
static int __init devclass_driverfs_init(void)
{
return driverfs_create_dir(&class_dir,NULL);
}
core_initcall(devclass_driverfs_init);
EXPORT_SYMBOL(devclass_create_file);
EXPORT_SYMBOL(devclass_remove_file);
...@@ -123,7 +123,7 @@ void device_remove_dir(struct device * dev) ...@@ -123,7 +123,7 @@ void device_remove_dir(struct device * dev)
driverfs_remove_dir(&dev->dir); driverfs_remove_dir(&dev->dir);
} }
static int get_devpath_length(struct device * dev) int get_devpath_length(struct device * dev)
{ {
int length = 1; int length = 1;
struct device * parent = dev; struct device * parent = dev;
...@@ -138,7 +138,7 @@ static int get_devpath_length(struct device * dev) ...@@ -138,7 +138,7 @@ static int get_devpath_length(struct device * dev)
return length; return length;
} }
static void fill_devpath(struct device * dev, char * path, int length) void fill_devpath(struct device * dev, char * path, int length)
{ {
struct device * parent; struct device * parent;
--length; --length;
......
extern int device_create_dir(struct driver_dir_entry * dir, struct driver_dir_entry * parent); extern int device_create_dir(struct driver_dir_entry * dir, struct driver_dir_entry * parent);
int get_devpath_length(struct device * dev);
void fill_devpath(struct device * dev, char * path, int length);
/*
* 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);
...@@ -50,6 +50,7 @@ enum { ...@@ -50,6 +50,7 @@ enum {
struct device; struct device;
struct device_driver; struct device_driver;
struct device_class;
struct bus_type { struct bus_type {
char * name; char * name;
...@@ -106,12 +107,14 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); ...@@ -106,12 +107,14 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *);
struct device_driver { struct device_driver {
char * name; char * name;
struct bus_type * bus; struct bus_type * bus;
struct device_class * devclass;
rwlock_t lock; rwlock_t lock;
atomic_t refcount; atomic_t refcount;
list_t bus_list; struct list_head bus_list;
list_t devices; struct list_head class_list;
struct list_head devices;
struct driver_dir_entry dir; struct driver_dir_entry dir;
...@@ -160,12 +163,105 @@ struct driver_attribute driver_attr_##_name = { \ ...@@ -160,12 +163,105 @@ struct driver_attribute driver_attr_##_name = { \
extern int driver_create_file(struct device_driver *, struct driver_attribute *); extern int driver_create_file(struct device_driver *, struct driver_attribute *);
extern void driver_remove_file(struct device_driver *, struct driver_attribute *); extern void driver_remove_file(struct device_driver *, struct driver_attribute *);
/*
* device classes
*/
struct device_class {
char * name;
u32 devnum;
struct list_head node;
struct list_head drivers;
struct list_head intf_list;
struct driver_dir_entry dir;
struct driver_dir_entry driver_dir;
struct driver_dir_entry device_dir;
int (*add_device)(struct device *);
void (*remove_device)(struct device *);
};
extern int devclass_register(struct device_class *);
extern void devclass_unregister(struct device_class *);
struct devclass_attribute {
struct attribute attr;
ssize_t (*show)(struct device_class *, char * buf, size_t count, loff_t off);
ssize_t (*store)(struct device_class *, const char * buf, size_t count, loff_t off);
};
#define DEVCLASS_ATTR(_name,_str,_mode,_show,_store) \
struct devclass_attribute devclass_attr_##_name = { \
.attr = {.name = _str, .mode = _mode }, \
.show = _show, \
.store = _store, \
};
extern int devclass_create_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 */
struct list_head node; /* node in sibling list */ struct list_head node; /* node in sibling list */
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 */
...@@ -183,6 +279,8 @@ struct device { ...@@ -183,6 +279,8 @@ struct device {
struct device_driver *driver; /* which driver has allocated this struct device_driver *driver; /* which driver has allocated this
device */ device */
void *driver_data; /* data private to the driver */ void *driver_data; /* data private to the driver */
u32 class_num; /* class-enumerated value */
void *platform_data; /* Platform specific data (e.g. ACPI, void *platform_data; /* Platform specific data (e.g. ACPI,
BIOS data relevant to device) */ BIOS data relevant to device) */
......
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