Bug fixes, cleanup, expanded test code
[keystone-rtos/rm-lld.git] / src / rm.c
1 /**\r
2  *   @file  rm.c\r
3  *\r
4  *   @brief   \r
5  *      This is the Resource Manager source.\r
6  *\r
7  *  \par\r
8  *  ============================================================================\r
9  *  @n   (C) Copyright 2012, Texas Instruments, Inc.\r
10  * \r
11  *  Redistribution and use in source and binary forms, with or without \r
12  *  modification, are permitted provided that the following conditions \r
13  *  are met:\r
14  *\r
15  *    Redistributions of source code must retain the above copyright \r
16  *    notice, this list of conditions and the following disclaimer.\r
17  *\r
18  *    Redistributions in binary form must reproduce the above copyright\r
19  *    notice, this list of conditions and the following disclaimer in the \r
20  *    documentation and/or other materials provided with the   \r
21  *    distribution.\r
22  *\r
23  *    Neither the name of Texas Instruments Incorporated nor the names of\r
24  *    its contributors may be used to endorse or promote products derived\r
25  *    from this software without specific prior written permission.\r
26  *\r
27  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \r
28  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT \r
29  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
30  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT \r
31  *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, \r
32  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT \r
33  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
34  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
35  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT \r
36  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE \r
37  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
38  *\r
39  *  \par\r
40 */\r
41 \r
42 /* RM Types */\r
43 #include <ti/drv/rm/rm_types.h>\r
44 \r
45 /* RM external includes */\r
46 #include <ti/drv/rm/rm.h>\r
47 #include <ti/drv/rm/rm_services.h>\r
48 #include <ti/drv/rm/rm_transport.h>\r
49 #include <ti/drv/rm/rm_policy.h>\r
50 \r
51 /* RM internal includes */\r
52 #include <ti/drv/rm/include/rm_loc.h>\r
53 #include <ti/drv/rm/include/rm_transportloc.h>\r
54 #include <ti/drv/rm/include/rm_servicesloc.h>\r
55 #include <ti/drv/rm/include/rm_nameserverloc.h>\r
56 #include <ti/drv/rm/include/rm_dtb_utilloc.h>\r
57 \r
58 /* RM LIBFDT includes */\r
59 #include <ti/drv/rm/src/libfdt/libfdt.h>\r
60 \r
61 /* AVL BBST includes */\r
62 #include <ti/drv/rm/include/tree.h>\r
63 \r
64 /* RM OSAL layer */\r
65 #include <rm_osal.h>\r
66 \r
67 /**********************************************************************\r
68  ************************** Globals ***********************************\r
69  **********************************************************************/\r
70 char rmIntegerAllocator[] = "integer";\r
71 char rmTreeAllocator[] = "tree";\r
72 \r
73 extern char rmDtbStartingNode[];\r
74 \r
75 /** @brief Global Variable which describes the RM Version Information */\r
76 const char   rmVersionStr[] = RM_VERSION_STR ":" __DATE__  ":" __TIME__;\r
77 \r
78 /**********************************************************************\r
79  ************** Red-Black BBST Tree Allocator Functions ***************\r
80  **********************************************************************/\r
81 \r
82 /* Prototype for function that allocates new tree nodes */\r
83 Rm_ResourceTreeNode *Rm_newResourceTreeNode(uint32_t resourceBase, uint32_t resourceLength, \r
84                                             char *allocatedTo)\r
85 {\r
86     Rm_ResourceTreeNode *newNode = NULL;\r
87 \r
88     newNode = Rm_osalMalloc(sizeof(Rm_ResourceTreeNode));\r
89 \r
90     /* Populate the RM relevant fields */\r
91     newNode->base = resourceBase;\r
92     newNode->length = resourceLength;\r
93     strcpy(newNode->allocatedTo, allocatedTo);\r
94 \r
95     return(newNode);\r
96 }\r
97 \r
98 /* Prototype for function that frees new tree nodes */\r
99 void Rm_freeResourceTreeNode(Rm_ResourceTreeNode *treeNode)\r
100 {\r
101     /* Free the memory associated with the tree node. */\r
102     Rm_osalFree((void *)treeNode, sizeof(Rm_ResourceTreeNode));\r
103 }\r
104 \r
105 /* Prototype for tree node comparison function\r
106  * element1 < element2 --> return < 0\r
107  * element1 = element2 --> return 0\r
108  * element1 > element2 --> return > 0 */\r
109 int Rm_ResourceTreeNodeCompare(Rm_ResourceTreeNode *element1, Rm_ResourceTreeNode *element2)\r
110 {\r
111     uint32_t element1End = element1->base + element1->length - 1;\r
112     uint32_t element2End = element2->base + element2->length - 1;\r
113 \r
114     if (element1End < element2->base)\r
115     {\r
116         /* End of element1 range is less than the start of element2's range.  Return a negative\r
117          * value */\r
118         return (-1);\r
119     }\r
120     else if (element1->base > element2End)\r
121     {\r
122         /* Start of element1 range is after end of element2's range.  Return a positive value */\r
123         return (1);\r
124     }\r
125     else\r
126     {\r
127         /* If neither of the latter conditions were satisfied there is some overlap between\r
128          * element1 and element2.  Return 0 since the application must handle this overlap. */\r
129         return (0);\r
130     }\r
131 }\r
132 \r
133 /* Generate the red-black tree manipulation functions */\r
134 RB_GENERATE(_Rm_ResourceTree, _Rm_ResourceTreeNode, linkage, Rm_ResourceTreeNodeCompare);\r
135 \r
136 /**********************************************************************\r
137  ********************** Internal Functions ****************************\r
138  **********************************************************************/\r
139 \r
140 Rm_Transaction *Rm_transactionQueueAdd(Rm_Inst *rmInst)\r
141 {\r
142     Rm_Transaction *transactionQueue = (Rm_Transaction *)rmInst->transactionQueue;\r
143     Rm_Transaction *newTransaction = NULL;\r
144     void *key;\r
145 \r
146     /* Lock access to the RM instance's transaction queue */\r
147     key = Rm_osalMtCsEnter();\r
148 \r
149     /* Get memory for a new transaction from local memory */\r
150     newTransaction = Rm_osalMalloc(sizeof(Rm_Transaction));\r
151 \r
152     /* Return if the memory allocated for the transaction entry is NULL */\r
153     if (newTransaction != NULL)\r
154     {\r
155         /* Clear the transaction */\r
156         memset((void *)newTransaction, 0, sizeof(Rm_Transaction));\r
157 \r
158         /* Create an ID for the new transaction.  The ID will be used for two purposes:\r
159          * 1) Matching responses from higher level RM agents to requests\r
160          * 2) Provided to the component that requested the service so that it can match its\r
161          *    request with the response it receives via its callback function it provided */\r
162         newTransaction->localId = Rm_transactionGetSequenceNum(rmInst);\r
163         /* New transaction's nextTransaction pointer will always be NULL */\r
164         newTransaction->nextTransaction = NULL;  \r
165 \r
166         /* Check if there are any transactions in the transaction queue */\r
167         if (transactionQueue)\r
168         {\r
169             /* At least one transaction in the transaction queue.  Add the new entry to the \r
170              * end of the transaction queue */\r
171             while (transactionQueue->nextTransaction != NULL)\r
172             {\r
173                 /* Traverse the list until arriving at the last transaction */\r
174                 transactionQueue = transactionQueue->nextTransaction;\r
175             }\r
176 \r
177             /* Add the new transaction to the end of the queue */\r
178             transactionQueue->nextTransaction = newTransaction;\r
179         }\r
180         else\r
181         {\r
182             /* The transaction queue does not currently exist.  The new transaction is the \r
183              * first transaction */\r
184             rmInst->transactionQueue = newTransaction;\r
185         }\r
186     }\r
187 \r
188     Rm_osalMtCsExit(key);\r
189     return (newTransaction);\r
190 }\r
191 \r
192 Rm_Transaction *Rm_transactionQueueFind(Rm_Inst *rmInst, uint32_t transactionId)\r
193 {\r
194     Rm_Transaction *transaction = (Rm_Transaction *)rmInst->transactionQueue;\r
195 \r
196     /* Make sure there is at least one transaction in the transaction queue */\r
197     if (transaction != NULL)\r
198     {\r
199         /* Find the transaction ID within the specified RM instance's transaction queue.\r
200          * If the end of the transaction queue is reached without finding the transaction the \r
201          * transaction pointer will be NULL */\r
202         while (transaction != NULL)\r
203         {\r
204             if (transaction->localId == transactionId)\r
205             {\r
206                 /* Match: break out of loop and return the transaction */\r
207                 break;             \r
208             }\r
209             transaction = transaction->nextTransaction;\r
210         }\r
211     }\r
212 \r
213     return (transaction);\r
214 }\r
215 \r
216 int32_t Rm_transactionQueueDelete(Rm_Inst *rmInst, uint32_t transactionId)\r
217 {\r
218     Rm_Transaction *transaction = (Rm_Transaction *) rmInst->transactionQueue;\r
219     Rm_Transaction *prevTransaction = NULL;\r
220     int32_t retVal = RM_SERVICE_STATE_OKAY;\r
221     void *key;\r
222 \r
223     /* Lock access to the RM instance's transaction queue */\r
224     key = Rm_osalMtCsEnter();\r
225 \r
226     /* Find the transaction ID within the specified RM instance's transaction queue. */\r
227     while (transaction != NULL)\r
228     {\r
229         if (transaction->localId == transactionId)\r
230         {\r
231             /* Match: break out of loop and delete the transaction */\r
232             break;             \r
233         }\r
234 \r
235         prevTransaction = transaction;\r
236         transaction = transaction->nextTransaction;\r
237     }\r
238 \r
239     /* Traversed entire queue but did not find transaction */\r
240     if (transaction == NULL)\r
241     {\r
242         retVal = RM_SERVICE_ERROR_SERVICE_TRANSACTION_DOES_NOT_EXIST;\r
243     }\r
244     else\r
245     {\r
246         /* Delete the transaction */\r
247         if (prevTransaction == NULL)\r
248         {\r
249             /* Transaction to be deleted exists at start of transaction queue.  Map second\r
250              * transaction to be start of transaction queue as long as there are more than\r
251              * one transactions. */\r
252             rmInst->transactionQueue = transaction->nextTransaction;\r
253         }\r
254         else\r
255         {\r
256             /* Transaction to be deleted is in the middle or at end of the queue.  Adjust \r
257              * adjacent transaction pointers.  This covers the case where the transaction to be \r
258              * removed is at the end of the queue. */\r
259             prevTransaction->nextTransaction = transaction->nextTransaction;\r
260         }\r
261 \r
262         /* Free the memory associated with the transaction. */\r
263         Rm_osalFree((void *)transaction, sizeof(Rm_Transaction));\r
264     }\r
265 \r
266     Rm_osalMtCsExit(key);\r
267     return (retVal);\r
268 }\r
269 \r
270 uint32_t Rm_transactionInitSequenceNum(void)\r
271 {\r
272     /* Sequence number can never have a value of zero so that there are no conflicts\r
273      * with transactions that have a remoteOriginatingId of zero */\r
274     return (1);\r
275 }\r
276 \r
277 uint32_t Rm_transactionGetSequenceNum(Rm_Inst *rmInst)\r
278 {\r
279     uint32_t sequenceNum = 0;\r
280 \r
281     /* Get the next sequence number and then increment.  If there's an overflow\r
282      * assign the initial value instead of incrementing. */\r
283     if (rmInst->transactionSeqNum + 1 < rmInst->transactionSeqNum)\r
284     {\r
285         /* Overflow */\r
286         sequenceNum = rmInst->transactionSeqNum;\r
287         rmInst->transactionSeqNum = Rm_transactionInitSequenceNum();\r
288     }\r
289     else\r
290     {\r
291         sequenceNum = rmInst->transactionSeqNum++;\r
292     }    \r
293 \r
294     return (sequenceNum);\r
295 }\r
296 \r
297 Rm_Allocator *Rm_allocatorAdd(Rm_Inst *rmInst, const char *resourceName)\r
298 {\r
299     Rm_Allocator *allocators = (Rm_Allocator *)rmInst->allocators;\r
300     Rm_Allocator *newAllocator = NULL;\r
301     void *key;\r
302 \r
303     /* Lock access to the RM instance's allocator list */\r
304     key = Rm_osalMtCsEnter();\r
305 \r
306     /* Get memory for a new allocator from local memory */\r
307     newAllocator = Rm_osalMalloc(sizeof(Rm_Allocator));\r
308 \r
309     /* Return if the memory allocated for the allocator is NULL */\r
310     if (newAllocator != NULL)\r
311     {\r
312         /* Clear the allocator */\r
313         memset((void *)newAllocator, 0, sizeof(Rm_Allocator));\r
314 \r
315         /* Populate the allocator */\r
316         strcpy(newAllocator->resourceName, resourceName);\r
317         /* allocator's root entry will be created by the invoking function */\r
318         newAllocator->allocatorRootEntry = NULL;\r
319         /* New allocator's nextAllocator pointer will always be NULL */\r
320         newAllocator->nextAllocator = NULL;  \r
321 \r
322         /* Check if there are any allocators in the allocator list */\r
323         if (allocators)\r
324         {\r
325             /* At least one allocator in the allocator list.  Add the new allocator to the \r
326              * end of the allocator list */\r
327             while (allocators->nextAllocator != NULL)\r
328             {\r
329                 /* Traverse the list until arriving at the last allocator */\r
330                 allocators = allocators->nextAllocator;\r
331             }\r
332 \r
333             /* Add the new allocator to the end of the list */\r
334             allocators->nextAllocator = newAllocator;\r
335         }\r
336         else\r
337         {\r
338             /* The allocator list does not currently exist.  The new allocator is the \r
339              * first allocator */\r
340             rmInst->allocators = newAllocator;\r
341         }\r
342     }\r
343 \r
344     Rm_osalMtCsExit(key);\r
345     return (newAllocator);\r
346 }\r
347 \r
348 Rm_Allocator *Rm_allocatorFind(Rm_Inst *rmInst, char *resourceName)\r
349 {\r
350     Rm_Allocator *allocator = (Rm_Allocator *)rmInst->allocators;\r
351 \r
352     /* Make sure there is at least one allocator in the allocator list */\r
353     if (allocator != NULL)\r
354     {\r
355         /* Find the resource name within the allocator list.  If the end of the\r
356          * allocator list is reached without finding the resource name the \r
357          * allocator pointer will be NULL */\r
358         while (allocator != NULL)\r
359         {\r
360             if (strcmp(allocator->resourceName, resourceName) == 0)\r
361             {\r
362                 /* Match: break out of loop and return the allocator */\r
363                 break;             \r
364             }\r
365             allocator = allocator->nextAllocator;\r
366         }\r
367     }\r
368 \r
369     return (allocator);\r
370 }\r
371 \r
372 int32_t Rm_allocatorDelete(Rm_Inst *rmInst, char *resourceName)\r
373 {\r
374     Rm_Allocator *allocator = (Rm_Allocator *) rmInst->allocators;\r
375     Rm_Allocator *prevAllocator = NULL;\r
376     int32_t retVal = RM_SERVICE_STATE_OKAY;\r
377     void *key;\r
378 \r
379     /* Lock access to the RM instance's allocator list */\r
380     key = Rm_osalMtCsEnter();\r
381 \r
382     /* Find the resource within the specified RM instance's allocator list. */\r
383     while (allocator != NULL)\r
384     {\r
385         if (strcmp(allocator->resourceName, resourceName) == 0)\r
386         {\r
387             /* Match: break out of loop and delete the transaction */\r
388             break;             \r
389         }\r
390 \r
391         prevAllocator = allocator;\r
392         allocator = allocator->nextAllocator;\r
393     }\r
394 \r
395     /* Traversed entire list but did not find allocator. */\r
396     if (allocator == NULL)\r
397     {\r
398         retVal = -22; /* TEMP ERROR: Can't conflict with LIBFDT errors */\r
399     }\r
400     else\r
401     {\r
402         /* Delete the allocator */\r
403         if (prevAllocator == NULL)\r
404         {\r
405             /* Allocator to be deleted exists at start of allocator list.  Map second\r
406              * allocator to be start of allocator list as long as there are more than\r
407              * one allocators. */\r
408             rmInst->allocators = allocator->nextAllocator;\r
409         }\r
410         else\r
411         {\r
412             /* Allocator to be deleted is in the middle or at end of the list.  Adjust \r
413              * adjacent allocator pointers.  This covers the case where the allocator to be \r
414              * removed is at the end of the list. */\r
415             prevAllocator->nextAllocator = allocator->nextAllocator;\r
416         }\r
417 \r
418         /* Free the memory associated with the allocator. */\r
419         Rm_osalFree((void *)allocator, sizeof(Rm_Allocator));\r
420     }\r
421 \r
422     Rm_osalMtCsExit(key);\r
423     return (retVal);\r
424 }\r
425 \r
426 int32_t Rm_createTreeAllocator(Rm_Inst *rmInst, const char *resourceName, Rm_ResourceRange *range)\r
427 {\r
428     Rm_Allocator *allocator = NULL;\r
429     Rm_ResourceTree *treeRootEntry = NULL;\r
430     Rm_ResourceTreeNode *treeNode = NULL;\r
431     Rm_ResourceTreeNode *collidingNode = NULL;\r
432 \r
433     /* Create the new base integer allocator */\r
434     allocator = Rm_allocatorAdd(rmInst, resourceName);\r
435 \r
436     /* Create the tree root entry and initialize it */\r
437     treeRootEntry = Rm_osalMalloc(sizeof(Rm_ResourceTree));\r
438     RB_INIT(treeRootEntry);\r
439 \r
440     /* Create a node in the tree for resource range and insert them into the tree. */\r
441     while (range != NULL)\r
442     {\r
443         treeNode = Rm_newResourceTreeNode(range->base, range->length, RM_NOT_ALLOCATED_STRING);\r
444 \r
445         /* Insert the node into the tree */\r
446         collidingNode = RB_INSERT(_Rm_ResourceTree, treeRootEntry, treeNode);\r
447 \r
448         if (collidingNode)\r
449         {\r
450             Rm_ResourceTreeNode *nextNode = NULL;\r
451             \r
452             /* Node that was inserted colliding with an existing node.  Clean up the tree\r
453              * that's been allocated thus far and return an error since there should be no\r
454              * collisions */\r
455             for (treeNode = RB_MIN(_Rm_ResourceTree, treeRootEntry); treeNode != NULL; treeNode = nextNode)\r
456             {\r
457                         nextNode = RB_NEXT(_Rm_ResourceTree, treeRootEntry, treeNode);\r
458                         RB_REMOVE(_Rm_ResourceTree, treeRootEntry, nextNode);\r
459                 Rm_freeResourceTreeNode(treeNode);\r
460                 }\r
461             /* Delete the tree root entry and the allocator */\r
462             Rm_osalFree((void *)treeRootEntry, sizeof(Rm_ResourceTree));\r
463             Rm_allocatorDelete(rmInst, allocator->resourceName);\r
464             return (-24); /* TODO FIX RETURN */\r
465         }\r
466 \r
467         range = range->nextRange;\r
468     }\r
469 \r
470     /* Assign the tree's root to the allocator */\r
471     allocator->allocatorRootEntry = treeRootEntry;\r
472 \r
473     return(0);   /* TODO: FIX THIS RETURN */\r
474 }\r
475 \r
476 /* Called when an allocate request is made but the base is unspecified.  RM must preallocate\r
477  * resources which then must be checked against the RM policy for the instance.  If the\r
478  * policy does not agree another resource(s) must be preallocated and tested against the \r
479  * policy.  Policy will provide initialize the preallocate with the base that it allows\r
480  * for the rm instance for the specified resource. */\r
481 int32_t Rm_treePreAllocate(Rm_Allocator *allocator, Rm_AllocatorOpInfo *opInfo)\r
482 {\r
483     Rm_ResourceTreeNode findNode;\r
484     Rm_ResourceTreeNode *matchingNode = NULL;\r
485     uint32_t policyRangeEnd = opInfo->policyBase + opInfo->policyLength - 1;\r
486     uint32_t index;\r
487     bool resourceFound = FALSE;\r
488     int32_t retVal = RM_SERVICE_PROCESSING;\r
489 \r
490     /* Find the tree node that contains the first value in the specified policy range. */\r
491     if (opInfo->policyBase)\r
492     {\r
493         findNode.base = opInfo->policyBase;\r
494     }\r
495     else\r
496     {\r
497         matchingNode = RB_MIN(_Rm_ResourceTree, allocator->allocatorRootEntry);\r
498         findNode.base = matchingNode->base;\r
499     }\r
500     \r
501     findNode.length = 1;\r
502     matchingNode = RB_FIND(_Rm_ResourceTree, allocator->allocatorRootEntry, &findNode);\r
503 \r
504     if (matchingNode != NULL)\r
505     {\r
506         /* Begin searching for an available range of resources starting from the\r
507          * matching node */\r
508         for (index = matchingNode->base; index <= policyRangeEnd;)\r
509         {\r
510             /* Is the matchingNode free? */\r
511             if (strcmp(matchingNode->allocatedTo, RM_NOT_ALLOCATED_STRING) == 0)\r
512             {\r
513                 uint32_t matchEnd = matchingNode->base + matchingNode->length - 1;\r
514 \r
515                 /* Move index to the first resource satisfying the alignment property */\r
516                 if ((index % opInfo->policyAlignment) != 0)\r
517                 {\r
518                     index += (opInfo->policyAlignment - (index % opInfo->policyAlignment));\r
519                 }\r
520                 \r
521                 /* Move through the node's resource range looking for a contiguous set of resources\r
522                  * that satisfy the request. */\r
523                 while ((index <= matchEnd) && (index <= policyRangeEnd))\r
524                 {\r
525                     if (((index + opInfo->resourceInfo->length - 1) <= matchEnd) &&\r
526                         ((index + opInfo->resourceInfo->length - 1) <= policyRangeEnd))\r
527                     {\r
528                         /* Found a resource range in the node that satisfies the requirements */\r
529                         opInfo->resourceInfo->base = index;\r
530                         resourceFound = TRUE;\r
531                         break;\r
532                     }\r
533 \r
534                     /* Move index to the next resource value that satisfies the alignment property */\r
535                     index += (opInfo->policyAlignment - (index % opInfo->policyAlignment));\r
536                 }\r
537             }\r
538             \r
539             if (!resourceFound)\r
540             {\r
541                 /* Move to the next tree node */\r
542                 matchingNode = RB_NEXT(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);\r
543                 if (matchingNode == NULL)\r
544                 {\r
545                     /* Reached end of tree.  Resource range does not exist.  Leave the search\r
546                      * loop */\r
547                     break;\r
548                 }\r
549                 else\r
550                 {\r
551                     index = matchingNode->base;\r
552                 }\r
553             }\r
554             else\r
555             {\r
556                 /* Found a resource range that satisfies the request properties.  Break out of the\r
557                  * search loop */\r
558                 break;\r
559             }\r
560         }\r
561 \r
562         if (!resourceFound)\r
563         {\r
564             retVal = RM_SERVICE_DENIED_RESOURCE_ALREADY_ALLOCATED;\r
565         }\r
566     }\r
567     else\r
568     {\r
569         retVal = RM_SERVICE_DENIED_RESOURCE_VALUE_RANGE_DOES_NOT_EXIST;\r
570     }\r
571 \r
572     return(retVal); \r
573 }\r
574 \r
575 /* Assume the policy has already approved of the allocation */\r
576 int32_t Rm_treeAllocate(Rm_Allocator *allocator, Rm_AllocatorOpInfo *opInfo)\r
577 {\r
578     Rm_ResourceTreeNode findNode;\r
579     Rm_ResourceTreeNode *matchingNode = NULL;\r
580     Rm_ResourceTreeNode *leftNode = NULL;\r
581     Rm_ResourceTreeNode *rightNode = NULL;  \r
582     uint32_t findEnd, matchingEnd;\r
583     int32_t retVal;\r
584 \r
585     /* Find the tree node that contains the specified resource range */\r
586     findNode.base = opInfo->resourceInfo->base;\r
587     findNode.length = opInfo->resourceInfo->length;\r
588     matchingNode = RB_FIND(_Rm_ResourceTree, allocator->allocatorRootEntry, &findNode);\r
589 \r
590     if (matchingNode != NULL)\r
591     {\r
592         findEnd = findNode.base + findNode.length - 1;\r
593         matchingEnd = matchingNode->base + matchingNode->length - 1;\r
594         \r
595         /* Does the request range fit within the matching nodes entire range? */\r
596         if ((findNode.base >= matchingNode->base) && (findEnd <= matchingEnd))\r
597         {\r
598             /* Handle node create, combine, deletion based on the request range if\r
599              * resources are available. */\r
600             if (strcmp(matchingNode->allocatedTo, RM_NOT_ALLOCATED_STRING) == 0)\r
601             {\r
602                 /* Handle case where the findNode range matches the matchingNode\r
603                  * range exactly.\r
604                  *\r
605                  * base0                                  base0+length0-1\r
606                  *   |<---------------length0------------------->|  => existing node\r
607                  *   |<---------------length1------------------->|  => requested resources\r
608                  * base1                                  base1+length1-1\r
609                  */    \r
610                 if ((findNode.base == matchingNode->base) && (findEnd == matchingEnd))\r
611                 {\r
612                     /* Can reserve matchingNode's resources in-place */\r
613                     strcpy(matchingNode->allocatedTo, opInfo->srcInstName);\r
614                 }\r
615                 /* Handle case where the findNode range is a subset of the matchingNode\r
616                  * range and neither of the boundaries of the two ranges are equivalent.\r
617                  *\r
618                  * base0                                  base0+length0-1\r
619                  *   |<---------------length0------------------->|  => existing node\r
620                  *         |<---------length1---------->|  => requested resources\r
621                  *       base1                   base1+length1-1\r
622                  */    \r
623                 else if ((findNode.base > matchingNode->base) && (findEnd < matchingEnd))\r
624                 {\r
625                     /* Split the matching node into three nodes:\r
626                      * left node - free resources to left of newly allocated resources\r
627                      * middle node - newly allocated resources that satisfy the request\r
628                      * right node - free resources to the right of newly allocated resources */\r
629 \r
630                     /* Remove the matching node from the tree for modification. */\r
631                     RB_REMOVE(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);\r
632 \r
633                     /* New left node attributes:\r
634                      * base: base of the matching node\r
635                      * length: base of requested resources - base of matching node */\r
636                     leftNode = Rm_newResourceTreeNode(matchingNode->base, findNode.base - matchingNode->base,\r
637                                                       RM_NOT_ALLOCATED_STRING);\r
638                     /* New right node attributes:\r
639                      * base: base of the requested resources + length of requested resources\r
640                      * length: right bound of matching node - right bound of request resources */\r
641                     rightNode = Rm_newResourceTreeNode(findNode.base + findNode.length,\r
642                                                        matchingEnd - findEnd, RM_NOT_ALLOCATED_STRING);\r
643 \r
644                     /* Base and length of matching node become the base and length of the\r
645                      * requested resources */\r
646                     matchingNode->base = findNode.base;                                    \r
647                     matchingNode->length = findNode.length;\r
648                     /* Reserve the resources */\r
649                     strcpy(matchingNode->allocatedTo, opInfo->srcInstName);\r
650 \r
651                     /* Insert all the nodes */\r
652                     RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);\r
653                     RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, leftNode);\r
654                     RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, rightNode);\r
655                 }\r
656                 /* Handle cases where one of findNode range boundaries is equivalent to\r
657                  * one of the matchingNode range boundaries.\r
658                  *\r
659                  * base0                                  base0+length0-1\r
660                  *   |<---------------length0------------------->|  => existing node\r
661                  *   |<---------length1---------->|  => requested resources\r
662                  * base1                   base1+length1-1\r
663                  *\r
664                  * OR\r
665                  *\r
666                  * base0                                  base0+length0-1\r
667                  *   |<---------------length0------------------->|  => existing node\r
668                  *                  |<---------length1---------->|  => requested resources\r
669                  *                base1                   base1+length1-1                 \r
670                  */    \r
671                 else\r
672                 {     \r
673                     /* Remove the matchingNode from the tree since it will be edited */\r
674                     RB_REMOVE(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);\r
675                     \r
676                     if (findNode.base == matchingNode->base)\r
677                     {\r
678                         /* There may be a combine possibility to the left. Extract leftNode to check */\r
679                         leftNode = RB_PREV(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);\r
680                         \r
681                         /* Can the node to the left of the matchingNode be combined with the \r
682                          * findNode's range? */\r
683                         if (leftNode && (strcmp(leftNode->allocatedTo, opInfo->srcInstName) == 0))\r
684                         {\r
685                             /* Remove the leftNode from the tree for editing */\r
686                             RB_REMOVE(_Rm_ResourceTree, allocator->allocatorRootEntry, leftNode);\r
687 \r
688                             /* Combine the leftNode and the findNode */\r
689                             leftNode->length += findNode.length;\r
690                         }\r
691                         else\r
692                         {\r
693                             /* Allocate a new leftNode that will take the place of the findNode\r
694                              * range in tree. */\r
695                             leftNode = Rm_newResourceTreeNode(findNode.base, findNode.length,\r
696                                                               opInfo->srcInstName);\r
697                         }\r
698 \r
699                         /* Account for the leftNode in the matchingNode */\r
700                         matchingNode->base = findNode.base + findNode.length;\r
701                         matchingNode->length = matchingEnd - findEnd;  \r
702 \r
703                         /* Insert the left node */\r
704                         RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, leftNode);\r
705                     }\r
706                     else if (findEnd == matchingEnd)\r
707                     {\r
708                         /* There may be a combine possibility to the right. Extract rightNode to check */\r
709                         rightNode = RB_NEXT(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);\r
710                         \r
711                         /* Can the node to the right of the matchingNode be combined with the \r
712                          * findNode's range? */\r
713                         if (rightNode && (strcmp(rightNode->allocatedTo, opInfo->srcInstName) == 0))\r
714                         {\r
715                             /* Remove the rightNode from the tree for editing */\r
716                             RB_REMOVE(_Rm_ResourceTree, allocator->allocatorRootEntry, rightNode);\r
717 \r
718                             /* Combine the rightNode and the findNode */\r
719                             rightNode->base = findNode.base;\r
720                             rightNode->length += findNode.length;\r
721                         }\r
722                         else\r
723                         {\r
724                             /* Allocate a new rightNode that will take the place of the findNode\r
725                              * range in tree. */\r
726                             rightNode = Rm_newResourceTreeNode(findNode.base, findNode.length,\r
727                                                                opInfo->srcInstName);\r
728                         }\r
729 \r
730                         /* Account for the rightNode in the matchingNode */\r
731                         matchingNode->length -= findNode.length;  \r
732 \r
733                         /* Insert the right node */\r
734                         RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, rightNode);\r
735                     }\r
736 \r
737                     /* Reinsert the edited matching node */\r
738                     RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);\r
739                 }\r
740                 \r
741                 retVal = RM_SERVICE_APPROVED_AND_COMPLETED;\r
742             }\r
743             else\r
744             {\r
745                 /* A resource superset containing the requested range has\r
746                  * already been allocated. */\r
747                 retVal = RM_SERVICE_DENIED_RESOURCE_ALREADY_ALLOCATED;\r
748             }\r
749         }\r
750         else\r
751         {\r
752             /* Request ranges that span multiple nodes signify resources are\r
753              * not available because nodes are combined into larger contiguous ranges\r
754              * on resource free operations. */\r
755             retVal = RM_SERVICE_DENIED_RESOURCE_ALREADY_ALLOCATED;\r
756         }\r
757     }\r
758     else\r
759     {\r
760         /* The requested resources could not be found in the allocator */\r
761         retVal = RM_SERVICE_DENIED_RESOURCE_VALUE_RANGE_DOES_NOT_EXIST;\r
762     }\r
763 \r
764     return(retVal);        \r
765 }\r
766 \r
767 /* Assume policy has already approved of the free */\r
768 int32_t Rm_treeFree(Rm_Allocator *allocator, Rm_AllocatorOpInfo *opInfo)\r
769 {\r
770     Rm_ResourceTreeNode findNode;\r
771     Rm_ResourceTreeNode *matchingNode = NULL;\r
772     Rm_ResourceTreeNode *leftNode = NULL;\r
773     Rm_ResourceTreeNode *rightNode = NULL;\r
774     bool combineLeft = FALSE;\r
775     bool combineRight = FALSE;\r
776     uint32_t findEnd, matchingEnd;\r
777     int32_t retVal;\r
778 \r
779     /* Find the tree node that contains the specified resource range */\r
780     findNode.base = opInfo->resourceInfo->base;\r
781     findNode.length = opInfo->resourceInfo->length;\r
782     matchingNode = RB_FIND(_Rm_ResourceTree, allocator->allocatorRootEntry, &findNode);\r
783 \r
784     if (matchingNode != NULL)\r
785     {\r
786         findEnd = findNode.base + findNode.length - 1;\r
787         matchingEnd = matchingNode->base + matchingNode->length - 1;\r
788         \r
789         /* Does the free range fit within the matching nodes entire range?  It should\r
790          * either be the entire range or a subset set of the found range. (the latter\r
791          * satisfies the case where an entity allocated a contiguous block of resources\r
792          * then attempts to free a contiguous subset of the allocated block. */\r
793         if ((findNode.base >= matchingNode->base) && (findEnd <= matchingEnd))\r
794         {            \r
795             if (strcmp(matchingNode->allocatedTo, opInfo->srcInstName) == 0)\r
796             {\r
797                 /* Resources can be freed */\r
798 \r
799                 if ((findNode.base == matchingNode->base) && (findEnd == matchingEnd))\r
800                 {\r
801                     /* Case 1: free range equals allocated matched node exactly. Attempt to combine \r
802                      *         the range to be freed with the resource nodes to the left and\r
803                      *         right of the free range.\r
804                      *\r
805                      * |<--left node-->||<---matched node--->||<--right node-->|\r
806                      *                  |<---free request--->|\r
807                      */ \r
808 \r
809                     leftNode = RB_PREV(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);\r
810                     rightNode = RB_NEXT(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);\r
811 \r
812                     /* Remove the matching node from the tree and the nodes to the left and\r
813                      * right of the matching node.  Removing from tree will not\r
814                      * wipe any of the base+length data in the node.  Can reuse since they won't\r
815                      * be freed */\r
816                     RB_REMOVE(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);\r
817 \r
818                     /* See if the left or right or both nodes can be combined with the matching\r
819                      * node that will be freed. */\r
820                     if (leftNode && (strcmp(leftNode->allocatedTo, RM_NOT_ALLOCATED_STRING) == 0))\r
821                     {\r
822                         /* Combine the left node and the matching node */\r
823                         RB_REMOVE(_Rm_ResourceTree, allocator->allocatorRootEntry, leftNode);\r
824                         combineLeft = TRUE;\r
825                     }\r
826                     if (rightNode && (strcmp(rightNode->allocatedTo, RM_NOT_ALLOCATED_STRING) == 0))\r
827                     {\r
828                         /* Combine the right node and the matching node */\r
829                         RB_REMOVE(_Rm_ResourceTree, allocator->allocatorRootEntry, rightNode);\r
830                         combineRight = TRUE;\r
831                     }\r
832 \r
833                     /* Perform any combines, insert the leftover nodes, and free any memory associated\r
834                      * with any nodes that weren't reinserted into the tree */\r
835                     if (combineLeft && combineRight)\r
836                     {\r
837                         /* Combine all three nodes into the matchingNode.  Insert the freed cumulative\r
838                          * matching node and delete the memory for the old left and right nodes */\r
839                         matchingNode->base = leftNode->base;\r
840                         matchingNode->length = leftNode->length + matchingNode->length + rightNode->length;\r
841 \r
842                         Rm_freeResourceTreeNode(leftNode);\r
843                         Rm_freeResourceTreeNode(rightNode);                        \r
844                     }\r
845                     else if (combineLeft)\r
846                     {\r
847                         /* Combine the left and matching nodes.  Reinsert the right. */\r
848                         matchingNode->base = leftNode->base;\r
849                         matchingNode->length += leftNode->length;\r
850                         \r
851                         Rm_freeResourceTreeNode(leftNode);\r
852                         RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, rightNode);                        \r
853                     }\r
854                     else if (combineRight)\r
855                     {\r
856                         /* Combine the right and matching nodes.  Reinsert the left. */\r
857                         matchingNode->length += rightNode->length;\r
858                         \r
859                         Rm_freeResourceTreeNode(rightNode);\r
860                         RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, leftNode);\r
861                     }\r
862                     else\r
863                     {\r
864                         /* Combine cannot be performed.  Reinsert the left and right nodes then\r
865                          * free the matching node and reinsert it */\r
866                         RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, leftNode);\r
867                         RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, rightNode);\r
868                     }\r
869 \r
870                     /* No matter the combine route taken the matching node will always be declared\r
871                      * free and reinserted */\r
872                     strcpy(matchingNode->allocatedTo, RM_NOT_ALLOCATED_STRING);\r
873                     RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);                    \r
874                 }\r
875                 else if ((findNode.base > matchingNode->base) && (findEnd < matchingEnd))\r
876                 {\r
877                     /* Case 2: free range is less than range in matched node. Need to split\r
878                      *         the matched node into three nodes.\r
879                      *\r
880                      * |<----------matched node---------->|\r
881                      *        |<---free request--->|\r
882                      */ \r
883 \r
884                     /* Remove matching node for editing. */\r
885                     RB_REMOVE(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);\r
886 \r
887                     /* New left node attributes:\r
888                      * base: base of the matching node\r
889                      * length: base of requested resources - base of matching node */\r
890                     leftNode = Rm_newResourceTreeNode(matchingNode->base, findNode.base - matchingNode->base,\r
891                                                       matchingNode->allocatedTo); \r
892                     /* New right node attributes:\r
893                      * base: base of the requested resources + length of requested resources\r
894                      * length: right bound of matching node - right bound of request resources */\r
895                     rightNode = Rm_newResourceTreeNode(findNode.base + findNode.length,\r
896                                                        matchingEnd - findEnd, matchingNode->allocatedTo);\r
897 \r
898                     /* Insert the left and right nodes into the tree. */\r
899                     RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, leftNode);\r
900                     RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, rightNode);\r
901 \r
902                     /* Base and length of matching node become the base and length of the freed resources */\r
903                     matchingNode->base = findNode.base;                                    \r
904                     matchingNode->length = findNode.length;\r
905                     /* Free the resources and insert them into the tree */\r
906                     strcpy(matchingNode->allocatedTo, RM_NOT_ALLOCATED_STRING);\r
907                     RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);\r
908                 }\r
909                 else\r
910                 {\r
911                     /* Remove the matchingNode from the tree since it will be edited */\r
912                     RB_REMOVE(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);\r
913                     \r
914                     if (findNode.base == matchingNode->base)\r
915                     {\r
916                         /* Case 3: Free range is on left boundary of matched node. Try to \r
917                          *         combine the free range with the left node if free.\r
918                          *\r
919                          * |<---left node (free)--->||<----------matched node---------->|\r
920                          *                           |<---findNode (free req)--->|\r
921                          */ \r
922                    \r
923                         /* There may be a combine possibility to the left. Extract leftNode to check */\r
924                         leftNode = RB_PREV(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);\r
925                         \r
926                         /* Can the node to the left of the matchingNode be combined with the \r
927                          * findNode's range? */\r
928                         if (leftNode && (strcmp(leftNode->allocatedTo, RM_NOT_ALLOCATED_STRING) == 0))\r
929                         {\r
930                             /* Remove the leftNode from the tree for editing */\r
931                             RB_REMOVE(_Rm_ResourceTree, allocator->allocatorRootEntry, leftNode);\r
932 \r
933                             /* Combine the leftNode and the findNode */\r
934                             leftNode->length += findNode.length;\r
935                         }\r
936                         else\r
937                         {\r
938                             /* Allocate a new leftNode that will take the place of the findNode\r
939                              * range in tree. */\r
940                             leftNode = Rm_newResourceTreeNode(findNode.base, findNode.length,\r
941                                                               RM_NOT_ALLOCATED_STRING);\r
942                         }\r
943 \r
944                         /* Account for the leftNode in the matchingNode */\r
945                         matchingNode->base = findNode.base + findNode.length;\r
946                         matchingNode->length = matchingEnd - findEnd;  \r
947 \r
948                         /* Insert the left node */\r
949                         RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, leftNode);\r
950                     }\r
951                     else if (findEnd == matchingEnd)\r
952                     {\r
953                         /* Case 4: Free range is on right boundary of matched node. Try to \r
954                          *         combine the free range with the right node if free.\r
955                          *\r
956                          * |<----------matched node---------->||<---right node (free)--->|\r
957                          *        |<---findNode (free req)--->|\r
958                          */ \r
959                         \r
960                         /* There may be a combine possibility to the right. Extract rightNode to check */\r
961                         rightNode = RB_NEXT(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);\r
962                         \r
963                         /* Can the node to the right of the matchingNode be combined with the \r
964                          * findNode's range? */\r
965                         if (rightNode && (strcmp(rightNode->allocatedTo, RM_NOT_ALLOCATED_STRING) == 0))\r
966                         {\r
967                             /* Remove the rightNode from the tree for editing */\r
968                             RB_REMOVE(_Rm_ResourceTree, allocator->allocatorRootEntry, rightNode);\r
969 \r
970                             /* Combine the rightNode and the findNode */\r
971                             rightNode->base = findNode.base;\r
972                             rightNode->length += findNode.length;\r
973                         }\r
974                         else\r
975                         {\r
976                             /* Allocate a new rightNode that will take the place of the findNode\r
977                              * range in tree. */\r
978                             rightNode = Rm_newResourceTreeNode(findNode.base, findNode.length,\r
979                                                                RM_NOT_ALLOCATED_STRING);\r
980                         }\r
981 \r
982                         /* Account for the rightNode in the matchingNode */\r
983                         matchingNode->length -= findNode.length;  \r
984 \r
985                         /* Insert the right node */\r
986                         RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, rightNode);\r
987                     }\r
988 \r
989                     /* Reinsert the edited matching node */\r
990                     RB_INSERT(_Rm_ResourceTree, allocator->allocatorRootEntry, matchingNode);\r
991                 }\r
992 \r
993                 retVal = RM_SERVICE_APPROVED_AND_COMPLETED;\r
994             }\r
995             else\r
996             {\r
997                 /* The matching allocated range to be freed was allocated to a different instance. */\r
998                 retVal = RM_SERVICE_DENIED_RESOURCE_NOT_ALLOCATED_TO_INSTANCE_REQUESTING_THE_SERVICE;\r
999             }\r
1000         }\r
1001         else\r
1002         {\r
1003             /* Free resource range crosses over node boundaries.  This signifies a\r
1004              * free of both allocated and unallocated resources since nodes are combined\r
1005              * on allocate and free operations if possible. */\r
1006             retVal = RM_SERVICE_DENIED_RESOURCE_ALREADY_FREE;\r
1007         }\r
1008     }\r
1009     else\r
1010     {\r
1011         /* The free resources could not be found in the allocator */\r
1012         retVal = RM_SERVICE_DENIED_RESOURCE_VALUE_RANGE_DOES_NOT_EXIST;\r
1013     }\r
1014 \r
1015     return(retVal);  \r
1016 }\r
1017 \r
1018 int32_t Rm_allocatorOperation(Rm_Inst *rmInst, Rm_AllocatorOpInfo *opInfo)\r
1019 {\r
1020     Rm_Allocator *allocator = NULL;\r
1021     int32_t retVal;\r
1022     void *key;\r
1023 \r
1024     /* Lock access to the RM instance's transaction queue */\r
1025     key = Rm_osalMtCsEnter();\r
1026 \r
1027     /* Get the specified resource's allocator */\r
1028     allocator = Rm_allocatorFind(rmInst, opInfo->resourceInfo->name);\r
1029 \r
1030     if (allocator)\r
1031     {\r
1032         /* Call the allocator's function */\r
1033         if (opInfo->operation == Rm_allocatorOp_PRE_ALLOCATE)\r
1034         {\r
1035             retVal = Rm_treePreAllocate(allocator, opInfo);\r
1036         }               \r
1037         else if (opInfo->operation == Rm_allocatorOp_ALLOCATE)\r
1038         {\r
1039             retVal = Rm_treeAllocate(allocator, opInfo);\r
1040         }\r
1041         else if (opInfo->operation == Rm_allocatorOp_FREE)\r
1042         {\r
1043             retVal = Rm_treeFree(allocator, opInfo);\r
1044         }         \r
1045     }\r
1046     else\r
1047     {\r
1048         /* Allocator could not be found for resource */\r
1049         retVal = RM_SERVICE_DENIED_RESOURCE_DOES_NOT_EXIST;\r
1050     }\r
1051 \r
1052     Rm_osalMtCsExit(key);\r
1053     return(retVal);\r
1054 }\r
1055 \r
1056 void Rm_allocationHandler (Rm_Inst *rmInst, Rm_Transaction *transaction)\r
1057 {\r
1058     Rm_AllocatorOpInfo opInfo;\r
1059     int32_t retVal = transaction->state;\r
1060 \r
1061     /* Initialize the opInfo structure */\r
1062     memset((void *)&opInfo, 0, sizeof(Rm_AllocatorOpInfo));\r
1063     \r
1064     if (rmInst->instType == Rm_instType_CLIENT_DELEGATE)\r
1065     {\r
1066         /* TEMP: For now forward all allocations to the RM Server */\r
1067         Rm_transactionForwarder(rmInst, transaction);\r
1068         \r
1069 #if 0  /* Policy psuedo-code.  Will be implemented after basic allocate functionality is ready and tested */   \r
1070         if (resourceBase is unspecified)\r
1071         {\r
1072            while (policy does not approve)\r
1073            {\r
1074                Rm_policy check get allowed base as starting point for prealloc\r
1075                preallocate resource based on the range and alignment\r
1076                Rm_policy...check\r
1077            }\r
1078         }\r
1079         else\r
1080         {\r
1081             /* Check local policy to see if the request can be satisfied with the\r
1082              * resources stored locally */\r
1083             Rm_policy...API()\r
1084 \r
1085             if (policy check approves the resource)\r
1086             {\r
1087                 /* call the allocator to allocate the resource */\r
1088                 if (allocator returns resource)\r
1089                 {\r
1090                     /* Populate the transaction with the allocated resources and the result */\r
1091                     transaction->state = approve reason;\r
1092                     return ...\r
1093                 }\r
1094                 else\r
1095                 {\r
1096                     /* allocator ran out of resources, need to contact Server for more\r
1097                      * resources */\r
1098                     Rm_resourcePoolModRequest(...);\r
1099                 }\r
1100             }\r
1101             else if (policy check denies resource)\r
1102             {\r
1103                 /* Policy check denied resource. */\r
1104                 transaction->state= deny reason;\r
1105                 return ...\r
1106             }\r
1107             else if (policy check says forward to Server for validation)\r
1108             {\r
1109                 /* Forward the transaction to the Server */\r
1110                 Rm_transactionForwarder(rmInst, transaction);\r
1111             }\r
1112         }\r
1113 #endif         \r
1114     }\r
1115     else if (rmInst->instType == Rm_instType_SERVER)\r
1116     {\r
1117         /* TEMP: If resource properties are unspecified allocate the next available.\r
1118          *       If resource properties are specified allocate if they are available. */\r
1119 \r
1120         /* Fill out the allocator operation general information */\r
1121         opInfo.resourceInfo = &transaction->resourceInfo;\r
1122         opInfo.srcInstName = transaction->sourceInstName;\r
1123 \r
1124         if (strlen(transaction->resourceInfo.nsName) > 0)\r
1125         {\r
1126             /* See if a NameServer name is being used to allocate a resource */\r
1127             if (transaction->resourceInfo.base != 0)\r
1128             {\r
1129                 /* A name and a value cannot be specified for the request.  It's one\r
1130                  * or the other. */\r
1131                 retVal = RM_SERVICE_ERROR_NAMESERVER_NAME_AND_RESOURCE_RANGE_BOTH_DEFINED;\r
1132             }\r
1133             else\r
1134             {\r
1135                 /* Get the resource information from the NameServer */\r
1136                 retVal = Rm_nsFindObject(rmInst, opInfo.resourceInfo);\r
1137             }\r
1138         }\r
1139         else if (transaction->resourceInfo.base == RM_RESOURCE_BASE_UNSPECIFIED)\r
1140         {\r
1141             /* Execute the allocator pre-allocate operation to get the next available resources.\r
1142              * NORMALLY CHECKED AGAINST THE POLICY */\r
1143             opInfo.operation = Rm_allocatorOp_PRE_ALLOCATE;\r
1144                 \r
1145             if (transaction->resourceInfo.alignment == RM_RESOURCE_ALIGNMENT_UNSPECIFIED)\r
1146             {   \r
1147                 /* TEMP: Default resource alignment of 1 if the resource alignment is not\r
1148                  *       specified */\r
1149                 opInfo.policyAlignment = 1;\r
1150             }\r
1151             else\r
1152             {\r
1153                 opInfo.policyAlignment = transaction->resourceInfo.alignment;\r
1154             }\r
1155 \r
1156             /* opInfo.policyBase = comes from policy once implemented */\r
1157             opInfo.policyLength = transaction->resourceInfo.length;\r
1158 \r
1159             /* If the pre-allocate operation succeeds the resourceInfo field pointed to\r
1160              * by opInfo will contain the next available resources taht satisfy the\r
1161              * resource properties */\r
1162             retVal = Rm_allocatorOperation(rmInst, &opInfo);\r
1163         }\r
1164 \r
1165         /* Call allocator as long as an error or denial hasn't occurred */\r
1166         if (retVal == RM_SERVICE_PROCESSING)\r
1167         {\r
1168             opInfo.operation = Rm_allocatorOp_ALLOCATE;\r
1169 \r
1170             retVal = Rm_allocatorOperation(rmInst, &opInfo);\r
1171         }\r
1172 \r
1173         transaction->state = retVal;\r
1174 \r
1175         if (strcmp(transaction->sourceInstName, rmInst->name))\r
1176         {\r
1177             /* Source of allocation was not the server instance, provide the transaction\r
1178              * to the transaction responder */\r
1179             Rm_transactionResponder(rmInst, transaction);\r
1180         }\r
1181         /* Otherwise let the return stack return the transaction to the serviceHandler */           \r
1182 \r
1183 #if 0  /* Policy psuedo-code.  Will be implemented after basic allocate functionality is ready and tested */        \r
1184         if (resourceBase is unspecified)\r
1185         {\r
1186            while (policy does not approve)\r
1187            {\r
1188                Rm_policy check get allowed base as starting point for prealloc\r
1189                preallocate resource based on the range and alignment\r
1190                Rm_policy...check\r
1191            }\r
1192         }\r
1193         else\r
1194         {\r
1195             /* Check global policy to see if resource can be allocated. return result\r
1196              * no matter what */\r
1197             Rm_policy...API()\r
1198 \r
1199             if (policy approves)\r
1200             {\r
1201                 /* call allocator to allocate resource */\r
1202             }\r
1203 \r
1204             transaction->state = approve or deny reason;\r
1205             transaction->resourceInfo.base = ...;\r
1206             transaction->resourceInfo.length = ...;\r
1207 \r
1208             /* If source instance name does not match the current instance\r
1209              * name the allocation request came from a Client.  The result\r
1210              * must be sent back to the Client */\r
1211             if (strcmp(transaction->sourceInstName, rmInst->name))\r
1212             {\r
1213                 /* Names don't match.  Send the transaction back to the Client */\r
1214                 Rm_transactionResponder(rmInst, transaction);\r
1215             }\r
1216             else\r
1217             {\r
1218                 /* Resource allocation request originated locally on the active\r
1219                  * instance. Send the response via the service responder. */          \r
1220                 Rm_serviceResponder(rmInst, transaction);                            \r
1221             }\r
1222         }\r
1223 #endif        \r
1224     }   \r
1225 }\r
1226 \r
1227 void Rm_freeHandler (Rm_Inst *rmInst, Rm_Transaction *transaction)\r
1228 {\r
1229     Rm_AllocatorOpInfo opInfo;\r
1230     int32_t retVal = transaction->state;\r
1231     \r
1232     if (rmInst->instType == Rm_instType_CLIENT_DELEGATE)\r
1233     {\r
1234         /* TEMP: Forward all free requests to the Server */\r
1235         Rm_transactionForwarder(rmInst, transaction);\r
1236         \r
1237 #if 0  /* Policy psuedo-code.  Will be implemented after basic allocate functionality is ready and tested */      \r
1238         /* Check local policy to see if the request can be satisfied with the\r
1239          * resources stored locally */\r
1240         Rm_policy...API()\r
1241 \r
1242         if (policy check approves the free)\r
1243         {\r
1244             /* call the allocator to free the resource */\r
1245             /* Run a resource pool check to see if the free combined a resource block\r
1246              * that can be returned to the server */\r
1247             if (resource block has been combined)\r
1248             {\r
1249                   /* allocator ran out of resources, need to contact Server for more\r
1250                  * resources */\r
1251                 Rm_resourcePoolModRequest(free pool block to server...);\r
1252             }\r
1253             else\r
1254             {\r
1255                 /* Populate the receipt with the freed resources and the result */\r
1256                 transaction->state = approve reason;\r
1257                 return ...\r
1258             }\r
1259         }\r
1260         else if (policy check denies resource free)\r
1261         {\r
1262             /* Policy check denied resource. */\r
1263             transaction->state = deny reason;\r
1264             return ...\r
1265         }\r
1266         else if (policy check says forward to Server for validation)\r
1267         {\r
1268             /* Forward the transaction to the Server */\r
1269             Rm_transactionForwarder(rmInst, transaction);\r
1270         }\r
1271 #endif         \r
1272     }\r
1273     else if (rmInst->instType == Rm_instType_SERVER)\r
1274     {\r
1275         /* TEMP: Free the resources if resources are allocated to the source instance. */\r
1276 \r
1277         /* Fill out the allocator operation general information */\r
1278         opInfo.resourceInfo = &transaction->resourceInfo;\r
1279         opInfo.srcInstName = transaction->sourceInstName;\r
1280 \r
1281         if (strlen(transaction->resourceInfo.nsName) > 0)\r
1282         {\r
1283             /* See if a NameServer name is being used to allocate a resource */\r
1284             if (transaction->resourceInfo.base != 0)\r
1285             {\r
1286                 /* A name and a value cannot be specified for the request.  It's one\r
1287                  * or the other. */\r
1288                 retVal = RM_SERVICE_ERROR_NAMESERVER_NAME_AND_RESOURCE_RANGE_BOTH_DEFINED;\r
1289             }\r
1290             else\r
1291             {\r
1292                 /* Get the resource information from the NameServer */\r
1293                 retVal = Rm_nsFindObject(rmInst, opInfo.resourceInfo);\r
1294             }\r
1295         }\r
1296         \r
1297         /* Call allocator as long as an error or denial hasn't occurred */\r
1298         if (retVal == RM_SERVICE_PROCESSING)\r
1299         {\r
1300             opInfo.operation = Rm_allocatorOp_FREE;\r
1301 \r
1302             retVal = Rm_allocatorOperation(rmInst, &opInfo);\r
1303         }\r
1304 \r
1305         transaction->state = retVal;\r
1306 \r
1307         if (strcmp(transaction->sourceInstName, rmInst->name))\r
1308         {\r
1309             /* Source of allocation was not the server instance, provide the transaction\r
1310              * to the transaction responder */\r
1311             Rm_transactionResponder(rmInst, transaction);\r
1312         }\r
1313         /* Otherwise let the return stack return the transaction to the serviceHandler */  \r
1314         \r
1315 #if 0  /* Policy psuedo-code.  Will be implemented after basic allocate functionality is ready and tested */ \r
1316         /* Check global policy to see if resource can be freed. return result\r
1317          * no matter what */\r
1318         Rm_policy...API()\r
1319         if (policy approves)\r
1320         {\r
1321             /* call allocator to free resources */\r
1322         }\r
1323             \r
1324         transaction->state = approve or deny reason;\r
1325         transaction->resourceInfo.base = ...;\r
1326         transaction->resourceInfo.length = ...;\r
1327 \r
1328         /* If source instance name does not match the current instance\r
1329          * name the allocation request came from a client.  The result\r
1330          * must be sent back to the Client */\r
1331         if (strcmp(transaction->sourceInstName, rmInst->name))\r
1332         {\r
1333             /* Names don't match.  Send the transaction back to the Client Delegate or Client */\r
1334             Rm_transactionResponder(rmInst, transaction);\r
1335         }\r
1336         else\r
1337         {\r
1338             /* Resource allocation request originated locally on the active\r
1339              * instance. Send the response via the service responder. */\r
1340             Rm_serviceResponder(rmInst, transaction);                            \r
1341         }\r
1342 #endif        \r
1343     }   \r
1344 }\r
1345 \r
1346 /* Function used to send RM response transactions to lower level agents */\r
1347 void Rm_transactionResponder (Rm_Inst *rmInst, Rm_Transaction *transaction)\r
1348 {\r
1349     Rm_TransportNode *dstTransportNode = NULL;\r
1350     Rm_Packet *rmPkt = NULL;\r
1351 \r
1352     /* Find the transport for the RM instance that sent the request. */\r
1353     dstTransportNode = Rm_transportNodeFindRemoteName(rmInst, transaction->sourceInstName);\r
1354 \r
1355     /* Create a RM packet using the service information */\r
1356     switch (transaction->type)\r
1357     {\r
1358         case Rm_service_RESOURCE_ALLOCATE:\r
1359         case Rm_service_RESOURCE_FREE:\r
1360         case Rm_service_RESOURCE_GET_BY_NAME:\r
1361             rmPkt = Rm_transportCreateResourceResponsePkt(rmInst, dstTransportNode, \r
1362                                                           transaction);\r
1363             break;\r
1364         case Rm_service_RESOURCE_MAP_TO_NAME:\r
1365         case Rm_service_RESOURCE_UNMAP_NAME:\r
1366             rmPkt = Rm_transportCreateNsResponsePkt(rmInst, dstTransportNode,\r
1367                                                     transaction);\r
1368             break;\r
1369         default:\r
1370             /* Invalid service type.  Flag the error and return */\r
1371             transaction->state = RM_SERVICE_ERROR_INVALID_SERVICE_TYPE;\r
1372             break;\r
1373     }\r
1374 \r
1375     if (transaction->state <= RM_SERVICE_ERROR_BASE)\r
1376     {\r
1377         /* Delete the transaction and return immediately because an error occurred \r
1378          * allocating the packet */\r
1379         Rm_transactionQueueDelete(rmInst, transaction->localId);\r
1380         return;\r
1381     }\r
1382 \r
1383     /* Send the RM packet to the application transport */\r
1384     if (rmInst->transport.rmSend((Rm_TransportHandle) dstTransportNode, rmPkt) < RM_TRANSPORT_SUCCESSFUL)\r
1385     {\r
1386         /* Negative value returned by transport send.  An error occurred\r
1387          * in the transport while attempting to send the packet.*/\r
1388         transaction->state = RM_SERVICE_ERROR_TRANPSPORT_SEND_ERROR;\r
1389         /* Clean up the packet */\r
1390         if (rmInst->transport.rmFreePkt((Rm_TransportHandle) dstTransportNode, rmPkt))\r
1391         {\r
1392             /* Non-NULL value returned by transport packet free. Flag the\r
1393              * error */\r
1394             transaction->state = RM_SERVICE_ERROR_TRANSPORT_FREE_PKT_ERROR;\r
1395         }\r
1396         return;\r
1397     }\r
1398 \r
1399     /* NEED TO DO SOMETHING IF GET AN ERROR IN THE transaction->state FIELD.  CREATE\r
1400      * NEW TRANSACTION WITH DATA FROM ORIGINAL?  THEN TRY TO SEND FAILED REQUEST BACK\r
1401      * TO REQUESTER???  KEEP RETRYING SEND OF RESPONSE??? */\r
1402 \r
1403     /* Delete the transaction */\r
1404     Rm_transactionQueueDelete(rmInst, transaction->localId);\r
1405 }\r
1406 \r
1407 /* Function used to forward RM transactions to higher level agents */\r
1408 void Rm_transactionForwarder (Rm_Inst *rmInst, Rm_Transaction *transaction)\r
1409 {\r
1410     Rm_TransportNode *dstTransportNode = NULL;\r
1411     Rm_Packet *rmPkt = NULL;\r
1412 \r
1413     /* Make sure the RM instance has a transport registered with a higher level agent */\r
1414     if (rmInst->registeredWithDelegateOrServer == false)\r
1415     {\r
1416         transaction->state = RM_SERVICE_ERROR_NOT_REGISTERED_WITH_DEL_OR_SERVER;\r
1417         return;\r
1418     }\r
1419 \r
1420     /* Find the transport for the higher level agent.  Check for a connection to a Client Delegate\r
1421      * or a Server.  Clients will be connected to either a Client Delegate or a Server.  Client\r
1422      * Delegates will be connected to a Server. */\r
1423     if (rmInst->instType == Rm_instType_CLIENT)\r
1424     {\r
1425         dstTransportNode = Rm_transportNodeFindRemoteInstType(rmInst, Rm_instType_CLIENT_DELEGATE);\r
1426 \r
1427         if (!dstTransportNode)\r
1428         {\r
1429             /* No Client Delegate connection found.  Check for a Server connection */\r
1430             dstTransportNode = Rm_transportNodeFindRemoteInstType(rmInst, Rm_instType_SERVER);\r
1431         }\r
1432     } \r
1433     else if (rmInst->instType == Rm_instType_CLIENT_DELEGATE)\r
1434     {\r
1435         dstTransportNode = Rm_transportNodeFindRemoteInstType(rmInst, Rm_instType_SERVER);\r
1436     }\r
1437 \r
1438     /* Create a RM packet using the service information */\r
1439     switch (transaction->type)\r
1440     {\r
1441         case Rm_service_RESOURCE_ALLOCATE:\r
1442         case Rm_service_RESOURCE_FREE:\r
1443         case Rm_service_RESOURCE_GET_BY_NAME:\r
1444             rmPkt = Rm_transportCreateResourceReqPkt(rmInst, dstTransportNode, \r
1445                                                      transaction);\r
1446             break;\r
1447         case Rm_service_RESOURCE_MAP_TO_NAME:\r
1448         case Rm_service_RESOURCE_UNMAP_NAME:\r
1449             rmPkt = Rm_transportCreateNsRequestPkt(rmInst, dstTransportNode,\r
1450                                                    transaction);\r
1451             break;\r
1452         default:\r
1453             /* Invalid service type.  Flag the error and return */\r
1454             transaction->state = RM_SERVICE_ERROR_INVALID_SERVICE_TYPE;\r
1455             break;\r
1456     }\r
1457 \r
1458     if (transaction->state <= RM_SERVICE_ERROR_BASE)\r
1459     {\r
1460         /* Return immediately because an error occurred allocating the packet */\r
1461         return;\r
1462     }\r
1463 \r
1464     /* Send the RM packet to the application transport */\r
1465     if (rmInst->transport.rmSend((Rm_TransportHandle) dstTransportNode, rmPkt) < RM_TRANSPORT_SUCCESSFUL)\r
1466     {\r
1467         /* Negative value returned by transport send.  An error occurred\r
1468          * in the transport while attempting to send the packet.*/\r
1469         transaction->state = RM_SERVICE_ERROR_TRANPSPORT_SEND_ERROR;\r
1470         /* Clean up the packet */\r
1471         if (rmInst->transport.rmFreePkt((Rm_TransportHandle) dstTransportNode, rmPkt))\r
1472         {\r
1473             /* Non-NULL value returned by transport packet free. Flag the\r
1474              * error */\r
1475             transaction->state = RM_SERVICE_ERROR_TRANSPORT_FREE_PKT_ERROR;\r
1476         }\r
1477         return;\r
1478     }\r
1479 \r
1480     /* Transaction is not deleted because it is awaiting a response from the higher level\r
1481      * RM instance */\r
1482 }\r
1483 \r
1484 void Rm_transactionProcessor (Rm_Inst *rmInst, Rm_Transaction *transaction)\r
1485 {\r
1486     /* Handle auto-forwarded transactions.  These transactions include:\r
1487      * - All request transactions received on Clients are forwarded to the Client Delegate\r
1488      * - NameServer requests received on the Client Delegate are forwarded to the Server */\r
1489     if ((rmInst->instType == Rm_instType_CLIENT) ||\r
1490         ((rmInst->instType == Rm_instType_CLIENT_DELEGATE) &&\r
1491          ((transaction->type == Rm_service_RESOURCE_MAP_TO_NAME) ||\r
1492           (transaction->type == Rm_service_RESOURCE_GET_BY_NAME) ||\r
1493           (transaction->type == Rm_service_RESOURCE_UNMAP_NAME))))\r
1494     {        \r
1495         /* Check if the transaction is a transaction that received a response to its\r
1496          * request. */\r
1497         if (transaction->state != RM_SERVICE_PROCESSING)\r
1498         {\r
1499 \r
1500             /* A transaction has received a response. Send the response to either the \r
1501              * transaction or service responder based on the source instance */\r
1502             if (strcmp(transaction->sourceInstName, rmInst->name))\r
1503             {\r
1504                 /* Transaction originated from another instance.  Use the \r
1505                  * transaction responder to send the result to the source instance.  This\r
1506                  * is not possible on RM Clients since they can't forward RM services */\r
1507                 Rm_transactionResponder(rmInst, transaction);\r
1508             }\r
1509             else\r
1510             {\r
1511                 /* Transaction originated on this instance.  Send to the\r
1512                  * service responder */\r
1513                 Rm_serviceResponder(rmInst, transaction);\r
1514             }\r
1515         }\r
1516         else\r
1517         {\r
1518             /* This is a new transaction that must be forwarded to a higher level RM instance. */\r
1519             Rm_transactionForwarder(rmInst, transaction);\r
1520         }\r
1521     }\r
1522     else\r
1523     {\r
1524         /* Client Delegate and Server transaction processors. */\r
1525         switch (transaction->type)\r
1526         {\r
1527             case Rm_service_RESOURCE_ALLOCATE:\r
1528             case Rm_service_RESOURCE_FREE:               \r
1529                 /* Check if the transaction is fulfilled request */\r
1530                 if (transaction->state != RM_SERVICE_PROCESSING)\r
1531                 {\r
1532                     /* If source instance name does not match the current instance\r
1533                      * name the allocation request came from a client.  The result\r
1534                      * must be sent back to the Client */\r
1535                     if (strcmp(transaction->sourceInstName, rmInst->name))\r
1536                     {\r
1537                         Rm_transactionResponder(rmInst, transaction);\r
1538                     }\r
1539                     else\r
1540                     {\r
1541                         /* Resource allocation request originated locally.  Send the response\r
1542                          * via the service responder. */\r
1543                         Rm_serviceResponder(rmInst, transaction);      \r
1544                     }\r
1545                 }\r
1546                 else\r
1547                 {\r
1548                     /* This is a new transaction request originating from an RM instance with fewer\r
1549                      * allocate/free privileges.  Run the allocation or free handler to see if the resource\r
1550                      * request can be handled locally or if it needs to be forwarded to a higher level\r
1551                      * agent */\r
1552                     if (transaction->type == Rm_service_RESOURCE_ALLOCATE)\r
1553                     {\r
1554                         Rm_allocationHandler(rmInst, transaction);\r
1555                     }\r
1556                     else\r
1557                     {\r
1558                         Rm_freeHandler(rmInst, transaction);\r
1559                     }\r
1560                 }\r
1561                 break;\r
1562             case Rm_service_RESOURCE_MAP_TO_NAME:\r
1563             case Rm_service_RESOURCE_GET_BY_NAME:\r
1564             case Rm_service_RESOURCE_UNMAP_NAME:                \r
1565                 /* Server is the only RM instance capable of adding NameServer objects */\r
1566                 if (rmInst->instType == Rm_instType_SERVER)\r
1567                 {\r
1568                     if (transaction->type == Rm_service_RESOURCE_MAP_TO_NAME)\r
1569                     {\r
1570                         /* Create a new NameServer object with the request transaction information.\r
1571                          * Transaction will contain the state result of the NameServer addition. */\r
1572                         if (Rm_nsAddObject(rmInst, &transaction->resourceInfo) == RM_NS_ACTION_APPROVED)\r
1573                         {\r
1574                             transaction->state = RM_SERVICE_APPROVED_AND_COMPLETED;\r
1575                         }\r
1576                         else\r
1577                         {\r
1578                             /* TEMP: UPDATE THIS STATE VALUE */\r
1579                             transaction->state = RM_SERVICE_DENIED_BEGIN;\r
1580                         }\r
1581                     }\r
1582                     else if (transaction->type == Rm_service_RESOURCE_GET_BY_NAME)\r
1583                     {\r
1584                         /* Create a new NameServer object with the request transaction information.\r
1585                          * Transaction will contain the state result of the NameServer addition. */\r
1586                         if (Rm_nsFindObject(rmInst, &transaction->resourceInfo) == RM_NS_ACTION_APPROVED)\r
1587                         {\r
1588                             transaction->state = RM_SERVICE_APPROVED_AND_COMPLETED;\r
1589                         }\r
1590                         else\r
1591                         {\r
1592                             /* TEMP: UPDATE THIS STATE VALUE */\r
1593                             transaction->state = RM_SERVICE_DENIED_BEGIN;\r
1594                         }                    \r
1595                     }\r
1596                     else\r
1597                     {\r
1598                         /* Delete an existing NameServer object with the request transaction information\r
1599                          * Transaction will contain the state result of the NameServer addition. */\r
1600                         if (Rm_nsDeleteObject(rmInst, &transaction->resourceInfo) == \r
1601                             RM_NS_ACTION_APPROVED)\r
1602                         {\r
1603                             transaction->state = RM_SERVICE_APPROVED_AND_COMPLETED;\r
1604                         }\r
1605                         else\r
1606                         {\r
1607                             /* TEMP: UPDATE THIS STATE VALUE */\r
1608                             transaction->state = RM_SERVICE_DENIED_BEGIN;\r
1609                         }\r
1610                     }\r
1611 \r
1612                     /* If source instance name does not match the local instance\r
1613                      * name the NameServer request came from a Client or Client Delegate.  The \r
1614                      * result must be sent back to the Client or Client Delegate.  Just return if it does\r
1615                      * match since the NameServer transaction result can be returned immediately by the\r
1616                      * Rm_serviceHandler. */\r
1617                     if (strcmp(transaction->sourceInstName, rmInst->name))\r
1618                     {\r
1619                         Rm_transactionResponder(rmInst, transaction);\r
1620                     }\r
1621                 }\r
1622                 else\r
1623                 {\r
1624                     transaction->state = RM_SERVICE_ERROR_NAMESERVER_OBJECT_MOD_ON_INVALID_INSTANCE;\r
1625                 }\r
1626                 break;\r
1627         }\r
1628     }\r
1629 }\r
1630 \r
1631 int32_t Rm_reserveLinuxResource(Rm_Inst *rmInst, Rm_LinuxAlias *linuxAlias, \r
1632                                 Rm_LinuxValueRange *linuxValues, Rm_AllocatorOpInfo *opInfo)\r
1633 {\r
1634     int32_t retVal = RM_DTB_UTIL_RESULT_OKAY;\r
1635     bool baseFound = FALSE;\r
1636     bool lengthFound = FALSE;\r
1637     uint32_t valueIndex = 0;\r
1638 \r
1639     while ((linuxValues != NULL) && (!baseFound || !lengthFound))\r
1640     {\r
1641         if (linuxAlias->baseOffset == valueIndex)\r
1642         {\r
1643             /* Found the resource base.  Store it in the operation info structure */\r
1644             opInfo->resourceInfo->base = linuxValues->value;\r
1645             baseFound = TRUE;\r
1646 \r
1647             /* length will always be 1 if there is no length specified in the Linux DTB */\r
1648             if (linuxAlias->lengthOffset == RM_DTB_LINUX_ALIAS_OFFSET_NOT_SET)\r
1649             {\r
1650                 opInfo->resourceInfo->length = 1;\r
1651                 lengthFound = TRUE;\r
1652             }\r
1653         }\r
1654         else if (linuxAlias->lengthOffset == valueIndex)\r
1655         {\r
1656             /* Found the resource length.  Store it in the operation info structure */\r
1657             opInfo->resourceInfo->length = linuxValues->value;\r
1658             lengthFound = TRUE;\r
1659         }\r
1660 \r
1661         linuxValues = (Rm_LinuxValueRange *)linuxValues->nextValue;\r
1662         valueIndex++;\r
1663     }\r
1664 \r
1665     if (!baseFound || !lengthFound)\r
1666     {\r
1667         retVal = -33; /* TODO: ERROR BASE OR LENGTH OFFSET IN LINUX DTB WAS INCORRECT */\r
1668     }\r
1669     else\r
1670     {\r
1671         /* Allocate the resource to Linux */\r
1672         retVal = Rm_allocatorOperation(rmInst, opInfo);\r
1673     }\r
1674 \r
1675     return (retVal);\r
1676 }\r
1677 \r
1678 int32_t Rm_findAndReserveLinuxResource(Rm_Inst *rmInst, const char *resourceName, void *linuxDtb, \r
1679                                        Rm_LinuxAlias *linuxAlias)\r
1680 {\r
1681     Rm_AllocatorOpInfo opInfo;\r
1682     Rm_ResourceInfo resourceInfo;\r
1683     uint32_t pathOffset;\r
1684     int32_t propOffset;\r
1685     int32_t nodeOffset = RM_DTB_UTIL_STARTING_NODE_OFFSET;\r
1686     int32_t prevDepth = RM_DTB_UTIL_STARTING_DEPTH;\r
1687     int32_t depth;\r
1688     int32_t propertyLen;\r
1689     const char *propertyName;\r
1690     const void *propertyData; \r
1691     Rm_LinuxValueRange *linuxValueRange;\r
1692     int32_t retVal = RM_DTB_UTIL_RESULT_OKAY; \r
1693 \r
1694     /* Initialize the allocator opInfo and resourceInfo structures that will be used to \r
1695      * reserve the resources taken by the Linux kernel */\r
1696     memset((void *) &opInfo, 0, sizeof(Rm_AllocatorOpInfo));\r
1697     memset((void *) &resourceInfo, 0, sizeof(Rm_ResourceInfo));\r
1698 \r
1699     strcpy(resourceInfo.name, resourceName);\r
1700 \r
1701     /* Set the source instance name for allocation to be the Linux Kernel */\r
1702     opInfo.srcInstName = RM_ALLOCATED_TO_LINUX;\r
1703     opInfo.operation = Rm_allocatorOp_ALLOCATE;\r
1704     opInfo.resourceInfo = &resourceInfo;    \r
1705 \r
1706     /* Find each resource specified in the Linux resource alias list and reserve that \r
1707      * resource as used */\r
1708     while(linuxAlias != NULL)\r
1709     {\r
1710         /* Reset the parsing variables */\r
1711         pathOffset = 0;\r
1712         nodeOffset = RM_DTB_UTIL_STARTING_NODE_OFFSET;\r
1713         prevDepth = RM_DTB_UTIL_STARTING_DEPTH;   \r
1714         resourceInfo.base = 0;\r
1715         resourceInfo.length = 0;\r
1716         \r
1717         while(pathOffset < linuxAlias->pathListLenBytes)\r
1718         {\r
1719             /* Move through the DTB nodes until the next alias path node is found */\r
1720             if (strcmp(linuxAlias->pathList + pathOffset, fdt_get_name(linuxDtb, nodeOffset, NULL)))\r
1721             {\r
1722                 nodeOffset = fdt_next_node(linuxDtb, nodeOffset, &depth);\r
1723 \r
1724                 if (depth < prevDepth)\r
1725                 {\r
1726                     /* Returning from subnode that matched part of alias path without finding\r
1727                      * the resource values */\r
1728                     retVal = (-31); /* TODO: COULD NOT FIND RESOURCE AT ALIAS PATH */\r
1729                     break;\r
1730                 }\r
1731             }\r
1732             else\r
1733             {\r
1734                 /* Found the next alias path node */\r
1735                 pathOffset += (strlen(linuxAlias->pathList + pathOffset) + 1);\r
1736                 prevDepth = fdt_node_depth(linuxDtb, nodeOffset);\r
1737 \r
1738                 /* Check the properties of the node to see if they match the next alias\r
1739                  * path string */\r
1740                 propOffset = fdt_first_property_offset(linuxDtb, nodeOffset);\r
1741            \r
1742                 /* Search the properties for the next alias path string */\r
1743                 while ((propOffset >= RM_DTB_UTIL_STARTING_NODE_OFFSET) &&\r
1744                        (pathOffset < linuxAlias->pathListLenBytes))\r
1745                 {\r
1746                     propertyData = fdt_getprop_by_offset(linuxDtb, propOffset, \r
1747                                                          &propertyName, &propertyLen);\r
1748 \r
1749                     if (strcmp(linuxAlias->pathList + pathOffset, propertyName) == 0)\r
1750                     {\r
1751                         pathOffset += (strlen(linuxAlias->pathList + pathOffset) + 1);\r
1752                         /* Found the alias property.  Extract the values that will\r
1753                          * contain the resource information that must be reserved. */\r
1754                         linuxValueRange = Rm_linuxExtractValues(propertyData, propertyLen);\r
1755                         /* Use the values to reserve resources for the Linux kernel */\r
1756                         retVal = Rm_reserveLinuxResource(rmInst, linuxAlias, \r
1757                                                          linuxValueRange, &opInfo);\r
1758                         \r
1759                         /* Free the memory used to store the values */\r
1760                         Rm_linuxFreeValues(linuxValueRange);\r
1761                     }\r
1762                     \r
1763                     propOffset = fdt_next_property_offset(linuxDtb, propOffset);\r
1764                 } \r
1765 \r
1766                 if (propOffset < -FDT_ERR_NOTFOUND)\r
1767                 {\r
1768                         /* Error was returned by LIBFDT when parsing the properties */\r
1769                     retVal = propOffset;\r
1770                     break;\r
1771                 }\r
1772             }\r
1773         }\r
1774 \r
1775         if (retVal < RM_DTB_UTIL_RESULT_OKAY)\r
1776         {\r
1777             /* Error occurred during parsing of Linux DTB.  Return the error */\r
1778             break;\r
1779         }\r
1780         linuxAlias = (Rm_LinuxAlias *) linuxAlias->nextLinuxAlias;\r
1781     }\r
1782 \r
1783     return (retVal);\r
1784 }\r
1785 \r
1786 int32_t Rm_createAndInitAllocator(Rm_Inst *rmInst, const char *resourceName, \r
1787                                   Rm_ResourceProperties *resourceProperties, void *linuxDtb)\r
1788 {\r
1789     Rm_ResourceRange *range = NULL;\r
1790     Rm_ResourceRange *rangeBasePtr = NULL;\r
1791     Rm_NsAssignment *nsAssignments = NULL;\r
1792     Rm_NsAssignment *nsAssignmentBasePtr = NULL;\r
1793     Rm_LinuxAlias *linuxAlias = NULL;\r
1794     Rm_ResourceInfo resourceInfo;\r
1795     int32_t retVal = RM_DTB_UTIL_RESULT_OKAY;\r
1796 \r
1797     if (resourceProperties->rangeData && (resourceProperties->rangeLen > 0))\r
1798     {\r
1799         /* Extract the resource properties from the DTB */\r
1800         range = rangeBasePtr = Rm_resourceExtractRange(resourceProperties->rangeData, \r
1801                                                        resourceProperties->rangeLen);\r
1802 \r
1803         /* Create a tree allocator using the resource properties */\r
1804         retVal = Rm_createTreeAllocator(rmInst, resourceName, range); \r
1805 \r
1806         if (retVal >= RM_DTB_UTIL_RESULT_OKAY)\r
1807         {\r
1808             if (resourceProperties->linuxAliasData && resourceProperties->linuxAliasLen)\r
1809             {\r
1810                 /* Reserve the resources taken by the Linux kernel specified in the Linux DTB */\r
1811                 linuxAlias = Rm_resourceExtractLinuxAlias(resourceProperties->linuxAliasData,\r
1812                                                           resourceProperties->linuxAliasLen);\r
1813 \r
1814                 retVal = Rm_findAndReserveLinuxResource(rmInst, resourceName, linuxDtb, linuxAlias);            \r
1815             }\r
1816         }\r
1817     }\r
1818     \r
1819     if (retVal >= RM_DTB_UTIL_RESULT_OKAY)\r
1820     {\r
1821         /* Create entries in the NameServer if any NameServer assignments were specified */\r
1822         if (resourceProperties->nsAssignData && resourceProperties->nsAssignLen)\r
1823         {\r
1824             nsAssignments = Rm_resourceExtractNsAssignment(resourceProperties->nsAssignData, \r
1825                                                            resourceProperties->nsAssignLen);\r
1826 \r
1827             /* Cycle through the list of assignments and add them to the NameServer */\r
1828             nsAssignmentBasePtr = nsAssignments;\r
1829             while (nsAssignments)\r
1830             {\r
1831                 memset((void *)&resourceInfo, 0, sizeof(Rm_ResourceInfo));\r
1832 \r
1833                 resourceInfo.base = nsAssignments->resourceBase;\r
1834                 resourceInfo.length = nsAssignments->resourceLength;\r
1835                 strcpy(resourceInfo.nsName, nsAssignments->nsName);\r
1836                 \r
1837                 /* TODO: RETURN IF ANY OF THE ADDS FAIL??? */\r
1838                 Rm_nsAddObject(rmInst, &resourceInfo);\r
1839                 nsAssignments = nsAssignments->nextNsAssignment;\r
1840             }\r
1841             /* Free the memory allocated for the NameServer assignments */\r
1842             Rm_resourceFreeNsAssignmentList(nsAssignmentBasePtr);\r
1843         }\r
1844     }\r
1845 \r
1846     /* Free the memory allocated for the resource properties */\r
1847     Rm_resourceFreeRange(rangeBasePtr);\r
1848     Rm_resourceFreeLinuxAlias(linuxAlias);\r
1849 \r
1850     return(retVal);\r
1851 }\r
1852 \r
1853 int32_t Rm_parseResourceProperty(void *globalResourceDtb, int32_t offset, Rm_ResourceProperties *propertyInfo)\r
1854 {\r
1855         int32_t propertyLen;\r
1856         const char *propertyName;\r
1857         const void *propertyData;\r
1858     Rm_ResourcePropType propertyType;\r
1859     int32_t retVal = RM_DTB_UTIL_RESULT_OKAY;\r
1860 \r
1861     /* Get the property data and store it in the corresponding propertyInfo field */\r
1862         propertyData = fdt_getprop_by_offset(globalResourceDtb, offset, &propertyName, &propertyLen);\r
1863     if (propertyData)\r
1864     {\r
1865         propertyType = Rm_resourceGetPropertyType(propertyName);\r
1866         if (propertyType == Rm_resourcePropType_RESOURCE_RANGE)\r
1867         {\r
1868             if (propertyInfo->rangeData || propertyInfo->rangeLen)\r
1869             {\r
1870                 /* The range fields have already been populated.  Return an error.\r
1871                  * The resource list has specified a property field more than once\r
1872                  * for a resource node */\r
1873                 retVal = -18; /* TEMP ERROR: Can't conflict with LIBFDT errors */\r
1874             }\r
1875             else\r
1876             {\r
1877                 propertyInfo->rangeData = propertyData;\r
1878                 propertyInfo->rangeLen = propertyLen;\r
1879             }\r
1880         }\r
1881         else if (propertyType == Rm_resourcePropType_NSASSIGNMENT)\r
1882         {\r
1883             if (propertyInfo->nsAssignData || propertyInfo->nsAssignLen)\r
1884             {\r
1885                 /* The nsAssign fields have already been populated.  Return an error.\r
1886                  * The resource list has specified a property field more than once\r
1887                  * for a resource node */\r
1888                 retVal = -19; /* TEMP ERROR: Can't conflict with LIBFDT errors */\r
1889             }\r
1890             else\r
1891             {\r
1892                 propertyInfo->nsAssignData = propertyData;\r
1893                 propertyInfo->nsAssignLen = propertyLen;\r
1894             }\r
1895         }\r
1896         else if (propertyType == Rm_resourcePropType_RESOURCE_LINUX_ALIAS)\r
1897         {\r
1898             if (propertyInfo->linuxAliasData || propertyInfo->linuxAliasLen)\r
1899             {\r
1900                 /* The linuxAlias fields have already been populated.  Return an error.\r
1901                  * The resource list has specified a property field more than once\r
1902                  * for a resource node */\r
1903                 retVal = -28; /* TEMP ERROR: Can't conflict with LIBFDT errors */\r
1904             }\r
1905             else\r
1906             {\r
1907                 propertyInfo->linuxAliasData = propertyData;\r
1908                 propertyInfo->linuxAliasLen = propertyLen;\r
1909             }\r
1910         }        \r
1911         else\r
1912         {\r
1913             retVal = -20; /* TEMP ERROR: Can't conflict with LIBFDT errors */\r
1914         }\r
1915     }\r
1916     else\r
1917     {\r
1918         retVal = -16; /* TEMP ERROR: Can't conflict with LIBFDT errors */\r
1919     }\r
1920 \r
1921     /* Don't get anymore properties if error occurred */\r
1922     if (retVal == RM_DTB_UTIL_RESULT_OKAY)\r
1923     {\r
1924         offset = fdt_next_property_offset(globalResourceDtb, offset);\r
1925         if (offset >= 0)\r
1926         {\r
1927             retVal = Rm_parseResourceProperty(globalResourceDtb, offset, propertyInfo);\r
1928         }\r
1929         else if (offset != -FDT_ERR_NOTFOUND)\r
1930         {\r
1931             /* Error was returned by LIBFDT when parsing the properties */\r
1932             retVal = offset;\r
1933         }\r
1934     }\r
1935     \r
1936     return (retVal);\r
1937 }\r
1938 \r
1939 int32_t Rm_parseResourceNode(Rm_Inst *rmInst, void *globalResourceDtb, int32_t nodeOffset, int32_t depth,\r
1940                              void *linuxDtb)\r
1941 {\r
1942         const char *resourceName = fdt_get_name(globalResourceDtb, nodeOffset, NULL);\r
1943     Rm_ResourceProperties resourceProperties;\r
1944         int32_t error = RM_DTB_UTIL_RESULT_OKAY;\r
1945         int32_t offset;\r
1946 \r
1947     /* Initialize the resource properties structure */\r
1948     memset((void *)&resourceProperties, 0, sizeof(Rm_ResourceProperties));\r
1949 \r
1950     /* Ignore properties of the base node */\r
1951     if (strcmp(resourceName, rmDtbStartingNode))\r
1952     {\r
1953         /* Get the properties for the resource node if any exist */\r
1954         offset = fdt_first_property_offset(globalResourceDtb, nodeOffset);\r
1955         if (offset >= RM_DTB_UTIL_STARTING_NODE_OFFSET)\r
1956         {\r
1957             /* Since at least one property exists attempt to parse the property nodes and \r
1958              * use them to create and initialize a resource allocator */\r
1959                 error =  Rm_parseResourceProperty(globalResourceDtb, offset, &resourceProperties);\r
1960             if (error < -FDT_ERR_NOTFOUND)\r
1961             {\r
1962                 return (error);\r
1963             }\r
1964             \r
1965             /* Initialize an allocator with the resource properties if no error was returned */\r
1966             Rm_createAndInitAllocator(rmInst, resourceName, &resourceProperties, linuxDtb);\r
1967         }\r
1968         else if (offset != -FDT_ERR_NOTFOUND)\r
1969         {\r
1970                 /* Error was returned by LIBFDT when parsing the properties */\r
1971             return (offset);\r
1972         }\r
1973     }\r
1974     \r
1975     /* Get the next resource node */\r
1976         offset = fdt_next_node(globalResourceDtb, nodeOffset, &depth);\r
1977     /* Check the offset and depth of the next node to make sure the current node\r
1978      * wasn't the last node in the Resource List.  A depth less than the depth set\r
1979      * at the start of the recursion will signal the end of the resource list */\r
1980     if ((offset >= RM_DTB_UTIL_STARTING_NODE_OFFSET) && (depth >= RM_DTB_UTIL_STARTING_DEPTH))\r
1981     {\r
1982         error = Rm_parseResourceNode(rmInst, globalResourceDtb, offset, depth, linuxDtb);\r
1983         if (error < -FDT_ERR_NOTFOUND)\r
1984         {\r
1985             return (error);\r
1986         }\r
1987     }\r
1988     else if (offset != -FDT_ERR_NOTFOUND)\r
1989     {\r
1990         /* Error was returned by LIBFDT when parsing the nodes */\r
1991         return (offset);\r
1992     }\r
1993 \r
1994     return (RM_DTB_UTIL_RESULT_OKAY);\r
1995 }\r
1996 \r
1997 int32_t Rm_initializeAllocators(Rm_Inst *rmInst, void *globalResourceDtb, void *linuxDtb)\r
1998 {\r
1999     int32_t nodeOffset = RM_DTB_UTIL_STARTING_NODE_OFFSET;\r
2000     int32_t startDepth = RM_DTB_UTIL_STARTING_DEPTH;\r
2001     int32_t result = RM_DTB_UTIL_RESULT_OKAY;\r
2002 \r
2003     /* Recursively parse the Global Resource List, creating an allocator for\r
2004      * each resource as specified in the node */\r
2005     result = Rm_parseResourceNode(rmInst, globalResourceDtb, nodeOffset, startDepth, linuxDtb);\r
2006 \r
2007     return(result);\r
2008 }\r
2009      \r
2010 /**********************************************************************\r
2011  ********************** Application visible APIs **********************\r
2012  **********************************************************************/\r
2013 \r
2014 /* Server Only */\r
2015 void Rm_printResourceStatus(Rm_Handle *rmHandle)\r
2016 {\r
2017     Rm_Inst *rmInst = (Rm_Inst *) rmHandle;\r
2018     Rm_Allocator *allocator = rmInst->allocators;\r
2019     Rm_ResourceTree *treeRoot;\r
2020     Rm_ResourceTreeNode *treeNode;\r
2021     uint32_t numLinuxResources;\r
2022 \r
2023     while (allocator != NULL)\r
2024     {\r
2025         numLinuxResources = 0;\r
2026 \r
2027         Rm_osalLog("Resource: %s\n", allocator->resourceName);\r
2028 \r
2029         treeRoot = allocator->allocatorRootEntry;\r
2030 \r
2031         RB_FOREACH(treeNode, _Rm_ResourceTree, treeRoot)\r
2032         {               \r
2033             Rm_osalLog("          %10d - %10d ", treeNode->base, \r
2034                                                  treeNode->base + treeNode->length -1);\r
2035             \r
2036             if (strcmp(treeNode->allocatedTo, RM_NOT_ALLOCATED_STRING) == 0)\r
2037             {\r
2038                 Rm_osalLog("NOT ALLOCATED\n");\r
2039             }\r
2040             else\r
2041             {\r
2042                 Rm_osalLog("allocated to %s\n", treeNode->allocatedTo);\r
2043             }\r
2044 \r
2045             if (strcmp(treeNode->allocatedTo, RM_ALLOCATED_TO_LINUX) == 0)\r
2046             {\r
2047                 numLinuxResources += treeNode->length;\r
2048             }\r
2049         }\r
2050         \r
2051         Rm_osalLog("Total allocated to Linux: %d\n", numLinuxResources);\r
2052         \r
2053         allocator = allocator->nextAllocator;\r
2054     }\r
2055 \r
2056     Rm_nsPrintObjects(rmInst);\r
2057 }\r
2058 \r
2059 Rm_Handle Rm_init(Rm_InitCfg *initCfg)\r
2060 {\r
2061     Rm_Inst *rmInst;\r
2062     void *globalResourceDtb = NULL;\r
2063     void *linuxResourceDtb = NULL;\r
2064 \r
2065     /* Instance creation checks.  Add one to strlen calculation for null character */\r
2066     if ((strlen(initCfg->instName) + 1) > RM_INSTANCE_NAME_MAX_CHARS)\r
2067     {\r
2068         /* Failure: Instance name is too big */\r
2069         return (NULL);\r
2070     }\r
2071     \r
2072     /* Get memory for RM instance from local memory */\r
2073     rmInst = Rm_osalMalloc (sizeof(Rm_Inst));\r
2074     /* Populate instance based on input parameters */\r
2075     strcpy (&rmInst->name[0], initCfg->instName);\r
2076     rmInst->instType = initCfg->instType;\r
2077     rmInst->registeredWithDelegateOrServer = false;\r
2078     rmInst->policyDtb = NULL;\r
2079 \r
2080     /* Initialize the transport routing map linked list pointer to NULL.  The linked list\r
2081      * nodes will be created when the application registers transports */\r
2082     rmInst->routeMap = NULL;\r
2083 \r
2084     /* Initialize the allocators linked list pointer to NULL.  The linked list nodes will\r
2085      * be created on the Server instance when the application reads in the resource list.\r
2086      * Nodes will also be created on Client Delegates when blocks of resources are requested\r
2087      * for allocation to clients. */\r
2088     rmInst->allocators = NULL;\r
2089 \r
2090     /* Initialize the NameServer pointer to NULL.  The NameServer should only be located\r
2091      * on the RM Server */\r
2092     rmInst->nameServer = NULL;\r
2093 \r
2094     /* Initialize the transaction queue elements. */\r
2095     rmInst->transactionSeqNum = Rm_transactionInitSequenceNum();\r
2096     rmInst->transactionQueue= NULL;\r
2097 \r
2098     /* RM Server specific actions */\r
2099     if (rmInst->instType == Rm_instType_SERVER)\r
2100     {\r
2101         /* Initialize the NameServer */\r
2102         Rm_nsInit(rmInst);\r
2103         \r
2104         /* Open the ResourceList file and provide it to the resource initializer.  The Linux\r
2105          * DTB will be parsed simultaneously for resource's consumed by the kernel.  The resources\r
2106          * used by the kernel will be marked as used in the resource allocators. */\r
2107         if (initCfg->globalResourceList)\r
2108         {\r
2109             globalResourceDtb = initCfg->globalResourceList;\r
2110             fdt_open_into(globalResourceDtb, globalResourceDtb, fdt_totalsize(globalResourceDtb));\r
2111 \r
2112             if (initCfg->linuxDtb)\r
2113             {\r
2114                 linuxResourceDtb = initCfg->linuxDtb;\r
2115                 fdt_open_into(linuxResourceDtb, linuxResourceDtb, fdt_totalsize(linuxResourceDtb));   \r
2116             }\r
2117             \r
2118             Rm_initializeAllocators(rmInst, globalResourceDtb, linuxResourceDtb);\r
2119         }\r
2120     }\r
2121 \r
2122     /* Instance startup policies are only used for Servers and Client Delegates */\r
2123     if (rmInst->instType != Rm_instType_CLIENT)\r
2124     {\r
2125         /* Open the instance's policy and store it */\r
2126         if (initCfg->startupPolicy)\r
2127         {\r
2128             rmInst->policyDtb = initCfg->startupPolicy;\r
2129             fdt_open_into(rmInst->policyDtb, rmInst->policyDtb, fdt_totalsize(rmInst->policyDtb));  \r
2130         }\r
2131 \r
2132         /* Store policy via policy APIs ... */\r
2133     }\r
2134 \r
2135     /* Return the RM Handle */\r
2136     return ((Rm_Handle) rmInst);\r
2137 }\r
2138 \r
2139 uint32_t Rm_getVersion (void)\r
2140 {\r
2141     return RM_VERSION_ID;\r
2142 }\r
2143 \r
2144 \r
2145 const char* Rm_getVersionStr (void)\r
2146 {\r
2147     return rmVersionStr;\r
2148 }\r
2149 \r
2150 /**\r
2151 @}\r
2152 */\r