This chapter describes both the common and family-specific DKI services available within the Driver Framework. Understanding the functions in this overview will make the tasks shown in the next chapters (writing device drivers and writing bus drivers) much clearer.
Refer to section 9DKI of the man pages for a complete description of each of the commands included in this chapter.
This section describes services that are common to all processor families.
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:
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.
There are two special cases in which a driver must use DKI thread services to ensure synchronization:
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:
synchronously invokes a routine in the context of the DKI thread.
asynchronously invokes a routine in the context of the DKI thread.
The driver and device registry mechanisms, described briefly in the first chapter, are explained in more detail below.
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.
In the static case, the device tree is populated by the system booter.
For instance, the system booter may include a pre-defined sequence of device tree function calls. Another possibility for the system booter is to build the device tree from a hardware description provided by firmware.
In the dynamic case, the device tree is populated at system initialization time using an enumeration/probing mechanism. The device tree is populated by propagating from parent to children.
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:
returns the root device node
returns the first child node
returns the next "sibling" device node
returns the parent device node
returns the pathname length of the given device node
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 '???'.
allocates a new device node object
releases all memory and properties attached to the node
adds a child node to the specified parent
detaches a node from its parent
return the first property of a node
return the next property of a node
returns the property value length (in bytes)
returns a pointer to the first byte of the property value
returns a pointer to the property name
allocates a new device property object
releases the memory allocated by the property object
attaches a property object to a device node
detaches a property object from a device node
Device tree high-level services
adds a named device node to the tree
looks for a named node in the list of children of a given device node
allocates a new property, sets its value and attaches it to a given device node
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:
adds a driver entry to the driver registry
returns the id of the first driver entity
returns the id of the next driver entity
releases the lock of a driver
returns a pointer to the driver entry structure (using an id)
returns a pointer to the driver actor capability (using an id)
removes a driver entry from the 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:
allocates a device registry entry for a given device driver instance
adds a given entry to the device registry
removes an entry from the device registry
notifies the device registry module that a given event has occurred
releases a previously allocated device registry entry
searches a device entry in the registry, matching given device class and logical unit.
returns the device entry associated to a client identifier returned by svDeviceLookup
releases the lock on a looked-up device entry
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.
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:
allocates a specified amount of memory from the supervisor address space
frees memory previously allocated with svMemAlloc
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:
allocates contiguous physical memory
frees memory allocated with svPhysAlloc
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.
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:
sets a timeout request
cancels a timeout request
returns the smallest possible difference between two distinct "time" values
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:
waits for (at least) the given number of micro-seconds
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:
establishes connection between a child device driver and the DKI
releases the DKI/driver connection
starts the propagation of an event to the device driver hierarchy
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:
masks all maskable interrupts at processor level, and increments imsIntrMaskCount_f kernel variable
unmasks interrupts at processor level (if calls are not nested)
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.
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
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.
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:
_16 for 16-bit data
_32 for 32-bit data
_64 for 64-bit data
Specific input/output related services are described below. See the man pages for complete descriptions of listed commands:
loads data from a given address and returns the corresponding byte swapped value. The addr argument specifies the address to read from
stores into a given address the value byte swapped
swap in place the bytes of the data stored at a given address
Processor family specific DKI services are available only on a given processor family, and should be used only by the drivers servicing devices directly connected to the local CPU bus. Drivers using these DKI services become "processor specific" and therefore can not be considered common.
Note that the availability of services is different between processor families, and not all services are listed here. An overview of commonly available services is provided below. For an accurate indication of what services are provided for each processor family see the "Processor-Specific DKI Services" section in Appendix A (or the appropriate 9DKI man pages).
Depending on the processor family architecture, the family-specific DKIs may offer the following services:
All processor families offer DKI services to manage interrupts. These services allow the driver to perform the following tasks:
attach a handler to a given interrupt
mask an interrupt attached to a handler
unmask an interrupt attached to a handler
detach an interrupt handler
Allows the host bus to manage memory coherence for DMA purposes by flushing and/or invalidating caches
Provides interface to processor specific I/O instructions, managing:
I/O ports
Synchronization of memory mapped I/O operations
allows device drivers to map physical space to virtual memory space. These services are used mainly by the host bus driver to map bus I/O space or DMA memory.