#####ECOSGPLCOPYRIGHTBEGIN#### ## ------------------------------------------- ## This file is part of eCos, the Embedded Configurable Operating System. ## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. ## ## eCos is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free ## Software Foundation; either version 2 or (at your option) any later version. ## ## eCos is distributed in the hope that it will be useful, but WITHOUT ANY ## WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ## for more details. ## ## You should have received a copy of the GNU General Public License along ## with eCos; if not, write to the Free Software Foundation, Inc., ## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ## ## As a special exception, if other files instantiate templates or use macros ## or inline functions from this file, or you compile this file and link it ## with other works to produce a work based on this file, this file does not ## by itself cause the resulting work to be covered by the GNU General Public ## License. However the source code for this file must still be made available ## in accordance with section (3) of the GNU General Public License. ## ## This exception does not invalidate any other reasons why a work based on ## this file might be covered by the GNU General Public License. ## ## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. ## at http://sources.redhat.com/ecos/ecos-license/ ## ------------------------------------------- #####ECOSGPLCOPYRIGHTEND####

# eCos Power Management Support

Introduction

# Introduction

eCos support for Power Management

## Introduction

The eCos Power Management package provides a framework for incorporating power management facilities in an embedded application. However its functionality is deliberately limited.

1. The package does not contain any support for controlling the current power mode of any given processor, device or board. Instead it is the responsibility of the appropriate HAL or device driver package to implement such support, by implementing power controllers. The power management package groups these power controllers together and provides an interface for manipulating them.

2. The package does not contain any power management policy support. Specifically, including this package in an application does not by itself ever cause the system to go into low-power mode. Instead it is the responsibility of a separate policy module, provided by higher-level application code or by some other package, to decide when it would be appropriate to switch from one power mode to another. The power management package then provides the mechanisms for making it happen.

## Including Power Management

The power management package is never included automatically in an eCos configuration: it is not part of any target specification or of any template. Instead it must be added explicitly to a configuration if the intended application requires power management functionality. When using the command-line `ecosconfig` tool this can be achieved using a command such as:

    $ ecosconfig add power

The generic eCos user documentation should be consulted for more information on how to use the various tools. The functionality provided by the power management package is defined in the header file `cyg/power/power.h`. This header file can be used by both C and C++ code.

## Power Modes

There are four defined modes of operation:

active

  The system is fully operational, and power consumption is expected to be high.

idle

  There has been little or no activity for a short period of time. It is up to the policy module to determine what constitutes a short period of time, but typically it will be some tenths of a second or some small number of seconds. A possible action when entering idle mode is to reduce the system's clock speed, thus reducing the power drawn by the cpu.

  Note that typically this power mode is not entered automatically whenever the idle thread starts running. Instead it is entered when the policy module discovers that for a certain period of time the system has been spending most of its time in the idle thread. Theoretically it is possible to implement a policy module that would cause a switch to idle mode as soon as the idle thread starts running, but that could result in a great many power mode changes for no immediate benefit.

sleep

  The system has been idle for a significant period of time, perhaps some tens of seconds. It is desirable to shut down any hardware that is drawing a significant amount of power, for example a screen backlight.

off

  The system is powered down. Power consumption should be minimized. Some special action may be needed before the system comes back up, for example the user may need to press a specific button.

The exact transitions that will happen are decided by the policy module. One policy module might include transitions from active to idle, from idle to sleep, from sleep to off, and from any of idle, sleep or off directly back to active. Another policy module might only use the active and off states, bypassing the intermediate ones.

## Power Controllers

The power management package operates primarily on power controllers. The main functionality provided by a power controller is to switch the power mode for some part of the system, for example the lcd display or the cpu. A power controller consists primarily of a function which will be invoked to switch the power mode for the part of the overall system being controlled, plus some auxiliary data. A typical system will include a number of different power controllers:

