[Previous] [Contents] [Next]

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

Power Manager Clients

This chapter contains the following topics:

Overview

This chapter describes how to use the client library reference. It describes QNX Neutrino's clients for power management support, as well as the APIs (including the datatypes) available in the client library. You need these functions to interact with the power management policy in order to control the use of power for your hardware peripherals. Several client examples are provided to show how you use and apply these functions for your implementation.

At the center of QNX Neutrino's power management architecture, the most important component is the power manager, a server, that implements the system-wide power management policy. The power manager interacts with the clients, such as power managed devices (or services), to determine device power capabilities and to manipulate device power states, based on the status of the power source (e.g. battery).


pm_client


The clients of the power management framework interacting with the power manager or server.


The diagram above shows the power manager interacting with three clients, as described below:

Drivers for power managed devices or services

These are device drivers that manage the power consumption of the hardware devices they control. These device drivers have the most intimate knowledge of the device's usage pattern and power consumption characteristics. Therefore, they determine when the devices should change power states or which states are appropriate for current usage.

Power managed services are some arbitrary software components whose behavior can be modeled via power modes.

You typically start a device driver in one of two ways:

  1. Explicitly by the power manager. This would be most suitable for an embedded system that has a static hardware configuration, where the power manager acts as the main configuration process.
  2. Dynamically, based on user requests or dynamic hardware detection. This would be most suitable for systems with dynamic hardware configurations (e.g. a desktop system) or systems with buses that support dynamically attached devices, such as USB.

Power sensitive applications

These are applications whose behavior depends on the system power mode, or the power mode of specific drivers or software subsystems.

System monitoring applications

These are applications that monitor specific system parameters that are relevant to the system-wide power management policy. For example, monitoring battery levels or other data that may require changing the system power mode.

Client API library

This section describes the client APIs from a functional point of view. The client API library provides interactions between the server and the clients. Specifically, it provides the control and application interfaces for the clients to:

API for drivers for power managed devices or services

The client library provides the basic services that allow the two-way communication between drivers and the power manager:

API for power sensitive applications

The client library provides the basic services for such applications to:

API for system monitoring applications

The client library provides the basic services for such applications to:

A summary of APIs and datatypes

Client library interfaces are categorized into the following functional groups. You should use these interfaces in conjunction with datatypes to build a system-wide power managed system. Consult the API Reference in this guide for full details about these APIs and datatypes.

Power manager namespace APIs

The client library provides the following interfaces for manipulating the power manager namespace:

Power manager namespace APIs: Purpose:
pm_create() Create a new object in the namespace
pm_unlink() Unlink an object from the namespace

The power manager manages a hierarchical namespace for power managed objects:

Power manager connection APIs

The client library provides the following interfaces for managing connections to the power manager objects:

Power manager connection APIs: Purpose:
pm_attach() Attach to a power manager object
pm_detach() Detach from a power manager object

Power mode APIs

The client library provides the following interfaces for manipulating power modes of the power manager objects:

Power mode APIs: Purpose:
pm_setmode() Change the power mode of an object
pm_getattr() Get an object's power mode attributes
pm_getmodes() Get the list of power modes supported by the object
pm_notify() Request notification when the object's power mode is changed

These APIs are called by both general purpose applications and device drivers. Each interface call uses a handle to an individual power manager object that was obtained from a pm_attach() call.

Device drivers are responsible for the implementation of the object's power mode, so they must obey a certain protocol to operate correctly with the power manager.

Property management APIs

The client library provides the following interfaces for managing object properties:

Property management APIs: Purpose:
pm_get_property() Get a property value
pm_set_property() Set a property value
pm_add_property() Add a new property
pm_properties() Get a list of supported properties

This property management mechanism provides a very basic interface for allowing arbitrary data or other attributes to be associated with a power manager object. These properties are opaque to the power manager itself. For example, a flag specifies an identifier that represents an arbitrary-sized data object with user-defined semantics. This lets you use a range of user-defined property identifiers that aren't managed or interpreted in any way by the power manager itself. Similarly, a callback is used to invoke the product-specific policy code to handle the property manipulation. This mechanism is intended to allow system monitoring applications to supply arbitrary product-specific data used by the product-specific policy code to manage events or conditions that require a change to the system power mode.

Datatypes

These data types are structures that contain all the power management related information, such as power modes, properties, or list of power modes.

Datatypes Description
pm_power_mode_t Power mode value
pm_power_attr_t Power mode attributes of a device or subsystem
pm_property_t Identifies a power manager property associated with a power manager object
pm_property_attr_t Specifies a property type and the size of the property data
pm_hdl_t Provides a client handle to a power manager object

