ChorusOS 4.0 Device Driver Framework Guide

Common Driver Services

This section describes services that are common to all processor families.

Synchronization

Synchronization services (handling calls for the same services from different threads) are performed through the DKI thread. This thread is typically used for the shutdown and initialization of drivers, so it makes sense that synchronization services be handled within the DKI thread as well. The DKI thread is launched by the ChorusOS operating system microkernel at initialization time.

By ensuring this type of synchronization the DKI thread avoids using any other synchronization mechanism (locks) in the driver implementations.

The DKI thread acts as a synchronization mechanism in the following two cases:

Normal Case

In the normal case, all calls related to initialization/shutdown of the drivers are performed implicitly in the context of the DKI thread. This means that drivers need not be concerned with synchronization issues, because their routines are called directly from the DKI thread.

Special Cases

There are two special cases in which a driver must use DKI thread services to ensure synchronization:

  • Hot-pluggable device drivers

    With a hot-pluggable device driver, the initialization/shutdown process must be executed at runtime (not as part of the kernel/drivers initialization process). In this case, drivers use DKI thread services (described below) to provide synchronization with any running drivers.

  • Deferred driver initialization

    In some cases, a device driver may defer its initialization until it is opened. In this scheme, initialization/shutdown processes are executed at runtime (at time of open/close) and not as part of the kernel/driver's initialization process. Thus, this kind of driver uses thread services to synchronize with drivers that are already running.

    This is a way to resolve conflicts that arise when the same resources are used by multiple drivers. By using deferred driver initialization, drivers which share resources can be loaded at the same time (as long as they are not opened at the me time).

DKI thread related services are described below. See the man pages for complete descriptions of the commands listed:

svDkiThreadCall

synchronously invokes a routine in the context of the DKI thread.

svDkiThreadTrigger

asynchronously invokes a routine in the context of the DKI thread.

Device and Driver Registration

The driver and device registry mechanisms, described briefly in the first chapter, are explained in more detail below.

Device Tree

The device tree is a data structure providing a description of the hardware topology and device properties of a given device. The hardware topology is specified in terms of parent/child relationships. Device properties associated with each device node in the tree are device specific.

A device property is a name/value pair. The property name is a null terminated ASCII string. The property value is a stream of bytes specified by the length/address pair. Note that the property value format is property specific and has to be standardized between the property producer and its consumers.

For instance, among all device node properties, there are some related to the bus resources allocated to the device (for example, interrupt lines, I/O registers, DMA channels). These properties must be standardized to be understood by the bus driver, as well as any device drivers connected to the given bus.

The device tree data structure may be built either statically or dynamically.

Note that it is possible to combine both methods. In other words, an initial (incomplete) device tree may be provided by the ChorusOS operating system booter, which will later be completed dynamically using an enumeration/probing mechanism. In any case, the device tree structure can be modified (extended/truncated) dynamically at run time using hot-plug insertion/removal service (for example, when using PCMCIA cards).

Device Tree related services are described below. See the man pages for complete descriptions of the commands listed:

Device Tree Browsing

dtreeNodeRoot

returns the root device node

dtreeNodeChild

returns the first child node

dtreeNodePeer

returns the next "sibling" device node

dtreeNodeParent

returns the parent device node

dtreePathLeng

returns the pathname length of the given device node

dtreePathGet

returns, in buf, the absolute pathname of the given device node. The trailing part of the pathname is the name of the node and is read in a node property. If this property does not exist, the trailing part of the returned pathname is set to '???'.

Device Tree Modification

dtreeNodeAlloc

allocates a new device node object

dtreeNodeFree

releases all memory and properties attached to the node

dtreeNodeAttach

adds a child node to the specified parent

dtreeNodeDetach

detaches a node from its parent

Device Node Properties

dtreePropFind

return the first property of a node

dtreePropFindNext

return the next property of a node

dtreePropLength

returns the property value length (in bytes)