1. Usually there will be one power controller `power_controller_cpu` associated with the processor or with the target platform, and provided by the corresponding HAL package. It is this controller which is responsible for switching off the system when entering the off mode, which makes it somewhat special: attempting to switch off the cpu before other devices like the lcd display does not make sense because the cpu would no longer be executing any instructions for the latter operation. Therefore this power controller has to be invoked last when switching to a lower-power mode, and similarly when switching back to a higher-power mode it will be invoked first.

   It should be noted that providing power management support is not a hard requirement when porting eCos to a new processor or platform, and many eCos ports predate the availability of power management support. Therefore for any given platform it is distinctly possible that `power_controller_cpu` is not yet provided, and if full power management functionality is desired then the appropriate HAL package would have to be extended first. System developers should examine the relevant HAL documentation and sources to determine what is actually available.

2. Some or all of the device drivers will supply their own power controllers, as part of the device driver package. It is not required that all device drivers provide power controllers. In some cases, especially for devices that are integrated with the processor, `power_controller_cpu` will take care of the integrated devices as a side effect. In other cases the hardware may not provide any functionality that allows power consumption to be controlled. For any given device driver it is also possible that no power controller exists either because it was not required when the driver was written, or because the driver predates the availability of power management. Again the relevant documentation and sources should be consulted for further information.

3. There may be power controllers which are not associated directly with any specific hardware. For example a TCP/IP stack could provide a power controller so that it gets informed when the system has been reactivated: by looking at the system clock it can determine for how long the system has been switched off; using this information it can then recover from expired dhcp leases, or even to shut down any stream connections that may have become invalid (although arguably the stack should have refused to go to off mode while there were open connections).

## Basic Operation

By default the Power Management package creates a thread during initialization. It is also possible for the package to be used without such a thread, for example in configurations which do not include a full kernel, and this alternative is described below. When a separate thread is used the stacksize and priority for this thread can be controlled by configuration options `CYGNUM_POWER_THREAD_STACKSIZE` and `CYGNUM_POWER_THREAD_PRIORITY`. Typically the thread will just wait on a semaphore internal to the package, and will do nothing until some other part of the system requests a change to the power mode.

At some point the policy module will decide that the system should move into a lower-power mode, for example from active to idle. This is achieved by calling the function `power_set_mode`, provided by the power management package and declared in `cyg/power/power.h`, with a single argument, `PowerMode_Idle`. This function manipulates some internal state and posts the semaphore, thus waking up the power management thread. Note that the function returns before the mode change has completed, and in fact depending on thread priorities this return may happen before any power controller has been invoked.

When the power management thread wakes up it examines the internal state to figure out what it should be doing. In this case it is supposed to change the global power mode, so it will iterate over all the power controllers requesting each one to switch to the idle mode. It is up to each power controller to handle this request appropriately. Optionally the thread will invoke a callback function after processing each power controller, so that higher-level code such as the policy module can more easily keep track of the actual state of each controller. Once the thread has iterated through all the power controllers it will again wait on the internal semaphore for the next request to arrive.

At present the power management thread always runs at a single priority, which defaults to a low priority. A possible future enhancement would be to support two separate priorities. When switching to a lower-powered mode the thread would run at a low priority as before, thus allowing other threads to run and get a chance to cancel this mode change. When switching to a higher-powered mode the thread would run at a high priority. This could be especially important when moving out of the off state: for example it would ensure that all device drivers get a chance to wake up before ordinary application threads get to run again and possibly attempt I/O operations.

Although usually calls to `power_set_mode` will come from just one place in the policy module, this is not a hard requirement. It is possible for multiple threads to call this function, with no need for any synchronization. If the power management thread is in the middle of performing a mode change and a new request comes in, the thread will detect this, abort the current operation, and start iterating through the power controllers again with the new mode. This check happens between every power controller invocation. Usefully this makes it possible for power controllers themselves to manipulate power modes: a power controller is invoked to change mode; for some reason it determines that the new mode is inappropriate; it calls `power_set_mode` to move the system back to another mode; when the power controller returns this event will be detected; the power management thread will abort the current mode change, and start the new one.

In addition to changing the power mode for the system as a whole, individual controllers can be manipulated using the function `power_set_controller_mode`. For example, while the system as a whole might be in active mode certain devices might be kept in sleep mode until they are explicitly activated. It is possible to mix concurrent calls to `power_set_mode` and `power_set_controller_mode`, and when a power controller is invoked it may use `power_set_controller_mode` to request further changes to its own or to another controller's mode as required.

