/** * @file rm.c * * @brief * This is the Resource Manager source. * * \par * ============================================================================ * @n (C) Copyright 2012, Texas Instruments, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * * Neither the name of Texas Instruments Incorporated nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * \par */ /* RM Types */ #include /* RM includes */ #include #include #include #include #include /* RM OSAL layer */ #include /********************************************************************** ************************** Globals *********************************** **********************************************************************/ /* Place QMSS PDSP permissions array */ #pragma DATA_SECTION (rmQmssPdspFirmwarePerms, ".rm"); #pragma DATA_ALIGN (rmQmssPdspFirmwarePerms, 128) Rm_Perms rmQmssPdspFirmwarePerms[RM_ALIGN_PERMISSIONS_ARRAY(RM_QMSS_FIRMWARE_PDSPS, Rm_Perms)]; /** @brief Global Variable which describes the RM Version Information */ const char rmVersionStr[] = RM_VERSION_STR ":" __DATE__ ":" __TIME__; /********************************************************************** ********************** Internal Functions **************************** **********************************************************************/ void Rm_transactionQueueAdd(void *rmHandle, uint32_t transactionId, void *callbackFunc) { Rm_Inst *rmInst = (Rm_Inst *)rmHandle; Rm_TransactionQueueEntry *transactionQueue = (Rm_TransactionQueueEntry *) rmInst->transactionQueue; Rm_TransactionQueueEntry *newEntry; void *key; /* Lock access to the RM instance's transaction queue */ key = Rm_osalLocalCsEnter(); /* Get memory for a new transaction queue entry from local memory */ newEntry = Rm_osalMalloc (sizeof(Rm_TransactionQueueNode), false); /* Populate the new entry. */ newEntry->transactionId = transactionId; newEntry->transactionCallback = callbackFunc; newEntry->nextEntry = NULL; /* New entry's nextEntry pointer will always be NULL */ /* Check if there are any entries in the transaction queue */ if (transactionQueue) { /* At least one entry in the transaction queue. Add the new entry to the end of the * transaction queue */ while (transactionQueue->nextEntry != NULL) { /* Traverse the list until arriving at the last entry */ transactionQueue = transactionQueue->nextEntry; } /* Add the new entry to the end of the queue */ transactionQueue->nextEntry = newEntry; } else { /* The transaction queue does not currently exist. The new entry is the first entry */ rmInst->routeMap = newEntry; } Rm_osalLocalCsExit(key); } Rm_TransactionQueueEntry *Rm_transactionQueueFind(void *rmHandle, uint32_t transactionId) { Rm_Inst *rmInst = (Rm_Inst *)rmHandle; Rm_TransactionQueueEntry *entry = (Rm_TransactionQueueEntry *) rmInst->transactionQueue; void *key; /* Lock access to the RM instance's transaction queue */ key = Rm_osalLocalCsEnter(); /* Make sure there is at least one entry in the transaction queue */ if (entry != NULL) { /* Find the transaction ID within the specified RM instance's transaction queue. * If the end of the transaction queue is reached without finding the entry the * entry pointer will be NULL */ while (entry != NULL) { if (entry->transactionId == transactionId) { /* Match: break out of loop and return the entry */ break; } entry = entry->nextEntry; } } Rm_osalLocalCsExit(key); return (entry); } uint32_t Rm_transactionQueueDelete(void *rmHandle, uint32_t transactionId) { Rm_Inst *rmInst = (Rm_Inst *)rmHandle; Rm_TransactionQueueEntry *currentEntry = (Rm_TransactionQueueEntry *) rmInst->transactionQueue; Rm_TransactionQueueEntry *prevEntry = NULL; void *key; /* Lock access to the RM instance's transaction queue */ key = Rm_osalLocalCsEnter(); /* Make sure there is at least one entry in the transaction queue */ if (currentEntry == NULL) { Rm_osalLocalCsExit(key); return (-1); /* TEMP ERROR RETURN */ } /* Find the transaction ID within the specified RM instance's transaction queue. */ while (currentEntry != NULL) { if (currentEntry->transactionId == transactionId) { /* Match: break out of loop and delete the entry */ break; } prevEntry = currentEntry; currentEntry = currentEntry->nextEntry; } /* Traversed entire queue but did not find transaction */ if (currentEntry == NULL) { Rm_osalLocalCsExit(key); return (-2); /* TEMP ERROR RETURN */ } else { /* Delete the transaction queue entry */ if ((prevEntry == NULL) && currentEntry->nextEntry) { /* Entry to be deleted exists at start of transaction queue. Map second * entry to be start of transaction queue as long as there are more than * one entries */ rmInst->transactionQueue = currentEntry->nextNode; } else { /* Entry to be deleted is in the middle or at end of the queue. Adjust adjacent * entry pointers. This covers the case where the entry to be removed is at the * end of the queue. */ prevEntry->nextNode = currentEntry->nextNode; } /* Delete the node, free the memory associated with the node. */ Rm_osalFree((void *) currentEntry, sizeof(Rm_TransactionQueueEntry), false); } Rm_osalLocalCsExit(key); return (0); /* RETURN OKAY - FIX MAGIC NUMBER */ } /* Used in Client Delegate case where it forwarded requests from Client to Server * callback will direct to internal function that forwards response from server back * to client */ void Rm_internalCallback (Rm_ServiceRespInfo *responseInfo) { } void Rm_transactionForwarder (void *rmHandle, Rm_Transaction *transInfo, Rm_TransactionReceipt *transReceipt) { Rm_Inst *rmInst = (Rm_Inst *) rmHandle; Rm_TransRouteMapNode *routeMap = (Rm_TransRouteMapNode *) rmInst->routeMap; uint32_t transactionId; Rm_Packet *rmPkt = NULL; Rm_ProtocolPkt *rmProtPkt = NULL; /* Make sure at least one transport has been registered */ if (routeMap == NULL) { transReceipt->transactionResult = RM_SERVICE_ERROR_NO_TRANSPORT_REGISTERED; return; } /* Make sure the RM instance has a transport registered with a higher level agent */ if (!rmInst->registeredWithDelegateOrServer) { transReceipt->transactionResult = RM_SERVICE_ERROR_NOT_REGISTERED_WITH_DEL_OR_SERVER; return; } /* Parse the RM instance's routing map to find the higher level agent for forwarding */ while ((routeMap->remoteInstType != Rm_instType_CLIENT_DELEGATE) || (routeMap->remoteInstType != Rm_instType_SERVER)) { /* Traverse the list until arriving at the upper level agent node */ routeMap = routeMap->nextNode; } /* Create a RM packet using the service information */ if (rmInst->transport.rmAllocPkt) { rmPkt = rmInst->transport.rmAllocPkt(routeMap->transHandle, sizeof(Rm_Packet)); } else { /* The transport packet alloc API is not plugged */ transReceipt->transactionResult = RM_SERVICE_ERROR_TRANSPORT_PKT_ALLOC_API_NULL; return; } /* Make sure a buffer for the packet was allocated */ if (rmPkt == NULL) { transReceipt->transactionResult = RM_SERVICE_ERROR_TRANSPORT_NULL_PKT_BUF; return; } /* Make sure allocated buffer is large enough to fit the protocol packet plus the * rmPktLen field */ if (rmPkt->rmPktLen < (sizeof(Rm_ProtocolPkt) + sizeof(uint32_t)) { transReceipt->transactionResult = RM_SERVICE_ERROR_TRANSPORT_PKT_BUF_TOO_SMALL; return; } /* Create an ID for this transaction. The ID will be used for two purposes: * 1) Matching the response from the higher level RM agent to the request * 2) Provided to the service requesting component so that it can match the * service request with the response it receives via its callback function */ transactionId = ...; /* NEED SCHEME FOR CREATING A MUTUALLY EXCLUSIVE PKT ID. CAN'T * CONFLICT WITH PCKT IDS CREATED ON OTHER RM INSTANCES */ /* Initialize the rmPkt. Do not assume the transport code will do it */ memset((void *)rmPkt, 0, sizeof(Rm_Packet)); /* Assign the rmData field to be the rmProtPkt */ rmProtPkt = &(rmPkt->rmData[0]); /* Populate the RM packet using the service information */ rmProtPkt->rmPktId = transactionId; strcpy((void *)rmProtPkt->rmInstName,(void *)rmInst->name); rmProtPkt->rmCmd = (uint32_t) transInfo->transType; if ((transInfo->transCommand == Rm_command_POLICY_REQUEST) || (transInfo->transCommand == Rm_command_POLICY_RESPONSE) { /* Copy the policy data if the transaction is policy based */ memcpy ((void *)rmProtPkt->u.rmPolicyData, (void *)transInfo->u.policyData, RM_MAX_POLICY_SIZE_BYTES); } else { /* Otherwise copy the resource data */ memcpy ((void *)&(rmProtPkt->u.resInfo), (void *) &(transInfo->u.resourceInfo), sizeof(Rm_ResourceInfo)); } /* Create an entry in the transaction queue */ Rm_transactionQueueAdd(rmHandle, transactionId, transInfo->transCallback); /* Send the RM packet to the application transport */ if (rmInst->transport.rmSend) { if (!(rmInst->transport.rmSend(routeMap->transHandle, rmPkt)) { /* Transport returned an error */ /* SHOULD TRANSPORT ERROR SOMEHOW BE OR'D WITH THE SERVICE ERROR ??? */ transReceipt->transactionResult = RM_SERVICE_ERROR_TRANPSPORT_SEND_ERROR; return; } } else { /* The transport packet send API is not plugged */ transReceipt->transactionResult = RM_SERVICE_ERROR_TRANSPORT_PKT_SEND_API_NULL; return; } /* Inform requesting component that the service is being forwarded to a higher lever * RM agent for processing. The result of the service will be provided to the * component via the specified callback function */ transReceipt->transactionResult = RM_SERVICE_PROCESSING; transReceipt->transactionId = transactionId; return; } void Rm_transactionProcessor (void *rmHandle, Rm_Transaction *transaction, Rm_TransactionReceipt *transactionReceipt) { Rm_Inst *rmInst = (Rm_Inst *)rmHandle; /* All service requests on Clients are forwarded to the higher level RM agent * either a Client Delegate or Server, based on the RM system architecture */ if (rmInst->instType == Rm_instType_CLIENT) { Rm_transactionForwarder(rmHandle, transaction, transactionReceipt); if (requestInfo->serviceCallback == NULL) { /* RM Client's always use a blocking RM transport to consult with a high-level * RM agent prior to providing a response to the component. It is assumed the * component's cannot block. Therefore, all responses must go through the * callback function provided by the component. Return an error if the * component does not provide a callback function */ } /* Provide a transaction ID back to the component so that it can map service requests * to the responses received via the component's callback function */ responseInfo->requestId = transactionReceipt.transactionId; } else { } } void Rm_serviceHandler (void *rmHandle, Rm_ServiceReqInfo *requestInfo, Rm_ServiceRespInfo *responseInfo) { Rm_Inst *rmInst = (Rm_Inst *) rmHandle; Rm_Transaction transaction; Rm_TransactionReceipt transactionReceipt; void *key; /* Prevent another component using the same instance from wiping out the current * service request */ key = Rm_osalLocalCsEnter(); /* Make sure serviceType is valid and that a callback function has been provided */ if ((requestInfo->serviceType < Rm_service_FIRST) || (requestInfo->serviceType > Rm_service_LAST)) { responseInfo->serviceResult = RM_SERVICE_ERROR_INVALID_SERVICE_TYPE; Rm_osalLocalCsExit(key); return; } else if (requestInfo->serviceCallback == NULL) { responseInfo->serviceResult = RM_SERVICE_ERROR_CALLBACK_NOT_PROVIDED; Rm_osalLocalCsExit(key); return; } /* Clear the transaction and receipt prior to using them */ memset((void *) &transaction, 0, sizeof(Rm_Transaction)); memset((void *) &transactionReceipt, 0, sizeof(Rm_TransactionReceipt)); /* Transfer request information into the internal transaction format */ transaction.transCommand = (Rm_Command) requestInfo->serviceType; transaction.transCallback = requestInfo->serviceCallback; /* Policy modifications will never originate from components */ strcpy ((void *) &(transaction.u.resourceInfo.resName)[0], (void *)requestInfo->resName); transaction.u.resourceInfo.resBase = requestInfo->resourceBase; transaction.u.resourceInfo.resNum = requestInfo->resourceNum; transaction.u.resourceInfo.resAlign = requestInfo->resourceAlign; strcpy ((void *) &(transaction.u.resourceInfo.resNsName)[0], (void *)requestInfo->resNsName); /* Pass the new transaction to the transaction processor */ Rm_transactionProcessor (rmHandle, &transaction, &transactionReceipt); /* Copy transaction receipt information into the response info fields for the * component based on the result of the transaction. Denied service requests * will have only a valid serviceResult field */ responseInfo->serviceResult = transactionReceipt->transactionResult; if (responseInfo->serviceResult == RM_SERVICE_APPROVED) { /* Service was approved. The resource data should be passed back * to the component */ responseInfo->resBase = transactionReceipt.resBase; responseInfo->resNum = transactionReceipt.resNum; } else if (responseInfo->serviceResult == RM_SERVICE_PROCESSING) { /* The service is still being processed. Provide the transaction ID * back to the component so that it can sort service responses received * via the provided callback function */ responseInfo->requestId = transactionReceipt.transactionId; } Rm_osalLocalCsExit(key); return; } /********************************************************************** *********************** Application visible APIs *************************** **********************************************************************/ Rm_Handle Rm_init(Rm_InitCfg *initCfg) { Rm_Inst *rmInst; /* Instance creation checks. Add one to strlen calculation for null character */ if ((strlen(initCfg->instName) + 1) > RM_INSTANCE_NAME_MAX_CHARS) { /* Failure: Instance name is too big */ return (NULL); } /* Get memory for RM instance from local memory */ rmInst = Rm_osalMalloc (sizeof(Rm_Inst), false); /* Populate instance based on input parameters */ strcpy (&rmInst->name[0], initCfg->instName); rmInst->instType = initCfg->instType; rmInst->instState = RM_state_IDLE; rmInst->registeredWithDelegateOrServer = false; rmInst->serviceCallback = NULL; /* Populate the instance transport callouts */ rmInst->transport.rmAllocPkt = initCfg->rmAllocPktFuncPtr; rmInst->transport.rmFreePkt = initCfg->rmFreePktFuncPtr; rmInst->transport.rmSend = initCfg->rmSendFuncPtr; rmInst->transport.rmReceive = initCfg->rmReceiveFuncPtr; rmInst->transport.rmNumPktsReceived = initCfg->rmNumPktsReceivedFuncPtr; /* Initialize the transport routing map linked list pointer to NULL. The linked list * nodes will be created when the application registers transports */ rmInst->routeMap = NULL; /* Initialize the transaction queue linked list pointer to NULL. The linked list * nodes will be created when the transactions are forwarded to higher level RM * agents. */ rmInst->transactionQueue= NULL; /* RM Server specific actions */ if (rmInst->instType == Rm_instType_SERVER) { /* parse DTB, etc */ } /* Instance startup policies are only used for Servers and Client Delegates */ if (rmInst->instType != Rm_instType_CLIENT) { rmInst->instPolicy = initCfg->startupPolicy; /* Store policy via policy APIs ... */ } /* Return the RM Handle */ return ((Rm_Handle) rmInst); } Rm_Result Rm_preMainAllocResource(Rm_PreMainAllocInfo *preMainAllocInfo) { } Rm_ServiceHandle Rm_getServiceHandle(Rm_Handle rmHandle) { Rm_Inst *rmInst = (Rm_Inst *) rmHandle; Rm_ServicesPort *newServicePort; /* Create a new service handle for the specified RM instance */ /* Get memory for a new service port from local memory */ newServicePort = Rm_osalMalloc (sizeof(Rm_ServicesPort), false); newServicePort->rmHandle = rmHandle; newServicePort->rmService = rmServiceHandler; return ((Rm_ServiceHandle) newServicePort); } /* Resource requests that come directly from application go through this API */ void Rm_requestService (void *rmHandle, Rm_ServiceReqInfo *requestInfo, Rm_ServiceRespInfo *responseInfo) { Rm_serviceHandler(rmHandle, requestInfo, responseInfo); } Rm_TransportHandle Rm_registerTransport (Rm_Handle rmHandle, Rm_TransCfg *transCfg) { Rm_Inst *rmInst = (Rm_Inst *) rmHandle; Rm_TransRouteMapNode *existingRouteMap = (Rm_TransRouteMapNode *) rmInst->routeMap; Rm_TransRouteMapNode *newRouteMapNode; /* RM Servers cannot connect to other Servers */ if ((rmInst->instType == Rm_instType_SERVER) && (transCfg->rmRemoteInstType == Rm_instType_SERVER)) { return (NULL); /* Error -return null */ } /* Verify Clients and Client Delegates are not registering with more than one * Client Delegate or Server. Assuming a Client Delegate can register with another * Client Delegate that can "act" as a server */ if (((rmInst->instType == Rm_instType_CLIENT) || (rmInst->instType == Rm_instType_CLIENT_DELEGATE)) && ((transCfg->rmRemoteInstType == Rm_instType_CLIENT_DELEGATE) || (transCfg->rmRemoteInstType == Rm_instType_SERVER)) && rmInst->registeredWithDelegateOrServer) { return (NULL); /* Error -return null */ } /* Error checks passed - Create a new transport handle for the specified RM instance */ /* Get memory for a new routing map node from local memory */ newRouteMapNode = Rm_osalMalloc (sizeof(Rm_TransRouteMapNode), false); /* Populate the new node. The address of the node itself is stored since this address is returned * to the application as the Rm_TransportHandle */ newRouteMapNode->transHandle = newRouteMapNode; newRouteMapNode->remoteInstType = transCfg->rmRemoteInstType; newRouteMapNode->nextNode = NULL; /* New node will always be NULL */ /* Check if there are any entries in the routing map */ if (existingRouteMap) { /* At least one entry in the routing map. Add the new routing map node to the end of the * routing map node list */ while (existingRouteMap->nextNode != NULL) { /* Traverse the list until arriving at the last node */ existingRouteMap = existingRouteMap->nextNode; } /* Add the new node to the end of the list */ existingRouteMap->nextNode = newRouteMapNode; } else { /* A routing map does not currently exist. The new node is the first entry */ rmInst->routeMap = newRouteMapNode; } /* Specify RM instance has registered with a higher level agent */ if ((newRouteMapNode->remoteInstType == Rm_instType_CLIENT_DELEGATE) || (newRouteMapNode->remoteInstType == Rm_instType_SERVER)) { rmInst->registeredWithDelegateOrServer = true; } return ((Rm_TransportHandle) newRouteMapNode); } Rm_Result Rm_unregisterTransport (Rm_Handle rmHandle, Rm_TransportHandle transHandle) { Rm_Inst *rmInst = (Rm_Inst *) rmHandle; Rm_TransRouteMapNode *routeMapNode = (Rm_TransRouteMapNode *) rmInst->routeMap; Rm_TransRouteMapNode *prevNode = NULL; /* Make sure a routing map exists for the RM instance */ if (routeMapNode == NULL) { return (-1); /* TEMP ERROR RETURN */ } /* Find the transport handle within the specified RM instance's routing map. */ while (1) { if (routeMapNode->transHandle == transHandle) { /* Match: break out of loop and delete the node */ break; } else if (routeMapNode->nextNode == NULL) { return (-1); /* TEMP ERROR: transhandle does not exist for RM instance */ } prevNode = routeMapNode; routeMapNode = routeMapNode->nextNode; } /* Delete the routing map node */ if ((prevNode == NULL) && routeMapNode->nextNode) { /* Node to be deleted exists at start of route map. Map second node to be start of * node list as long as there are more than one routing map nodes */ rmInst->routeMap = routeMapNode->nextNode; } else { /* Node to be deleted is in the middle or at end of the list. Adjust adjacent * nodes pointers. This covers the case where the node to be removed is at the * end of the list. */ prevNode->nextNode = routeMapNode->nextNode; } /* Remove specification in RM instance that it has been connected to an upper level agent * if the node to be deleted has a remote instance type of Client Delegate or Server */ if ((routeMapNode->remoteInstType == Rm_instType_CLIENT_DELEGATE) || (routeMapNode->remoteInstType == Rm_instType_SERVER)) { rmInst->registeredWithDelegateOrServer = false; } /* Delete the node, free the memory associated with the node. */ Rm_osalFree((void *) routeMapNode, sizeof(Rm_TransRouteMapNode), false); return (RM_OK); } Rm_TransVerifyResult Rm_verifyTransport (Rm_Handle, uint32_t timeout, Rm_TransFailData *failData) { } uint32_t Rm_getVersion (void) { return RM_VERSION_ID; } const char* Rm_getVersionStr (void) { return rmVersionStr; } /** @} */