Implement the PSL for the target

This document describes how to implement the Platform Specific Layer for the Power Resource Manager.

Purpose

The PSL must be implemented in order for the PRM and the hardware to be able to communicate.

Introduction

The PSL side of the Resource Controller should be derived from the class DPowerResourceController.

Implementing the PSL

The following tasks are covered in this tutorial:

Create an entry Point

The PIL layer of the PRM offers the following macro. This needs to be invoked from the PSL. The macro calls the DoInitController() and DoInitResources() functions implemented by the PSL.

/**
Extension entry point
*/
DECLARE_RESOURCE_MANAGER_EXTENSION()
    {
    __KTRACE_OPT(KBOOT, Kern::Printf("CreateController called"));
    return new DH4PowerResourceController;
    }

If the PRM is a PDD the entry point is as follows:

static DH4PowerResourceController TheController;
DECLARE_STANDARD_PDD()
    {
    TInt r = DPowerResourceController::InitController();     
    if® == KErrNone)
        r = TheController->InitResources();
    if® == KErrNone)
        return new DResConPddFactory;
    return NULL;                            
    }

Override the pure virtual functions

Override the virtual functions of the DPowerResourceController base class:

Here is an example H4 HRP implementation:

class DH4PowerResourceController : DPowerResourceController
   {
public:
   DH4PowerResourceController();
   TInt DoInitController();
   TInt DoRegisterStaticResources(DStaticPowerResource**& aStaticResourceArray, TUint16& aStaticResourceCount);
private:
   static DStaticPowerResource* iResources[];

DoInitController()

Override DPowerResourceController::DoInitController() so that it creates a DFC queue and then invokes the base class function DPowerResourceController::SetDfcQ(). DoInitController() should also initialise the pools by invoking DPowerResourceController::InitPools() passing the platform defined sizes for the pools of clients, client levels and request objects. DoInitController() is called by the PSL during its initialisation. Below is the reference implementation code.

#define KERNEL_CLIENTS 0x2 // Expected number of kernel clients (MMC and Sound_SC)
#define USER_CLIENTS   0x0 // No user side clients expected.
#define CLIENT_LEVELS  0x8 // Expected resource state change of 8 resources 
#define REQUESTS       0x0 // No asynchronous operation (state change or read) expected. 

TInt DH4PowerResourceController::DoInitController()
    {
    // Create a DFC queue and then invoke SetDfcQ()
__KTRACE_OPT(KRESMANAGER, Kern::Printf(">DH4PowerResourceController::DoInitController\n"));

    // NKern::ThreadEnterCS(); // Not needed, as this is executed during kernel boot which is done within a critical section.
    TInt r = Kern::DfcQCreate(iDfcQ,28,&KResThreadName);

    // NKern::ThreadLeaveCS();
    if (KErrNone!=r) return r;
    SetDfcQ(iDfcQ);
    r = InitPools(KERNEL_CLIENTS,USER_CLIENTS,CLIENT_LEVELS,REQUESTS);
__KTRACE_OPT(KRESMANAGER, Kern::Printf("<DH4PowerResourceController::DoInitController\n"));
    return r;
    }

DoRegisterStaticResources()

DoRegisterStaticResources() is called by the PIL during its initialisation. Override the pure virtual function DPowerResourceController::DoRegisterStaticResources() so that it:

  • creates all the static resources in the kernel heap,

  • creates an array of pointers to the static resources (indexed by their id),

  • sets the passed pointer (aStaticResourceArray) to point to the created array of pointers,

  • updates the count of the static resources (in aStaticResourceCount).

The reference implementation is below:

TInt DH4PowerResourceController::DoRegisterStaticResources(DStaticPowerResource**& aStaticResourceArray, TUint16& aStaticResourceCount)
    {
    aStaticResourceCount = 0;
   __KTRACE_OPT(KRESMANAGER, Kern::Printf(">DH4PowerResourceController::DoRegisterStaticResources\n"));
   aStaticResourceArray = (DStaticPowerResource**) new (DStaticPowerResource*[EH4ResourceCount]);
   if(!aStaticResourceArray)
      return KErrNoMemory;

   DH4MmcFclkResource *pMmcFclk = new (DH4MmcFclkResource);
   if (!pMmcFclk) 
      return KErrNoMemory;
   aStaticResourceArray[EMmcFclkRes] = pMmcFclk;

   DH4MmcIclkResource *pMmcIclk = new (DH4MmcIclkResource);
   if (!pMmcIclk)
      CLEAN_AND_RETURN(KErrNoMemory, EMmcIclkRes)
   aStaticResourceArray[EMmcIclkRes] = pMmcIclk;

   DH4MmcClkResource *pMmcBusClk = new (DH4MmcClkResource);
   if (!pMmcBusClk)
      CLEAN_AND_RETURN(KErrNoMemory, EMmcClkRes)
   aStaticResourceArray[EMmcClkRes] = pMmcBusClk;

   DH4MmcCtrllerPwrResource *pMmcCtrlrPwr = new (DH4MmcCtrllerPwrResource);
   if (!pMmcCtrlrPwr)
      CLEAN_AND_RETURN(KErrNoMemory, EMmcCtrlrPwrRes)
   aStaticResourceArray[EMmcCtrlrPwrRes] = pMmcCtrlrPwr;
    
   DH4MmcPowerResource *pMmcPwr = new (DH4MmcPowerResource);
   if (!pMmcPwr)
      CLEAN_AND_RETURN(KErrNoMemory, EMmcPwrRes)
   aStaticResourceArray[EMmcPwrRes] = pMmcPwr;
    
   DH4SndFclkResource *pSndFclk = new (DH4SndFclkResource);
   if (!pSndFclk)
      CLEAN_AND_RETURN(KErrNoMemory, ESndFclkRes)
   aStaticResourceArray[ESndFclkRes] = pSndFclk;

   DH4SndIclkResource *pSndIclk = new (DH4SndIclkResource);
   if (!pSndIclk)
      CLEAN_AND_RETURN(KErrNoMemory ,ESndIclkRes)
   aStaticResourceArray[ESndIclkRes] = pSndIclk;

   DH4SndMClkResource *pSndMclk = new (DH4SndMClkResource);
   if (!pSndMclk)
      CLEAN_AND_RETURN(KErrNoMemory, ESndMClkRes)
   aStaticResourceArray[ESndMClkRes] = pSndMclk;

   aStaticResourceCount = EH4ResourceCount;

   __KTRACE_OPT(KRESMANAGER, Kern::Printf("<DH4PowerResourceController::DoRegisterStaticResources, Total resources:%d\n", EH4ResourceCount));

   return KErrNone;
   }

Create static resources that support dependencies

If there are static resources that support dependencies then the PSL must implement the function DPowerResourceController::DoRegisterStaticResourcesDependency(). Note: Static resources that support dependencies are only available in the extended version of the library. See setup and configuration.

The function DoRegisterStaticResourcesDependency():

  • creates all static resources that support dependencies in kernel heap,

  • creates an array of pointers to them,

  • establishes the dependencies between them (using the resource's DStaticPowerResourceD::AddNode() function), See creating dependencies between static resources for a description of AddNode() and the SNode structure.

  • sets the passed pointer (aStaticResourceDArray) to point to the array of pointers to static resource with dependency,

  • updates the count of the static resource that supports dependency in aStaticResourceDCount.

This function is called by the PIL during PRM initialisation. Below is an example:

/** 
This function creates all static resources that support dependencies and establishes the dependencies between them. This is called by the PIL.
This is the dependency tree input:
   ResourceA <---------> ResourceD <-------> ResourceE <--------> ResourceC
                             |                   |
                             |                   |
                             |                   |
                         ResourceF           ResourceG       
*/
TInt DSimulatedPowerResourceController::DoRegisterStaticResourcesDependency(DStaticPowerResourceD**& aStaticResourceDArray, TUint16& aStaticResourceDCount)
   {
   aStaticResourceDArray = (DStaticPowerResourceD**) new (DStaticPowerResourceD*[MAX_DEPENDENT_RESOURCE_COUNT]);
   if(!aStaticResourceDArray)
     return KErrNoMemory;

   DStaticPowerResourceD* pR = NULL;

   pR = new DMLSLGLSPDependResource();
   if(!pR)
     CLEAN_AND_RETURN(iStaticResDependencyCount, aStaticResourceDArray, KErrNoMemory)
   aStaticResourceDArray[iStaticResDependencyCount++] = pR;

   pR = new DMLSIGLSNDependResource();
   if(!pR)
     CLEAN_AND_RETURN(iStaticResDependencyCount, aStaticResourceDArray, KErrNoMemory)
   aStaticResourceDArray[iStaticResDependencyCount++] = pR;
    
   pR = new DBSIGLSPDependResource();
   if(!pR)
     CLEAN_AND_RETURN(iStaticResDependencyCount, aStaticResourceDArray, KErrNoMemory)
   aStaticResourceDArray[iStaticResDependencyCount++] = pR;

   pR = new DMLSHIGLSPDependResource();
   if(!pR)
     CLEAN_AND_RETURN(iStaticResDependencyCount, aStaticResourceDArray, KErrNoMemory)
   aStaticResourceDArray[iStaticResDependencyCount++] = pR;

   pR = new DBSHLGLSNDependResource();
   if(!pR)
     CLEAN_AND_RETURN(iStaticResDependencyCount, aStaticResourceDArray, KErrNoMemory)
   aStaticResourceDArray[iStaticResDependencyCount++] = pR;

   pR = new DMLSHLGLSNDependResource();
   if(!pR)
     CLEAN_AND_RETURN(iStaticResDependencyCount, aStaticResourceDArray, KErrNoMemory)
   aStaticResourceDArray[iStaticResDependencyCount++] = pR;

   // Establish resource dependencies
   if(CreateResourceDependency(aStaticResourceDArray))
     CLEAN_AND_RETURN(iStaticResDependencyCount, aStaticResourceDArray, KErrNoMemory)

   iDependencyResources = aStaticResourceDArray;

   aStaticResourceDCount = iStaticResDependencyCount;
   return KErrNone;
   }    

// This function establishes above dependency between static dependent resource
TInt DSimulatedPowerResourceController::CreateResourceDependency(DStaticPowerResourceD** pResArray)
   {
   iNodeArray = new SNode[10];
   SNode* pN1;
   SNode* pN2;
   iNodeCount = 0;

   if(!iNodeArray)
     return KErrNoMemory; 
   // Create Dependency between Resource A and Resource D 
   pN1 = &iNodeArray[iNodeCount++];
   pN2 = &iNodeArray[iNodeCount++];
   CREATE_DEPENDENCY_BETWEEN_NODES(pN1, pN2, pResArray[0], pResArray[1], 1, 1)

   // Create Dependency between Resource D and Resource F
   pN1 = &iNodeArray[iNodeCount++];
   pN2 = &iNodeArray[iNodeCount++];
   CREATE_DEPENDENCY_BETWEEN_NODES(pN1, pN2, pResArray[0], pResArray[2], 1, 3)

   // Create Dependency between Resource D and Resource E
   pN1 = &iNodeArray[iNodeCount++];
   pN2 = &iNodeArray[iNodeCount++];
   CREATE_DEPENDENCY_BETWEEN_NODES(pN1, pN2, pResArray[0], pResArray[3], 3, 2)

   // Create Dependency between Resource E and Resource C
   pN1 = &iNodeArray[iNodeCount++];
   pN2 = &iNodeArray[iNodeCount++];
   CREATE_DEPENDENCY_BETWEEN_NODES(pN1, pN2, pResArray[3], pResArray[4], 1, 1)

   // Create Dependency between Resource E and Resource G
   pN1 = &iNodeArray[iNodeCount++];
   pN2 = &iNodeArray[iNodeCount++];
   CREATE_DEPENDENCY_BETWEEN_NODES(pN1, pN2, pResArray[3], pResArray[5], 1, 2)

   return KErrNone;
   }

#define CREATE_DEPENDENCY_BETWEEN_NODES(pN1, pN2, pRes1, pRes2, priRes1, priRes2)    \
        pN1->iPriority = priRes1;                                                 \
        pN1->iResource = pRes1;                                                   \
        pN1->iPropagatedLevel = 0;                                                \
        pN1->iVisited = EFalse;                                                   \
        pN2->iPriority = priRes2;                                                 \
        pN2->iResource = pRes2;                                                   \
        pN2->iPropagatedLevel = 0;                                                \
        pN2->iVisited = EFalse;                                                   \
        pN1->iResource->AddNode(pN2);                                             \
        pN2->iResource->AddNode(pN1);

Creating dependencies between static resources

DStaticPowerResourceD::AddNode() is used to establish a dependency between static resources. Dependencies between static resources cannot be removed.

AddNode() takes a pointer to an SNode object that contains the dependent resource information. This passed node pointed is added to the resource dependency list within the class DStaticPowerResourceD. Note: the kernel panics if the specified dependency priority is already in use.

Information within SNode includes:

  • iResource is a pointer to dependent resource,

  • iPriority is the priority of the dependency resource. This is used by the PRM when propagating the resource change,

  • iSpare is reserved for future use,

  • iNext is a pointer to next node in the list.

The members iPropagatedLevel, iRequiresChange and iVisited are used internally by the PRM.

To link to dynamic resources that support dependency use the PRM function RegisterResourceDependency() as described in creating resource dependencies.

Initialise the pools

Pools are implemented as singly linked lists during the initialisation of the PRM. The PSL initialises the pools by invoking DPowerResourceController::InitPools() and passing the platform defined sizes for the pools of kernel side clients, user side clients, client levels and requests. See the example DoInitController() implementation.

The PRM has three types of pools:

  • client pools to store client information,

  • request pools to send requests for operation on long latency resource to the Resource Controller thread,

  • client level pools to capture the resource level request by each client for a resource.

If the client pool is set to zero, then the PRM will not allow a client of that type to register. For example, if the kernel side clients pool is set to zero, then the PIL will not allow any kernel side clients to register. The size of the client pools to specify depends on how many kernel side device drivers (clients) and user side clients will access PRM and size of request pool depends on the number of long latency resource available in the system. Size of client level pool depends on number of clients that will request a resource state change. If the client pools are exhausted the PIL will try to increase the size of the pool. The size of the pool never decreases.

Registering with the Power Controller

The Resource Controller must call DPowerController::RegisterResourceController() to register itself with the Power Controller. RegisterResourceController() sets the iResourceControllerData.iResourceController data member within DPowerController to the passed Resource Controller pointer and sets iResouceControllerData.iClientId to the client ID used by the Power Controller when calling the Resource Controller's APIs.

The platform specific implementation of the Power Controller overrides the RegisterResourceController() function. See DPowerController::RegisterResourceController().

Idle power management

The resource controller's APIs cannot be called from a NULL thread. However, the PSL may need to know the state of the resources from a NULL thread to take the system to appropriate power states. To allow access to this information the PRM provides the virtual function DPowerResourceController::RegisterResourcesForIdle().