Examples

This section describes some examples to demonstrate how you use the client library APIs for:


Note: This doesn't cover device drivers acting as clients of the power manager. This is described in detail in Implementing Power Management in a Device Driver in this guide.

Attaching to a power managed object

You may want to attach an object with the power manager namespace as follows:

pm_hdl_t  hdl;

// attach with read-only access to object (use O_RDWR to allow modify access)
hdl = pm_attach("some/power/manager/object", O_RDONLY);

if (!pm_valid_hdl(hdl)) {
   error...
}

The objects we refer belong to one of the following:

Receiving notification of power mode changes

This example shows you how a client receives notification of power mode changes. This could be used by a power-sensitive application to respond to changes to the power mode of specific devices or subsystems.

The client needs to obtain a connection to the relevant power manager object, with O_RDONLY access is required to receive notifications.

Once the connection is established, the client needs to:

Notification via a pulse

Since a pulse can be delivered to its existing channel, this mechanism is suitable for a client that already implements some form of message handling. Also, there are some advantages of using pulses:

The following code snippets demonstrate the basic steps required:

  1. Define a data structure that represents the power manager object:
    struct object {
       pm_hdl_t  hdl;
      ...
    };

    The primary purpose of the above code is to hold the pm_hdl_t to allow the event handler to communicate with the power manager.

  2. Create the sigevent:
    int  chid;
    int  coid;
    struct sigevent ev;
    
    if ((chid = ChannelCreate(0)) == -1) {
       error...
    }
    if ((coid = ConnectAttach(0, 0, chid, _NTO_SIDE_CHANNEL, 0)) == -1) {
       error...
    }
    
    SIGEV_PULSE_INIT(&ev, coid, prio, pulse_code, object);
    
    if (pm_notify(object->hdl, PM_CHANGE_START|PM_CHANGE_DONE, &ev) == -1) {
       error...
    }

    Note: If the application is already using a dispatch_* interface for receiving messages, it can use pulse_attach() to register the pulse with the dispatch loop instead of explicitly creating its own channel.

  3. Handle the pulse:
         
    while (1) {
       rcvid = MsgReceive(chid, buffer, sizeof buffer, 0);
       if (rcvid == 0) {
          struct _pulse *pulse = buffer;
          struct object *object = pulse->value.sival_ptr;
    
          do stuff with object
       }
    }

    In the event the application explicitly creates its own channel, it needs to receive the pulses from that channel. If the application uses pulse_attach() to register the pulse with its existing dispatch handling, the function specified in pulse_attach() will be called directly.

Notification via a signal

This mechanism is suitable for a client that doesn't want to create a channel specifically to handle the notification. There are a number of issues that need to be taken into account:

The following code snippets demonstrate the basic steps required:

  1. Define a data structure that represents the power manager object:
    struct object {
       pm_hdl_t  hdl;
     ...
    };

    The primary purpose of the above code is to hold the pm_hdl_t structure to allow the event handler to communicate with the power manager:

  2. Create the sigevent:
    struct sigaction sa;
    struct sigevent  ev;
    
    sa.sa_sigaction = my_handler;
    sa.sa_flags = 0;
    sigemptyset(&sa.sa_mask);
    
    if (sigaction(signo, &sa, 0) == -1) {
       error...
    }
    
    SIGEV_SIGNAL_CODE_INIT(&ev, signo, object, SI_MINAVAIL);
    
    if (pm_notify(object->hdl, PM_CHANGE_START|PM_CHANGE_DONE, &ev) == -1) {
       error...
    }
  3. Handle the notification:
    void
    my_handler(int signo, siginfo_t *siginfo, void *data)
    {
       struct object *object = siginfo->si_value.sival_ptr;
    
       do stuff using object
    }

Notification of device driver attachment

This form of notification is useful in a system where device drivers are started and stopped dynamically. For example, the power manager populates its namespace with objects that represent the various services that are required. Individual device drivers then dynamically attach to the appropriate power manager objects when they are started.

Clients are notified of these driver attachments by using the PM_DRIVER_ATTACH flag to pm_notify(). These notifications are used to enable specific application features that rely on the presence of that device.

Similarly, the PM_DRIVER_ATTACH flag is used by the application to notify when the driver detaches from the power manager object. For example, these notifications are used to disable specific application features that rely on the device being attached.

Manipulating power modes

