Commit 28926b3d authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

driver core: rework driver class structures and logic

Removes the device_class, devclass_attribute, and device_interface structures
and replaces them with class, class_device, and class_interface structures.

This allows us to have multiple class_device structures per device structures
which mirrors the ways things really are within the kernel.  It also allows 
class_device structures to be created later than struct devices as they
are naturally created much later in the initialization process of a device.
parent 5d4d8070
# 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 intf.o platform.o \ driver.o class.o platform.o \
cpu.o firmware.o init.o cpu.o firmware.o init.o
obj-$(CONFIG_NUMA) += node.o memblk.o obj-$(CONFIG_NUMA) += node.o memblk.o
obj-y += fs/
obj-$(CONFIG_HOTPLUG) += hotplug.o
extern struct semaphore device_sem; extern struct semaphore device_sem;
extern struct semaphore devclass_sem;
extern int bus_add_device(struct device * dev); extern int bus_add_device(struct device * dev);
extern void bus_remove_device(struct device * dev); extern void bus_remove_device(struct device * dev);
...@@ -7,22 +6,3 @@ extern void bus_remove_device(struct device * dev); ...@@ -7,22 +6,3 @@ extern void bus_remove_device(struct device * dev);
extern int bus_add_driver(struct device_driver *); extern int bus_add_driver(struct device_driver *);
extern void bus_remove_driver(struct device_driver *); extern void bus_remove_driver(struct device_driver *);
extern int devclass_add_device(struct device *);
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_dev(struct device *);
extern void interface_remove_dev(struct device *);
#ifdef CONFIG_HOTPLUG
extern int class_hotplug(struct device *dev, const char *action);
#else
static inline int class_hotplug(struct device *dev, const char *action)
{
return 0;
}
#endif
...@@ -311,8 +311,7 @@ static int device_attach(struct device * dev) ...@@ -311,8 +311,7 @@ static int device_attach(struct device * dev)
* Walk the list of devices that the bus has on it and try to match * Walk the list of devices that the bus has on it and try to match
* the driver with each one. * the driver with each one.
* If bus_match() returns 0 and the @dev->driver is set, we've found * If bus_match() returns 0 and the @dev->driver is set, we've found
* a compatible pair, so we call devclass_add_device() to add the * a compatible pair.
* device to the class.
* *
* Note that we ignore the error from bus_match(), since it's perfectly * Note that we ignore the error from bus_match(), since it's perfectly
* valid for a driver not to bind to any devices. * valid for a driver not to bind to any devices.
...@@ -328,8 +327,7 @@ static void driver_attach(struct device_driver * drv) ...@@ -328,8 +327,7 @@ static void driver_attach(struct device_driver * drv)
list_for_each(entry,&bus->devices.list) { list_for_each(entry,&bus->devices.list) {
struct device * dev = container_of(entry,struct device,bus_list); struct device * dev = container_of(entry,struct device,bus_list);
if (!dev->driver) { if (!dev->driver) {
if (!bus_match(dev,drv)) bus_match(dev,drv);
devclass_add_device(dev);
} }
} }
} }
...@@ -351,7 +349,6 @@ void device_release_driver(struct device * dev) ...@@ -351,7 +349,6 @@ void device_release_driver(struct device * dev)
if (drv) { if (drv) {
sysfs_remove_link(&drv->kobj,dev->kobj.name); sysfs_remove_link(&drv->kobj,dev->kobj.name);
list_del_init(&dev->driver_list); list_del_init(&dev->driver_list);
devclass_remove_device(dev);
if (drv->remove) if (drv->remove)
drv->remove(dev); drv->remove(dev);
dev->driver = NULL; dev->driver = NULL;
...@@ -443,8 +440,7 @@ int bus_add_driver(struct device_driver * drv) ...@@ -443,8 +440,7 @@ int bus_add_driver(struct device_driver * drv)
} }
down_write(&bus->subsys.rwsem); down_write(&bus->subsys.rwsem);
if (!(error = devclass_add_driver(drv))) driver_attach(drv);
driver_attach(drv);
up_write(&bus->subsys.rwsem); up_write(&bus->subsys.rwsem);
if (error) { if (error) {
...@@ -471,7 +467,6 @@ void bus_remove_driver(struct device_driver * drv) ...@@ -471,7 +467,6 @@ void bus_remove_driver(struct device_driver * drv)
down_write(&drv->bus->subsys.rwsem); down_write(&drv->bus->subsys.rwsem);
pr_debug("bus %s: remove driver %s\n",drv->bus->name,drv->name); pr_debug("bus %s: remove driver %s\n",drv->bus->name,drv->name);
driver_detach(drv); driver_detach(drv);
devclass_remove_driver(drv);
up_write(&drv->bus->subsys.rwsem); up_write(&drv->bus->subsys.rwsem);
kobject_unregister(&drv->kobj); kobject_unregister(&drv->kobj);
put_bus(drv->bus); put_bus(drv->bus);
......
/* /*
* class.c - basic device class management * class.c - basic device class management
*
* Copyright (c) 2001-2003 Patrick Mochel <mochel@osdl.org>
*/ */
#undef DEBUG #undef DEBUG
...@@ -10,16 +12,14 @@ ...@@ -10,16 +12,14 @@
#include <linux/string.h> #include <linux/string.h>
#include "base.h" #include "base.h"
#define to_class_attr(_attr) container_of(_attr,struct devclass_attribute,attr) #define to_class_attr(_attr) container_of(_attr,struct class_attribute,attr)
#define to_class(obj) container_of(obj,struct device_class,subsys.kset.kobj) #define to_class(obj) container_of(obj,struct class,subsys.kset.kobj)
DECLARE_MUTEX(devclass_sem);
static ssize_t static ssize_t
devclass_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) class_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
{ {
struct devclass_attribute * class_attr = to_class_attr(attr); struct class_attribute * class_attr = to_class_attr(attr);
struct device_class * dc = to_class(kobj); struct class * dc = to_class(kobj);
ssize_t ret = 0; ssize_t ret = 0;
if (class_attr->show) if (class_attr->show)
...@@ -28,11 +28,11 @@ devclass_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) ...@@ -28,11 +28,11 @@ devclass_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
} }
static ssize_t static ssize_t
devclass_attr_store(struct kobject * kobj, struct attribute * attr, class_attr_store(struct kobject * kobj, struct attribute * attr,
const char * buf, size_t count) const char * buf, size_t count)
{ {
struct devclass_attribute * class_attr = to_class_attr(attr); struct class_attribute * class_attr = to_class_attr(attr);
struct device_class * dc = to_class(kobj); struct class * dc = to_class(kobj);
ssize_t ret = 0; ssize_t ret = 0;
if (class_attr->store) if (class_attr->store)
...@@ -41,242 +41,376 @@ devclass_attr_store(struct kobject * kobj, struct attribute * attr, ...@@ -41,242 +41,376 @@ devclass_attr_store(struct kobject * kobj, struct attribute * attr,
} }
static struct sysfs_ops class_sysfs_ops = { static struct sysfs_ops class_sysfs_ops = {
.show = devclass_attr_show, .show = class_attr_show,
.store = devclass_attr_store, .store = class_attr_store,
}; };
static struct kobj_type ktype_devclass = { static struct kobj_type ktype_class = {
.sysfs_ops = &class_sysfs_ops, .sysfs_ops = &class_sysfs_ops,
}; };
/* Classes can't use the kobject hotplug logic, as /* Hotplug events for classes go to the class_obj subsys */
* they do not add new kobjects to the system */ static decl_subsys(class,&ktype_class,NULL);
static decl_subsys(class,&ktype_devclass,NULL);
static int devclass_dev_link(struct device_class * cls, struct device * dev) int class_create_file(struct class * cls, struct class_attribute * attr)
{ {
char linkname[16]; int error;
snprintf(linkname,16,"%u",dev->class_num); if (cls) {
return sysfs_create_link(&cls->devices.kobj,&dev->kobj,linkname); error = sysfs_create_file(&cls->subsys.kset.kobj,&attr->attr);
} else
error = -EINVAL;
return error;
} }
static void devclass_dev_unlink(struct device_class * cls, struct device * dev) void class_remove_file(struct class * cls, struct class_attribute * attr)
{ {
char linkname[16]; if (cls)
snprintf(linkname,16,"%u",dev->class_num); sysfs_remove_file(&cls->subsys.kset.kobj,&attr->attr);
sysfs_remove_link(&cls->devices.kobj,linkname);
} }
static int devclass_drv_link(struct device_driver * drv) struct class * class_get(struct class * cls)
{ {
char name[KOBJ_NAME_LEN * 3]; if (cls)
snprintf(name,KOBJ_NAME_LEN * 3,"%s:%s",drv->bus->name,drv->name); return container_of(subsys_get(&cls->subsys),struct class,subsys);
return sysfs_create_link(&drv->devclass->drivers.kobj,&drv->kobj,name); return NULL;
} }
static void devclass_drv_unlink(struct device_driver * drv) void class_put(struct class * cls)
{ {
char name[KOBJ_NAME_LEN * 3]; subsys_put(&cls->subsys);
snprintf(name,KOBJ_NAME_LEN * 3,"%s:%s",drv->bus->name,drv->name);
return sysfs_remove_link(&drv->devclass->drivers.kobj,name);
} }
int class_register(struct class * cls)
{
pr_debug("device class '%s': registering\n",cls->name);
INIT_LIST_HEAD(&cls->children);
INIT_LIST_HEAD(&cls->interfaces);
strncpy(cls->subsys.kset.kobj.name,cls->name,KOBJ_NAME_LEN);
subsys_set_kset(cls,class_subsys);
subsystem_register(&cls->subsys);
return 0;
}
int devclass_create_file(struct device_class * cls, struct devclass_attribute * attr) void class_unregister(struct class * cls)
{ {
int error; pr_debug("device class '%s': unregistering\n",cls->name);
if (cls) { subsystem_unregister(&cls->subsys);
error = sysfs_create_file(&cls->subsys.kset.kobj,&attr->attr); }
} else
error = -EINVAL; /* Class Device Stuff */
int class_device_create_file(struct class_device * class_dev,
struct class_device_attribute * attr)
{
int error = -EINVAL;
if (class_dev)
error = sysfs_create_file(&class_dev->kobj, &attr->attr);
return error; return error;
} }
void devclass_remove_file(struct device_class * cls, struct devclass_attribute * attr) void class_device_remove_file(struct class_device * class_dev,
struct class_device_attribute * attr)
{ {
if (cls) if (class_dev)
sysfs_remove_file(&cls->subsys.kset.kobj,&attr->attr); sysfs_remove_file(&class_dev->kobj, &attr->attr);
} }
static int class_device_dev_link(struct class_device * class_dev)
{
if (class_dev->dev)
return sysfs_create_link(&class_dev->kobj,
&class_dev->dev->kobj, "device");
return 0;
}
int devclass_add_driver(struct device_driver * drv) static void class_device_dev_unlink(struct class_device * class_dev)
{ {
struct device_class * cls = get_devclass(drv->devclass); if (class_dev->dev)
int error = 0; sysfs_remove_link(&class_dev->kobj, "device");
}
if (cls) { #define to_class_dev(obj) container_of(obj,struct class_device,kobj)
down_write(&cls->subsys.rwsem); #define to_class_dev_attr(_attr) container_of(_attr,struct class_device_attribute,attr)
pr_debug("device class %s: adding driver %s:%s\n",
cls->name,drv->bus->name,drv->name); static ssize_t
error = devclass_drv_link(drv); class_device_attr_show(struct kobject * kobj, struct attribute * attr,
char * buf)
if (!error) {
list_add_tail(&drv->class_list,&cls->drivers.list); struct class_device_attribute * class_dev_attr = to_class_dev_attr(attr);
up_write(&cls->subsys.rwsem); struct class_device * cd = to_class_dev(kobj);
} ssize_t ret = 0;
return error;
if (class_dev_attr->show)
ret = class_dev_attr->show(cd,buf);
return ret;
} }
void devclass_remove_driver(struct device_driver * drv) static ssize_t
class_device_attr_store(struct kobject * kobj, struct attribute * attr,
const char * buf, size_t count)
{ {
struct device_class * cls = drv->devclass; struct class_device_attribute * class_dev_attr = to_class_dev_attr(attr);
if (cls) { struct class_device * cd = to_class_dev(kobj);
down_write(&cls->subsys.rwsem); ssize_t ret = 0;
pr_debug("device class %s: removing driver %s:%s\n",
cls->name,drv->bus->name,drv->name); if (class_dev_attr->store)
list_del_init(&drv->class_list); ret = class_dev_attr->store(cd,buf,count);
devclass_drv_unlink(drv); return ret;
up_write(&cls->subsys.rwsem);
put_devclass(cls);
}
} }
static struct sysfs_ops class_dev_sysfs_ops = {
.show = class_device_attr_show,
.store = class_device_attr_store,
};
static struct kobj_type ktype_class_device = {
.sysfs_ops = &class_dev_sysfs_ops,
};
static void enum_device(struct device_class * cls, struct device * dev) static int class_hotplug_filter(struct kset *kset, struct kobject *kobj)
{ {
u32 val; struct kobj_type *ktype = get_ktype(kobj);
val = cls->devnum++;
dev->class_num = val; if (ktype == &ktype_class_device) {
devclass_dev_link(cls,dev); struct class_device *class_dev = to_class_dev(kobj);
if (class_dev->class)
return 1;
}
return 0;
} }
static void unenum_device(struct device_class * cls, struct device * dev) static char *class_hotplug_name(struct kset *kset, struct kobject *kobj)
{ {
devclass_dev_unlink(cls,dev); struct class_device *class_dev = to_class_dev(kobj);
dev->class_num = 0;
return class_dev->class->name;
} }
/** static int class_hotplug(struct kset *kset, struct kobject *kobj, char **envp,
* devclass_add_device - register device with device class int num_envp, char *buffer, int buffer_size)
* @dev: device to be registered
*
* This is called when a device is either registered with the
* core, or after the a driver module is loaded and bound to
* the device.
* The class is determined by looking at @dev's driver, so one
* way or another, it must be bound to something. Once the
* class is determined, it's set to prevent against concurrent
* calls for the same device stomping on each other.
*
* /sbin/hotplug should be called once the device is added to
* class and all the interfaces.
*/
int devclass_add_device(struct device * dev)
{ {
struct device_class * cls; struct class_device *class_dev = to_class_dev(kobj);
int error = 0; int retval = 0;
down(&devclass_sem); pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id);
if (dev->driver) { if (class_dev->class->hotplug) {
cls = get_devclass(dev->driver->devclass); /* have the bus specific function add its stuff */
retval = class_dev->class->hotplug (class_dev, envp, num_envp,
if (!cls) buffer, buffer_size);
goto Done; if (retval) {
pr_debug ("%s - hotplug() returned %d\n",
pr_debug("device class %s: adding device %s\n", __FUNCTION__, retval);
cls->name,dev->name);
if (cls->add_device)
error = cls->add_device(dev);
if (error) {
put_devclass(cls);
goto Done;
} }
}
down_write(&cls->subsys.rwsem); return retval;
enum_device(cls,dev); }
list_add_tail(&dev->class_list,&cls->devices.list);
/* notify userspace (call /sbin/hotplug) */
class_hotplug (dev, "add");
up_write(&cls->subsys.rwsem); static struct kset_hotplug_ops class_hotplug_ops = {
.filter = class_hotplug_filter,
.name = class_hotplug_name,
.hotplug = class_hotplug,
};
interface_add_dev(dev); static decl_subsys(class_obj, &ktype_class_device, &class_hotplug_ops);
}
Done: void class_device_initialize(struct class_device *class_dev)
up(&devclass_sem); {
return error; kobject_init(&class_dev->kobj);
INIT_LIST_HEAD(&class_dev->node);
} }
void devclass_remove_device(struct device * dev) int class_device_add(struct class_device *class_dev)
{ {
struct device_class * cls; struct class * parent;
struct class_interface * class_intf;
struct list_head * entry;
int error;
down(&devclass_sem); class_dev = class_device_get(class_dev);
if (dev->driver) { if (!class_dev || !strlen(class_dev->class_id))
cls = dev->driver->devclass; return -EINVAL;
if (!cls)
goto Done; parent = class_get(class_dev->class);
if (class_dev->dev)
get_device(class_dev->dev);
pr_debug("CLASS: registering class device: ID = '%s'\n",
class_dev->class_id);
/* first, register with generic layer. */
strncpy(class_dev->kobj.name, class_dev->class_id, KOBJ_NAME_LEN);
kobj_set_kset_s(class_dev, class_subsys);
kobj_set_kset_s(class_dev, class_obj_subsys);
if (parent)
class_dev->kobj.parent = &parent->subsys.kset.kobj;
if ((error = kobject_add(&class_dev->kobj)))
goto register_done;
/* now take care of our own registration */
if (parent) {
down_write(&parent->subsys.rwsem);
list_add_tail(&class_dev->node, &parent->children);
list_for_each(entry, &parent->interfaces) {
class_intf = container_of(entry, struct class_interface, node);
if (class_intf->add)
class_intf->add(class_dev);
}
up_write(&parent->subsys.rwsem);
}
interface_remove_dev(dev); class_device_dev_link(class_dev);
down_write(&cls->subsys.rwsem); register_done:
pr_debug("device class %s: removing device %s\n", if (error && parent)
cls->name,dev->name); class_put(parent);
class_device_put(class_dev);
return error;
}
unenum_device(cls,dev); int class_device_register(struct class_device *class_dev)
{
class_device_initialize(class_dev);
return class_device_add(class_dev);
}
list_del(&dev->class_list); void class_device_del(struct class_device *class_dev)
{
struct class * parent = class_dev->class;
struct class_interface * class_intf;
struct list_head * entry;
if (parent) {
down_write(&parent->subsys.rwsem);
list_del_init(&class_dev->node);
list_for_each(entry, &parent->interfaces) {
class_intf = container_of(entry, struct class_interface, node);
if (class_intf->remove)
class_intf->remove(class_dev);
}
up_write(&parent->subsys.rwsem);
}
/* notify userspace (call /sbin/hotplug) */ if (class_dev->dev) {
class_hotplug (dev, "remove"); class_device_dev_unlink(class_dev);
put_device(class_dev->dev);
}
kobject_del(&class_dev->kobj);
up_write(&cls->subsys.rwsem); if (parent)
class_put(parent);
}
if (cls->remove_device) void class_device_unregister(struct class_device *class_dev)
cls->remove_device(dev); {
put_devclass(cls); pr_debug("CLASS: Unregistering class device. ID = '%s'\n",
} class_dev->class_id);
Done: class_device_del(class_dev);
up(&devclass_sem); class_device_put(class_dev);
} }
struct device_class * get_devclass(struct device_class * cls) struct class_device * class_device_get(struct class_device *class_dev)
{ {
return cls ? container_of(subsys_get(&cls->subsys),struct device_class,subsys) : NULL; if (class_dev)
return to_class_dev(kobject_get(&class_dev->kobj));
return NULL;
} }
void put_devclass(struct device_class * cls) void class_device_put(struct class_device *class_dev)
{ {
subsys_put(&cls->subsys); kobject_put(&class_dev->kobj);
} }
int devclass_register(struct device_class * cls) int class_interface_register(struct class_interface *class_intf)
{ {
pr_debug("device class '%s': registering\n",cls->name); struct class * parent;
strncpy(cls->subsys.kset.kobj.name,cls->name,KOBJ_NAME_LEN); struct class_device * class_dev;
subsys_set_kset(cls,class_subsys); struct list_head * entry;
subsystem_register(&cls->subsys);
if (!class_intf || !class_intf->class)
return -ENODEV;
parent = class_get(class_intf->class);
if (!parent)
return -EINVAL;
snprintf(cls->devices.kobj.name,KOBJ_NAME_LEN,"devices"); down_write(&parent->subsys.rwsem);
cls->devices.subsys = &cls->subsys; list_add_tail(&class_intf->node, &parent->interfaces);
kset_register(&cls->devices);
snprintf(cls->drivers.kobj.name,KOBJ_NAME_LEN,"drivers"); if (class_intf->add) {
cls->drivers.subsys = &cls->subsys; list_for_each(entry, &parent->children) {
kset_register(&cls->drivers); class_dev = container_of(entry, struct class_device, node);
class_intf->add(class_dev);
}
}
up_write(&parent->subsys.rwsem);
return 0; return 0;
} }
void devclass_unregister(struct device_class * cls) void class_interface_unregister(struct class_interface *class_intf)
{ {
pr_debug("device class '%s': unregistering\n",cls->name); struct class * parent = class_intf->class;
kset_unregister(&cls->drivers); struct list_head * entry;
kset_unregister(&cls->devices);
subsystem_unregister(&cls->subsys); if (!parent)
return;
down_write(&parent->subsys.rwsem);
list_del_init(&class_intf->node);
if (class_intf->remove) {
list_for_each(entry, &parent->children) {
struct class_device *class_dev = container_of(entry, struct class_device, node);
class_intf->remove(class_dev);
}
}
up_write(&parent->subsys.rwsem);
class_put(parent);
} }
int __init classes_init(void) int __init classes_init(void)
{ {
return subsystem_register(&class_subsys); int retval;
}
EXPORT_SYMBOL(devclass_create_file); retval = subsystem_register(&class_subsys);
EXPORT_SYMBOL(devclass_remove_file); if (retval)
EXPORT_SYMBOL(devclass_register); return retval;
EXPORT_SYMBOL(devclass_unregister);
EXPORT_SYMBOL(get_devclass); /* ick, this is ugly, the things we go through to keep from showing up
EXPORT_SYMBOL(put_devclass); * in sysfs... */
subsystem_init(&class_obj_subsys);
if (!class_obj_subsys.kset.subsys)
class_obj_subsys.kset.subsys = &class_obj_subsys;
return 0;
}
EXPORT_SYMBOL(class_create_file);
EXPORT_SYMBOL(class_remove_file);
EXPORT_SYMBOL(class_register);
EXPORT_SYMBOL(class_unregister);
EXPORT_SYMBOL(class_get);
EXPORT_SYMBOL(class_put);
EXPORT_SYMBOL(class_device_register);
EXPORT_SYMBOL(class_device_unregister);
EXPORT_SYMBOL(class_device_initialize);
EXPORT_SYMBOL(class_device_add);
EXPORT_SYMBOL(class_device_del);
EXPORT_SYMBOL(class_device_get);
EXPORT_SYMBOL(class_device_put);
EXPORT_SYMBOL(class_device_create_file);
EXPORT_SYMBOL(class_device_remove_file);
EXPORT_SYMBOL(class_interface_register);
EXPORT_SYMBOL(class_interface_unregister);
...@@ -185,7 +185,6 @@ void device_initialize(struct device *dev) ...@@ -185,7 +185,6 @@ void device_initialize(struct device *dev)
INIT_LIST_HEAD(&dev->children); INIT_LIST_HEAD(&dev->children);
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->class_list);
} }
/** /**
...@@ -235,7 +234,6 @@ int device_add(struct device *dev) ...@@ -235,7 +234,6 @@ int device_add(struct device *dev)
if (platform_notify) if (platform_notify)
platform_notify(dev); platform_notify(dev);
devclass_add_device(dev);
register_done: register_done:
if (error && parent) if (error && parent)
put_device(parent); put_device(parent);
......
...@@ -82,7 +82,6 @@ void put_driver(struct device_driver * drv) ...@@ -82,7 +82,6 @@ void put_driver(struct device_driver * drv)
int driver_register(struct device_driver * drv) int driver_register(struct device_driver * drv)
{ {
INIT_LIST_HEAD(&drv->devices); INIT_LIST_HEAD(&drv->devices);
INIT_LIST_HEAD(&drv->class_list);
init_MUTEX_LOCKED(&drv->unload_sem); init_MUTEX_LOCKED(&drv->unload_sem);
return bus_add_driver(drv); return bus_add_driver(drv);
} }
......
/* /*
* device.h - generic, centralized driver model * device.h - generic, centralized driver model
* *
* Copyright (c) 2001 Patrick Mochel <mochel@osdl.org> * Copyright (c) 2001-2003 Patrick Mochel <mochel@osdl.org>
* *
* This is a relatively simple centralized driver model. * This is a relatively simple centralized driver model.
* The data structures were mainly lifted directly from the PCI * The data structures were mainly lifted directly from the PCI
...@@ -60,7 +60,8 @@ enum device_state { ...@@ -60,7 +60,8 @@ enum device_state {
struct device; struct device;
struct device_driver; struct device_driver;
struct device_class; struct class;
struct class_device;
struct bus_type { struct bus_type {
char * name; char * name;
...@@ -116,11 +117,9 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); ...@@ -116,11 +117,9 @@ 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;
struct semaphore unload_sem; struct semaphore unload_sem;
struct kobject kobj; struct kobject kobj;
struct list_head class_list;
struct list_head devices; struct list_head devices;
int (*probe) (struct device * dev); int (*probe) (struct device * dev);
...@@ -160,74 +159,106 @@ extern void driver_remove_file(struct device_driver *, struct driver_attribute * ...@@ -160,74 +159,106 @@ extern void driver_remove_file(struct device_driver *, struct driver_attribute *
/* /*
* device classes * device classes
*/ */
struct device_class { struct class {
char * name; char * name;
u32 devnum;
struct subsystem subsys; struct subsystem subsys;
struct kset devices; struct list_head children;
struct kset drivers; struct list_head interfaces;
int (*add_device)(struct device *); int (*hotplug)(struct class_device *dev, char **envp,
void (*remove_device)(struct device *);
int (*hotplug)(struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size); int num_envp, char *buffer, int buffer_size);
}; };
extern int devclass_register(struct device_class *); extern int class_register(struct class *);
extern void devclass_unregister(struct device_class *); extern void class_unregister(struct class *);
extern struct device_class * get_devclass(struct device_class *); extern struct class * class_get(struct class *);
extern void put_devclass(struct device_class *); extern void class_put(struct class *);
struct devclass_attribute { struct class_attribute {
struct attribute attr; struct attribute attr;
ssize_t (*show)(struct device_class *, char * buf); ssize_t (*show)(struct class *, char * buf);
ssize_t (*store)(struct device_class *, const char * buf, size_t count); ssize_t (*store)(struct class *, const char * buf, size_t count);
}; };
#define DEVCLASS_ATTR(_name,_str,_mode,_show,_store) \ #define CLASS_ATTR(_name,_mode,_show,_store) \
struct devclass_attribute devclass_attr_##_name = { \ struct class_attribute class_attr_##_name = { \
.attr = {.name = _str, .mode = _mode }, \ .attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \ .show = _show, \
.store = _store, \ .store = _store, \
}; };
extern int devclass_create_file(struct device_class *, struct devclass_attribute *); extern int class_create_file(struct class *, struct class_attribute *);
extern void devclass_remove_file(struct device_class *, struct devclass_attribute *); extern void class_remove_file(struct class *, struct class_attribute *);
/* struct class_device {
* device interfaces struct list_head node;
* 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 device_interface { struct kobject kobj;
char * name; struct class * class; /* required */
struct device_class * devclass; struct device * dev; /* not necessary, but nice to have */
void * class_data; /* class-specific data */
char class_id[BUS_ID_SIZE]; /* unique to this class */
};
static inline void *
class_get_devdata (struct class_device *dev)
{
return dev->class_data;
}
static inline void
class_set_devdata (struct class_device *dev, void *data)
{
dev->class_data = data;
}
extern int class_device_register(struct class_device *);
extern void class_device_unregister(struct class_device *);
extern void class_device_initialize(struct class_device *);
extern int class_device_add(struct class_device *);
extern void class_device_del(struct class_device *);
extern struct class_device * class_device_get(struct class_device *);
extern void class_device_put(struct class_device *);
struct class_device_attribute {
struct attribute attr;
ssize_t (*show)(struct class_device *, char * buf);
ssize_t (*store)(struct class_device *, const char * buf, size_t count);
};
#define CLASS_DEVICE_ATTR(_name,_mode,_show,_store) \
struct class_device_attribute class_device_attr_##_name = { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
};
struct kset kset; extern int class_device_create_file(struct class_device *, struct class_device_attribute *);
u32 devnum; extern void class_device_remove_file(struct class_device *, struct class_device_attribute *);
int (*add_device) (struct device *);
int (*remove_device) (struct device *); struct class_interface {
struct list_head node;
struct class *class;
int (*add) (struct class_device *);
void (*remove) (struct class_device *);
}; };
extern int interface_register(struct device_interface *); extern int class_interface_register(struct class_interface *);
extern void interface_unregister(struct device_interface *); extern void class_interface_unregister(struct class_interface *);
struct device { struct device {
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 class_list;
struct list_head driver_list; struct list_head driver_list;
struct list_head children; struct list_head children;
struct device * parent; struct device * parent;
...@@ -240,14 +271,10 @@ struct device { ...@@ -240,14 +271,10 @@ 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 * class_data; /* class-specific data */
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) */
u32 power_state; /* Current operating state. In u32 power_state; /* Current operating state. In
ACPI-speak, this is D0-D3, D0 ACPI-speak, this is D0-D3, D0
being fully functional, and D3 being fully functional, and D3
being off. */ being off. */
...@@ -347,6 +374,7 @@ struct sys_device { ...@@ -347,6 +374,7 @@ struct sys_device {
u32 id; u32 id;
struct sys_root * root; struct sys_root * root;
struct device dev; struct device dev;
struct class_device class_dev;
}; };
extern int sys_device_register(struct sys_device *); extern int sys_device_register(struct sys_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