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 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).
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:
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:
These are applications whose behavior depends on the system power mode, or the power mode of specific drivers or software subsystems.
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.
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:
The client library provides the basic services that allow the two-way communication between drivers and the power manager:
The client library provides the basic services for such applications to:
The client library provides the basic services for such applications to:
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.
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:
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 |
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.
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.
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 |
This section describes some examples to demonstrate how you use the client library APIs for:
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. |
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:
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:
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:
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.
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... }
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. |
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.
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:
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:
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... }
void my_handler(int signo, siginfo_t *siginfo, void *data) { struct object *object = siginfo->si_value.sival_ptr; do stuff using object }
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.
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.
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);
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. |
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"); }
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... } }
The client must specify O_RDWR to pm_attach() to be able to use pm_setmode(). |
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... }
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.
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:
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); } }
#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);
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. |
struct my_prop value; pm_get_property(hdl, MY_PROP_ID, &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);