Device Driver Architecture==========================This document tries to give you a short introduction into the new devicemanager, and how to write drivers for it. Haiku still supports thelegacy device driver architecture introduced with BeOS.The new device driver architecture of Haiku is still a moving target,although most of its details are already specificed.The Basics----------The device manager functionality builds upon *device_node* objects.Every driver in the system publishes one or more of such nodes, buildinga tree of device nodes. This tree is in theory a dynamic representationof the current hardware devices in the system, but in practice will alsocontain implementation specific details; since every node comes with anAPI specific to that node, you'll find device nodes that only come witha number of support functions for a certain class of drivers.Structurally, a *device_node* is a set of a module, attributes, andresources, as well as a parent and children. At a minimum, a node musthave a module, all other components are optional... image:: device_tree.svg:width: 800When the system starts, there is only a root node registered. Onlyprimary hardware busses register with the root node, such as PCI, andISA on x86. Since the PCI bus is an intelligent bus, it knows whathardware is installed, and registers a child node for each device on thebus.Every driver can also publish a device in */dev* for communication withuserland applications. All drivers and devices are kernel modules.Exploring the Device Tree-------------------------So how does it all work? When building the initial device tree, thesystem only explores a minimum of device drivers only, resulting in atree that basically only shows the hardware found in the computer.Now, if the system requires disk access, it will scan the device filesystem for a driver that provides such functionality, in this case, itwill look for drivers under "/dev/disk/". The device manager has a setof 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 thePCI definitions. In this case, the "disk" sub-path will translate intothe *PCI_mass_storage* type, and hence, the device manager will thencompletely explore all device nodes of that type.It will also use that path information to only ask drivers that actuallyare in a matching module directory. In the above example of a diskdriver, this would be either in "busses/scsi", "busses/ide","drivers/disk", ...For untyped or generic busses, it will use the context informationgained from the devfs query directly, and will search for drivers inthat sub directory only. The only exception to this rule are the devfsdirectories "disk", "ports", and "bus", which will also allow to searchmatching drivers in "busses". While this is relatively limited, it is agood way to cut down the number of drivers to be loaded.Writing a Driver----------------The device manager assumes the following API from a driver module:- **supports_device()**Determines wether or not the driver supports a given parent devicenode, that is the hardware device it represents (if any), and the APIthe node exports.- **register_device()**The driver should register its device node here. The parent driver isalways initialized at this point. When registering the node, thedriver can also attach certain I/O resources (like I/O ports, ormemory ranges) to the node -- the device manager will make sure thatonly one node can claim these resources.- **init_driver()**Any initialization necessary to get the driver going. For mostdrivers, this will be reduced to the creation of a private datastructure that is going to be used for all of the followingfunctions.- **uninit_driver()**Uninitializes resources acquired by **init_driver()**.- **register_child_devices()**If the driver wants to register any child device nodes or to publishany devices, it should do so here. This function is called onlyduring the initial registration process of the device node.- **rescan_child_devices()**Is called whenever a manual rescan is triggered.- **device_removed()** Is called when the device node is about to beunregistered when its device is gone, for example when a USB deviceis unplugged.- **suspend()**Enters different sleep modes.- **resume()**Resumes a device from a previous sleep mode.To ensure that a module exports this API, it **must** end its modulename with "driver_v1" to denote the version of the API it supports. Notethat **suspend()** and **resume()** are currently never called, as Haikuhas no power management implemented yet.If your driver can give the device it is attached to a nice name thatcan be presented to the user, it should add the **B_DEVICE_PRETTY_NAME**attribute to the device node.The **B_DEVICE_UNIQUE_ID** should be used in case the device has aunique ID that can be used to identify it, and also differentiate itfrom other devices of the same model and vendor. This information willbe added to the file system attributes of all devices published by yourdriver, so that user applications can identify, say, a USB printer nomatter what USB slot it is attached to, and assign it additional data,like paper configuration, or recognize it as the default printer.If your driver implements an API that is used by a support or busmodule, you will usually use the **B_DEVICE_FIXED_CHILD** attribute tospecify exactly which child device node you will be talking to. If yousupport several child nodes, you may want to have a closer look at thesection explaining `how to write a bus driver <#bus_driver>`__.In addition to the child nodes a driver registers itself, a driver caneither have dynamic children or fixed children, never both. Also, fixedchildren are registered before **register_child_devices()** is called,while dynamic children are registered afterwards.Publishing a Device-------------------To publish a device entry in the device file system under */dev*, allyour driver has to do is to call the::publish_device(device_node *node, const char *path,const char *deviceModuleName);function the device manager module exports. The *path* is the pathcomponent that follows "/dev", for example "net/ipro1000/0". The*deviceModuleName* is the module exporting the device functionality. Itshould end with "device_v1" to show the device manager which protocol itsupports. If the device node your device belongs to is removed, yourdevice is removed automatically with it. On the other hand, you areallowed to unpublish the device at any point using the**unpublish_device()** function the device manager delivers for this.A device module must export the following API:- **init_device()**This is called when the open() is called on this device for the firsttime. You may want to create a private data structure that is passedon to all subsequent calls of the **open()** function that yourdevice exports.- **uninit_device()**Is called when the last file descriptor to the device had beenclosed.- **device_removed()**When the device node your device belongs to is going to be removed,you're notified about this in this function.- **open()**Called whenever your device is opened.- **close()**- **free()**Free the private data structure you allocated in **open()**.- **read()**- **write()**- **io()**This is a replacement for the **read()**, and **write()** calls, andallows, among other things, for asynchronous I/O. This functionalityhas not yet been implemented, though (see below).- **control()**- **select()**- **deselect()**Writing a Bus Driver--------------------A bus driver is a driver that represents a bus where one or morearbitrary devices can be attached to.There are two basic types of busses: intelligent busses like PCI or USBthat know a lot about the devices attached to it, like a generic devicetype, as well as device and vendor ID information, and simpleuntyped/generic busses that either have not all the information (likedevice type) or don't even know what and if any devices are attached.The device manager has been written in such a way that deviceexploration makes use of additional information the bus can provide inorder to find a responsible device driver faster, and with lessoverhead.Writing an Intelligent Bus Driver^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^If your bus knows what type of device is attached to, and also hasvendor and device ID information about that device, it is considered tobe an intelligent bus. The bus driver is supposed to have one parentnode representing the bus, and to create a child node for each deviceattached to the bus.The additional information you have about the devices are attached tothe device node in the following attributes:- **B_DEVICE_VENDOR_ID**The vendor ID - this ID has only to be valid in the namespace of yourbus.- **B_DEVICE_ID**The device ID.- **B_DEVICE_TYPE**The device type as defined by the PCI class base information.- **B_DEVICE_SUB_TYPE**The device sub type as defined by the PCI sub class information.- **B_DEVICE_INTERFACE**The device interface type as defined by the PCI class APIinformation.You can use the **B_DEVICE_FLAGS** attribute to define how the devicemanager finds the children of the devices you exported. For this kind ofbus drivers, you will usually only want to specify**B_FIND_CHILD_ON_DEMAND** here, which causes the driver only to besearched when the system asks for it.5.2. Writing a Simple Bus Driver^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^A bus can be simple in a number of ways:#. It may not know how many or if any devices are attached to it#. It cannot retrieve any type information about the devices it has, butknows all devices that are attached to itAn example of the latter would be the Zorro bus of the Amiga - it onlyhas information about the vendor and device ID, but no type information.It should be implemented like an intelligent bus, though, with the typeinformation simply omitted.Therefore, this section is about the former case, that is, a simple buslike the ISA bus. Since it doesn't know anything about its children, itdoes not publish any child nodes, instead, it will just specify theB_FIND_MULTIPLE_CHILDREN and B_FIND_CHILD_ON_DEMAND flags for its devicenode. Since there is no additional information about this bus, thedevice manager will assume a simple bus, and will try to find drivers ondemand only.The generic bus---------------Some devices are not tied to a specific bus. This is the case for alldrivers that do not relate to a physical device: /dev/null, /dev/zero,/dev/random, etc. A "generic" bus has been added, and these drivers canattach to it.Open Issues-----------While most of the new device manager is fledged out, there are someareas that could use improvements or are problematic under certainrequirements. Also, some parts just haven't been written yet.generic/simple busses^^^^^^^^^^^^^^^^^^^^^Unpublishing^^^^^^^^^^^^Versioning^^^^^^^^^^The way the device manager works, it makes versioning of modules (whichare supposed to be one of the strong points of the module system) muchharder or even impossible. While the device manager could introduce anew API and could translate between a "driver_v1", and a "driver_v2" APIon the fly, it's not yet possible for a PCI sub module to do the samething.**Proposed Solution:** Add attribute **B_DEVICE_ALTERNATE_VERSION** thatspecifies alternate versions of the module API this device nodesupports. We would then need a **request_version()** or**set_version()** function (to be called from **supports_device()**)that allows to specify the version of the parent node this device nodewants to talk to.Unregistering Nodes^^^^^^^^^^^^^^^^^^^Support for generic drivers is missing^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^This should probably be done by simply adding a simple bus driver named"generic" that generic drivers need to ask for.Mappings, And Other Optimizations^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^Due to the way the device tree is built, the device manager couldremember which driver served a given device node. That way, it wouldn'tneed to search for a driver anymore, but could just pick it up.Practically, the device manager should cache the type (and/orvendor/device) information of a node, and assign one or more drivers(via module name) to this information. It should also remember negativeoutcome, that is if there is no driver supporting the hardware.This way, only the first boot would require an actual search fordrivers, as subsequent boots would reuse the type-driver assignments. Ifa new driver is installed, the cached assignments would need to beupdated immediately. If a driver has been installed outside of therunning system, the device manager might want to create a hash permodule directory to see if anything changed to flush the cache.Alternatively or additionally, the boot loader could have a menu causingthe cache to be ignored.It would be nice to find a way for generic and simple busses to reducethe amount of searching necessary for them. One way would be to rememberwhich driver supports which bus - but this information is currently onlyaccessible derived from what the driver does, and is therefore notreliable or complete. A separately exported information would benecessary for this.Also, when looking for a generic or simple bus driver, actualdirectories could be omitted; currently, driver search is alwaysrecursive, as that's how the module mechanism is working. Eventually, wemight want to extend the open_module_list_etc() call a bit more toaccomplish that.