1 /**\r
2 * @file rm_transport.c\r
3 *\r
4 * @brief \r
5 * This is the Resource Manager transport source.\r
6 *\r
7 * \par\r
8 * ============================================================================\r
9 * @n (C) Copyright 2012-2014, 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 external includes */\r
43 #include <ti/drv/rm/rm.h>\r
44 #include <ti/drv/rm/rm_transport.h>\r
45 \r
46 /* RM internal includes */\r
47 #include <ti/drv/rm/include/rm_loc.h>\r
48 #include <ti/drv/rm/include/rm_transportloc.h>\r
49 \r
50 /* RM OSAL layer */\r
51 #include <rm_osal.h>\r
52 \r
53 /**********************************************************************\r
54 ************************ Local Functions *****************************\r
55 **********************************************************************/\r
56 \r
57 /* FUNCTION PURPOSE: Adds a transport\r
58 ***********************************************************************\r
59 * DESCRIPTION: Returns a pointer to a transport structure that\r
60 * was created, initialized, and added to the \r
61 * instance transport list.\r
62 */\r
63 static Rm_Transport *transportAdd(const Rm_TransportCfg *transportCfg)\r
64 {\r
65 Rm_Inst *rmInst = (Rm_Inst *) transportCfg->rmHandle;\r
66 Rm_Transport *transports = rmInst->transports;\r
67 Rm_Transport *newTransport = NULL;\r
68 \r
69 newTransport = Rm_osalMalloc (sizeof(Rm_Transport));\r
70 memset((void *)newTransport, 0, sizeof(Rm_Transport));\r
71 \r
72 if (newTransport) {\r
73 newTransport->rmHandle = transportCfg->rmHandle;\r
74 newTransport->appTransportHandle = transportCfg->appTransportHandle;\r
75 newTransport->remoteInstType = transportCfg->remoteInstType;\r
76 newTransport->callouts.rmAllocPkt = transportCfg->transportCallouts.rmAllocPkt;\r
77 newTransport->callouts.rmSendPkt = transportCfg->transportCallouts.rmSendPkt;\r
78 newTransport->nextTransport = NULL;\r
79 \r
80 if (transports) {\r
81 while (transports->nextTransport) {\r
82 transports = transports->nextTransport;\r
83 }\r
84 transports->nextTransport = newTransport;\r
85 }\r
86 else {\r
87 rmInst->transports = newTransport;\r
88 }\r
89 }\r
90 return (newTransport);\r
91 }\r
92 \r
93 /* FUNCTION PURPOSE: Tests if a transport is registered to an instance\r
94 ***********************************************************************\r
95 * DESCRIPTION: Returns TRUE if the supplied transport is found in\r
96 * the instance transport list. Otherwise, returns FALSE\r
97 */\r
98 static int transportIsRegistered(Rm_Handle rmHandle, Rm_Transport *transport)\r
99 {\r
100 Rm_Inst *rmInst = (Rm_Inst *)rmHandle;\r
101 Rm_Transport *transportList = (Rm_Transport *)rmInst->transports;\r
102 \r
103 while (transportList) {\r
104 if (transportList == transport) {\r
105 return(RM_TRUE); \r
106 }\r
107 transportList = transportList->nextTransport;\r
108 }\r
109 return (RM_FALSE);\r
110 }\r
111 \r
112 /* FUNCTION PURPOSE: Deletes a transport\r
113 ***********************************************************************\r
114 * DESCRIPTION: Removes a transport from an instance transport list\r
115 * and then frees the memory associated with the transport\r
116 * data structure\r
117 */\r
118 static void transportDelete(Rm_Transport *transport)\r
119 {\r
120 Rm_Inst *rmInst = (Rm_Inst *)transport->rmHandle;\r
121 Rm_Transport *transportList = (Rm_Transport *)rmInst->transports;\r
122 Rm_Transport *prevTransport = NULL;\r
123 \r
124 /* Get previous transport in list */\r
125 while (transportList) {\r
126 if (transportList == transport) {\r
127 break; \r
128 }\r
129 prevTransport = transportList;\r
130 transportList = transportList->nextTransport;\r
131 }\r
132 \r
133 if (prevTransport == NULL) {\r
134 rmInst->transports = transport->nextTransport;\r
135 }\r
136 else {\r
137 prevTransport->nextTransport = transport->nextTransport;\r
138 }\r
139 Rm_osalFree((void *)transport, sizeof(Rm_Transport));\r
140 }\r
141 \r
142 /**********************************************************************\r
143 ********************** Internal Functions ****************************\r
144 **********************************************************************/\r
145 \r
146 /* FUNCTION PURPOSE: Finds a transport based on remote inst type\r
147 ***********************************************************************\r
148 * DESCRIPTION: Returns a pointer to the transport within an instance's\r
149 * transport list that matches the provided remote\r
150 * instance type. NULL is returned if no transports in \r
151 * the list match the remote instance type.\r
152 */\r
153 Rm_Transport *rmTransportFindRemoteInstType(Rm_Transport *transports, Rm_InstType remoteInstType)\r
154 {\r
155 while (transports) {\r
156 if (transports->remoteInstType == remoteInstType) {\r
157 break; \r
158 }\r
159 transports = transports->nextTransport;\r
160 }\r
161 return (transports);\r
162 }\r
163 \r
164 /* FUNCTION PURPOSE: Returns RM packet source instance\r
165 ***********************************************************************\r
166 * DESCRIPTION: Can return the RM instance name for one of two things:\r
167 * - RM instance from which provided RM packet originated\r
168 * - RM instance from which the service request contained\r
169 * in the provided RM packet originated\r
170 */\r
171 int32_t getPktSrcNames(const Rm_Packet *pkt, char *pktSrc, char *serviceSrc, int32_t bufLen)\r
172 {\r
173 int32_t retVal = RM_OK;\r
174 \r
175 if (bufLen != RM_NAME_MAX_CHARS) {\r
176 retVal = RM_ERROR_SRC_NAME_BUF_INVALID_SIZE;\r
177 }\r
178 else {\r
179 switch (pkt->pktType) {\r
180 case Rm_pktType_RESOURCE_REQUEST:\r
181 {\r
182 Rm_ResourceRequestPkt *resourceReqPkt = (Rm_ResourceRequestPkt *)pkt->data;\r
183 \r
184 if (pktSrc){\r
185 rm_strncpy(pktSrc, resourceReqPkt->pktSrcInstName, RM_NAME_MAX_CHARS);\r
186 }\r
187 \r
188 if (serviceSrc) {\r
189 rm_strncpy(serviceSrc, resourceReqPkt->serviceSrcInstName, RM_NAME_MAX_CHARS);\r
190 }\r
191 \r
192 break;\r
193 }\r
194 case Rm_pktType_NAMESERVER_REQUEST:\r
195 {\r
196 Rm_NsRequestPkt *nsRequestPkt = (Rm_NsRequestPkt *)pkt->data;\r
197 \r
198 if (pktSrc){\r
199 rm_strncpy(pktSrc, nsRequestPkt->pktSrcInstName, RM_NAME_MAX_CHARS);\r
200 }\r
201 \r
202 if (serviceSrc) {\r
203 rm_strncpy(serviceSrc, nsRequestPkt->serviceSrcInstName, RM_NAME_MAX_CHARS);\r
204 }\r
205 \r
206 break;\r
207 }\r
208 case Rm_pktType_RESOURCE_RESPONSE:\r
209 case Rm_pktType_NAMESERVER_RESPONSE:\r
210 default:\r
211 {\r
212 retVal = RM_ERROR_PKT_AND_SERVICE_SRC_NOT_AVAIL;\r
213 break;\r
214 }\r
215 }\r
216 }\r
217 \r
218 return (retVal);\r
219 }\r
220 \r
221 /**********************************************************************\r
222 ********************* Application visible APIs ***********************\r
223 **********************************************************************/\r
224 \r
225 /* FUNCTION PURPOSE: Registers an app transport with a RM instance\r
226 ***********************************************************************\r
227 * DESCRIPTION: Returns a transport handle to the application that\r
228 * has been registered with an RM instance. The handle\r
229 * is used by the application to route packets to the\r
230 * proper RM instance's based on the application\r
231 * transport receive code. The handle is also used\r
232 * internally by RM to route request and response \r
233 * packets to the correct application transports. NULL\r
234 * is returned for the transport handle if any errors\r
235 * are encountered.\r
236 */\r
237 Rm_TransportHandle Rm_transportRegister (const Rm_TransportCfg *transportCfg, int32_t *result)\r
238 {\r
239 Rm_Inst *rmInst = (Rm_Inst *) transportCfg->rmHandle;\r
240 Rm_Transport *transport = NULL;\r
241 void *key;\r
242 \r
243 *result = RM_OK;\r
244 \r
245 RM_SS_INST_INV_ENTER_CS(key);\r
246 \r
247 /* Shared servers and clients cannot connect to anyone */\r
248 if ((rmInst->instType == Rm_instType_SHARED_SERVER) ||\r
249 (rmInst->instType == Rm_instType_SHARED_CLIENT)){\r
250 *result = RM_ERROR_SHARED_INSTANCE_CANNOT_REG_TRANS;\r
251 goto errorExit;\r
252 }\r
253 \r
254 /* No one can connect to a shared server\r
255 * RM Servers cannot connect to other Servers. \r
256 * RM Client Delegates cannot connect to other Client Delegates.\r
257 * RM Clients cannot connect to other Clients */\r
258 if ((transportCfg->remoteInstType == Rm_instType_SHARED_SERVER) ||\r
259 ((rmInst->instType == Rm_instType_SERVER) &&\r
260 (transportCfg->remoteInstType == Rm_instType_SERVER)) ||\r
261 ((rmInst->instType == Rm_instType_CLIENT_DELEGATE) &&\r
262 (transportCfg->remoteInstType == Rm_instType_CLIENT_DELEGATE)) ||\r
263 ((rmInst->instType == Rm_instType_CLIENT) &&\r
264 (transportCfg->remoteInstType == Rm_instType_CLIENT))) {\r
265 *result = RM_ERROR_INVALID_REMOTE_INST_TYPE;\r
266 goto errorExit;\r
267 }\r
268 \r
269 /* Verify Clients are not registering with more than one Client Delegate or Server. And\r
270 * that Client Delegate is not registering with more than one Server. */\r
271 if (rmInst->registeredWithDelegateOrServer &&\r
272 (((rmInst->instType == Rm_instType_CLIENT) &&\r
273 (transportCfg->remoteInstType == Rm_instType_CLIENT_DELEGATE)) || \r
274 ((rmInst->instType == Rm_instType_CLIENT_DELEGATE) &&\r
275 (transportCfg->remoteInstType == Rm_instType_SERVER)))) {\r
276 *result = RM_ERROR_ALREADY_REGD_SERVER_OR_CD;\r
277 goto errorExit;\r
278 } \r
279 \r
280 if (!transportCfg->transportCallouts.rmAllocPkt) {\r
281 *result = RM_ERROR_TRANSPORT_ALLOC_PKT_NOT_REGD;\r
282 goto errorExit;\r
283 }\r
284 else if (!transportCfg->transportCallouts.rmSendPkt) {\r
285 *result = RM_ERROR_TRANSPORT_SEND_NOT_REGD;\r
286 goto errorExit;\r
287 }\r
288 \r
289 transport = transportAdd(transportCfg);\r
290 if ((transport->remoteInstType == Rm_instType_CLIENT_DELEGATE) ||\r
291 (transport->remoteInstType == Rm_instType_SERVER)) {\r
292 rmInst->registeredWithDelegateOrServer = RM_TRUE;\r
293 }\r
294 errorExit:\r
295 RM_SS_INST_WB_EXIT_CS(key); \r
296 return ((Rm_TransportHandle) transport);\r
297 }\r
298 \r
299 /* FUNCTION PURPOSE: Reconfigures an instance's transport handle\r
300 ***********************************************************************\r
301 * DESCRIPTION: Reconfigures a transport handle based on the provided\r
302 * configuration parameters if it exists within the \r
303 * instance.\r
304 */\r
305 int32_t Rm_transportReconfig (Rm_TransportHandle transportHandle, const Rm_TransportReCfg *reCfg)\r
306 {\r
307 Rm_Transport *transport = (Rm_Transport *)transportHandle;\r
308 Rm_Inst *rmInst = (Rm_Inst *)transport->rmHandle;\r
309 void *key;\r
310 int32_t retVal = RM_OK;\r
311 \r
312 RM_SS_INST_INV_ENTER_CS(key);\r
313 \r
314 if (transportIsRegistered(transport->rmHandle, transport)) {\r
315 /* Reconfigure existing transport's appTransportHandle. Used in cases where instances\r
316 * running on same core connected by function call. */\r
317 transport->appTransportHandle = reCfg->appTransportHandle;\r
318 \r
319 if (reCfg->transportCallouts.rmAllocPkt) {\r
320 transport->callouts.rmAllocPkt = reCfg->transportCallouts.rmAllocPkt;\r
321 }\r
322 if (reCfg->transportCallouts.rmSendPkt) {\r
323 transport->callouts.rmSendPkt = reCfg->transportCallouts.rmSendPkt;\r
324 }\r
325 }\r
326 else {\r
327 retVal = RM_ERROR_TRANSPORT_HANDLE_DOES_NOT_EXIST;\r
328 } \r
329 \r
330 RM_SS_INST_WB_EXIT_CS(key); \r
331 return (retVal);\r
332 }\r
333 \r
334 /* FUNCTION PURPOSE: Unregisters an app transport from a RM instance\r
335 ***********************************************************************\r
336 * DESCRIPTION: Deletes a registered transport from the transport\r
337 * list and cleans up the memory associated with the\r
338 * transport data structure.\r
339 */\r
340 int32_t Rm_transportUnregister(Rm_TransportHandle transportHandle)\r
341 {\r
342 Rm_Transport *transport = (Rm_Transport *)transportHandle;\r
343 Rm_Inst *rmInst = (Rm_Inst *)transport->rmHandle;\r
344 int32_t retVal = RM_OK;\r
345 \r
346 if (transportIsRegistered(transport->rmHandle, transport)) {\r
347 if ((transport->remoteInstType == Rm_instType_CLIENT_DELEGATE) ||\r
348 (transport->remoteInstType == Rm_instType_SERVER)) {\r
349 rmInst->registeredWithDelegateOrServer = RM_FALSE;\r
350 }\r
351 transportDelete(transport);\r
352 }\r
353 else {\r
354 retVal = RM_ERROR_TRANSPORT_HANDLE_DOES_NOT_EXIST;\r
355 }\r
356 return (retVal);\r
357 }\r
358 \r
359 /* FUNCTION PURPOSE: Returns a RM packet's service source instance name\r
360 ***********************************************************************\r
361 * DESCRIPTION: Returns the RM instance name from which the service\r
362 * encapsulated in the RM packet originated.\r
363 */\r
364 int32_t Rm_receiveGetPktServiceSrcName(const Rm_Packet *pkt, char *serviceInstName, \r
365 int32_t charBufLen)\r
366 {\r
367 return(getPktSrcNames(pkt, NULL, serviceInstName, charBufLen));\r
368 }\r
369 \r
370 /* FUNCTION PURPOSE: Returns a RM packet's source instance name\r
371 ***********************************************************************\r
372 * DESCRIPTION: Returns the RM instance name from which the RM packet\r
373 * originated.\r
374 */\r
375 int32_t Rm_receiveGetPktSrcName(const Rm_Packet *pkt, char *pktInstName, \r
376 int32_t charBufLen)\r
377 {\r
378 return(getPktSrcNames(pkt, pktInstName, NULL, charBufLen));\r
379 }\r
380 \r
381 /* FUNCTION PURPOSE: Receives RM packets\r
382 ***********************************************************************\r
383 * DESCRIPTION: The application provides RM packets received on the\r
384 * application transports to RM via this API. Function\r
385 * can be called from polling or ISR contexts. Assume \r
386 * invoking application will free packet after this\r
387 * function returns.\r
388 */\r
389 int32_t Rm_receivePacket(Rm_TransportHandle transportHandle, const Rm_Packet *pkt)\r
391 Rm_Transport *transport = (Rm_Transport *)transportHandle;\r
392 Rm_Inst *rmInst = (Rm_Inst *)transport->rmHandle;\r
393 Rm_Transaction *transaction;\r
394 int32_t retVal = RM_OK;\r
395 \r
396 if (!transportIsRegistered(transport->rmHandle, transport)) {\r
397 retVal = RM_ERROR_TRANSPORT_HANDLE_DOES_NOT_EXIST;\r
398 goto errorExit;\r
399 }\r
400 \r
401 switch (pkt->pktType) {\r
402 case Rm_pktType_RESOURCE_REQUEST:\r
403 {\r
404 Rm_ResourceRequestPkt *resourceReqPkt = (Rm_ResourceRequestPkt *)pkt->data;\r
405 \r
406 transaction = rmTransactionQueueAdd(rmInst);\r
407 transaction->remoteOriginatingId = resourceReqPkt->requestId;\r
408 transaction->u.respTrans = transport;\r
409 if (resourceReqPkt->resourceReqType == Rm_resReqPktType_ALLOCATE_INIT) {\r
410 transaction->type = Rm_service_RESOURCE_ALLOCATE_INIT;\r
411 }\r
412 else if (resourceReqPkt->resourceReqType == Rm_resReqPktType_ALLOCATE_USE) {\r
413 transaction->type = Rm_service_RESOURCE_ALLOCATE_USE;\r
414 }\r
415 else if (resourceReqPkt->resourceReqType == Rm_resReqPktType_GET_STATUS) {\r
416 transaction->type = Rm_service_RESOURCE_STATUS;\r
417 }\r
418 else if (resourceReqPkt->resourceReqType == Rm_resReqPktType_FREE) {\r
419 transaction->type = Rm_service_RESOURCE_FREE;\r
420 }\r
421 else if (resourceReqPkt->resourceReqType == Rm_resReqPktType_GET_NAMED) {\r
422 transaction->type = Rm_service_RESOURCE_GET_BY_NAME;\r
423 } \r
424 rm_strncpy(transaction->serviceSrcInstName, resourceReqPkt->serviceSrcInstName, RM_NAME_MAX_CHARS);\r
425 transaction->state = RM_SERVICE_PROCESSING;\r
426 memcpy ((void *)&(transaction->resourceInfo), (void *)&(resourceReqPkt->resourceInfo),\r
427 sizeof(Rm_ResourceInfo));\r
428 break;\r
429 }\r
430 case Rm_pktType_RESOURCE_RESPONSE:\r
431 {\r
432 Rm_ResourceResponsePkt *resourceRespPkt = (Rm_ResourceResponsePkt *)pkt->data;\r
433 \r
434 if (transaction = rmTransactionQueueFind(rmInst,resourceRespPkt->responseId)) {\r
435 if ((transaction->state == RM_SERVICE_APPROVED_STATIC) &&\r
436 (resourceRespPkt->requestState != RM_SERVICE_APPROVED)) {\r
437 /* Lock the RM instance since service validated against static policy failed against \r
438 * Server's global policy */\r
439 rmInst->isLocked = RM_TRUE;\r
440 }\r
441 transaction->state = resourceRespPkt->requestState;\r
442 \r
443 if ((transaction->state == RM_SERVICE_APPROVED) &&\r
444 ((transaction->type == Rm_service_RESOURCE_ALLOCATE_INIT) ||\r
445 (transaction->type == Rm_service_RESOURCE_ALLOCATE_USE) ||\r
446 (transaction->type == Rm_service_RESOURCE_STATUS) ||\r
447 (transaction->type == Rm_service_RESOURCE_GET_BY_NAME))) {\r
448 memcpy ((void *)&(transaction->resourceInfo), (void *)&(resourceRespPkt->resourceInfo),\r
449 sizeof(Rm_ResourceInfo));\r
450 }\r
451 else {\r
452 /* Always copy owner count and instance allocation count */\r
453 transaction->resourceInfo.ownerCount = resourceRespPkt->resourceInfo.ownerCount;\r
454 transaction->resourceInfo.instAllocCount = resourceRespPkt->resourceInfo.instAllocCount;\r
455 }\r
456 }\r
457 else {\r
458 retVal = RM_ERROR_PKT_RESP_DOES_NOT_MATCH_ANY_REQ;\r
459 goto errorExit;\r
460 }\r
461 break;\r
462 }\r
463 case Rm_pktType_NAMESERVER_REQUEST:\r
464 {\r
465 Rm_NsRequestPkt *nsRequestPkt = (Rm_NsRequestPkt *)pkt->data;\r
466 \r
467 transaction = rmTransactionQueueAdd(rmInst);\r
468 transaction->remoteOriginatingId = nsRequestPkt->requestId;\r
469 transaction->u.respTrans = transport;\r
470 \r
471 if (nsRequestPkt->nsRequestType == Rm_nsReqPktType_MAP_RESOURCE) {\r
472 transaction->type = Rm_service_RESOURCE_MAP_TO_NAME;\r
473 }\r
474 else if (nsRequestPkt->nsRequestType == Rm_nsReqPktType_UNMAP_RESOURCE) {\r
475 transaction->type = Rm_service_RESOURCE_UNMAP_NAME;\r
476 }\r
477 \r
478 rm_strncpy(transaction->serviceSrcInstName, nsRequestPkt->serviceSrcInstName, RM_NAME_MAX_CHARS);\r
479 transaction->state = RM_SERVICE_PROCESSING;\r
480 memcpy ((void *)&(transaction->resourceInfo), (void *)&(nsRequestPkt->resourceInfo),\r
481 sizeof(Rm_ResourceInfo)); \r
482 break;\r
483 }\r
484 case Rm_pktType_NAMESERVER_RESPONSE:\r
485 {\r
486 Rm_NsResponsePkt *nsResponsePkt = (Rm_NsResponsePkt *)pkt->data;\r
487 \r
488 if (transaction = rmTransactionQueueFind(rmInst, nsResponsePkt->responseId)) {\r
489 if ((transaction->state == RM_SERVICE_APPROVED_STATIC) &&\r
490 (nsResponsePkt->requestState != RM_SERVICE_APPROVED)) {\r
491 /* Lock the RM instance since service validated against static policy failed against \r
492 * Server's global policy */ \r
493 rmInst->isLocked = RM_TRUE;\r
494 } \r
495 transaction->state = nsResponsePkt->requestState; \r
496 }\r
497 else {\r
498 retVal = RM_ERROR_PKT_RESP_DOES_NOT_MATCH_ANY_REQ;\r
499 goto errorExit;\r
500 }\r
501 break;\r
502 }\r
503 default:\r
504 retVal = RM_ERROR_RECEIVED_INVALID_PACKET_TYPE;\r
505 goto errorExit;\r
506 }\r
507 \r
508 /* Process received transaction */\r
509 rmProcessRouter(rmInst, transaction); \r
510 errorExit:\r
511 return(retVal);\r
512 }\r
513 \r