dtreePropValue

returns a pointer to the first byte of the property value

dtreePropName

returns a pointer to the property name

dtreePropAlloc

allocates a new device property object

dtreePropFree

releases the memory allocated by the property object

dtreePropAttach

attaches a property object to a device node

dtreePropDetach

detaches a property object from a device node

Device tree high-level services

dtreeNodeAdd

adds a named device node to the tree

dtreeNodeFind

looks for a named node in the list of children of a given device node

dtreePropAdd

allocates a new property, sets its value and attaches it to a given device node

Driver Registry

The driver registry module implements a data base of drivers registered in the ChorusOS operating system. The driver registry data base is populated by drivers which perform self-registration (using svDriverRegister) at driver initialization time.

The bus/nexus drivers perform a search in the driver registry data base to find a driver they are interested in. Typically, there are two kinds of searches used by the bus/nexus drivers. The first one is done at device enumeration/probing time when the bus/nexus driver is interested in all drivers matching the bus/nexus class (specified as the parent device class). The second is at device instance creation time, when the bus/nexus driver looks for a driver which must be started for a particular device node.

Driver Registry related services are described below. See the man pages for complete descriptions of the commands listed:

svDriverRegister

adds a driver entry to the driver registry

svDriverLookupFirst

returns the id of the first driver entity

svDriverLookupNext

returns the id of the next driver entity

svDriverRelease

releases the lock of a driver

svDriverEntry

returns a pointer to the driver entry structure (using an id)

svDriverCap

returns a pointer to the driver actor capability (using an id)

svDriverUnregister

removes a driver entry from the registry

Device Registry

The device registry microkernel module implements a data base of driver instances servicing devices currently supported by the system. The device registry data base is populated by drivers that perform self-registration (using svDeviceRegister) at device initialization time.

The device registry data base is accessed by driver clients in order to obtain a pointer to the driver instance servicing a given (logical) device.

The device registry API is described in detail in the man pages. Note that only the svDeviceLookup, svDeviceRelease and svDeviceEntry microkernel calls should be used by driver clients. The rest of API is dedicated to device drivers.

Device Registry related services are described below. See the man pages for complete descriptions of listed commands:

svDeviceAlloc

allocates a device registry entry for a given device driver instance

svDeviceRegister

adds a given entry to the device registry

svDeviceUnregister

removes an entry from the device registry

svDeviceEvent

notifies the device registry module that a given event has occurred

svDeviceFree

releases a previously allocated device registry entry

svDeviceLookup

searches a device entry in the registry, matching given device class and logical unit.

svDeviceEntry

returns the device entry associated to a client identifier returned by svDeviceLookup

svDeviceRelease

releases the lock on a looked-up device entry

General Purpose Memory Allocation

The microkernel provides general purpose memory management services for device drivers that need to dynamically allocate and free pieces of memory in supervisor memory space. As initialization schemes are normally dynamic, device drivers need to dynamically allocate and free small pieces of supervisor data.

Typically, a device driver needs to dynamically allocate data associated to each instance that it will register in the Device Registry at initialization time. Moreover, most of the DDI services called from base level by the driver clients lead to the dynamic allocation and freeing of certain linked list elements for internal management purposes.


Note -

The memory allocated using these services is anonymous. That means it is not associated to any actor context. For this reason, all the allocated memory must be freed by drivers before they terminate, as the kernel won't be able to do it at actor deletion time.


General purpose memory allocation related services are described below. See the man pages for complete descriptions of listed commands:

svMemAlloc

allocates a specified amount of memory from the supervisor address space

svMemFree

frees memory previously allocated with svMemAlloc

Special Purpose Physical Memory Allocation

Typically, different I/O buses impose different constraints on the memory used by their devices for Direct Memory Access (DMA), such as alignment, specific boundary crossing, maximum size, or specific location within the physical memory space.