The following code snippets demonstrate how the power mode can be manipulated, assuming that a connection to the relevant power manager object has already been established.

  1. Query the power mode:
    pm_power_attr_t attr;
    
    if (pm_getattr(hdl, &attr) == -1) {
       error...
    }
    
    printf("Current mode is %d\\n", attr.cur_mode);
    if (attr.new_mode != attr.cur_mode)
       printf("Device is changing modes to %d\\n", attr.new_mode);
    if (attr.nxt_mode != attr.new_mode)
       printf("Pending mode change to %d\\n", attr.nxt_mode);

    Note: A power mode of PM_MODE_UNKNOWN indicates that there is no driver associated with the power manager. For example, this is the case when the driver has not started or has detached from the power manager.

  2. Query the power modes supported by the device:
    int  i;
    int  nmodes;
    pm_power_mode_t *modes;
    
    // find out how many modes the device supports
    nmodes = pm_getmodes(hdl, 0, 0);
    
    if (nmodes > 0) {
       // allocate an array to hold the list of modes
       modes = malloc(nmodes * sizeof(pm_power_mode_t));
    
       // fill the array with the modes supported by the driver
       pm_get_modes(fd, modes, nmodes);
    
       printf("Device supports %d modes: [");
       for (i = 0; i < nmodes; i++) {
          printf(" %d", modes[i]);
       }
       printf(" ]\\n");
    }
  3. Change the power mode:
    int status;
    
    // attempt to change mode, subject to the power manager policy
    if (pm_setmode(hdl, mode, 0) == -1) {
       if (errno == EACCES) {
          // force the power mode to change
          pm_setmode(hdl, mode, PM_MODE_FORCE);
       } else {
          error...
       }
    }

Note: The client must specify O_RDWR to pm_attach() to be able to use pm_setmode().

Configuring the power manager namespace

There are applications external to the power manager that can manipulate the power manager namespace. For example, enumerators or other similar system-configuration utilities could populate the power manager namespace with objects representing the devices in the system.

The following example creates this hierarchy in the namespace:

This example creates the devices with access permissions granted to everyone. You should use a real configuration utility that would specify the appropriate permission in place of 0777 and 0666.

if (pm_create("bus1", PM_NODE_NEXUS | 0777) == -1) {
  error...
}
if (pm_create("bus1/dev1", 0666) == -1) {
 error...
}
if (pm_create("bus1/bridge1", PM_NODE_NEXUS | 0777) == -1) {
 error...
}
if (pm_create("bus1/bridge1/dev1", 0666) == -1) {
 error...
}

Manipulating power manager properties

The power manager supports the association of arbitrary properties with individual power manager objects. The properties are arbitrary-sized data, named by an integer property identifier.

Applications add new properties to a power manager object, or query/modify the value of an existing property. This is used to influence the policy-specific power management policy. For example, a monitoring application may update data that causes the power manager policy to re-evaluate the system's power mode state.


Note: Currently only user-defined, policy-specific properties are supported.

The semantics of manipulating properties must be agreed upon between product-specific applications and the power manager policy code. Later versions of the framework may define generic, policy-independent properties that are used by application or policy.

Assuming a connection is already established to a relevant power manager object, the following code snippets demonstrate how properties are manipulated:

Listing all properties

pm_property_attr_t  *list;
int                 nprop;

// find out how many properties are associated with object
nprop = pm_properties(hdl, 0, 0);

if (nprop > 0) {
    // allocate an array to hold the list of properties
    list = malloc(nprop * sizeof(*list));

    // read list of properties
    pm_properties(hdl, list, nprop);

    printf("Object has %d properties:\\n", nprop);
    for (i = 0; i < nprop; i++) {
        printf("\tid=0x%08x size=%d bytes\\n", list[i].id, list[i].size);
    }
}

adding a new property

#define MY_PROP_ID  (PM_PROPERTY_USER | 1)   // policy specific id value
struct my_prop      value;     // property value

// initialise the value data members
   :
pm_add_property(hdl, MY_PROP_ID, &value);

Note: property identifiers with CPM_PROPERTY_USER set are policy-specific and their semantics are defined by the product specific policy code.

This means that the identifier and format of the property value must be agreed between the power manager policy and applications that manipulate these properties.


Obtaining a specific property value

struct my_prop  value;

pm_get_property(hdl, MY_PROP_ID, &value);

Changing a property value

struct my_prop  value;

// get current property value
pm_get_property(hdl, MY_PROP_ID, &value);

// modify data members in value
   :
pm_set_property(hdl, MY_PROP_ID, &value);

[Previous] [Contents] [Next]