ChorusOS 5.0 Board Support Package Developer's Guide

Driver Framework Mechanisms and Principles

This section describes the mechansims that are implemented to enforce well-defined behavior regarding driver component initialization, dynamic loading/unloading and bus event management.

As mentioned above, the ChorusOS operating system microkernel implements mechanisms to enforce a well-defined behavior regarding driver component initialization, dynamic loading/unloading, and bus events management. An overview of these mechanisms is contained in the following sections, though more detailed information is included in the chapters: Chapter 9, Driver Kernel Interface Overview, Chapter 10, Writing a New Device Driver and Chapter 11, Writing a New Bus Driver.

Driver Registration

The driver framework defines three device and driver registration entities which are managed through the DKI interface:

Driver Initialization

The microkernel initialization process is split into two steps. The first step is carried out inside the critical section with interrupts masked at the CPU level. The second step is executed outside the critical section with interrupts enabled at CPU level.

Therefore, the BSP drivers may be split into two groups: the first group of drivers that are initialized within the critical section, and the second group of drivers that are initialized later with interrupts enabled at CPU level.

Such an initialization process enables the majority of the BSP drivers to be moved from the critical section. Basically, only the host bus driver (which typically manages the master interrupt controller) should be initialized within the critical section in order to allow interrupts to be enabled at CPU level, prior to the second initialization step.

This is extremely important for a platform where the control of a critical device should be taken by a driver as soon as possible. For example, this is the case on a platform where a watchdog timer is enabled only once by the boot code, and the microkernel watchdog driver has to be started before the watchdog timer interval expires. Note that the driver of such a watchdog timer device typically needs to have microkernel timeout services available in order to reset (pat) the watchdog device periodically.

In the ChorusOS operating system, the microkernel initialization process goes through the following steps:

  1. Microkernel module initialization (including DATE and TICK)

  2. Critical device initialization

  3. Interrupt enabling at CPU level

  4. Normal device initialization

A BSP developer should split all platform devices into two groups. The first group of (critical) devices is initialized in step 2, that is, inside the critical section with interrupts masked at CPU level. The second group of (normal) devices is initialized in step 4, that is, outside of the critical section with interrupts enabled at CPU level. Note that the host bus bridge should be part of the critical group in order to disable interrupts at the bus interrupt controller level. Otherwise, the system behavior may be unpredictable once interrupts are enabled at CPU level (spurious interrupts may occur).

The PROP_INIT_LEVEL property is used to specify whether a device is critical or normal, and therefore, whether it should be initialized at step 2 or 4. The PROP_INIT_LEVEL property value is an integer value that specifies the device initialization level allowed. Attached to a device node, this property specifies at which step the associated device should be initialized.


Note -

The TICK and DATE modules are initialized at step 1. These modules use the svDeviceNewNotify() DKI service in order to be notified when a new device is registered in the device registry. Therefore, the DATE and TICK modules are initialized prior to the device drivers initialization. However, they only become operational when the associated device drivers are started (registered) and a connection is established between the module and the underlying device driver.


At step 2, each driver actor's main() function is invoked sequentially by the microkernel initialization thread. The driver's main() function should perform a self-registration of the driver component within the system using the DKI interface.

When registering, the driver exports its properties to the system. It exports the following:

Once the driver component is self-registered, future management of the driver is controlled by its parent bus/nexus driver, using the registered driver properties. The four possible entry points that a driver component may register are:

Once all of the driver main() functions are invoked, the microkernel initiates the device initialization process. This can be seen as the microkernel implementing a local bus driver (bound to the device tree root node) for a DKI/FDKI bus class.

The initialization process starts from driver components servicing bus or device controllers directly connected to the CPU local bus; the driver registry is searched to find out the appropriate drivers and to call their registered entry points. Typically, the drv_probe() registered function is called for all driver components requiring a DKI/FDKI parent bus class. After probing, the drv_bind function is called for all driver components requiring a DKI/FDKI parent bus class. Finally, after binding, the initialize registered function is called for all driver components requiring a DKI/FDKI parent bus class, that are bound to a child of the device tree root node (nodes representing a bus or a device controller directly connected to the CPU local bus).


Note -

All the driver entry point routines drv_probe(), drv_bind() and drv_init() are optional.


The drv_probe() routine detects device(s) residing on the bus and creates corresponding device nodes in the device tree. The drv_bind() routine allows drivers to perform a driver-to-device binding. The driver examines the properties attached to the device node in order to determine the type of device and to check whether the device may be serviced by the driver. If the check is positive, the driver attaches a driver property to the device node. The name of the driver node is "driver" and it has a string type value, specifying its name. The initialization process is propagated by the drv_init function of the bus/nexus drivers started by the microkernel.

In addition, when a driver instance is activated by a parent bus/nexus driver (through its registered drv_init() function), it establishes a connection to its parent bus driver (typically through an open service of the bus API) specifying a callback event handler and a load handler. The parent bus/nexus driver uses the call-back event handler mechanism to propagate the bus events to the connected child driver instances. These events are typically bus-class specific, but are usually used to shut down child driver instances. The load handler is used (together with the unload entry point) to manage dynamic loading/unloading of the driver components.