There are some scenarios where the power management package should not use its own thread. One scenario is if the configuration is specifically for a single-threaded application such as RedBoot. Another scenario is if the policy module already involves a separate thread: it may make more sense if the various power management operations are synchronous with respect to the calling thread. The use of a separate thread inside the power management package is controlled by the configuration option `CYGPKG_POWER_THREAD`, which is active only if the kernel package is present and enabled by default.

If no separate power management thread is used then obviously the implementations of `power_set_mode` and `power_set_controller_mode` will be somewhat different: instead of waking up a separate thread to do the work, these functions will now manipulate the power controllers directly. If the system does still involve multiple threads then only one thread may call `power_set_mode` or `power_set_controller_mode` at a time: the power management package will not provide any synchronization, that must happen at a higher level. However when a power controller is invoked it can still call these functions as required.

Power Management Information

# Obtaining Power Management Information

finding out about the various power controllers in the system

    #include <cyg/power/power.h>

    extern PowerController __POWER__[], __POWER_END__;
    extern PowerController power_controller_cpu;
    extern cyg_handle_t    power_thread_handle;

        PowerMode power_get_mode

        PowerMode power_get_desired_mode

        PowerMode power_get_controller_mode

        PowerController* controller

        PowerMode power_get_controller_desired_mode

        PowerController* controller

        const char* power_get_controller_id

        PowerController* controller

## Accessing Power Controllers

All the power controllers in a system are held in a table, filled in at link-time. The symbols `__POWER__` and `__POWER_END` can be used to iterate through this table, for example:

    PowerController* controller;
    for (controller  = &(__POWER__[0]);
         controller != &(__POWER_END__);
         controller++) {

         …
    }

Each controller has an associated priority, controlling the order in which they appear in the table. Typically a software-only component such as a TCP/IP stack would use a small number for the priority, so that it appears near the start of the table, whereas a device driver would be nearer the back of the table. When switching to a lower-powered mode the power management package will iterate through this table from front to back, thus ensuring that for example the TCP/IP stack gets a chance to shut down before the underlying ethernet or other hardware that the stack depends on. Similarly when switching to a higher-powered mode the power management package will iterate through this table from back to front.

In most systems there will be one special controller, `power_controller_cpu`, which should be provided by one of the architectural, variant or platform HAL packages. This controller will always be the last entry in the table. It is responsible for the final power down operation when switching to off mode. Other packages such as device drivers may or may not declare variable identifiers for their power controllers, allowing those controllers to be accessed by name as well as by their entries in the global table.

## Global Power Modes

The function `power_get_mode` can be called at any time to determine the current power mode for the system as a whole. The return value will be one of `PowerMode_Active`, `PowerMode_Idle`, `PowerMode_Sleep` or `PowerMode_Off`. In normal circumstances it is unlikely that `PowerMode_Off` would be returned since that mode generally means that the cpu is no longer running.

The function `power_get_desired_mode` returns the power mode that the system should be running at. Most of the time this will be the same value as returned by `power_get_mode`. However a different value may be returned when in the middle of changing power modes. For example, if the current thread runs at a higher priority than the power management thread then the latter may have been pre-empted in the middle of a mode change: `power_get_mode` will return the mode the system was running at before the mode change started, and `power_get_desired_mode` will return the mode the system should end up in when the mode change completes, barring further calls to `power_set_mode`.

## Individual Controller Power Modes

The power management package keeps track of the current and desired modes for each power controller, as well as the modes for the system as a whole. The function `power_get_controller_mode` takes a single argument, a pointer to a power controller, and returns the power mode that controller is currently running at. Similarly `power_get_controller_desired_mode` returns the power mode that controller should be running at. Most of the time the current and desired modes for a given controller will be the same, and will also be the same as the global power mode. However if the power management thread is preeempted in the middle of a mode change then some of the controllers will have been updated to the desired global mode, whereas others will still be at the old mode. The power management package also provides functionality for manipulating individual controllers, and for detaching controllers from global mode changes.

