Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
This commit is contained in:
102
Documentation/driver-model/binding.txt
Normal file
102
Documentation/driver-model/binding.txt
Normal file
@@ -0,0 +1,102 @@
|
||||
|
||||
Driver Binding
|
||||
|
||||
Driver binding is the process of associating a device with a device
|
||||
driver that can control it. Bus drivers have typically handled this
|
||||
because there have been bus-specific structures to represent the
|
||||
devices and the drivers. With generic device and device driver
|
||||
structures, most of the binding can take place using common code.
|
||||
|
||||
|
||||
Bus
|
||||
~~~
|
||||
|
||||
The bus type structure contains a list of all devices that are on that bus
|
||||
type in the system. When device_register is called for a device, it is
|
||||
inserted into the end of this list. The bus object also contains a
|
||||
list of all drivers of that bus type. When driver_register is called
|
||||
for a driver, it is inserted at the end of this list. These are the
|
||||
two events which trigger driver binding.
|
||||
|
||||
|
||||
device_register
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
When a new device is added, the bus's list of drivers is iterated over
|
||||
to find one that supports it. In order to determine that, the device
|
||||
ID of the device must match one of the device IDs that the driver
|
||||
supports. The format and semantics for comparing IDs is bus-specific.
|
||||
Instead of trying to derive a complex state machine and matching
|
||||
algorithm, it is up to the bus driver to provide a callback to compare
|
||||
a device against the IDs of a driver. The bus returns 1 if a match was
|
||||
found; 0 otherwise.
|
||||
|
||||
int match(struct device * dev, struct device_driver * drv);
|
||||
|
||||
If a match is found, the device's driver field is set to the driver
|
||||
and the driver's probe callback is called. This gives the driver a
|
||||
chance to verify that it really does support the hardware, and that
|
||||
it's in a working state.
|
||||
|
||||
Device Class
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Upon the successful completion of probe, the device is registered with
|
||||
the class to which it belongs. Device drivers belong to one and only one
|
||||
class, and that is set in the driver's devclass field.
|
||||
devclass_add_device is called to enumerate the device within the class
|
||||
and actually register it with the class, which happens with the
|
||||
class's register_dev callback.
|
||||
|
||||
NOTE: The device class structures and core routines to manipulate them
|
||||
are not in the mainline kernel, so the discussion is still a bit
|
||||
speculative.
|
||||
|
||||
|
||||
Driver
|
||||
~~~~~~
|
||||
|
||||
When a driver is attached to a device, the device is inserted into the
|
||||
driver's list of devices.
|
||||
|
||||
|
||||
sysfs
|
||||
~~~~~
|
||||
|
||||
A symlink is created in the bus's 'devices' directory that points to
|
||||
the device's directory in the physical hierarchy.
|
||||
|
||||
A symlink is created in the driver's 'devices' directory that points
|
||||
to the device's directory in the physical hierarchy.
|
||||
|
||||
A directory for the device is created in the class's directory. A
|
||||
symlink is created in that directory that points to the device's
|
||||
physical location in the sysfs tree.
|
||||
|
||||
A symlink can be created (though this isn't done yet) in the device's
|
||||
physical directory to either its class directory, or the class's
|
||||
top-level directory. One can also be created to point to its driver's
|
||||
directory also.
|
||||
|
||||
|
||||
driver_register
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
The process is almost identical for when a new driver is added.
|
||||
The bus's list of devices is iterated over to find a match. Devices
|
||||
that already have a driver are skipped. All the devices are iterated
|
||||
over, to bind as many devices as possible to the driver.
|
||||
|
||||
|
||||
Removal
|
||||
~~~~~~~
|
||||
|
||||
When a device is removed, the reference count for it will eventually
|
||||
go to 0. When it does, the remove callback of the driver is called. It
|
||||
is removed from the driver's list of devices and the reference count
|
||||
of the driver is decremented. All symlinks between the two are removed.
|
||||
|
||||
When a driver is removed, the list of devices that it supports is
|
||||
iterated over, and the driver's remove callback is called for each
|
||||
one. The device is removed from that list and the symlinks removed.
|
||||
|
160
Documentation/driver-model/bus.txt
Normal file
160
Documentation/driver-model/bus.txt
Normal file
@@ -0,0 +1,160 @@
|
||||
|
||||
Bus Types
|
||||
|
||||
Definition
|
||||
~~~~~~~~~~
|
||||
|
||||
struct bus_type {
|
||||
char * name;
|
||||
|
||||
struct subsystem subsys;
|
||||
struct kset drivers;
|
||||
struct kset devices;
|
||||
|
||||
struct bus_attribute * bus_attrs;
|
||||
struct device_attribute * dev_attrs;
|
||||
struct driver_attribute * drv_attrs;
|
||||
|
||||
int (*match)(struct device * dev, struct device_driver * drv);
|
||||
int (*hotplug) (struct device *dev, char **envp,
|
||||
int num_envp, char *buffer, int buffer_size);
|
||||
int (*suspend)(struct device * dev, u32 state);
|
||||
int (*resume)(struct device * dev);
|
||||
};
|
||||
|
||||
int bus_register(struct bus_type * bus);
|
||||
|
||||
|
||||
Declaration
|
||||
~~~~~~~~~~~
|
||||
|
||||
Each bus type in the kernel (PCI, USB, etc) should declare one static
|
||||
object of this type. They must initialize the name field, and may
|
||||
optionally initialize the match callback.
|
||||
|
||||
struct bus_type pci_bus_type = {
|
||||
.name = "pci",
|
||||
.match = pci_bus_match,
|
||||
};
|
||||
|
||||
The structure should be exported to drivers in a header file:
|
||||
|
||||
extern struct bus_type pci_bus_type;
|
||||
|
||||
|
||||
Registration
|
||||
~~~~~~~~~~~~
|
||||
|
||||
When a bus driver is initialized, it calls bus_register. This
|
||||
initializes the rest of the fields in the bus object and inserts it
|
||||
into a global list of bus types. Once the bus object is registered,
|
||||
the fields in it are usable by the bus driver.
|
||||
|
||||
|
||||
Callbacks
|
||||
~~~~~~~~~
|
||||
|
||||
match(): Attaching Drivers to Devices
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The format of device ID structures and the semantics for comparing
|
||||
them are inherently bus-specific. Drivers typically declare an array
|
||||
of device IDs of devices they support that reside in a bus-specific
|
||||
driver structure.
|
||||
|
||||
The purpose of the match callback is provide the bus an opportunity to
|
||||
determine if a particular driver supports a particular device by
|
||||
comparing the device IDs the driver supports with the device ID of a
|
||||
particular device, without sacrificing bus-specific functionality or
|
||||
type-safety.
|
||||
|
||||
When a driver is registered with the bus, the bus's list of devices is
|
||||
iterated over, and the match callback is called for each device that
|
||||
does not have a driver associated with it.
|
||||
|
||||
|
||||
|
||||
Device and Driver Lists
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The lists of devices and drivers are intended to replace the local
|
||||
lists that many buses keep. They are lists of struct devices and
|
||||
struct device_drivers, respectively. Bus drivers are free to use the
|
||||
lists as they please, but conversion to the bus-specific type may be
|
||||
necessary.
|
||||
|
||||
The LDM core provides helper functions for iterating over each list.
|
||||
|
||||
int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data,
|
||||
int (*fn)(struct device *, void *));
|
||||
|
||||
int bus_for_each_drv(struct bus_type * bus, struct device_driver * start,
|
||||
void * data, int (*fn)(struct device_driver *, void *));
|
||||
|
||||
These helpers iterate over the respective list, and call the callback
|
||||
for each device or driver in the list. All list accesses are
|
||||
synchronized by taking the bus's lock (read currently). The reference
|
||||
count on each object in the list is incremented before the callback is
|
||||
called; it is decremented after the next object has been obtained. The
|
||||
lock is not held when calling the callback.
|
||||
|
||||
|
||||
sysfs
|
||||
~~~~~~~~
|
||||
There is a top-level directory named 'bus'.
|
||||
|
||||
Each bus gets a directory in the bus directory, along with two default
|
||||
directories:
|
||||
|
||||
/sys/bus/pci/
|
||||
|-- devices
|
||||
`-- drivers
|
||||
|
||||
Drivers registered with the bus get a directory in the bus's drivers
|
||||
directory:
|
||||
|
||||
/sys/bus/pci/
|
||||
|-- devices
|
||||
`-- drivers
|
||||
|-- Intel ICH
|
||||
|-- Intel ICH Joystick
|
||||
|-- agpgart
|
||||
`-- e100
|
||||
|
||||
Each device that is discovered on a bus of that type gets a symlink in
|
||||
the bus's devices directory to the device's directory in the physical
|
||||
hierarchy:
|
||||
|
||||
/sys/bus/pci/
|
||||
|-- devices
|
||||
| |-- 00:00.0 -> ../../../root/pci0/00:00.0
|
||||
| |-- 00:01.0 -> ../../../root/pci0/00:01.0
|
||||
| `-- 00:02.0 -> ../../../root/pci0/00:02.0
|
||||
`-- drivers
|
||||
|
||||
|
||||
Exporting Attributes
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
struct bus_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct bus_type *, char * buf);
|
||||
ssize_t (*store)(struct bus_type *, const char * buf, size_t count);
|
||||
};
|
||||
|
||||
Bus drivers can export attributes using the BUS_ATTR macro that works
|
||||
similarly to the DEVICE_ATTR macro for devices. For example, a definition
|
||||
like this:
|
||||
|
||||
static BUS_ATTR(debug,0644,show_debug,store_debug);
|
||||
|
||||
is equivalent to declaring:
|
||||
|
||||
static bus_attribute bus_attr_debug;
|
||||
|
||||
This can then be used to add and remove the attribute from the bus's
|
||||
sysfs directory using:
|
||||
|
||||
int bus_create_file(struct bus_type *, struct bus_attribute *);
|
||||
void bus_remove_file(struct bus_type *, struct bus_attribute *);
|
||||
|
||||
|
162
Documentation/driver-model/class.txt
Normal file
162
Documentation/driver-model/class.txt
Normal file
@@ -0,0 +1,162 @@
|
||||
|
||||
Device Classes
|
||||
|
||||
|
||||
Introduction
|
||||
~~~~~~~~~~~~
|
||||
A device class describes a type of device, like an audio or network
|
||||
device. The following device classes have been identified:
|
||||
|
||||
<Insert List of Device Classes Here>
|
||||
|
||||
|
||||
Each device class defines a set of semantics and a programming interface
|
||||
that devices of that class adhere to. Device drivers are the
|
||||
implemention of that programming interface for a particular device on
|
||||
a particular bus.
|
||||
|
||||
Device classes are agnostic with respect to what bus a device resides
|
||||
on.
|
||||
|
||||
|
||||
Programming Interface
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
The device class structure looks like:
|
||||
|
||||
|
||||
typedef int (*devclass_add)(struct device *);
|
||||
typedef void (*devclass_remove)(struct device *);
|
||||
|
||||
struct device_class {
|
||||
char * name;
|
||||
rwlock_t lock;
|
||||
u32 devnum;
|
||||
struct list_head node;
|
||||
|
||||
struct list_head drivers;
|
||||
struct list_head intf_list;
|
||||
|
||||
struct driver_dir_entry dir;
|
||||
struct driver_dir_entry device_dir;
|
||||
struct driver_dir_entry driver_dir;
|
||||
|
||||
devclass_add add_device;
|
||||
devclass_remove remove_device;
|
||||
};
|
||||
|
||||
A typical device class definition would look like:
|
||||
|
||||
struct device_class input_devclass = {
|
||||
.name = "input",
|
||||
.add_device = input_add_device,
|
||||
.remove_device = input_remove_device,
|
||||
};
|
||||
|
||||
Each device class structure should be exported in a header file so it
|
||||
can be used by drivers, extensions and interfaces.
|
||||
|
||||
Device classes are registered and unregistered with the core using:
|
||||
|
||||
int devclass_register(struct device_class * cls);
|
||||
void devclass_unregister(struct device_class * cls);
|
||||
|
||||
|
||||
Devices
|
||||
~~~~~~~
|
||||
As devices are bound to drivers, they are added to the device class
|
||||
that the driver belongs to. Before the driver model core, this would
|
||||
typically happen during the driver's probe() callback, once the device
|
||||
has been initialized. It now happens after the probe() callback
|
||||
finishes from the core.
|
||||
|
||||
The device is enumerated in the class. Each time a device is added to
|
||||
the class, the class's devnum field is incremented and assigned to the
|
||||
device. The field is never decremented, so if the device is removed
|
||||
from the class and re-added, it will receive a different enumerated
|
||||
value.
|
||||
|
||||
The class is allowed to create a class-specific structure for the
|
||||
device and store it in the device's class_data pointer.
|
||||
|
||||
There is no list of devices in the device class. Each driver has a
|
||||
list of devices that it supports. The device class has a list of
|
||||
drivers of that particular class. To access all of the devices in the
|
||||
class, iterate over the device lists of each driver in the class.
|
||||
|
||||
|
||||
Device Drivers
|
||||
~~~~~~~~~~~~~~
|
||||
Device drivers are added to device classes when they are registered
|
||||
with the core. A driver specifies the class it belongs to by setting
|
||||
the struct device_driver::devclass field.
|
||||
|
||||
|
||||
sysfs directory structure
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
There is a top-level sysfs directory named 'class'.
|
||||
|
||||
Each class gets a directory in the class directory, along with two
|
||||
default subdirectories:
|
||||
|
||||
class/
|
||||
`-- input
|
||||
|-- devices
|
||||
`-- drivers
|
||||
|
||||
|
||||
Drivers registered with the class get a symlink in the drivers/ directory
|
||||
that points to the driver's directory (under its bus directory):
|
||||
|
||||
class/
|
||||
`-- input
|
||||
|-- devices
|
||||
`-- drivers
|
||||
`-- usb:usb_mouse -> ../../../bus/drivers/usb_mouse/
|
||||
|
||||
|
||||
Each device gets a symlink in the devices/ directory that points to the
|
||||
device's directory in the physical hierarchy:
|
||||
|
||||
class/
|
||||
`-- input
|
||||
|-- devices
|
||||
| `-- 1 -> ../../../root/pci0/00:1f.0/usb_bus/00:1f.2-1:0/
|
||||
`-- drivers
|
||||
|
||||
|
||||
Exporting Attributes
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
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);
|
||||
};
|
||||
|
||||
Class drivers can export attributes using the DEVCLASS_ATTR macro that works
|
||||
similarly to the DEVICE_ATTR macro for devices. For example, a definition
|
||||
like this:
|
||||
|
||||
static DEVCLASS_ATTR(debug,0644,show_debug,store_debug);
|
||||
|
||||
is equivalent to declaring:
|
||||
|
||||
static devclass_attribute devclass_attr_debug;
|
||||
|
||||
The bus driver can add and remove the attribute from the class's
|
||||
sysfs directory using:
|
||||
|
||||
int devclass_create_file(struct device_class *, struct devclass_attribute *);
|
||||
void devclass_remove_file(struct device_class *, struct devclass_attribute *);
|
||||
|
||||
In the example above, the file will be named 'debug' in placed in the
|
||||
class's directory in sysfs.
|
||||
|
||||
|
||||
Interfaces
|
||||
~~~~~~~~~~
|
||||
There may exist multiple mechanisms for accessing the same device of a
|
||||
particular class type. Device interfaces describe these mechanisms.
|
||||
|
||||
When a device is added to a device class, the core attempts to add it
|
||||
to every interface that is registered with the device class.
|
||||
|
154
Documentation/driver-model/device.txt
Normal file
154
Documentation/driver-model/device.txt
Normal file
@@ -0,0 +1,154 @@
|
||||
|
||||
The Basic Device Structure
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
struct device {
|
||||
struct list_head g_list;
|
||||
struct list_head node;
|
||||
struct list_head bus_list;
|
||||
struct list_head driver_list;
|
||||
struct list_head intf_list;
|
||||
struct list_head children;
|
||||
struct device * parent;
|
||||
|
||||
char name[DEVICE_NAME_SIZE];
|
||||
char bus_id[BUS_ID_SIZE];
|
||||
|
||||
spinlock_t lock;
|
||||
atomic_t refcount;
|
||||
|
||||
struct bus_type * bus;
|
||||
struct driver_dir_entry dir;
|
||||
|
||||
u32 class_num;
|
||||
|
||||
struct device_driver *driver;
|
||||
void *driver_data;
|
||||
void *platform_data;
|
||||
|
||||
u32 current_state;
|
||||
unsigned char *saved_state;
|
||||
|
||||
void (*release)(struct device * dev);
|
||||
};
|
||||
|
||||
Fields
|
||||
~~~~~~
|
||||
g_list: Node in the global device list.
|
||||
|
||||
node: Node in device's parent's children list.
|
||||
|
||||
bus_list: Node in device's bus's devices list.
|
||||
|
||||
driver_list: Node in device's driver's devices list.
|
||||
|
||||
intf_list: List of intf_data. There is one structure allocated for
|
||||
each interface that the device supports.
|
||||
|
||||
children: List of child devices.
|
||||
|
||||
parent: *** FIXME ***
|
||||
|
||||
name: ASCII description of device.
|
||||
Example: " 3Com Corporation 3c905 100BaseTX [Boomerang]"
|
||||
|
||||
bus_id: ASCII representation of device's bus position. This
|
||||
field should be a name unique across all devices on the
|
||||
bus type the device belongs to.
|
||||
|
||||
Example: PCI bus_ids are in the form of
|
||||
<bus number>:<slot number>.<function number>
|
||||
This name is unique across all PCI devices in the system.
|
||||
|
||||
lock: Spinlock for the device.
|
||||
|
||||
refcount: Reference count on the device.
|
||||
|
||||
bus: Pointer to struct bus_type that device belongs to.
|
||||
|
||||
dir: Device's sysfs directory.
|
||||
|
||||
class_num: Class-enumerated value of the device.
|
||||
|
||||
driver: Pointer to struct device_driver that controls the device.
|
||||
|
||||
driver_data: Driver-specific data.
|
||||
|
||||
platform_data: Platform data specific to the device.
|
||||
|
||||
current_state: Current power state of the device.
|
||||
|
||||
saved_state: Pointer to saved state of the device. This is usable by
|
||||
the device driver controlling the device.
|
||||
|
||||
release: Callback to free the device after all references have
|
||||
gone away. This should be set by the allocator of the
|
||||
device (i.e. the bus driver that discovered the device).
|
||||
|
||||
|
||||
Programming Interface
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
The bus driver that discovers the device uses this to register the
|
||||
device with the core:
|
||||
|
||||
int device_register(struct device * dev);
|
||||
|
||||
The bus should initialize the following fields:
|
||||
|
||||
- parent
|
||||
- name
|
||||
- bus_id
|
||||
- bus
|
||||
|
||||
A device is removed from the core when its reference count goes to
|
||||
0. The reference count can be adjusted using:
|
||||
|
||||
struct device * get_device(struct device * dev);
|
||||
void put_device(struct device * dev);
|
||||
|
||||
get_device() will return a pointer to the struct device passed to it
|
||||
if the reference is not already 0 (if it's in the process of being
|
||||
removed already).
|
||||
|
||||
A driver can access the lock in the device structure using:
|
||||
|
||||
void lock_device(struct device * dev);
|
||||
void unlock_device(struct device * dev);
|
||||
|
||||
|
||||
Attributes
|
||||
~~~~~~~~~~
|
||||
struct device_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct device * dev, char * buf, size_t count, loff_t off);
|
||||
ssize_t (*store)(struct device * dev, const char * buf, size_t count, loff_t off);
|
||||
};
|
||||
|
||||
Attributes of devices can be exported via drivers using a simple
|
||||
procfs-like interface.
|
||||
|
||||
Please see Documentation/filesystems/sysfs.txt for more information
|
||||
on how sysfs works.
|
||||
|
||||
Attributes are declared using a macro called DEVICE_ATTR:
|
||||
|
||||
#define DEVICE_ATTR(name,mode,show,store)
|
||||
|
||||
Example:
|
||||
|
||||
DEVICE_ATTR(power,0644,show_power,store_power);
|
||||
|
||||
This declares a structure of type struct device_attribute named
|
||||
'dev_attr_power'. This can then be added and removed to the device's
|
||||
directory using:
|
||||
|
||||
int device_create_file(struct device *device, struct device_attribute * entry);
|
||||
void device_remove_file(struct device * dev, struct device_attribute * attr);
|
||||
|
||||
Example:
|
||||
|
||||
device_create_file(dev,&dev_attr_power);
|
||||
device_remove_file(dev,&dev_attr_power);
|
||||
|
||||
The file name will be 'power' with a mode of 0644 (-rw-r--r--).
|
||||
|
287
Documentation/driver-model/driver.txt
Normal file
287
Documentation/driver-model/driver.txt
Normal file
@@ -0,0 +1,287 @@
|
||||
|
||||
Device Drivers
|
||||
|
||||
struct device_driver {
|
||||
char * name;
|
||||
struct bus_type * bus;
|
||||
|
||||
rwlock_t lock;
|
||||
atomic_t refcount;
|
||||
|
||||
list_t bus_list;
|
||||
list_t devices;
|
||||
|
||||
struct driver_dir_entry dir;
|
||||
|
||||
int (*probe) (struct device * dev);
|
||||
int (*remove) (struct device * dev);
|
||||
|
||||
int (*suspend) (struct device * dev, u32 state, u32 level);
|
||||
int (*resume) (struct device * dev, u32 level);
|
||||
|
||||
void (*release) (struct device_driver * drv);
|
||||
};
|
||||
|
||||
|
||||
|
||||
Allocation
|
||||
~~~~~~~~~~
|
||||
|
||||
Device drivers are statically allocated structures. Though there may
|
||||
be multiple devices in a system that a driver supports, struct
|
||||
device_driver represents the driver as a whole (not a particular
|
||||
device instance).
|
||||
|
||||
Initialization
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The driver must initialize at least the name and bus fields. It should
|
||||
also initialize the devclass field (when it arrives), so it may obtain
|
||||
the proper linkage internally. It should also initialize as many of
|
||||
the callbacks as possible, though each is optional.
|
||||
|
||||
Declaration
|
||||
~~~~~~~~~~~
|
||||
|
||||
As stated above, struct device_driver objects are statically
|
||||
allocated. Below is an example declaration of the eepro100
|
||||
driver. This declaration is hypothetical only; it relies on the driver
|
||||
being converted completely to the new model.
|
||||
|
||||
static struct device_driver eepro100_driver = {
|
||||
.name = "eepro100",
|
||||
.bus = &pci_bus_type,
|
||||
.devclass = ðernet_devclass, /* when it's implemented */
|
||||
|
||||
.probe = eepro100_probe,
|
||||
.remove = eepro100_remove,
|
||||
.suspend = eepro100_suspend,
|
||||
.resume = eepro100_resume,
|
||||
};
|
||||
|
||||
Most drivers will not be able to be converted completely to the new
|
||||
model because the bus they belong to has a bus-specific structure with
|
||||
bus-specific fields that cannot be generalized.
|
||||
|
||||
The most common example of this are device ID structures. A driver
|
||||
typically defines an array of device IDs that it supports. The format
|
||||
of these structures and the semantics for comparing device IDs are
|
||||
completely bus-specific. Defining them as bus-specific entities would
|
||||
sacrifice type-safety, so we keep bus-specific structures around.
|
||||
|
||||
Bus-specific drivers should include a generic struct device_driver in
|
||||
the definition of the bus-specific driver. Like this:
|
||||
|
||||
struct pci_driver {
|
||||
const struct pci_device_id *id_table;
|
||||
struct device_driver driver;
|
||||
};
|
||||
|
||||
A definition that included bus-specific fields would look like
|
||||
(using the eepro100 driver again):
|
||||
|
||||
static struct pci_driver eepro100_driver = {
|
||||
.id_table = eepro100_pci_tbl,
|
||||
.driver = {
|
||||
.name = "eepro100",
|
||||
.bus = &pci_bus_type,
|
||||
.devclass = ðernet_devclass, /* when it's implemented */
|
||||
.probe = eepro100_probe,
|
||||
.remove = eepro100_remove,
|
||||
.suspend = eepro100_suspend,
|
||||
.resume = eepro100_resume,
|
||||
},
|
||||
};
|
||||
|
||||
Some may find the syntax of embedded struct initialization awkward or
|
||||
even a bit ugly. So far, it's the best way we've found to do what we want...
|
||||
|
||||
Registration
|
||||
~~~~~~~~~~~~
|
||||
|
||||
int driver_register(struct device_driver * drv);
|
||||
|
||||
The driver registers the structure on startup. For drivers that have
|
||||
no bus-specific fields (i.e. don't have a bus-specific driver
|
||||
structure), they would use driver_register and pass a pointer to their
|
||||
struct device_driver object.
|
||||
|
||||
Most drivers, however, will have a bus-specific structure and will
|
||||
need to register with the bus using something like pci_driver_register.
|
||||
|
||||
It is important that drivers register their driver structure as early as
|
||||
possible. Registration with the core initializes several fields in the
|
||||
struct device_driver object, including the reference count and the
|
||||
lock. These fields are assumed to be valid at all times and may be
|
||||
used by the device model core or the bus driver.
|
||||
|
||||
|
||||
Transition Bus Drivers
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
By defining wrapper functions, the transition to the new model can be
|
||||
made easier. Drivers can ignore the generic structure altogether and
|
||||
let the bus wrapper fill in the fields. For the callbacks, the bus can
|
||||
define generic callbacks that forward the call to the bus-specific
|
||||
callbacks of the drivers.
|
||||
|
||||
This solution is intended to be only temporary. In order to get class
|
||||
information in the driver, the drivers must be modified anyway. Since
|
||||
converting drivers to the new model should reduce some infrastructural
|
||||
complexity and code size, it is recommended that they are converted as
|
||||
class information is added.
|
||||
|
||||
Access
|
||||
~~~~~~
|
||||
|
||||
Once the object has been registered, it may access the common fields of
|
||||
the object, like the lock and the list of devices.
|
||||
|
||||
int driver_for_each_dev(struct device_driver * drv, void * data,
|
||||
int (*callback)(struct device * dev, void * data));
|
||||
|
||||
The devices field is a list of all the devices that have been bound to
|
||||
the driver. The LDM core provides a helper function to operate on all
|
||||
the devices a driver controls. This helper locks the driver on each
|
||||
node access, and does proper reference counting on each device as it
|
||||
accesses it.
|
||||
|
||||
|
||||
sysfs
|
||||
~~~~~
|
||||
|
||||
When a driver is registered, a sysfs directory is created in its
|
||||
bus's directory. In this directory, the driver can export an interface
|
||||
to userspace to control operation of the driver on a global basis;
|
||||
e.g. toggling debugging output in the driver.
|
||||
|
||||
A future feature of this directory will be a 'devices' directory. This
|
||||
directory will contain symlinks to the directories of devices it
|
||||
supports.
|
||||
|
||||
|
||||
|
||||
Callbacks
|
||||
~~~~~~~~~
|
||||
|
||||
int (*probe) (struct device * dev);
|
||||
|
||||
probe is called to verify the existence of a certain type of
|
||||
hardware. This is called during the driver binding process, after the
|
||||
bus has verified that the device ID of a device matches one of the
|
||||
device IDs supported by the driver.
|
||||
|
||||
This callback only verifies that there actually is supported hardware
|
||||
present. It may allocate a driver-specific structure, but it should
|
||||
not do any initialization of the hardware itself. The device-specific
|
||||
structure may be stored in the device's driver_data field.
|
||||
|
||||
int (*init) (struct device * dev);
|
||||
|
||||
init is called during the binding stage. It is called after probe has
|
||||
successfully returned and the device has been registered with its
|
||||
class. It is responsible for initializing the hardware.
|
||||
|
||||
int (*remove) (struct device * dev);
|
||||
|
||||
remove is called to dissociate a driver with a device. This may be
|
||||
called if a device is physically removed from the system, if the
|
||||
driver module is being unloaded, or during a reboot sequence.
|
||||
|
||||
It is up to the driver to determine if the device is present or
|
||||
not. It should free any resources allocated specifically for the
|
||||
device; i.e. anything in the device's driver_data field.
|
||||
|
||||
If the device is still present, it should quiesce the device and place
|
||||
it into a supported low-power state.
|
||||
|
||||
int (*suspend) (struct device * dev, u32 state, u32 level);
|
||||
|
||||
suspend is called to put the device in a low power state. There are
|
||||
several stages to successfully suspending a device, which is denoted in
|
||||
the @level parameter. Breaking the suspend transition into several
|
||||
stages affords the platform flexibility in performing device power
|
||||
management based on the requirements of the system and the
|
||||
user-defined policy.
|
||||
|
||||
SUSPEND_NOTIFY notifies the device that a suspend transition is about
|
||||
to happen. This happens on system power state transitions to verify
|
||||
that all devices can successfully suspend.
|
||||
|
||||
A driver may choose to fail on this call, which should cause the
|
||||
entire suspend transition to fail. A driver should fail only if it
|
||||
knows that the device will not be able to be resumed properly when the
|
||||
system wakes up again. It could also fail if it somehow determines it
|
||||
is in the middle of an operation too important to stop.
|
||||
|
||||
SUSPEND_DISABLE tells the device to stop I/O transactions. When it
|
||||
stops transactions, or what it should do with unfinished transactions
|
||||
is a policy of the driver. After this call, the driver should not
|
||||
accept any other I/O requests.
|
||||
|
||||
SUSPEND_SAVE_STATE tells the device to save the context of the
|
||||
hardware. This includes any bus-specific hardware state and
|
||||
device-specific hardware state. A pointer to this saved state can be
|
||||
stored in the device's saved_state field.
|
||||
|
||||
SUSPEND_POWER_DOWN tells the driver to place the device in the low
|
||||
power state requested.
|
||||
|
||||
Whether suspend is called with a given level is a policy of the
|
||||
platform. Some levels may be omitted; drivers must not assume the
|
||||
reception of any level. However, all levels must be called in the
|
||||
order above; i.e. notification will always come before disabling;
|
||||
disabling the device will come before suspending the device.
|
||||
|
||||
All calls are made with interrupts enabled, except for the
|
||||
SUSPEND_POWER_DOWN level.
|
||||
|
||||
int (*resume) (struct device * dev, u32 level);
|
||||
|
||||
Resume is used to bring a device back from a low power state. Like the
|
||||
suspend transition, it happens in several stages.
|
||||
|
||||
RESUME_POWER_ON tells the driver to set the power state to the state
|
||||
before the suspend call (The device could have already been in a low
|
||||
power state before the suspend call to put in a lower power state).
|
||||
|
||||
RESUME_RESTORE_STATE tells the driver to restore the state saved by
|
||||
the SUSPEND_SAVE_STATE suspend call.
|
||||
|
||||
RESUME_ENABLE tells the driver to start accepting I/O transactions
|
||||
again. Depending on driver policy, the device may already have pending
|
||||
I/O requests.
|
||||
|
||||
RESUME_POWER_ON is called with interrupts disabled. The other resume
|
||||
levels are called with interrupts enabled.
|
||||
|
||||
As with the various suspend stages, the driver must not assume that
|
||||
any other resume calls have been or will be made. Each call should be
|
||||
self-contained and not dependent on any external state.
|
||||
|
||||
|
||||
Attributes
|
||||
~~~~~~~~~~
|
||||
struct driver_attribute {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct device_driver *, char * buf, size_t count, loff_t off);
|
||||
ssize_t (*store)(struct device_driver *, const char * buf, size_t count, loff_t off);
|
||||
};
|
||||
|
||||
Device drivers can export attributes via their sysfs directories.
|
||||
Drivers can declare attributes using a DRIVER_ATTR macro that works
|
||||
identically to the DEVICE_ATTR macro.
|
||||
|
||||
Example:
|
||||
|
||||
DRIVER_ATTR(debug,0644,show_debug,store_debug);
|
||||
|
||||
This is equivalent to declaring:
|
||||
|
||||
struct driver_attribute driver_attr_debug;
|
||||
|
||||
This can then be used to add and remove the attribute from the
|
||||
driver's directory using:
|
||||
|
||||
int driver_create_file(struct device_driver *, struct driver_attribute *);
|
||||
void driver_remove_file(struct device_driver *, struct driver_attribute *);
|
129
Documentation/driver-model/interface.txt
Normal file
129
Documentation/driver-model/interface.txt
Normal file
@@ -0,0 +1,129 @@
|
||||
|
||||
Device Interfaces
|
||||
|
||||
Introduction
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Device interfaces are the logical interfaces of device classes that correlate
|
||||
directly to userspace interfaces, like device nodes.
|
||||
|
||||
Each device class may have multiple interfaces through which you can
|
||||
access the same device. An input device may support the mouse interface,
|
||||
the 'evdev' interface, and the touchscreen interface. A SCSI disk would
|
||||
support the disk interface, the SCSI generic interface, and possibly a raw
|
||||
device interface.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Programming Interface
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
struct device_interface {
|
||||
char * name;
|
||||
rwlock_t lock;
|
||||
u32 devnum;
|
||||
struct device_class * devclass;
|
||||
|
||||
struct list_head node;
|
||||
struct driver_dir_entry dir;
|
||||
|
||||
int (*add_device)(struct device *);
|
||||
int (*add_device)(struct intf_data *);
|
||||
};
|
||||
|
||||
int interface_register(struct device_interface *);
|
||||
void interface_unregister(struct device_interface *);
|
||||
|
||||
|
||||
An interface must specify the device class it belongs to. It is added
|
||||
to that class's list of interfaces on registration.
|
||||
|
||||
|
||||
Interfaces can be added to a device class at any time. Whenever it is
|
||||
added, each device in the class is passed to the interface's
|
||||
add_device callback. When an interface is removed, each device is
|
||||
removed from the interface.
|
||||
|
||||
|
||||
Devices
|
||||
~~~~~~~
|
||||
Once a device is added to a device class, it is added to each
|
||||
interface that is registered with the device class. The class
|
||||
is expected to place a class-specific data structure in
|
||||
struct device::class_data. The interface can use that (along with
|
||||
other fields of struct device) to determine whether or not the driver
|
||||
and/or device support that particular interface.
|
||||
|
||||
|
||||
Data
|
||||
~~~~
|
||||
|
||||
struct intf_data {
|
||||
struct list_head node;
|
||||
struct device_interface * intf;
|
||||
struct device * dev;
|
||||
u32 intf_num;
|
||||
};
|
||||
|
||||
int interface_add_data(struct interface_data *);
|
||||
|
||||
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.
|
||||
|
||||
sysfs
|
||||
~~~~~
|
||||
Each interface is given a directory in the directory of the device
|
||||
class it belongs to:
|
||||
|
||||
Interfaces get a directory in the class's directory as well:
|
||||
|
||||
class/
|
||||
`-- input
|
||||
|-- devices
|
||||
|-- drivers
|
||||
|-- mouse
|
||||
`-- evdev
|
||||
|
||||
When a device is added to the interface, a symlink is created that points
|
||||
to the device's directory in the physical hierarchy:
|
||||
|
||||
class/
|
||||
`-- input
|
||||
|-- devices
|
||||
| `-- 1 -> ../../../root/pci0/00:1f.0/usb_bus/00:1f.2-1:0/
|
||||
|-- drivers
|
||||
| `-- usb:usb_mouse -> ../../../bus/drivers/usb_mouse/
|
||||
|-- mouse
|
||||
| `-- 1 -> ../../../root/pci0/00:1f.0/usb_bus/00:1f.2-1:0/
|
||||
`-- evdev
|
||||
`-- 1 -> ../../../root/pci0/00:1f.0/usb_bus/00:1f.2-1:0/
|
||||
|
||||
|
||||
Future Plans
|
||||
~~~~~~~~~~~~
|
||||
A device interface is correlated directly with a userspace interface
|
||||
for a device, specifically a device node. For instance, a SCSI disk
|
||||
exposes at least two interfaces to userspace: the standard SCSI disk
|
||||
interface and the SCSI generic interface. It might also export a raw
|
||||
device interface.
|
||||
|
||||
Many interfaces have a major number associated with them and each
|
||||
device gets a minor number. Or, multiple interfaces might share one
|
||||
major number, and each will receive a range of minor numbers (like in
|
||||
the case of input devices).
|
||||
|
||||
These major and minor numbers could be stored in the interface
|
||||
structure. Major and minor allocations could happen when the interface
|
||||
is registered with the class, or via a helper function.
|
||||
|
114
Documentation/driver-model/overview.txt
Normal file
114
Documentation/driver-model/overview.txt
Normal file
@@ -0,0 +1,114 @@
|
||||
The Linux Kernel Device Model
|
||||
|
||||
Patrick Mochel <mochel@osdl.org>
|
||||
|
||||
26 August 2002
|
||||
|
||||
|
||||
Overview
|
||||
~~~~~~~~
|
||||
|
||||
This driver model is a unification of all the current, disparate driver models
|
||||
that are currently in the kernel. It is intended to augment the
|
||||
bus-specific drivers for bridges and devices by consolidating a set of data
|
||||
and operations into globally accessible data structures.
|
||||
|
||||
Current driver models implement some sort of tree-like structure (sometimes
|
||||
just a list) for the devices they control. But, there is no linkage between
|
||||
the different bus types.
|
||||
|
||||
A common data structure can provide this linkage with little overhead: when a
|
||||
bus driver discovers a particular device, it can insert it into the global
|
||||
tree as well as its local tree. In fact, the local tree becomes just a subset
|
||||
of the global tree.
|
||||
|
||||
Common data fields can also be moved out of the local bus models into the
|
||||
global model. Some of the manipulations of these fields can also be
|
||||
consolidated. Most likely, manipulation functions will become a set
|
||||
of helper functions, which the bus drivers wrap around to include any
|
||||
bus-specific items.
|
||||
|
||||
The common device and bridge interface currently reflects the goals of the
|
||||
modern PC: namely the ability to do seamless Plug and Play, power management,
|
||||
and hot plug. (The model dictated by Intel and Microsoft (read: ACPI) ensures
|
||||
us that any device in the system may fit any of these criteria.)
|
||||
|
||||
In reality, not every bus will be able to support such operations. But, most
|
||||
buses will support a majority of those operations, and all future buses will.
|
||||
In other words, a bus that doesn't support an operation is the exception,
|
||||
instead of the other way around.
|
||||
|
||||
|
||||
|
||||
Downstream Access
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Common data fields have been moved out of individual bus layers into a common
|
||||
data structure. But, these fields must still be accessed by the bus layers,
|
||||
and sometimes by the device-specific drivers.
|
||||
|
||||
Other bus layers are encouraged to do what has been done for the PCI layer.
|
||||
struct pci_dev now looks like this:
|
||||
|
||||
struct pci_dev {
|
||||
...
|
||||
|
||||
struct device device;
|
||||
};
|
||||
|
||||
Note first that it is statically allocated. This means only one allocation on
|
||||
device discovery. Note also that it is at the _end_ of struct pci_dev. This is
|
||||
to make people think about what they're doing when switching between the bus
|
||||
driver and the global driver; and to prevent against mindless casts between
|
||||
the two.
|
||||
|
||||
The PCI bus layer freely accesses the fields of struct device. It knows about
|
||||
the structure of struct pci_dev, and it should know the structure of struct
|
||||
device. PCI devices that have been converted generally do not touch the fields
|
||||
of struct device. More precisely, device-specific drivers should not touch
|
||||
fields of struct device unless there is a strong compelling reason to do so.
|
||||
|
||||
This abstraction is prevention of unnecessary pain during transitional phases.
|
||||
If the name of the field changes or is removed, then every downstream driver
|
||||
will break. On the other hand, if only the bus layer (and not the device
|
||||
layer) accesses struct device, it is only that layer that needs to change.
|
||||
|
||||
|
||||
User Interface
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
By virtue of having a complete hierarchical view of all the devices in the
|
||||
system, exporting a complete hierarchical view to userspace becomes relatively
|
||||
easy. This has been accomplished by implementing a special purpose virtual
|
||||
file system named sysfs. It is hence possible for the user to mount the
|
||||
whole sysfs filesystem anywhere in userspace.
|
||||
|
||||
This can be done permanently by providing the following entry into the
|
||||
/etc/fstab (under the provision that the mount point does exist, of course):
|
||||
|
||||
none /sys sysfs defaults 0 0
|
||||
|
||||
Or by hand on the command line:
|
||||
|
||||
# mount -t sysfs sysfs /sys
|
||||
|
||||
Whenever a device is inserted into the tree, a directory is created for it.
|
||||
This directory may be populated at each layer of discovery - the global layer,
|
||||
the bus layer, or the device layer.
|
||||
|
||||
The global layer currently creates two files - 'name' and 'power'. The
|
||||
former only reports the name of the device. The latter reports the
|
||||
current power state of the device. It will also be used to set the current
|
||||
power state.
|
||||
|
||||
The bus layer may also create files for the devices it finds while probing the
|
||||
bus. For example, the PCI layer currently creates 'irq' and 'resource' files
|
||||
for each PCI device.
|
||||
|
||||
A device-specific driver may also export files in its directory to expose
|
||||
device-specific data or tunable interfaces.
|
||||
|
||||
More information about the sysfs directory layout can be found in
|
||||
the other documents in this directory and in the file
|
||||
Documentation/filesystems/sysfs.txt.
|
||||
|
99
Documentation/driver-model/platform.txt
Normal file
99
Documentation/driver-model/platform.txt
Normal file
@@ -0,0 +1,99 @@
|
||||
Platform Devices and Drivers
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Platform devices
|
||||
~~~~~~~~~~~~~~~~
|
||||
Platform devices are devices that typically appear as autonomous
|
||||
entities in the system. This includes legacy port-based devices and
|
||||
host bridges to peripheral buses.
|
||||
|
||||
|
||||
Platform drivers
|
||||
~~~~~~~~~~~~~~~~
|
||||
Drivers for platform devices are typically very simple and
|
||||
unstructured. Either the device was present at a particular I/O port
|
||||
and the driver was loaded, or it was not. There was no possibility
|
||||
of hotplugging or alternative discovery besides probing at a specific
|
||||
I/O address and expecting a specific response.
|
||||
|
||||
|
||||
Other Architectures, Modern Firmware, and new Platforms
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
These devices are not always at the legacy I/O ports. This is true on
|
||||
other architectures and on some modern architectures. In most cases,
|
||||
the drivers are modified to discover the devices at other well-known
|
||||
ports for the given platform. However, the firmware in these systems
|
||||
does usually know where exactly these devices reside, and in some
|
||||
cases, it's the only way of discovering them.
|
||||
|
||||
|
||||
The Platform Bus
|
||||
~~~~~~~~~~~~~~~~
|
||||
A platform bus has been created to deal with these issues. First and
|
||||
foremost, it groups all the legacy devices under a common bus, and
|
||||
gives them a common parent if they don't already have one.
|
||||
|
||||
But, besides the organizational benefits, the platform bus can also
|
||||
accommodate firmware-based enumeration.
|
||||
|
||||
|
||||
Device Discovery
|
||||
~~~~~~~~~~~~~~~~
|
||||
The platform bus has no concept of probing for devices. Devices
|
||||
discovery is left up to either the legacy drivers or the
|
||||
firmware. These entities are expected to notify the platform of
|
||||
devices that it discovers via the bus's add() callback:
|
||||
|
||||
platform_bus.add(parent,bus_id).
|
||||
|
||||
|
||||
Bus IDs
|
||||
~~~~~~~
|
||||
Bus IDs are the canonical names for the devices. There is no globally
|
||||
standard addressing mechanism for legacy devices. In the IA-32 world,
|
||||
we have Pnp IDs to use, as well as the legacy I/O ports. However,
|
||||
neither tell what the device really is or have any meaning on other
|
||||
platforms.
|
||||
|
||||
Since both PnP IDs and the legacy I/O ports (and other standard I/O
|
||||
ports for specific devices) have a 1:1 mapping, we map the
|
||||
platform-specific name or identifier to a generic name (at least
|
||||
within the scope of the kernel).
|
||||
|
||||
For example, a serial driver might find a device at I/O 0x3f8. The
|
||||
ACPI firmware might also discover a device with PnP ID (_HID)
|
||||
PNP0501. Both correspond to the same device and should be mapped to the
|
||||
canonical name 'serial'.
|
||||
|
||||
The bus_id field should be a concatenation of the canonical name and
|
||||
the instance of that type of device. For example, the device at I/O
|
||||
port 0x3f8 should have a bus_id of "serial0". This places the
|
||||
responsibility of enumerating devices of a particular type up to the
|
||||
discovery mechanism. But, they are the entity that should know best
|
||||
(as opposed to the platform bus driver).
|
||||
|
||||
|
||||
Drivers
|
||||
~~~~~~~
|
||||
Drivers for platform devices should have a name that is the same as
|
||||
the canonical name of the devices they support. This allows the
|
||||
platform bus driver to do simple matching with the basic data
|
||||
structures to determine if a driver supports a certain device.
|
||||
|
||||
For example, a legacy serial driver should have a name of 'serial' and
|
||||
register itself with the platform bus.
|
||||
|
||||
|
||||
Driver Binding
|
||||
~~~~~~~~~~~~~~
|
||||
Legacy drivers assume they are bound to the device once they start up
|
||||
and probe an I/O port. Divorcing them from this will be a difficult
|
||||
process. However, that shouldn't prevent us from implementing
|
||||
firmware-based enumeration.
|
||||
|
||||
The firmware should notify the platform bus about devices before the
|
||||
legacy drivers have had a chance to load. Once the drivers are loaded,
|
||||
they driver model core will attempt to bind the driver to any
|
||||
previously-discovered devices. Once that has happened, it will be free
|
||||
to discover any other devices it pleases.
|
||||
|
445
Documentation/driver-model/porting.txt
Normal file
445
Documentation/driver-model/porting.txt
Normal file
@@ -0,0 +1,445 @@
|
||||
|
||||
Porting Drivers to the New Driver Model
|
||||
|
||||
Patrick Mochel
|
||||
|
||||
7 January 2003
|
||||
|
||||
|
||||
Overview
|
||||
|
||||
Please refer to Documentation/driver-model/*.txt for definitions of
|
||||
various driver types and concepts.
|
||||
|
||||
Most of the work of porting devices drivers to the new model happens
|
||||
at the bus driver layer. This was intentional, to minimize the
|
||||
negative effect on kernel drivers, and to allow a gradual transition
|
||||
of bus drivers.
|
||||
|
||||
In a nutshell, the driver model consists of a set of objects that can
|
||||
be embedded in larger, bus-specific objects. Fields in these generic
|
||||
objects can replace fields in the bus-specific objects.
|
||||
|
||||
The generic objects must be registered with the driver model core. By
|
||||
doing so, they will exported via the sysfs filesystem. sysfs can be
|
||||
mounted by doing
|
||||
|
||||
# mount -t sysfs sysfs /sys
|
||||
|
||||
|
||||
|
||||
The Process
|
||||
|
||||
Step 0: Read include/linux/device.h for object and function definitions.
|
||||
|
||||
Step 1: Registering the bus driver.
|
||||
|
||||
|
||||
- Define a struct bus_type for the bus driver.
|
||||
|
||||
struct bus_type pci_bus_type = {
|
||||
.name = "pci",
|
||||
};
|
||||
|
||||
|
||||
- Register the bus type.
|
||||
This should be done in the initialization function for the bus type,
|
||||
which is usually the module_init(), or equivalent, function.
|
||||
|
||||
static int __init pci_driver_init(void)
|
||||
{
|
||||
return bus_register(&pci_bus_type);
|
||||
}
|
||||
|
||||
subsys_initcall(pci_driver_init);
|
||||
|
||||
|
||||
The bus type may be unregistered (if the bus driver may be compiled
|
||||
as a module) by doing:
|
||||
|
||||
bus_unregister(&pci_bus_type);
|
||||
|
||||
|
||||
- Export the bus type for others to use.
|
||||
|
||||
Other code may wish to reference the bus type, so declare it in a
|
||||
shared header file and export the symbol.
|
||||
|
||||
From include/linux/pci.h:
|
||||
|
||||
extern struct bus_type pci_bus_type;
|
||||
|
||||
|
||||
From file the above code appears in:
|
||||
|
||||
EXPORT_SYMBOL(pci_bus_type);
|
||||
|
||||
|
||||
|
||||
- This will cause the bus to show up in /sys/bus/pci/ with two
|
||||
subdirectories: 'devices' and 'drivers'.
|
||||
|
||||
# tree -d /sys/bus/pci/
|
||||
/sys/bus/pci/
|
||||
|-- devices
|
||||
`-- drivers
|
||||
|
||||
|
||||
|
||||
Step 2: Registering Devices.
|
||||
|
||||
struct device represents a single device. It mainly contains metadata
|
||||
describing the relationship the device has to other entities.
|
||||
|
||||
|
||||
- Embedd a struct device in the bus-specific device type.
|
||||
|
||||
|
||||
struct pci_dev {
|
||||
...
|
||||
struct device dev; /* Generic device interface */
|
||||
...
|
||||
};
|
||||
|
||||
It is recommended that the generic device not be the first item in
|
||||
the struct to discourage programmers from doing mindless casts
|
||||
between the object types. Instead macros, or inline functions,
|
||||
should be created to convert from the generic object type.
|
||||
|
||||
|
||||
#define to_pci_dev(n) container_of(n, struct pci_dev, dev)
|
||||
|
||||
or
|
||||
|
||||
static inline struct pci_dev * to_pci_dev(struct kobject * kobj)
|
||||
{
|
||||
return container_of(n, struct pci_dev, dev);
|
||||
}
|
||||
|
||||
This allows the compiler to verify type-safety of the operations
|
||||
that are performed (which is Good).
|
||||
|
||||
|
||||
- Initialize the device on registration.
|
||||
|
||||
When devices are discovered or registered with the bus type, the
|
||||
bus driver should initialize the generic device. The most important
|
||||
things to initialize are the bus_id, parent, and bus fields.
|
||||
|
||||
The bus_id is an ASCII string that contains the device's address on
|
||||
the bus. The format of this string is bus-specific. This is
|
||||
necessary for representing devices in sysfs.
|
||||
|
||||
parent is the physical parent of the device. It is important that
|
||||
the bus driver sets this field correctly.
|
||||
|
||||
The driver model maintains an ordered list of devices that it uses
|
||||
for power management. This list must be in order to guarantee that
|
||||
devices are shutdown before their physical parents, and vice versa.
|
||||
The order of this list is determined by the parent of registered
|
||||
devices.
|
||||
|
||||
Also, the location of the device's sysfs directory depends on a
|
||||
device's parent. sysfs exports a directory structure that mirrors
|
||||
the device hierarchy. Accurately setting the parent guarantees that
|
||||
sysfs will accurately represent the hierarchy.
|
||||
|
||||
The device's bus field is a pointer to the bus type the device
|
||||
belongs to. This should be set to the bus_type that was declared
|
||||
and initialized before.
|
||||
|
||||
Optionally, the bus driver may set the device's name and release
|
||||
fields.
|
||||
|
||||
The name field is an ASCII string describing the device, like
|
||||
|
||||
"ATI Technologies Inc Radeon QD"
|
||||
|
||||
The release field is a callback that the driver model core calls
|
||||
when the device has been removed, and all references to it have
|
||||
been released. More on this in a moment.
|
||||
|
||||
|
||||
- Register the device.
|
||||
|
||||
Once the generic device has been initialized, it can be registered
|
||||
with the driver model core by doing:
|
||||
|
||||
device_register(&dev->dev);
|
||||
|
||||
It can later be unregistered by doing:
|
||||
|
||||
device_unregister(&dev->dev);
|
||||
|
||||
This should happen on buses that support hotpluggable devices.
|
||||
If a bus driver unregisters a device, it should not immediately free
|
||||
it. It should instead wait for the driver model core to call the
|
||||
device's release method, then free the bus-specific object.
|
||||
(There may be other code that is currently referencing the device
|
||||
structure, and it would be rude to free the device while that is
|
||||
happening).
|
||||
|
||||
|
||||
When the device is registered, a directory in sysfs is created.
|
||||
The PCI tree in sysfs looks like:
|
||||
|
||||
/sys/devices/pci0/
|
||||
|-- 00:00.0
|
||||
|-- 00:01.0
|
||||
| `-- 01:00.0
|
||||
|-- 00:02.0
|
||||
| `-- 02:1f.0
|
||||
| `-- 03:00.0
|
||||
|-- 00:1e.0
|
||||
| `-- 04:04.0
|
||||
|-- 00:1f.0
|
||||
|-- 00:1f.1
|
||||
| |-- ide0
|
||||
| | |-- 0.0
|
||||
| | `-- 0.1
|
||||
| `-- ide1
|
||||
| `-- 1.0
|
||||
|-- 00:1f.2
|
||||
|-- 00:1f.3
|
||||
`-- 00:1f.5
|
||||
|
||||
Also, symlinks are created in the bus's 'devices' directory
|
||||
that point to the device's directory in the physical hierarchy.
|
||||
|
||||
/sys/bus/pci/devices/
|
||||
|-- 00:00.0 -> ../../../devices/pci0/00:00.0
|
||||
|-- 00:01.0 -> ../../../devices/pci0/00:01.0
|
||||
|-- 00:02.0 -> ../../../devices/pci0/00:02.0
|
||||
|-- 00:1e.0 -> ../../../devices/pci0/00:1e.0
|
||||
|-- 00:1f.0 -> ../../../devices/pci0/00:1f.0
|
||||
|-- 00:1f.1 -> ../../../devices/pci0/00:1f.1
|
||||
|-- 00:1f.2 -> ../../../devices/pci0/00:1f.2
|
||||
|-- 00:1f.3 -> ../../../devices/pci0/00:1f.3
|
||||
|-- 00:1f.5 -> ../../../devices/pci0/00:1f.5
|
||||
|-- 01:00.0 -> ../../../devices/pci0/00:01.0/01:00.0
|
||||
|-- 02:1f.0 -> ../../../devices/pci0/00:02.0/02:1f.0
|
||||
|-- 03:00.0 -> ../../../devices/pci0/00:02.0/02:1f.0/03:00.0
|
||||
`-- 04:04.0 -> ../../../devices/pci0/00:1e.0/04:04.0
|
||||
|
||||
|
||||
|
||||
Step 3: Registering Drivers.
|
||||
|
||||
struct device_driver is a simple driver structure that contains a set
|
||||
of operations that the driver model core may call.
|
||||
|
||||
|
||||
- Embed a struct device_driver in the bus-specific driver.
|
||||
|
||||
Just like with devices, do something like:
|
||||
|
||||
struct pci_driver {
|
||||
...
|
||||
struct device_driver driver;
|
||||
};
|
||||
|
||||
|
||||
- Initialize the generic driver structure.
|
||||
|
||||
When the driver registers with the bus (e.g. doing pci_register_driver()),
|
||||
initialize the necessary fields of the driver: the name and bus
|
||||
fields.
|
||||
|
||||
|
||||
- Register the driver.
|
||||
|
||||
After the generic driver has been initialized, call
|
||||
|
||||
driver_register(&drv->driver);
|
||||
|
||||
to register the driver with the core.
|
||||
|
||||
When the driver is unregistered from the bus, unregister it from the
|
||||
core by doing:
|
||||
|
||||
driver_unregister(&drv->driver);
|
||||
|
||||
Note that this will block until all references to the driver have
|
||||
gone away. Normally, there will not be any.
|
||||
|
||||
|
||||
- Sysfs representation.
|
||||
|
||||
Drivers are exported via sysfs in their bus's 'driver's directory.
|
||||
For example:
|
||||
|
||||
/sys/bus/pci/drivers/
|
||||
|-- 3c59x
|
||||
|-- Ensoniq AudioPCI
|
||||
|-- agpgart-amdk7
|
||||
|-- e100
|
||||
`-- serial
|
||||
|
||||
|
||||
Step 4: Define Generic Methods for Drivers.
|
||||
|
||||
struct device_driver defines a set of operations that the driver model
|
||||
core calls. Most of these operations are probably similar to
|
||||
operations the bus already defines for drivers, but taking different
|
||||
parameters.
|
||||
|
||||
It would be difficult and tedious to force every driver on a bus to
|
||||
simultaneously convert their drivers to generic format. Instead, the
|
||||
bus driver should define single instances of the generic methods that
|
||||
forward call to the bus-specific drivers. For instance:
|
||||
|
||||
|
||||
static int pci_device_remove(struct device * dev)
|
||||
{
|
||||
struct pci_dev * pci_dev = to_pci_dev(dev);
|
||||
struct pci_driver * drv = pci_dev->driver;
|
||||
|
||||
if (drv) {
|
||||
if (drv->remove)
|
||||
drv->remove(pci_dev);
|
||||
pci_dev->driver = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
The generic driver should be initialized with these methods before it
|
||||
is registered.
|
||||
|
||||
/* initialize common driver fields */
|
||||
drv->driver.name = drv->name;
|
||||
drv->driver.bus = &pci_bus_type;
|
||||
drv->driver.probe = pci_device_probe;
|
||||
drv->driver.resume = pci_device_resume;
|
||||
drv->driver.suspend = pci_device_suspend;
|
||||
drv->driver.remove = pci_device_remove;
|
||||
|
||||
/* register with core */
|
||||
driver_register(&drv->driver);
|
||||
|
||||
|
||||
Ideally, the bus should only initialize the fields if they are not
|
||||
already set. This allows the drivers to implement their own generic
|
||||
methods.
|
||||
|
||||
|
||||
Step 5: Support generic driver binding.
|
||||
|
||||
The model assumes that a device or driver can be dynamically
|
||||
registered with the bus at any time. When registration happens,
|
||||
devices must be bound to a driver, or drivers must be bound to all
|
||||
devices that it supports.
|
||||
|
||||
A driver typically contains a list of device IDs that it supports. The
|
||||
bus driver compares these IDs to the IDs of devices registered with it.
|
||||
The format of the device IDs, and the semantics for comparing them are
|
||||
bus-specific, so the generic model does attempt to generalize them.
|
||||
|
||||
Instead, a bus may supply a method in struct bus_type that does the
|
||||
comparison:
|
||||
|
||||
int (*match)(struct device * dev, struct device_driver * drv);
|
||||
|
||||
match should return '1' if the driver supports the device, and '0'
|
||||
otherwise.
|
||||
|
||||
When a device is registered, the bus's list of drivers is iterated
|
||||
over. bus->match() is called for each one until a match is found.
|
||||
|
||||
When a driver is registered, the bus's list of devices is iterated
|
||||
over. bus->match() is called for each device that is not already
|
||||
claimed by a driver.
|
||||
|
||||
When a device is successfully bound to a device, device->driver is
|
||||
set, the device is added to a per-driver list of devices, and a
|
||||
symlink is created in the driver's sysfs directory that points to the
|
||||
device's physical directory:
|
||||
|
||||
/sys/bus/pci/drivers/
|
||||
|-- 3c59x
|
||||
| `-- 00:0b.0 -> ../../../../devices/pci0/00:0b.0
|
||||
|-- Ensoniq AudioPCI
|
||||
|-- agpgart-amdk7
|
||||
| `-- 00:00.0 -> ../../../../devices/pci0/00:00.0
|
||||
|-- e100
|
||||
| `-- 00:0c.0 -> ../../../../devices/pci0/00:0c.0
|
||||
`-- serial
|
||||
|
||||
|
||||
This driver binding should replace the existing driver binding
|
||||
mechanism the bus currently uses.
|
||||
|
||||
|
||||
Step 6: Supply a hotplug callback.
|
||||
|
||||
Whenever a device is registered with the driver model core, the
|
||||
userspace program /sbin/hotplug is called to notify userspace.
|
||||
Users can define actions to perform when a device is inserted or
|
||||
removed.
|
||||
|
||||
The driver model core passes several arguments to userspace via
|
||||
environment variables, including
|
||||
|
||||
- ACTION: set to 'add' or 'remove'
|
||||
- DEVPATH: set to the device's physical path in sysfs.
|
||||
|
||||
A bus driver may also supply additional parameters for userspace to
|
||||
consume. To do this, a bus must implement the 'hotplug' method in
|
||||
struct bus_type:
|
||||
|
||||
int (*hotplug) (struct device *dev, char **envp,
|
||||
int num_envp, char *buffer, int buffer_size);
|
||||
|
||||
This is called immediately before /sbin/hotplug is executed.
|
||||
|
||||
|
||||
Step 7: Cleaning up the bus driver.
|
||||
|
||||
The generic bus, device, and driver structures provide several fields
|
||||
that can replace those defined privately to the bus driver.
|
||||
|
||||
- Device list.
|
||||
|
||||
struct bus_type contains a list of all devices registered with the bus
|
||||
type. This includes all devices on all instances of that bus type.
|
||||
An internal list that the bus uses may be removed, in favor of using
|
||||
this one.
|
||||
|
||||
The core provides an iterator to access these devices.
|
||||
|
||||
int bus_for_each_dev(struct bus_type * bus, struct device * start,
|
||||
void * data, int (*fn)(struct device *, void *));
|
||||
|
||||
|
||||
- Driver list.
|
||||
|
||||
struct bus_type also contains a list of all drivers registered with
|
||||
it. An internal list of drivers that the bus driver maintains may
|
||||
be removed in favor of using the generic one.
|
||||
|
||||
The drivers may be iterated over, like devices:
|
||||
|
||||
int bus_for_each_drv(struct bus_type * bus, struct device_driver * start,
|
||||
void * data, int (*fn)(struct device_driver *, void *));
|
||||
|
||||
|
||||
Please see drivers/base/bus.c for more information.
|
||||
|
||||
|
||||
- rwsem
|
||||
|
||||
struct bus_type contains an rwsem that protects all core accesses to
|
||||
the device and driver lists. This can be used by the bus driver
|
||||
internally, and should be used when accessing the device or driver
|
||||
lists the bus maintains.
|
||||
|
||||
|
||||
- Device and driver fields.
|
||||
|
||||
Some of the fields in struct device and struct device_driver duplicate
|
||||
fields in the bus-specific representations of these objects. Feel free
|
||||
to remove the bus-specific ones and favor the generic ones. Note
|
||||
though, that this will likely mean fixing up all the drivers that
|
||||
reference the bus-specific fields (though those should all be 1-line
|
||||
changes).
|
||||
|
Reference in New Issue
Block a user