diff options
| author | Patrick Mochel <mochel@osdl.org> | 2003-06-09 02:40:02 -0700 |
|---|---|---|
| committer | Patrick Mochel <mochel@osdl.org> | 2003-06-09 02:40:02 -0700 |
| commit | a4ff342afe2d05c6bb8f479b51c7365412763e9c (patch) | |
| tree | bfb3dd3f5f56b67d7d4500086308413ca8b0010f /include | |
| parent | cb4acf11b7dea9799986130b6955d3dc5984956d (diff) | |
[driver model] Rewrite system device API
System devices are special, and after two years of listening to Linus
preach this, it finally sunk in enough to do something about. We don't
need to regard them as real devices that reside on a peripheral bus and
can be dynamically bound to drivers. If we discover, e.g. a CPU, we know
by default that we have a driver for it, and we know damn well that we
have a CPU. We still need to keep track of all the devices, and all the
devices of a particular type. The kobject infrastructure allows us to do
this, without the overhead of the regular model.
A new subsystem is defined that registers as a child object of
devices_subsys, giving us:
/sys/devices/system/
struct sysdev_class {
struct list_head drivers;
/* Default operations for these types of devices */
int (*shutdown)(struct sys_device *);
int (*suspend)(struct sys_device *, u32 state);
int (*resume)(struct sys_device *);
struct kset kset;
};
Defines a type of system device. These are registered on startup, by e.g.
drivers/base/cpu.c. The methods are default operations for devices of that
type that may or may not be used. For things like the i8259 controller,
these will be filled in, since it is registered by the same component that
the device controls reside in.
For things like CPUs, generic code will register the class, but other
architecture-specific or otherwise configurable drivers may register
auxillary drivers, that look like:
struct sysdev_driver {
struct list_head entry;
int (*add)(struct sys_device *);
int (*remove)(struct sys_device *);
int (*shutdown)(struct sys_device *);
int (*suspend)(struct sys_device *, u32 state);
int (*resume)(struct sys_device *);
};
Each auxillary driver gets called during each operation on a device of a
particular class.
Auxillary drivers may register with a NULL class parameter, in which case
they will be added to a list of 'global drivers' that get called for each
device of each class.
Besides providing a decent of cleanup for system device drivers, this also
allows:
- Special handling of system devices during power transitions.
We no longer have to worry about shutting down the PIC before we shut
down any devices. We can shut down the system devices after we've shut
down every other device.
Ditto for suspend/resume cycles. Almost (if not) all PM actions for
system devices happen with interrupts off, and require only one call,
which makes that easier. But, we can also make sure we take care of
these last during suspend and first during resume.
- Easy expression of configurable device-specific interfaces.
Namely cpufreq and mtrr. We don't have to worry about mispresentation in
the driver model (like recent MTRR patches) or using a cumbersome
interface ({device,class}_interface) that don't receive all the
necessary calls.
- Consolidation of userspace representation.
No longer do we have /sys/devices/sys, /sys/bus/sys, and /sys/class/cpu,
etc. We have only /sys/devices/system:
# tree /sys/devices/system/
/sys/devices/system/
|-- cpu
| `-- cpu0
|-- i8259
| `-- i82590
|-- lapic
| `-- lapic0
|-- rtc
| `-- rtc0
`-- timer
`-- timer0
Each directory in 'system' is the class, and each directory under that is
the instance of each device in that class.
Diffstat (limited to 'include')
| -rw-r--r-- | include/linux/device.h | 68 |
1 files changed, 58 insertions, 10 deletions
diff --git a/include/linux/device.h b/include/linux/device.h index 3604d351f3f0..de674eaea31c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -351,24 +351,72 @@ extern int (*platform_notify_remove)(struct device * dev); extern struct device * get_device(struct device * dev); extern void put_device(struct device * dev); + /* drivers/base/sys.c */ -struct sys_root { - u32 id; - struct device dev; - struct device sysdev; +/** + * System devices follow a slightly different driver model. + * They don't need to do dynammic driver binding, can't be probed, + * and don't reside on any type of peripheral bus. + * So, we represent and treat them a little differently. + * + * We still have a notion of a driver for a system device, because we still + * want to perform basic operations on these devices. + * + * We also support auxillary drivers binding to devices of a certain class. + * + * This allows configurable drivers to register themselves for devices of + * a certain type. And, it allows class definitions to reside in generic + * code while arch-specific code can register specific drivers. + * + * Auxillary drivers registered with a NULL cls are registered as drivers + * for all system devices, and get notification calls for each device. + */ + +struct sys_device; + +struct sysdev_class { + struct list_head drivers; + + /* Default operations for these types of devices */ + int (*shutdown)(struct sys_device *); + int (*suspend)(struct sys_device *, u32 state); + int (*resume)(struct sys_device *); + struct kset kset; +}; + + +extern int sysdev_class_register(struct sysdev_class *); +extern void sysdev_class_unregister(struct sysdev_class *); + + +/** + * Auxillary system device drivers. + */ + +struct sysdev_driver { + struct list_head entry; + int (*add)(struct sys_device *); + int (*remove)(struct sys_device *); + int (*shutdown)(struct sys_device *); + int (*suspend)(struct sys_device *, u32 state); + int (*resume)(struct sys_device *); }; -extern int sys_register_root(struct sys_root *); -extern void sys_unregister_root(struct sys_root *); + +extern int sysdev_driver_register(struct sysdev_class *, struct sysdev_driver *); +extern void sysdev_driver_unregister(struct sysdev_class *, struct sysdev_driver *); +/** + * sys_devices can be simplified a lot from regular devices, because they're + * simply not as versatile. + */ + struct sys_device { - char * name; u32 id; - struct sys_root * root; - struct device dev; - struct class_device class_dev; + struct sysdev_class * cls; + struct kobject kobj; }; extern int sys_device_register(struct sys_device *); |