## Power Controller Identification

In some scenarios the power management package will run completely automated, and there is no need to identify individual power controllers. Any form of identification such as a string description would serve no purpose, but would still consume memory in the final system. In other scenarios it may be very desirable to provide some means of identification. For example, while still debugging it may be useful to see a simple string when printing the contents of a power controller structure. Alternatively, if the application is expected to provide some sort of user interface that gives control over which parts of the system are enabled or disabled, a string identifier for each controller would be useful. To cope with these scenarios the power management package provides a configuration option `CYGIMP_POWER_PROVIDE_STRINGS`. When enabled, each power controller will contain a pointer to a constant string which can be accessed via a function `power_get_controller_id`. When disabled the system will not contain these strings, and the function will not be provided. The following code illustrates how to use this function.

    #include <stdio.h>
    #include <pkgconf/system.h>
    #ifndef CYGPKG_POWER
    # error The power management package is not present.
    #endif
    #include <pkgconf/power.h>
    #ifndef CYGIMP_POWER_PROVIDE_STRINGS
    # error Power controller identifiers are not available.
    #endif
    #include <cyg/power/power.h>

    static const char*
    mode_to_string(PowerMode mode)
    {
        const char* result;
        switch(mode) {
          case PowerMode_Active : result = "active"; break;
          case PowerMode_Idle   : result = "idle"; break;
          case PowerMode_Sleep  : result = "sleep"; break;
          case PowerMode_Off    : result = "off"; break;
          default               : result = "<unknown>"; break;
        }
        return result;
    }

    int
    main(int argc, char** argv)
    {
        PowerController* controller;

        for (controller = &(__POWER__[0]);
             controller != &(__POWER_END__);
             controller++) {
            printf("Controller @ %p: %s, %s\n", controller,
                   power_get_controller_id(controller),
                   mode_to_string(power_get_controller_mode(controller)));
        }
        return 0;
    }

## The Power Management Thread

If the power management package is configured to use a separate thread then a handle for that thread is made available to higher-level code via the variable `power_thread_handle`. This handle can be used for a variety of purposes, including manipulating that thread's priority.

Changing Power Modes

# Changing Power Modes

reducing or increasing power consumption as needed

    #include <cyg/power/power.h>

        void power_set_mode

        PowerMode new_mode

        void power_set_controller_mode

        PowerController* controller

        PowerMode new_mode

        void power_set_controller_mode_now

        PowerController* controller

        PowerMode new_mode

## Changing the Global Power Mode

The primary functionality supported by the power management package is to change the system's global power mode. This is achieved by calling the function `power_set_mode` with a single argument, which should be one of `PowerMode_Active`, `PowerMode_Idle`, `PowerMode_Sleep` or `PowerMode_Off`. Typically this function will only be invoked in certain scenarios:

1. A typical system will contain a policy module which is primarily responsible for initiating power mode changes, and a thread inside the power management package. The policy module will call `power_set_mode`, which has the effect of manipulating some internal state in the power management package and waking up its thread. When this thread gets scheduled to run (its priority is controlled by a configuration option), it will iterate over the power controllers and invoke each controller to change its power mode. There is support for a callback function, and for detached power controllers.

2. After a call to `power_set_mode` but before the power management thread has had a chance to iterate over all the controllers, or even before the thread has been rescheduled at all, the policy module may decide that a different power mode would be more appropriate for the current situation and calls `power_set_mode` again. This has the effect of aborting the previous mode change, followed by the power management thread iterating over the power controllers again for the new mode.

3. If there is no single policy module responsible for power mode changes, any code can call `power_set_mode`. If there are multiple calls in quick succession, earlier calls will be aborted and the system should end up in the power mode corresponding to the last call

4. As a special case, it is possible for a power controller to call `power_set_mode` when invoked by the power management thread. For example a power controller could decide that it is inappropriate for the system to go to sleep because the device it is associated with is still busy. The effect is as if the policy module had called `power_set_mode` again before the mode change had completed.

