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 gives you the resources to implement a system-wide power management policy. As already mentioned in the Power Management Architecture chapter, QNX Neutrino's power manager framework has two important parts: the server and the client. This chapter describes the server for building the power manager, including a rich set of server library routines (or APIs) that help you build the power manager itself.
The server provides the following sets of services for building a product-specific power manager:
The server library provides a number of APIs broken down into the following functional groups:
Please refer to the API Reference chapter for more information.
Power manager nodes represent the power managed entities in a system. A node is a (power manager) library specific concept/data structure that contains the following:
Not all nodes have a driver process responsible for managing the power mode. These nodes are not power managed. |
Nodes are created in a number of ways:
The organization and meaning of the namespace is entirely defined by the
product-specific power manager policy.
This implies that some sort of cooperation is needed between device drivers and the power manager in order to agree on the naming conventions that allow drivers to determine and attach to the relevant nodes. |
The power manager implements a hierarchical namespace that represents the power managed entities. The namespace is built using two kinds of nodes:
The directory nodes are also used for configuration or organizational purposes. In this case, there is no power management for the node itself, and it simply provides a naming context for other nodes.
The leaf nodes are also used for control purposes without having an associated driver. In this case, power manager properties are associated with the node. Manipulation of these properties by clients can be used to control aspects of the power manager behavior or policy.
The server provides the basic support for triggering and monitoring changes to the power mode on individual nodes.
The product-specific policy can change the power mode of a node. This results in a request being made to the driver responsible for the node's power mode. This request is asynchronous, and the power mode state is updated when the driver completes the request and confirms its new power mode. The library delivers notification of changes to the power mode state to registered clients.
The product-specific policy is informed when the driver changes the power mode. This is in response to a request initiated by the power manager or from the driver itself independent of the power manager policy.
The library implements control of individual node only; it doesn't manage
the sequencing of power mode changes for groups of nodes.
This is the responsibility of the product-specific policy code. For example, when powering down a subsystem, the policy code is responsible for powering down individual devices within the subsystem before powering down the subsystem itself. |
The server provides support for product-specific policy implementation in order to control many aspects of the life cycle for a power manager node. This is achieved by a set of policy functions that are invoked when:
This allows the policy to specify whether the node is unlinked, or should remain in the namespace.
The power manager you implement in accord with a product-specific must:
Once the server interface is started, the power manager becomes event driven, and the policy receives control via the policy-specific functions. These policy functions use some policy specific data to manage the overall system power mode state.
It is possible to use only default policy functions to provide a very basic power manager. This power manager simply acts as a conduit between device drivers and other clients using power manager interfaces, such that:
This simple policy doesn't handle power dependencies between devices. Control and management of these devices must be performed by client applications using the power manager interface.
To create a functional power manager using the default policy, you may use the following code:
#include <sys/pmm.h> #include <sys/procmgr.h> int main(int argc, char *argv[]) { // initialize and start the power manager interface if (pmm_init(0) != EOK) { exit(1); } pmm_start(0, 0); // become a daemon procmgr_daemon(EXIT_SUCCESS, 0); pthread_detach(pthread_self()); pthread_exit(0); return 0; }
The server library provides the following interfaces to handle power manager clients:
Use this function: | To: |
---|---|
pmm_init() | Initialize power manager resource manager interface |
pmm_start() | Start a thread pool to handle client requests |
The resource manger interface implements a subset of the standard POSIX iofunc layer calls. It also implements power manager specific operations via the custom message structures described in the client routines in the API Reference chapter:
The iofunc_read_default() function is used for nondirectory nodes.
You can manage the namespace using any combination of the following:
Access control and ownership of power manager nodes follow the standard filesystem semantics:
Each power managed object is represented by a _pmm_node_t structure. This is an extension of the iofunc_attr_t that adds power manager-specific data, as follows:
This structure is opaque to the product-specific policy code, and can be manipulated only via:
The public interface for manipulating power manager nodes consists of the following functions:
Use this function: | To: |
---|---|
pmm_node_create() | Create a new node |
pmm_node_lookup() | Lookup a node by name |
pmm_node_unlink() | Unlink a node |
pmm_mode_get() | Get the current power mode of a node |
pmm_mode_list() | Get the list of modes supported by a node |
pmm_mode_change() | Change the power mode of a node |
pmm_mode_wait() | Wait until a mode change has completed |
pmm_property_add() | Add a new property to a node |
pmm_property_set() | Set the value of a property |
pmm_property_get() | Get the current value of a property |
pmm_property_list() | List all properties associated with a node |
In order to receive control during client requests on a particular node, the server library provides hooks for:
This support is provided by the following function and structure:
Callbacks are available as follows:
Call this callback: | When a: |
---|---|
create() | New node is created |
destroy() | Node is destroyed |
unlink() | Node to be unlinked from the namespace |
attach() | Client attaches to a power manager node |
detach() | Client detaches from power manager node |
mode_init() | Driver attaches to a power manager node and supplies the initial power modes |
mode_request() | Request is made to change the power mode for a node |
mode_confirm() | Driver confirms a mode change has been completed |
property_add() | Client adds a new property to a node |
property_set() | Client modifies a property value |
The following table lists the state machine datatype and APIs:
Datatype and APIs | Purpose |
---|---|
pmm_state_t | Represent the state |
pmm_state_init() | Create a state machine |
pmm_state_machine() | Execute the state machine from the calling thread |
pmm_state_trigger() | Trigger the state machine thread to re-evaluate the current state |
pmm_state_check() | Evaluate the current state |
pmm_state_change() | Change state |
This section describes how to construct state machines that are used to describe and manage the system's power mode state.
Each power mode state is represented by a pmm_state_t structure that specifies:
The pmm_state_init() creates a new state machine using an array of pmm_state_t structures that describe the set of allowable states.
This is used to create multiple state machines that operate independently, or interact together to implement nested state machines.
The state machine APIs support the following modes of operation:
The basic steps involved are:
pmm_state_t my_states[NSTATES] = { : }; struct my_data *my_data;
hdl = pmm_state_init(NSTATES, my_states, my_data, MY_INITIAL_STATE);
if (pmm_state_machine(hdl) != EOK) { error handling... }
Because this call never returns, it must be the last initialization action that this thread performs.
Once the state machine thread is running, other threads can trigger state changes by calling pmm_state_trigger(hdl). This will cause the state machine thread to re-evaluate the current state and perform a state transition to a new state, if necessary.
The basic steps involved are:
pmm_state_t my_states[NSTATES] = { : }; struct my_data *my_data;
hdl = pmm_state_init(NSTATES, my_states, my_data, MY_INITIAL_STATE);
Once the state machine is initialized, any thread in the power manager can re-evaluate and change the system state as follows:
pmm_state_check(hdl, &cur_state, &new_state); if (new_state != cur_state) pmm_state_change(hdl, new_state);
Alternatively, a thread may unconditionally change the state using (?)
The implementation ensures mutual exclusion between these two functions so that:
You can build nested state machines where sub-state machines can affect state transitions on higher-level state machines:
For example, the power manager could implement separate state machines for each subsystem that implements a set of services, and the state of these individual subsystems can be used to control the system's power mode state.