An early version of an introduction to the new device manager. Will be expanded

more over the following days, and published on the website.
Comments welcome!


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25760 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2008-06-02 15:45:22 +00:00
parent 64b2d16995
commit 30f7fc408b
1 changed files with 249 additions and 0 deletions

View File

@ -0,0 +1,249 @@
<html>
<body bgcolor=white>
<h1>Introduction to Haiku's Device Driver Architecture</h1>
<p>This document tries to give you a short introduction into the new device
manager, and how to write drivers for it. Haiku still supports the legacy
device driver architecture introduced with BeOS.</p>
<p>The new device driver architecture of Haiku is still a moving target,
although most of its details are already specificed.</p>
<h3>1. The Basics</h3>
<p>The device manager functionality builds upon <i>device_node</i> objects.
Every driver in the system publishes one or more of such nodes, building a
tree of device nodes. This tree is in theory a dynamic representation of the
current hardware devices in the system, but in practice will also contain
implementation specific details; since every node comes with an API specific
to that node, you'll find device nodes that only come with a number of support
functions for a certain class of drivers.</p>
<p>Structurally, a <i>device_node</i> is a set of a module, attributes,
and resources, as well as a parent and children. At a minimum, a node must
have a module, all other components are optional.</p>
TODO: picture of the device node tree
<p>When the system starts, there is only a root node registered. Only primary
hardware busses register with the root node, such as PCI, and ISA on x86.
Since the PCI bus is an intelligent bus, it knows what hardware is installed,
and registers a child node for each device on the bus.</p>
<p>Every driver can also publish a device in <i>/dev</i> for communication with
userland applications. All drivers and devices are kernel modules.</p>
<h3>2. Exploring the Device Tree</h3>
<p>So how does it all work? When building the initial device tree, the system only
explores a minimum of device drivers only, resulting in a tree that basically
only shows the hardware found in the computer.</p>
<p>Now, if the system requires disk access, it will scan the device file system
for a driver that provides such functionality, in this case, it will look for
drivers under "/dev/disk/". The device manager has a set of built-in rules for
how to translate a device path into a device node, and vice versa: every node
representing a device of an intelligent bus (such as PCI) will also contain
device type information following the PCI definitions. In this case, the "disk"
sub-path will translate into the <i>PCI_mass_storage</i> type, and hence, the
device manager will then completely explore all device nodes of that type.</p>
<p>It will also use that path information to only ask drivers that actually
are in a matching module directory. In the above example of a disk driver, this
would be either in "busses/scsi", "busses/ide", "drivers/disk", ...</p>
TODO: untyped/generic busses are not yet supported!
<h3>3. Writing a Driver</h3>
<p>The device manager assumes the following API from a driver module:</p>
<ul>
<li><b>supports_device()</b><br>
Determines wether or not the driver supports a given parent device node,
that is the hardware device it represents (if any), and the API the node
exports.</li>
<li><b>register_device()</b><br>
The driver should register its device node here. The parent driver is
always initialized at this point. When registering the node, the driver
can also attach certain I/O resources (like I/O ports, or memory ranges)
to the node -- the device manager will make sure that only one node can
claim these resources.</li>
<li><b>init_driver()</b><br>
Any initialization necessary to get the driver going. For most drivers,
this will be reduced to the creation of a private data structure that is
going to be used for all of the following functions.</li>
<li><b>uninit_driver()</b><br>
Uninitializes resources acquired by <b>init_driver()</b>.</li>
<li><b>register_child_devices()</b><br>
If the driver wants to register any child device nodes or to publish
any devices, it should do so here. This function is called only during
the initial registration process of the device node.</li>
<li><b>rescan_child_devices()</b><br>
Is called whenever a manual rescan is triggered.</li>
<li><b>device_removed()</b></br>
Is called when the device node is about to be unregistered when its
device is gone, for example when a USB device is unplugged.</li>
<li><b>suspend()</b><br>
Enters different sleep modes.</li>
<li><b>resume()</b><br>
Resumes a device from a previous sleep mode.</li>
</ul>
<p>To ensure that a module exports this API, it <b>must</b> end its module name
with "driver_v1" to denote the version of the API it supports. Note that
<b>suspend()</b> and <b>resume()</b> are currently never called, as Haiku has
no power management implemented yet.</p>
<p>If your driver can give the device it is attached to a nice name that can be
presented to the user, it should add the <b>B_DEVICE_PRETTY_NAME</b> attribute
to the device node.
<p>The <b>B_DEVICE_UNIQUE_ID</b> should be used in case the device has a unique
ID that can be used to identify it, and also differentiate it from other devices
of the same model and vendor. This information will be added to the file system
attributes of all devices published by your driver, so that user applications
can identify, say, a USB printer no matter what USB slot it is attached to, and
assign it additional data, like paper configuration, or recognize it as the
default printer.</p>
<p>If your driver implements an API that is used by a support or bus module, you
will usually use the <b>B_DEVICE_FIXED_CHILD</b> attribute to specify exactly
which child device node you will be talking to. If you support several child
nodes, you may want to have a closer look at the section explaining
<a href="#bus_driver">how to write a bus driver</a>.</p>
<h3>4. Publishing a Device</h3>
To publish a device entry in the device file system under <i>/dev</i>, all your
driver has to do is to call the
<pre>
publish_device(device_node *node, const char *path,
const char *deviceModuleName);
</pre>
function the device manager module exports. The <i>path</i> is the path
component that follows "/dev", for example "net/ipro1000/0". The
<i>deviceModuleName</i> is the module exporting the device functionality.
It should end with "device_v1" to show the device manager which protocol it
supports. If the device node your device belongs to is removed, your device
is removed automatically with it. On the other hand, you are allowed to
unpublish the device at any point using the <b>unpublish_device()</b> function
the device manager delivers for this.</p>
<p>A device module must export the following API:</p>
<ul>
<li><b>init_device()</b><br>
This is called when the open() is called on this device for the first
time. You may want to create a private data structure that is passed on
to all subsequent calls of the <b>open()</b> function that your device
exports.</li>
<li><b>uninit_device()</b><br>
Is called when the last file descriptor to the device had been closed.</li>
<li><b>device_removed()</b><br>
When the device node your device belongs to is going to be removed,
you're notified about this in this function.</li>
<li><b>open()</b><br>
Called whenever your device is opened.</li>
<li><b>close()</b><br>
</li>
<li><b>free()</b><br>
Free the private data structure you allocated in <b>open()</b>.</li>
<li><b>read()</b><br>
</li>
<li><b>write()</b><br>
</li>
<li><b>io()</b><br>
This is a replacement for the <b>read()</b>, and <b>write()</b> calls,
and allows, among other things, for asynchronous I/O. This functionality
has not yet been implemented, though (see below).</li>
<li><b>control()</b><br>
</li>
<li><b>select()</b><br>
</li>
<li><b>deselect()</b><br>
</li>
</ul>
<h3>5. <a name="bus_driver">Writing a Bus Driver</a></h3>
<p>A bus driver is a driver that represents a bus where one or more arbitrary
devices can be attached to.</p>
<p>There are two basic types of busses: intelligent busses like PCI or USB that
know a lot about the devices attached to it, like a generic device type, as
well as device and vendor ID information, and simple untyped/generic busses that
either have not all the information (like device type) or don't even know what
and if any devices are attached. The device manager has been written in such a
way that device exploration makes use of additional information the bus can
provide in order to find a responsible device driver faster, and with less
overhead.</p>
<h4>5.1. Writing an Intelligent Bus Driver</h4>
<p>If your bus knows what type of device is attached to, and also has vendor and
device ID information about that device, it is considered to be an intelligent
bus. The bus driver is supposed to have one parent node representing the bus,
and to create a child node for each device attached to the bus.</p>
<p>The additional information you have about the devices are attached to the
device node in the following attributes:</p>
<ul>
<li><b>B_DEVICE_VENDOR_ID</b><br>
The vendor ID - this ID has only to be valid in the namespace of your
bus.</li>
<li><b>B_DEVICE_ID</b><br>
The device ID.</li>
<li><b>B_DEVICE_TYPE</b><br>
The device type as defined by the PCI class base information.</li>
<li><b>B_DEVICE_SUB_TYPE</b><br>
The device sub type as defined by the PCI sub class information.</li>
<li><b>B_DEVICE_INTERFACE</b><br>
The device interface type as defined by the PCI class API information.</li>
</ul>
<p>You can use the <b>B_DEVICE_FLAGS</b> attribute to define how the device
manager finds the children of the devices you exported. For this kind of bus
drivers, you will usually only want to specify <b>B_FIND_CHILD_ON_DEMAND</b>
here, which causes the driver only to be searched when the system asks for it.
</p>
<h4>5.2. Writing a Simple Bus Driver</h4>
TODO: support for these drivers is still missing!
<h3>6. Open Issues</h3>
While most of the new device manager is fledged out, there are some areas that
could use improvements or are problematic under certain requirements. Also, some
parts just haven't been written yet.
<h4>6.1. generic/simple busses</h4>
<h4>6.2. Unpublishing</h4>
<h4>6.3. listdev functionality</h4>
The "listdev" command has been removed from the image, and it is currently not
functional anymore.
<h4>6.4. Versioning</h4>
<p>The way the device manager works, it makes versioning of modules (which are
supposed to be one of the strong points of the module system) much harder or
even impossible. While the device manager could introduce a new API and could
translate between a "driver_v1", and a "driver_v2" API on the fly, it's not
yet possible for a PCI sub module to do the same thing.</p>
<p><b>Proposed Solution:</b> Add attribute <b>B_DEVICE_ALTERNATE_VERSION</b>
that specifies alternate versions of the module API this device node supports.
We would then need a <b>request_version()</b> or <b>set_version()</b> function
(to be called from <b>supports_device()</b>) that allows to specify the version
of the parent node this device node wants to talk to.</p>
<h4>6.5. Unregistering Nodes</h4>
</body>
</html>