If the power management package has been configured not to use a separate thread then obviously the behaviour is somewhat different. The call to `power_set_mode` will now iterate over the various power controllers immediately, rather than leaving this to a separate thread, and the whole mode change completes before `power_set_mode` returns. If some other thread or a DSR calls `power_set_mode` concurrently the behaviour of the system is undefined. However, it is still legal for a power controller to call `power_set_mode`: effectively this is a recursive call; it is detected by the system, and internal state is updated; the recursive `power_set_mode` call now returns, and when the power controller returns back to the original `power_set_mode` call it detects what has happened, aborts the previous mode change, and starts a new mode change as requested by the controller.

`power_set_mode` is normally invoked from thread context. If a separate power management thread is used it can be invoked safely from DSR context. If the system is configured not to use such a thread, it may or may not be safe to invoke this function from DSR context: essentially the function just iterates through the various power controllers, and the documentation or source code of each controller present in the current system will have to be examined to determine whether or not this can happen safely in DSR context. `power_set_mode` should never be invoked from ISR context.

## Manipulating an Individual Power Controller

In some cases it is desirable to set the power mode of an individual controller separately from the mode for the system as a whole. For example if a device is not currently being used then the associated power controller could be set to `PowerMode_Off`, even while the system as a whole is still active. This can be achieved by calling the function `power_set_controller_mode`. It takes two arguments: the first identifies a particular controller; the second specifies the desired new power mode for that controller. The function operates in much the same way as `power_set_mode`, for example if a separate power management thread is being used then `power_set_controller_mode` operates by manipulating some internal state and waking up that thread. The limitations are also much the same as for `power_set_mode`, so for example `power_set_controller_mode` should not be invoked from inside ISRs.

Manipulating individual controllers is often used in conjunction with the function `power_set_controller_attached`, allowing the policy module to specify which controllers are affected by global mode changes.

## Direct Manipulation of a Power Controller

In exceptional circumstances it may be necessary to invoke a power controller directly, bypassing the power management thread and higher-level functionality such as callback functions. The function `power_set_controller_mode_now` allows this. It takes two arguments, a controller and a mode, just like `power_set_controller_mode`.

Use of `power_set_controller_mode_now` is dangerous. For example no attempt is made to synchronise with any other power mode changes that might be happening concurrently. A possible use is when the system gets woken up out of sleep mode: depending on the hardware, on which power controllers are present, and on the application code it may be necessary to wake up some power controllers immediately before the system as a whole is ready to run again.

Support for Policy Modules

# Support for Policy Modules

closer integration with higher-level code

    #include <cyg/power/power.h>

        void power_set_policy_callback

        void (*)(PowerController*, PowerMode, PowerMode, PowerMode, PowerMode) callback

        void (*)(PowerController*, PowerMode, PowerMode, PowerMode, PowerMode) power_get_policy_callback

        CYG_ADDRWORD power_get_controller_policy_data

        PowerController* controller

        void power_set_controller_policy_data

        PowerController* controller

        CYG_ADDRWORD data

## Policy Callbacks

The use of a separate thread to perform power mode changes in typical configurations can cause problems for some policy modules. Specifically, the policy module can request a mode change for the system as a whole or for an individual controller, but it does not know when the power management thread actually gets scheduled to run again and carry out the request. Although it would be possible for the policy module to perform some sort of polling, in general that is undesirable.

To avoid such problems the policy module can install a callback function using `power_set_policy_callback`. The current callback function can be retrieved using `power_get_policy_callback`. If a callback function has been installed then it will be called by the power management package whenever a power controller has been invoked to perform a mode change. The callback will be called in the context of the power management thread, so usually it will have to make use of thread synchronisation primitives to interact with the main policy module. It is passed five arguments:

1. The power controller that has just been invoked to perform a mode change.

2. The mode this controller was running at before the invocation.

3. The current mode this controller is now running at.

4. The desired mode before the power controller was invoked. Usually this will be the same as the current mode, unless the controller has decided for some reason that this was inappropriate.

5. The current desired mode. This will differ from the previous argument only if there has was another call to `power_set_mode` or `power_set_controller_mode` while the power controller was being invoked, probably by the power controller itself.

