This version of this document is no longer maintained. For the latest documentation, see http://www.qnx.com/developers/docs. |
This chapter contains the following topics:
This chapter describes the client library support for device drivers or for other power managed subsystems. The contents of this chapter help you build a device driver. For simplicity, we use the term device driver, although in principle, these clients represent any arbitrary service whose functions are modeled using the notion of power modes.
Device drivers act as clients of the power manager. They register with the power manager as named objects, and are responsible for implementing power mode changes. Using low-level APIs, the power manager communicates with the driver (via the named objects) to coordinate power mode changes in accord with the system power management policy.
From the power manager's perspective, there are two different kinds of power managed objects:
In order to prepare a device driver for power management support, modifications are needed to handle the interaction between the device drivers and the power manager.
The device driver kit or DDK implements interfaces or mechanisms for different classes of drivers. These interfaces encapsulate low-level APIs in such a way that fits in with the DDK framework. For details, see the appropriate DDK documentation. |
You need to take several steps in order to modify a device driver for power management support. This section provides an overview of these steps:
At the time of initialization, the driver needs to perform the following actions for each power managed device it manages:
The driver needs to allocate and initialize a pmd_attr_t structure that maintains the power mode attributes for the device:
pmd_attr_t *my_pmd; pm_power_mode_t my_modes[] = { ... }; my_pmd = malloc(sizeof *my_pmd); pmd_attr_init(my_pmd); pmd_attr_setmodes(my_pmd, initial_mode, my_modes, sizeof(my_modes) sizeof(pm_power_mode_t)); pmd_attr_setpower(my_pmd, my_setpower, data ptr passed to my_setpof.
This only shows an outline of the calls the driver needs to use:
The driver needs to attach the pmd_attr_t structure to the device's iofunc_mount_t structure to support the _IO_POWER interface.
This enables the iofunc layer to invoke pmd_power() to handle the necessary power manager interactions and call the driver's setpower() function to change the device power mode:
iofunc_attr_t my_attr; iofunc_mount_t my_mount; initialise my_attr ... intiialise my_mount ... my_attr.mount = &my_mount; my_mount.power = my_pmd;
A driver has a dispatch structure that it uses for receiving its messages. The following example attaches a pulse handler to the dispatch structure (i.e. my_dpp):
struct sigevent my_event; int code; int coid; code = pulse_attach(my_dpp, MSG_FLAG_ALLOC_PULSE, 0, my_pulse_handler, 0); if (code == -1) { error ... } coid = message_connect(my_dpp, MSG_FLAG_SIDE_CHANNEL); if (coid == -1) { error ... } SIGEV_PULSE_INIT(&my_event, coid, prio, code, my_pmd);
The driver needs to call pmd_attach() to register with the power manager. This supplies the power manager with:
if (pmd_attach(name, my_pmd, &my_event, mode) == -1) { error ... }
The mode argument specifies:
These power manager objects behave like directories that contain the names of child objects. These are used for bus devices where the child objects represent individual devices attached to the bus, or a subsystem where the child objects represent individual devices or other power managed components within that subsystem.
The name argument specifies the name within the power manager namespace for the object. This takes the form of a pathname (with no leading slash) and it is the name visible under the root of the power manager namespace (/dev/pmm/).
If there is no existing object, a new power manager object is created to represent this device.
In the event that the pathname contains multiple components (i.e. a pathname separated by slashes), all intermediate components of that pathname must exist, because the power manager doesn't automatically create intermediate components. |
The driver interacts with the power manager in two ways:
These requests are sent using the event registered by pmd_attach().
This is typically a pulse, attached to the driver's dispatch handler using pulse_attach(). The pulse handler function uses pmd_handler() to perform the necessary interaction with the power manager and invoke the driver-specific power callout:
int my_pulse_handler(message_context_t *ctp, int code, unsigned flags, void *data) { pmd_handler(ctp->msg->pulse.value.si_ptr); return 0; }
We assume that the pulse is initialized with the value set to a pointer to pmd_attr_t structure for the device. |
The pmd_handler() function performs all the necessary interaction with the power manager, and calls the driver's setpower() function to change the device power mode.
This is handled automatically by pmd_power() if the driver attaches
its pmd_attr_t structure to the iofunc_mount_t
structure for the device.
The driver simply has its setpower() function called to change the device power mode. |
When the hardware is not fully functional, but the device changes to a low-power mode -- the driver needs to perform the following actions before changing the power mode:
The driver must be able to respond to power mode change requests via:
|
Once the power mode is restored to a level such that the hardware becomes functional, any blocked client requests can be unblocked and processed.
The following APIs describe the client library support for device drivers or for other power managed subsystems.
Function or structure | Purpose |
---|---|
pmd_attach() | Register device with power manager |
pmd_attr_init() | Initialize pmd_attr_t structure with default values |
pmd_attr_setmodes() | Initialize device power modes |
pmd_attr_setpower() | Initialize driver specific power mode handling |
pmd_attr_t | Power manager client information for device |
pmd_detach() | Detach device from the power manager |
pmd_handler() | Implement power manager initiated power mode changes |
pmd_power() | Implement support for iofunc_power initiated changes |
For details about these APIs, consult the API Reference chapter in this guide.
You can implement a power managed driver using only the pmd_* client API; these pmd_* functions build on the lower level APIs to provide more convenient APIs suitable for most drivers. |