summaryrefslogtreecommitdiff
path: root/Documentation/driver-model/driver.txt
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /Documentation/driver-model/driver.txt
downloadlinux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz
linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz
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!
Diffstat (limited to 'Documentation/driver-model/driver.txt')
-rw-r--r--Documentation/driver-model/driver.txt287
1 files changed, 287 insertions, 0 deletions
diff --git a/Documentation/driver-model/driver.txt b/Documentation/driver-model/driver.txt
new file mode 100644
index 000000000000..12447787d329
--- /dev/null
+++ b/Documentation/driver-model/driver.txt
@@ -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 = &ethernet_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 = &ethernet_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 *);