A simple example of a policy callback function would be:

    static void
    power_callback(
        PowerController* controller,
        PowerMode old_mode,
        PowerMode new_mode,
        PowerMode old_desired_mode,
        powerMode new_desired_mode)
    {
        printf("Power mode change: %s, %s -> %d\n",
            power_get_controller_id(controller),
            mode_to_string(old_mode),
            mode_to_string(new_mode));

        CYG_UNUSED_PARAM(PowerMode, old_desired_mode);
        CYG_UNUSED_PARAM(PowerMode, new_desired_mode);
    }

    int
    main(int argc, char** argv)
    {
        …
        power_set_policy_callback(&power_callback);
        …
    }

If `power_set_controller_mode_now` is used to manipulate an individual controller the policy callback will not be invoked. This function may get called from any context including DSRs, and even if there is already a call to the policy callback happening in some other context, so invoking the callback would usually be unsafe.

If the power management package has not been configured to use a separate thread then `power_set_mode` and `power_set_controller_mode` will manipulate the power controllers immediately and invoke the policy callback afterwards. Therefore the policy callback will typically run in the same context as the main policy module.

## Policy-specific Controller Data

Some policy modules may want to associate some additional data with each power controller. This could be achieved by for example maintaining a hash table or similar data structure, but for convenience the power management package allows higher-level code, typically the policy module, to store and retrieve one word of data in each power controller. The function `power_set_controller_policy_data` takes two arguments, a pointer to a power controller and a CYG_ADDRWORD of data: by appropriate use of casts this word could be an integer or a pointer to some data structure. The matching function `power_get_controller_policy_data` retrieves the word previously installed, and can be cast back to an integer or pointer. The default value for the policy data is 0.

For example the following code fragment stores a simple index value in each power controller. This could then be retrieved by the policy callback.

        unsigned int     i = 0;
        PowerController* controller;

        for (controller = &(__POWER__[0]);
             controller != &(__POWER_END__);
             controller++) {
            power_set_controller_policy_data(controller, (CYG_ADDRWORD) i++);
        }

Not all policy modules will require per-controller data. The configuration option `CYGIMP_POWER_PROVIDE_POLICY_DATA` can be used to control this functionality, thus avoiding wasting a small amount of memory inside each power controller structure.

Attached and Detached Controllers

# Attached and Detached Controllers

control which power controllers are affected by global changes

    #include <cyg/power/power.h>

        cyg_bool power_get_controller_attached

        PowerController* controller

        void power_set_controller_attached

        PowerController* controller

        cyg_bool new_state

## Detaching Power Controllers

By default the global operation `power_set_mode` affects all power controllers. There may be circumstances when this is not desirable. For example if a particular device is not currently being used then it can be left switched off: the rest of the system could be moving between active, idle and sleep modes, but there is no point in invoking the power controller for the unused device. To support this the power management package supports the concept of attached and detached controllers. By default all controllers are attached, and hence will be affected by global mode changes. A specific controller can be detached using the function `power_set_controller_attached`. This function takes two arguments, one to specify a particular controller and another to specify the desired new state. `power_get_controller_attached` can be used to determine whether or not a specific controller is currently attached.

The attached or detached state of a controller only affects what happens during a global mode change, in other words following a call to `power_set_mode`. It is still possible to manipulate a detached controller using `power_set_controller_mode` or `power_set_controller_mode_now`.

Implementing a Power Controller

# Implementing a Power Controller

adding power management support to device drivers and other packages

## Implementing a Power Controller

A system will have some number of power controllers. Usually there will be one power controller for the cpu, `power_controller_cpu`, typically provided by one of the HAL packages and responsible for managing the processor itself and associated critical components such as memory. Some or all of the device drivers will provide power controllers, allowing the power consumption of the associated devices to be controlled. There may be some arbitrary number of other controllers present in the system. The power management package does not impose any restrictions on the number or nature of the power controllers in the system, other than insisting that at most one `power_controller_cpu` be provided.

