1 /*
2 * Copyright (c) 2013-2015, Texas Instruments Incorporated
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of Texas Instruments Incorporated nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
33 /*
34 * ======== GateMP_daemon.c ========
35 */
38 /* Standard headers */
39 #include <ti/ipc/Std.h>
41 /* Linux specific header files */
42 #include <pthread.h>
44 /* System headers */
45 #include <sys/mman.h>
46 #include <sys/stat.h>
47 #include <fcntl.h>
48 #include <stdlib.h>
49 #include <assert.h>
50 #include <dirent.h>
51 #include <errno.h>
52 #include <string.h>
54 /* Module level headers */
55 #include <ti/ipc/GateMP.h>
56 #include <ti/ipc/NameServer.h>
57 #include <ti/ipc/MultiProc.h>
58 #include <_MultiProc.h>
59 #include <GateMP_config.h>
60 #include <_GateMP.h>
62 #include <IGateProvider.h>
63 #include <_GateMP_daemon.h>
64 #include <_lad.h>
66 #if defined (__cplusplus)
67 extern "C" {
68 #endif
70 #define NUM_INFO_FIELDS 6 /* Number of fields in info entry */
72 /* Values used to populate the resource 'inUse' arrays */
73 #define UNUSED ((UInt8)0)
74 #define USED ((UInt8)1)
75 #define RESERVED ((UInt8)-1)
77 /* Name of GateMP's nameserver */
78 #define GateMP_NAMESERVER "GateMP"
80 /* sysfs path for the UIO user-space drivers */
81 #define UIO_SYSFS "/sys/class/uio"
83 #define PAGE_ALIGN(size, psz) (((size) + psz - 1) & ~(psz -1))
85 /* =============================================================================
86 * Structures & Enums
87 * =============================================================================
88 */
90 /* structure for GateMP module state */
91 typedef struct {
92 Int numRemoteSystem;
93 Int numRemoteCustom1;
94 Int numRemoteCustom2;
95 UInt8 * remoteSystemInUse;
96 UInt8 * remoteCustom1InUse;
97 UInt8 * remoteCustom2InUse;
98 GateMP_Handle defaultGate;
99 NameServer_Handle nameServer;
100 Bool isSetup;
101 Int refCount[MultiProc_MAXPROCESSORS];
102 UInt16 attachedProcId;
103 } GateMP_ModuleObject;
105 /* Internal functions */
106 static Int GateMP_get_sr0(Char *name, UInt32 name_len, UInt32 *baseaddr);
107 static Int GateMP_openDefaultGate(GateMP_Handle *handlePtr, UInt16 procId[]);
108 static Int GateMP_closeDefaultGate(GateMP_Handle *handlePtr);
110 /* =============================================================================
111 * Globals
112 * =============================================================================
113 */
114 /*
115 * GateMP_state
116 */
117 static GateMP_ModuleObject GateMP_state = {
118 .numRemoteSystem = 0,
119 .numRemoteCustom1 = 0,
120 .numRemoteCustom2 = 0,
121 .remoteSystemInUse = NULL,
122 .remoteCustom1InUse = NULL,
123 .remoteCustom2InUse = NULL,
124 .defaultGate = NULL,
125 .nameServer = NULL,
126 .isSetup = FALSE,
127 .refCount = {0},
128 .attachedProcId = MultiProc_INVALIDID,
129 };
131 static GateMP_ModuleObject * GateMP_module = &GateMP_state;
133 /* =============================================================================
134 * APIS
135 * =============================================================================
136 */
138 /*
139 * Function to setup the gatemp module.
140 *
141 * No need to refCount this since it's not callable by user.
142 */
143 Int GateMP_setup(Void)
144 {
145 Int status = GateMP_S_SUCCESS;
146 NameServer_Params params;
148 NameServer_Params_init(¶ms);
149 params.maxRuntimeEntries = MAX_RUNTIME_ENTRIES;
150 params.maxNameLen = MAX_NAME_LEN;
152 /* Assume info entry has more fields than other entries */
153 params.maxValueLen = NUM_INFO_FIELDS * sizeof(UInt32);
155 GateMP_module->nameServer =
156 NameServer_create(GateMP_NAMESERVER, ¶ms);
158 if (GateMP_module->nameServer == NULL) {
159 status = GateMP_E_FAIL;
160 LOG0("GateMP_setup: NameServer_create failed\n");
161 }
162 else {
163 GateMP_module->isSetup = TRUE;
164 }
166 return status;
167 }
169 Int GateMP_attach(UInt16 procId)
170 {
171 GateMP_Handle deflateGate; /* that's right, Pats fan here */
172 Int status = GateMP_S_SUCCESS;
173 UInt32 nsValue[NUM_INFO_FIELDS];
174 UInt32 len;
175 UInt32 size;
176 UInt32 alignDiff;
177 UInt32 offset;
178 UInt32 baseaddr;
179 Int32 fdMem;
180 UInt16 procList[2];
181 UInt16 clId;
182 Char filename[PATH_MAX];
184 /* procId already validated in API layer */
185 clId = procId - MultiProc_getBaseIdOfCluster();
186 if (clId >= MultiProc_getNumProcsInCluster()) {
187 LOG1("GateMP_attach: procId %d not in range for local cluster\n",
188 procId);
189 return GateMP_E_INVALIDARG;
190 }
192 /* must reference count because we have multiple clients */
193 if (GateMP_module->refCount[clId] > 0) {
194 GateMP_module->refCount[clId]++;
195 goto done;
196 }
198 procList[0] = procId;
199 procList[1] = MultiProc_INVALIDID;
200 status = GateMP_openDefaultGate(&deflateGate, procList);
202 if (status < 0) {
203 LOG1("GateMP_attach: failed to open default gate on procId %d\n",
204 procId);
205 goto done;
206 }
208 if (status == GateMP_S_SUCCESS) {
209 if (GateMP_module->attachedProcId != MultiProc_INVALIDID) {
210 LOG1("GateMP_attach: can't attach to procId %d\n", procId);
211 LOG1(" already attached to %d\n",
212 GateMP_module->attachedProcId);
213 status = GateMP_E_ALREADYEXISTS;
214 goto done;
215 }
217 GateMP_module->attachedProcId = procId;
218 GateMP_module->defaultGate = deflateGate;
220 GateMP_module->refCount[clId]++;
222 /* Process global info NameServer entry */
223 len = sizeof(nsValue);
225 status = NameServer_get(GateMP_module->nameServer, "_GateMP_TI_info",
226 &nsValue, &len, procList);
228 if (status < 0) {
229 LOG0("GateMP_attach: failed to find info entry\n");
230 status = GateMP_E_NOTFOUND;
231 }
232 else {
233 status = GateMP_get_sr0(filename, PATH_MAX, &baseaddr);
234 if (status < 0) {
235 LOG1("GateMP_attach: failed to find sr0: %s\n", strerror(status));
236 status = GateMP_E_NOTFOUND;
237 }
239 fdMem = open (filename, O_RDWR | O_SYNC);
240 if (fdMem < 0){
241 LOG1("GateMP_attach: failed to open the %s!\n", filename);
242 status = GateMP_E_OSFAILURE;
243 goto done;
244 }
246 GateMP_module->numRemoteSystem = nsValue[3];
247 GateMP_module->numRemoteCustom1 = nsValue[4];
248 GateMP_module->numRemoteCustom2 = nsValue[5];
250 /* Map InUse arrays to daemon's address space */
251 size = GateMP_module->numRemoteSystem * sizeof (UInt8) +
252 (nsValue[0] & (sysconf(_SC_PAGE_SIZE) - 1));
253 size = PAGE_ALIGN(size, sysconf(_SC_PAGE_SIZE));
254 offset = nsValue[0] & ~(sysconf(_SC_PAGE_SIZE) - 1);
255 offset -= baseaddr;
257 #if defined(IPC_BUILDOS_ANDROID)
258 GateMP_module->remoteSystemInUse = mmap64(NULL, size,
259 (PROT_READ|PROT_WRITE), (MAP_SHARED), fdMem,
260 (off64_t)offset);
261 #else
262 GateMP_module->remoteSystemInUse = mmap(NULL, size,
263 (PROT_READ|PROT_WRITE), (MAP_SHARED), fdMem,
264 (off_t)offset);
265 #endif
266 if (GateMP_module->remoteSystemInUse == MAP_FAILED) {
267 LOG1("Failed to map remoteSystemInUse=0x%p to host address" \
268 " space!", GateMP_module->remoteSystemInUse);
269 GateMP_module->remoteSystemInUse = NULL;
270 status = GateMP_E_MEMORY;
271 }
272 else {
273 alignDiff = nsValue[0] - baseaddr - offset;
274 GateMP_module->remoteSystemInUse =
275 GateMP_module->remoteSystemInUse + alignDiff;
276 }
278 size = GateMP_module->numRemoteCustom1 * sizeof (UInt8) +
279 (nsValue[1] & (sysconf(_SC_PAGE_SIZE) - 1));
280 size = PAGE_ALIGN(size, sysconf(_SC_PAGE_SIZE));
281 offset = nsValue[1] & ~(sysconf(_SC_PAGE_SIZE) - 1);
282 offset -= baseaddr;
284 if (status == GateMP_S_SUCCESS) {
285 #if defined(IPC_BUILDOS_ANDROID)
286 GateMP_module->remoteCustom1InUse = mmap64(NULL, size,
287 (PROT_READ|PROT_WRITE), (MAP_SHARED), fdMem,
288 (off64_t)offset);
289 #else
290 GateMP_module->remoteCustom1InUse = mmap(NULL, size,
291 (PROT_READ|PROT_WRITE), (MAP_SHARED), fdMem,
292 (off_t)offset);
293 #endif
294 if (GateMP_module->remoteCustom1InUse == MAP_FAILED) {
295 LOG1("Failed to map remoteCustom1InUse=%p to host address" \
296 " space!", GateMP_module->remoteCustom1InUse);
297 GateMP_module->remoteCustom1InUse = NULL;
298 status = GateMP_E_MEMORY;
299 }
300 else {
301 alignDiff = nsValue[1] - baseaddr - offset;
302 GateMP_module->remoteCustom1InUse =
303 GateMP_module->remoteCustom1InUse + alignDiff;
304 }
305 }
307 size = GateMP_module->numRemoteCustom2 * sizeof (UInt8) +
308 (nsValue[2] & (sysconf(_SC_PAGE_SIZE) - 1));
309 size = PAGE_ALIGN(size, sysconf(_SC_PAGE_SIZE));
310 offset = nsValue[2] & ~(sysconf(_SC_PAGE_SIZE) - 1);
311 offset -= baseaddr;
313 if (status == GateMP_S_SUCCESS) {
314 #if defined(IPC_BUILDOS_ANDROID)
315 GateMP_module->remoteCustom2InUse = mmap64(NULL, size,
316 (PROT_READ|PROT_WRITE), (MAP_SHARED), fdMem,
317 (off64_t)offset);
318 #else
319 GateMP_module->remoteCustom2InUse = mmap(NULL, size,
320 (PROT_READ|PROT_WRITE), (MAP_SHARED), fdMem,
321 (off_t)offset);
322 #endif
323 if (GateMP_module->remoteCustom2InUse == MAP_FAILED) {
324 LOG1("Failed to map remoteCustom2InUse=%p to host address" \
325 " space!", GateMP_module->remoteCustom2InUse);
326 GateMP_module->remoteCustom2InUse = NULL;
327 status = GateMP_E_MEMORY;
328 }
329 else {
330 alignDiff = nsValue[2] - baseaddr - offset;
331 GateMP_module->remoteCustom2InUse =
332 GateMP_module->remoteCustom2InUse + alignDiff;
333 }
334 }
335 }
336 }
338 /* TODO: setup the proxy map */
340 done:
342 return (status);
343 }
345 Int GateMP_detach(UInt16 procId)
346 {
347 UInt16 clId;
349 if (procId != GateMP_module->attachedProcId) {
350 return GateMP_E_NOTFOUND;
351 }
353 /* procId already validated in API layer */
354 clId = procId - MultiProc_getBaseIdOfCluster();
355 if (clId >= MultiProc_getNumProcsInCluster()) {
356 LOG1("GateMP_detach: procId %d not in range for local cluster\n",
357 procId);
358 return GateMP_E_INVALIDARG;
359 }
362 /* decrement reference count regardless of outcome below */
363 if (--GateMP_module->refCount[clId] > 0) {
364 goto done;
365 }
367 if (GateMP_module->remoteSystemInUse) {
368 munmap((unsigned int *)GateMP_module->remoteSystemInUse,
369 GateMP_module->numRemoteSystem * sizeof (UInt8));
370 GateMP_module->remoteSystemInUse = NULL;
371 }
373 if (GateMP_module->remoteCustom1InUse) {
374 munmap((unsigned int *)GateMP_module->remoteCustom1InUse,
375 GateMP_module->numRemoteCustom1 * sizeof (UInt8));
376 GateMP_module->remoteCustom1InUse = NULL;
377 }
379 if (GateMP_module->remoteCustom2InUse) {
380 munmap((unsigned int *)GateMP_module->remoteCustom2InUse,
381 GateMP_module->numRemoteCustom2 * sizeof (UInt8));
382 GateMP_module->remoteCustom2InUse = NULL;
383 }
385 if (GateMP_module->defaultGate) {
386 GateMP_closeDefaultGate(&GateMP_module->defaultGate);
387 GateMP_module->attachedProcId = MultiProc_INVALIDID;
388 }
390 done:
392 return GateMP_S_SUCCESS;
393 }
395 Void GateMP_destroy(Void)
396 {
397 if (GateMP_module->nameServer) {
398 NameServer_delete(&GateMP_module->nameServer);
399 GateMP_module->nameServer = NULL;
400 }
402 GateMP_module->isSetup = FALSE;
404 return;
405 }
407 static Int GateMP_get_sr0(Char *name, UInt32 name_len, UInt32 *baseaddr)
408 {
409 Char filename[PATH_MAX];
410 Char format[80];
411 Char uio_name[80];
412 DIR *dir;
413 struct dirent *de;
414 struct stat st;
415 FILE *fd;
416 Bool found = false;
417 Int ret = 0;
419 if (stat(UIO_SYSFS, &st) < 0) {
420 if ((errno == ENOENT) || (errno == ENOTDIR)) {
421 LOG0("GateMP_get_sr0: UIO support not found\n");
422 ret = errno;
423 goto out;
424 }
425 }
427 dir = opendir(UIO_SYSFS);
428 if (!dir) {
429 LOG1("GateMP_get_sr0: failed to open UIO sysfs %s\n", UIO_SYSFS);
430 ret = errno;
431 goto out;
432 }
434 while (!found) {
435 de = readdir(dir);
436 if (!de) {
437 break;
438 }
440 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
441 continue;
442 }
444 /* Skip if this is not an UIO entry */
445 snprintf(filename, sizeof(filename), "%s/%s/name", UIO_SYSFS, de->d_name);
446 fd = fopen(filename, "r");
447 if (fd == NULL) {
448 continue;
449 }
451 snprintf(format, sizeof(format), "%%%ds", sizeof(uio_name)-1);
452 fscanf(fd, format, uio_name);
453 fclose(fd);
455 /* Skip if this UIO entry is not SR0 */
456 if (strncmp(uio_name, "sr0", sizeof(uio_name)-1)) {
457 continue;
458 }
460 /* Find the base address of the SR0 UIO */
461 snprintf(filename, sizeof(filename), "%s/%s/maps/map0/addr", UIO_SYSFS, de->d_name);
462 fd = fopen(filename, "r");
463 if (fd == NULL) {
464 LOG0("Gate_sr0_filename: failed to open uio map sysfs\n");
465 ret = errno;
466 break;
467 }
469 fscanf(fd, "0x%x", baseaddr);
470 fclose(fd);
472 snprintf(name, name_len, "/dev/%s", de->d_name);
473 found = true;
474 }
476 closedir(dir);
478 out:
479 /* Fall back to /dev/mem if UIO is not supported */
480 if (!found) {
481 strncpy(name, "/dev/mem", name_len);
482 *baseaddr = 0x0;
483 }
485 return ret;
486 }
488 /* Open default gate during GateMP_attach. Should only be called once */
489 static Int GateMP_openDefaultGate(GateMP_Handle *handlePtr, UInt16 procId[])
490 {
491 Int status = GateMP_S_SUCCESS;
492 UInt32 len;
493 UInt32 nsValue[4];
494 GateMP_Object * obj = NULL;
495 UInt32 arg;
496 UInt32 mask;
498 GateMP_RemoteSystemProxy_Params systemParams;
500 /* assert that a valid pointer has been supplied */
501 if (handlePtr == NULL) {
502 LOG0("GateMP_openDefaultGate: argument cannot be null\n");
503 status = GateMP_E_INVALIDARG;
504 }
506 if (status == GateMP_S_SUCCESS) {
507 len = sizeof(nsValue);
509 status = NameServer_get(GateMP_module->nameServer, "_GateMP_TI_dGate",
510 &nsValue, &len, procId);
512 if (status < 0) {
513 *handlePtr = NULL;
514 status = GateMP_E_NOTFOUND;
515 }
516 else {
517 arg = nsValue[2];
518 mask = nsValue[3];
519 }
520 }
522 if (status == GateMP_S_SUCCESS) {
523 /* allocate the instance object */
524 obj = (GateMP_Object *)calloc(1, sizeof (GateMP_Object));
525 if (obj != NULL) {
526 obj->localGate = NULL; /* TODO: create the local gate instance */
527 obj->localProtect = GETLOCAL(mask);
528 obj->remoteProtect = GETREMOTE(mask);
529 obj->nsKey = 0;
530 obj->numOpens = 1;
531 obj->objType = Ipc_ObjType_OPENDYNAMIC;
532 obj->resourceId = arg;
534 assert(obj->remoteProtect == GateMP_RemoteProtect_SYSTEM);
536 /* create the proxy object */
537 GateMP_RemoteSystemProxy_Params_init(&systemParams);
538 systemParams.resourceId = obj->resourceId;
539 systemParams.openFlag = TRUE;
541 /*
542 * TODO: Currently passing in localProtect instead of localGate,
543 * since GateHWSpinlock owns the local gate
544 */
545 obj->gateHandle = (IGateProvider_Handle)
546 GateMP_RemoteSystemProxy_create(obj->localProtect,
547 &systemParams);
549 if (obj->gateHandle == NULL) {
550 LOG0("GateMP_openDefaultGate: failed to create proxy\n");
551 free(obj);
552 obj = NULL;
553 }
554 }
555 else {
556 LOG0("GateMP_openDefaultGate: Memory allocation failed")
557 }
559 if (obj == NULL) {
560 status = GateMP_E_FAIL;
561 }
562 }
564 /* Return the "opened" GateMP instance */
565 *handlePtr = (GateMP_Handle)obj;
567 return status;
568 }
570 static Int GateMP_closeDefaultGate(GateMP_Handle *handlePtr)
571 {
572 Int status = GateMP_S_SUCCESS;
573 GateMP_Object * obj = *(GateMP_Object **)handlePtr;
575 if (obj->gateHandle != NULL) {
576 /* Default gate is always of type System when more than 1 processor */
577 GateMP_RemoteSystemProxy_delete(
578 (GateMP_RemoteSystemProxy_Handle *)&obj->gateHandle);
579 }
581 free(*handlePtr);
582 *handlePtr = NULL;
584 return(status);
585 }
587 Int GateMP_getFreeResource(GateMP_RemoteProtect type)
588 {
589 IArg key;
590 Bool flag = FALSE;
591 Int resourceId = -1;
592 UInt8* inUse = NULL;
593 Int num = 0;
595 /* Remote case */
596 switch (type) {
597 /* TODO: currently only support System proxy */
598 case GateMP_RemoteProtect_SYSTEM:
599 case GateMP_RemoteProtect_CUSTOM1:
600 case GateMP_RemoteProtect_CUSTOM2:
601 inUse = GateMP_module->remoteSystemInUse;
602 num = GateMP_module->numRemoteSystem;
603 break;
605 default:
606 LOG0("GateMP_getFreeResource: Invalid remote protection type\n");
607 break;
608 }
610 if (inUse != NULL) {
611 assert(GateMP_module->defaultGate != NULL);
612 key = GateMP_enter(GateMP_module->defaultGate);
614 /*
615 * Find a free resource id. Note: zero is reserved on the
616 * system proxy for the default gate.
617 */
618 for (resourceId = 0; resourceId < num; resourceId++) {
619 /*
620 * If not in-use, set the inUse to TRUE to prevent other
621 * creates from getting this one.
622 */
623 if (inUse[resourceId] == UNUSED) {
624 flag = TRUE;
626 /* Denote in shared memory that the resource is used */
627 inUse[resourceId] = USED;
628 break;
629 }
630 }
632 GateMP_leave(GateMP_module->defaultGate, key);
633 }
635 if (flag == FALSE) {
636 resourceId = -1;
637 }
639 return (resourceId);
640 }
642 Int GateMP_releaseResource(UInt id, GateMP_RemoteProtect type)
643 {
644 Int status = GateMP_S_SUCCESS;
645 IArg key;
646 UInt8* inUse = NULL;
647 Int num = 0;
649 /* Remote case */
650 switch (type) {
651 /* TODO: currently only support System proxy */
652 case GateMP_RemoteProtect_SYSTEM:
653 case GateMP_RemoteProtect_CUSTOM1:
654 case GateMP_RemoteProtect_CUSTOM2:
655 inUse = GateMP_module->remoteSystemInUse;
656 num = GateMP_module->numRemoteSystem;
657 break;
659 default:
660 LOG0("GateMP_releaseResource: Invalid remote protection type\n");
661 status = GateMP_E_FAIL;
662 break;
663 }
665 if ((inUse != NULL) && (id < num)) {
666 assert(GateMP_module->defaultGate != NULL);
667 key = GateMP_enter(GateMP_module->defaultGate);
668 inUse[id] = UNUSED;
669 GateMP_leave(GateMP_module->defaultGate, key);
670 }
671 else {
672 /* Should not happen if module is properly setup */
673 status = GateMP_E_FAIL;
674 }
676 return (status);
677 }
679 Int GateMP_getNumResources(GateMP_RemoteProtect type)
680 {
681 Int num = -1;
683 /* Remote case */
684 switch (type) {
685 /* TODO: currently only support System proxy */
686 case GateMP_RemoteProtect_SYSTEM:
687 case GateMP_RemoteProtect_CUSTOM1:
688 case GateMP_RemoteProtect_CUSTOM2:
689 num = GateMP_module->numRemoteSystem;
690 break;
692 default:
693 LOG0("GateMP_getNumResources: Invalid remote protection type\n");
694 break;
695 }
697 return (num);
698 }
700 NameServer_Handle GateMP_getNameServer(Void)
701 {
702 return (GateMP_module->nameServer);
703 }
705 Bool GateMP_isSetup(Void)
706 {
707 return (GateMP_module->isSetup);
708 }
710 IArg GateMP_enter(GateMP_Handle handle)
711 {
712 GateMP_Object * obj;
713 IArg key;
715 obj = (GateMP_Object *)handle;
716 key = IGateProvider_enter(obj->gateHandle);
718 return(key);
719 }
721 Void GateMP_leave(GateMP_Handle handle, IArg key)
722 {
723 GateMP_Object *obj;
725 obj = (GateMP_Object *)handle;
726 IGateProvider_leave(obj->gateHandle, key);
727 }