To satisfy all constraints on physical memory imposed by the different I/O buses, (mainly for DMA purposes), the DKI provides an interface to allocate and free special purpose physical memory that satisfy the given constraints.

Special purpose memory allocation related services are described below. See the man pages for complete descriptions of listed commands:

svPhysAlloc

allocates contiguous physical memory

svPhysFree

frees memory allocated with svPhysAlloc

Timeouts

Device drivers may need timeout services to check whether there is activity on a device, or to verify that a started action will terminate before a given time limit is reached.


Note -

As these services should be implemented using drivers, they are not available and must not be used by drivers at initialization time.


Timeout related services are described below. See the man pages for complete descriptions of listed commands:

svTimeoutSet

sets a timeout request

svTimeoutCancel

cancels a timeout request

svTimeoutGetRes

returns the smallest possible difference between two distinct "time" values

Precise Busy Wait

Device drivers may use precise busy wait services to wait for a very short time. Note that busy wait means that the caller waits without releasing the CPU, as if executing a busy loop.

Note that these services may be used during the driver initialization process.

Precise busy wait related services are described below. See the man pages for complete descriptions of listed commands:

usecBusyWait

waits for (at least) the given number of micro-seconds

System Event Management

System event management services are provided by the microkernel to the lowest-layer drivers. They are intended to register event handlers for all the running drivers, and to start propagating events from the microkernel.

Typically a system reboot starts propagating a specific event from the microkernel to the lowest-layer drivers. Those drivers then recursively propagate the event to the upper layer drivers by calling their event handler (BusEventHandler) registered at open time).

System event management related services are described below. See the man pages for complete descriptions of listed commands:

svDkiOpen

establishes connection between a child device driver and the DKI

svDkiClose

releases the DKI/driver connection

svDkiEvent

starts the propagation of an event to the device driver hierarchy

Global Interrupts Masking

Some of the Interrupt Management Service (IMS) routines are included as part of the DKI to provide drivers with global interrupts masking services.

These services may be used by a driver to protect a critical section from interrupts, if needed.

Global interrupts masking related services are described below. See the man pages for complete descriptions of listed commands:

imsIntrMask_f

masks all maskable interrupts at processor level, and increments imsIntrMaskCount_f kernel variable

imsIntrUnmask_f

unmasks interrupts at processor level (if calls are not nested)

Thread Preemption Disabling

The DKI API provides a means for a driver to disable/enable the preemption of the current thread. These services may be useful for a driver to prevent the current thread being preempted while interrupts are masked at bus/device level. Note that these services are implemented as macros.

DISABLE_PREEMPT()

disables preemption of the currently executed thread. Basically, this macro increments a per-processor preemption mask count. When the preemption mask count is not zero, the ChorusOS scheduler is locked, such as when there is a preemption request, the scheduler just raises a pending preemption flag deferring the real thread preemption until the preemption mask count drops to zero

ENABLE_PREEMPT()

enables preemption of the currently executed thread which has been previously disabled by DISABLE_PREEMPT(). Basically, this macro decrements the preemption mask count and, if it drops to zero, checks whether the current thread should be preempted because the pending preemption flag is raised.

Note that, as DISABLE_PREEMPT()/ENABLE_PREEMPT() rely on the preemption mask count, a driver may issue nested calls to these services.

Specific Input/Output Services

The DKI provides specific I/O routines optimized to handle byte swapping. Typically, these services are intended to be used by a host bus driver to handle different byte ordering between the processor bus and the host bus.

Specific I/O services are defined below as sets of routines where the _xx suffix indicates the bit length of the data on which the services apply. This suffix may take one of the following values:

Specific input/output related services are described below. See the man pages for complete descriptions of listed commands:

loadSwap_xx

loads data from a given address and returns the corresponding byte swapped value. The addr argument specifies the address to read from

storeSwap_xx

stores into a given address the value byte swapped

swap_xx

swap in place the bytes of the data stored at a given address