Each power controller involves a single data structure of type PowerController, defined in the header file `cyg/power/power.h`. These data structures should all be placed in the table `__POWER__`, so that the power management package and other code can easily locate all the controllers in the system. This table is constructed at link-time, avoiding code-size or run-time overheads. To facilitate this the package provides two macros which should be used to define a power controller, `POWER_CONTROLLER()` and `POWER_CONTROLLER_CPU()`.

The macro `POWER_CONTROLLER` takes four arguments:

1. A variable name. This can be used to access the power controller directly, as well as via the table.

2. A priority. The table of power controllers is sorted, such that power controllers with a numerically lower priority come earlier in the table. The special controller `power_controller_cpu` always comes at the end of the table. When moving from a high-power mode to a lower-powered mode, the power management package iterates through the table from front to back. When moving to a higher-powered mode the reverse direction is used. The intention is that the power controller for a software-only package such as a TCP/IP stack should appear near the start of the table, whereas the controllers for the ethernet and similar devices would be near the end of the table. Hence when the policy module initiates a mode change to a lower-powered mode the TCP/IP stack gets a chance to cancel this mode change, before the devices it depends on are powered down. Similarly when moving to a higher-powered mode the devices will be re-activated before any software that depends on those devices.

   The header file `cyg/power/power.h` defines three priorities `PowerPri_Early`, `PowerPri_Typical` and `PowerPri_Late`. For most controllers one of these priorities, possibly with a small number added or subtracted, will give sufficient control. If an application developer is uncertain about the relative priorities of the various controllers, a simple test program that iterates over the table will quickly eliminate any confusion.

3. A constant string identifier. If the system has been configured without support for such identifiers (`CYGIMP_POWER_PROVIDE_STRINGS`) then this identifer will be discarded at compile-time. Otherwise it will be made available to higher-level code using the function `power_get_controller_id`.

4. A function pointer. This will be invoked to perform actual mode changes, as described below.

A typical example of the use of the `POWER_CONTROLLER` macro would be as follows:

    #include <pkgconf/system.h>

    #ifdef CYGPKG_POWER
    # include <cyg/power/power.h>

    static void
    xyzzy_device_power_mode_change(
        PowerController* controller,
        PowerMode        desired_mode,
        PowerModeChange  change)
    {
       // Do the work
    }

    static POWER_CONTROLLER(xyzzy_power_controller, \
                            PowerPri_Late,          \
                            "xyzzy device",         \
                            &xyzzy_device_power_mode_change);
    #endif

This creates a variable `xyzzy_power_controller`, which is a power controller data structure that will end up near the end of the table of power controllers. Higher-level code can iterate through this table and report the string `"xyzzy device"` to the user. Whenever there is a mode change operation that affects this controller, the function `xyzzy_device_power_mode_change` will be invoked. The variable is declared static so this controller cannot be manipulated by name in any other code. Alternatively, if the variable had not been declared static other code could manipulate this controller by name as well as through the table, especially if the package for the xyzzy device driver explicitly declared this variable in an exported header file. Obviously exporting the variable involves a slight risk of a name clash at link time.

The above code explicitly checks for the presence of the power management package before including that package's header file or providing any related functionality. Since power management functionality is optional, such checks are recommended.

The macro `POWER_CONTROLLER_CPU` only takes two arguments, a string identifier and a mode change function pointer. This macro always instantiates a variable `power_controller_cpu` so there is no need to provide a variable name. The resulting power controller structure always appears at the end of the table, so there is no need to specify a priority. Typical usage of the `POWER_CONTROLLER_CPU` macro would be:

    static void
    wumpus_processor_power_mode_change(
        PowerController* controller,
        PowerMode        desired_mode,
        PowerModeChange  change)
    {
       // Do the work
    }

    POWER_CONTROLLER_CPU("wumpus processor", \
                         &wumpus_processor_power_mode_change);

This defines a power controller structure `power_controller_cpu`. It should not be declared static since higher-level code may well want to manipulate the cpu's power mode directly, and the variable is declared by the power management package's header file.

