[Previous] [Contents] [Next]

Caution: This version of this document is no longer maintained. For the latest documentation, see http://www.qnx.com/developers/docs.

Implementing Power Management in a Device Driver

This chapter contains the following topics:

Introduction

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 driver support

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.


Note: 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.

How to modify a device driver

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:

  1. During initialization, device drivers need to create a pmd_attr_t structure and register with the power manager.
  2. Device drivers need to interact with the power manager to implement power mode changes.
  3. Device drivers need to handle pending client requests during power mode changes.

Driver initialization

At the time of initialization, the driver needs to perform the following actions for each power managed device it manages:

  1. Create and initialize a pmd_attr_t structure for the device.
  2. Attach the pmd_attr_t structure to the device's iofunc_mount_t structure to support the _IO_POWER interface.
  3. Create a sigevent -- which the power manager uses to trigger the driver to change power modes.
  4. Register the device with the power manager using pmd_attach().

Initializing pmd_attr_t

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:

my_modes[]
Contains all the power modes supported by the device. The driver needs to specify the initial power mode of the device. Both the initial mode and the set of supported modes are passed to the power manager to initialize the power managed object representing the device.
my_setpower()
Driver-specific function used to change power mode.
my_setpower()
Called with a driver-specific data pointer. This is typically the driver data structure that is used to control the device.

Supporting the _IO_POWER interface

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;

Creating an event for power manager communication

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);

Registering with the power manager

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:

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.


Note: 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.

Power manager interaction

The driver interacts with the power manager in two ways:

Handling driver client requests

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:

  1. Complete all pending client requests.
  2. Arrange for new client requests to be blocked until the power mode is restored.
    Note: The driver must be able to respond to power mode change requests via:
    • the event registered by pmd_attach()
    • the pmd_power() support for _IO_POWER initiated changes.

Once the power mode is restored to a level such that the hardware becomes functional, any blocked client requests can be unblocked and processed.

A summary of power manager driver APIs

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.


Note: 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.


[Previous] [Contents] [Next]