Some care has to be taken to ensure that the power controllers actually end up in the final executable. If a power controller variable ends up in an ordinary library and is never referenced directly then typically the linker will believe that the variable is not needed and it will not end up in the executable. For eCos packages this can be achieved in the CDL, by specifying that the containing source file should end up in `libextras.a` rather than the default `libtarget.a`:

    cdl_package CYGPKG_HAL_WUMPUS_ARCH {
        …
        compile -library=libextras.a data.c
    }

If the file `data.c` instantiates a power controller this is now guaranteed to end up in the final executable, as intended. Typically HAL and device driver packages will already have some data that must not be eliminated by the linker, so they will already contain a file that gets built into `libextras.a`. For power controllers defined inside application code it is important that the power controllers end up in `.o` object files rather than in `.a` library archive files.

All the real work of a power controller is done by the mode change function. If the power management package has been configured to use a separate thread then this mode change function will be invoked by that thread (except for the special case of `power_set_controller_mode_now`). If no separate thread is used then the mode change function will be invoked directly by `power_set_mode` or `power_set_controller_mode`.

The mode change function will be invoked with three arguments. The first argument identifies the power controller. Usually this argument is not actually required since a given mode change function will only ever be invoked for a single power controller. For example, `xyzzy_device_power_mode_change` will only ever be used in conjunction with `xyzzy_power_controller`. However there may be some packages which contain multiple controllers, all of which can share a single mode change function, and in that case it is essential to identify the specific controller. The second argument specifies the mode the controller should switch to, if possible: it will be one of `PowerMode_Active`, `PowerMode_Idle`, `PowerMode_Sleep` or `PowerMode_Off`. The final argument will be one of `PowerModeChange_Controller`, PowerModeChange_ControllerNow, or `PowerModeChange_Global`, and identifies the call that caused this invocation. For example, if the mode change function was invoked because of a call to `power_set_mode` then this argument will be `PowerModeChange_Global`. It is up to each controller to decide how to interpret this final argument. A typical controller might reject a global request to switch to off mode if the associated device is still busy, but if the request was aimed specifically at this controller then it could instead abort any current I/O operations and switch off the device.

The PowerController data structure contains one field, mode, that needs to be updated by the power mode change function. At all times it should indicate the current mode for this controller. When a mode change is requested the desired mode is passed as the second argument. The exact operation of the power mode change function depends very much on what is being controlled and the current circumstances, but some guidelines are possible:

1. If the request can be satisfied without obvious detriment, do so and update the mode field. Reducing the power consumption of a device that is not currently being used is generally harmless.

2. If a request is a no-op, for example if the system is switching from idle to sleep mode and the controller does not distinguish between these modes, simply act as if the request was satisfied.

3. If a request is felt to be unsafe, for example shutting down a device that is still in use, then the controller may decide to reject this request. This is especially true if the request was a global mode change as opposed to one intended specifically for this controller: in the latter case the policy module should be given due deference. There are a number of ways in which a request can be rejected:

   1. If the request cannot be satisfied immediately but may be feasible in a short while, leave the mode field unchanged. Higher-level code in the policy module can interpret this as a hint to retry the operation a little bit later. This approach is also useful if the mode change can be started but will take some time to complete, for example shutting down a socket connection, and additional processing will be needed later on.

   2. If the request is felt to be inappropriate, for example switching off a device that is still in use, the mode change function can call `power_set_controller_mode` to reset the desired mode for this controller back to the current mode. Higher-level code can then interpret this as a hint that there is more activity in the system than had been apparent.

   3. For a global mode change, if the new mode is felt to be inappropriate then the power controller can call `power_set_mode` to indicate this. An example of this would be the policy module deciding to switch off the whole unit while there is still I/O activity.

Mode change functions should not directly manipulate any other fields in the PowerController data structure. If it is necessary to keep track of additional data then static variables can be used.

It should be noted that the above are only guidelines. Their application in any given situation may be unclear. In addition the detailed requirements of specific systems will vary, so even if the power controller for a given device driver follows the above guidelines exactly it may turn out that slightly different behaviour would be more appropriate for the actual system that is being developed. Fortunately the open source nature of eCos allows system developers to fine-tune power controllers to meet their exact requirements.