Bind message queue to new remote processor
[ipc/ipcdev.git] / linux / src / api / Ipc.c
1 /*
2  * Copyright (c) 2012-2015 Texas Instruments Incorporated - http://www.ti.com
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  */
32 /*!
33  *  @file       Ipc.c
34  *
35  *  @brief      Starts and stops user side Ipc
36  *              All setup/destroy APIs on user side will be call from this
37  *              module.
38  */
40 /* standard headers */
41 #include <pthread.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <signal.h>
45 #include <string.h>
47 /* package headers */
48 #include <ti/ipc/Std.h>
49 #include <ti/ipc/Ipc.h>
50 #include <ti/ipc/NameServer.h>
52 /* User side headers */
53 #include <ladclient.h>
55 /* IPC startup/shutdown stuff: */
56 #include <ti/ipc/MultiProc.h>
57 #include <GateHWSpinlock.h>
58 #include <_GateMP.h>
59 #include <_Ipc.h>
60 #include <_MultiProc.h>
61 #include <_MessageQ.h>
62 #include <_NameServer.h>
64 /* module definition */
65 typedef struct {
66     Int                         refCount;
67     pthread_mutex_t             gate;
68     Ipc_TransportFactoryFxns   *transportFactory;
69     Ipc_Config                  config;
70     Int                         attached[MultiProc_MAXPROCESSORS];
71 } Ipc_Module;
73 /* hack: rpmsgproto driver work around */
74 Void MessageQ_bind(UInt16 procId);
75 Void MessageQ_unbind(UInt16 procId);
78 /* =============================================================================
79  *  Globals
80  * =============================================================================
81  */
82 static Ipc_Module Ipc_module = {
83     .refCount           = 0,
84 #if defined(IPC_BUILDOS_ANDROID)
85     .gate               = PTHREAD_RECURSIVE_MUTEX_INITIALIZER,
86 #else
87 // only _NP (non-portable) type available in CG tools which we're using
88     .gate               = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
89 #endif
90     .transportFactory   = NULL,
91     .config.procSync    = Ipc_ProcSync_NONE
92 };
94 GateHWSpinlock_Config _GateHWSpinlock_cfgParams;
95 static LAD_ClientHandle ladHandle;
97 /* traces in this file are controlled via _Ipc_verbose */
98 Bool _Ipc_verbose = FALSE;
99 #define verbose _Ipc_verbose
101 /** ============================================================================
102  *  Functions
103  *  ============================================================================
104  */
105 static void cleanup(int arg);
108 /*
109  *  ======== Ipc_start ========
110  */
111 Int Ipc_start(Void)
113     MessageQ_Config         msgqCfg;
114     MultiProc_Config        mpCfg;
115 #if defined(GATEMP_SUPPORT)
116     GateHWSpinlock_Config   gateHWCfg;
117 #endif
118     Int         status;
119     LAD_Status  ladStatus;
120     UInt16      procId;
121     UInt16      clusterSize;
122     UInt16      baseId;
123     UInt16      clusterId;
124     Int         i;
126     /* function must be serialized */
127     pthread_mutex_lock(&Ipc_module.gate);
129     /* ensure only first thread performs startup procedure */
130     if (Ipc_module.refCount >= 1) {
131         Ipc_module.refCount++;
132         status = Ipc_S_ALREADYSETUP;
133         goto exit;
134     }
136     /* make sure transport factory has been configured */
137     if (Ipc_module.transportFactory == NULL) {
138         status = Ipc_E_INVALIDSTATE;
139         goto exit;
140     }
142     /* initialize module object */
143     for (i = 0; i < MultiProc_MAXPROCESSORS; i++) {
144         Ipc_module.attached[i] = 0;
145     }
147     /* Catch ctrl-C, and cleanup: */
148     (void) signal(SIGINT, cleanup);
150     if (getenv("IPC_DEBUG") != NULL) {
151         /* turn on tracing */
152         if (getenv("IPC_DEBUG")[0] == '1') {
153             /* level 1 enables typical user API tracing */
154             _MessageQ_verbose = TRUE;
155             _MultiProc_verbose = TRUE;
156             _NameServer_verbose = TRUE;
157 #if defined(GATEMP_SUPPORT)
158             _GateMP_verbose = TRUE;
160             _GateHWSpinlock_verbose = TRUE;
161 #endif
162         }
163         else if ((getenv("IPC_DEBUG")[0] == '2') ||
164                 (getenv("IPC_DEBUG")[0] == '3')) {
165             /* levels 2 and 3 add socket and LAD client tracing */
166             _MessageQ_verbose = TRUE;
167             _MultiProc_verbose = TRUE;
168             _NameServer_verbose = TRUE;
170 #if defined(GATEMP_SUPPORT)
171             _GateMP_verbose = TRUE;
173             _GateHWSpinlock_verbose = TRUE;
174 #endif
176             /* internals - should be declared in respective _*.h files */
177             extern Bool _SocketFxns_verbose;
178             extern Bool _LAD_Client_verbose;
180             _SocketFxns_verbose = TRUE;
181             _LAD_Client_verbose = TRUE;
182         }
183     }
185     /* establish a communication link to the LAD daemon */
186     ladStatus = LAD_connect(&ladHandle);
188     if (ladStatus != LAD_SUCCESS) {
189         printf("Ipc_start: LAD_connect() failed: %d\n", ladStatus);
190         status = Ipc_E_FAIL;
191         goto exit;
192     }
194     /* get global configuration from LAD */
195     Ipc_getConfig(&Ipc_module.config);
197     /* get global configuration from LAD */
198     MultiProc_getConfig(&mpCfg);
199     _MultiProc_initCfg(&mpCfg);
201     /* setup name server thread in LAD daemon */
202     status = NameServer_setup();
204     if (status < 0) {
205         printf("Ipc_start: NameServer_setup() failed: %d\n", status);
206         status = Ipc_E_FAIL;
207         goto exit;
208     }
210     /* get global configuration from LAD */
211     MessageQ_getConfig(&msgqCfg);
212     MessageQ_setup(&msgqCfg);
214     /* invoke the transport factory create method */
215     status = Ipc_module.transportFactory->createFxn();
217     if (status < 0) {
218         goto exit;
219     }
221     /* if using ProcSync_ALL, then attach to all processors in the cluster */
222     if (Ipc_module.config.procSync == Ipc_ProcSync_ALL) {
223         clusterSize = MultiProc_getNumProcsInCluster();
224         baseId = MultiProc_getBaseIdOfCluster();
226         for (clusterId = 0; clusterId < clusterSize; clusterId++) {
227             procId = baseId + clusterId;
229             if (procId == MultiProc_self()) {
230                 continue;
231             }
233             status = Ipc_attach(procId);
235             /*  For backward compatibility, it is okay for attach to fail.
236              *  We don't expect all remote processors to be running.
237              */
238             if (status < 0) {
239                 status = 0;
240                 /* do nothing */
241             }
242         }
243     }
245     /* Start GateMP only if device has support */
246 #if defined(GATEMP_SUPPORT)
247     if (GateMP_isSetup()) {
248         /*
249          * Get HWSpinlock base address and size from LAD and
250          * initialize the local config structure.
251          */
252         GateHWSpinlock_getConfig(&gateHWCfg);
253         _GateHWSpinlock_cfgParams = gateHWCfg;
255         status = GateHWSpinlock_start();
256         if (status < 0) {
257             printf("Ipc_start: GateHWSpinlock_start failed: %d\n", status);
258             status = Ipc_E_FAIL;
259             goto exit;
260         }
262         status = GateMP_start();
263         if (status < 0) {
264             printf("Ipc_start: GateMP_start failed: %d\n", status);
265             status = Ipc_E_FAIL;
266             GateHWSpinlock_stop();
267             goto exit;
268         }
269     }
270 #endif
272     /* getting here means we have successfully started */
273     Ipc_module.refCount++;
275 exit:
276     pthread_mutex_unlock(&Ipc_module.gate);
278     return (status);
281 /*
282  *  ======== Ipc_stop ========
283  */
284 Int Ipc_stop(Void)
286     Int32       status = Ipc_S_SUCCESS;
287     LAD_Status  ladStatus;
288     UInt16      procId;
289     UInt16      clusterSize;
290     UInt16      baseId;
291     UInt16      clusterId;
293     /* function must be serialized */
294     pthread_mutex_lock(&Ipc_module.gate);
296     if (Ipc_module.refCount == 0) {
297         status = Ipc_E_INVALIDSTATE;
298         goto exit;
299     }
301     /* ensure only last thread performs stop procedure */
302     if (--Ipc_module.refCount > 0) {
303         goto exit;
304     }
306     /* if using ProcSync_ALL, then detach from all processors in the cluster */
307     if (Ipc_module.config.procSync == Ipc_ProcSync_ALL) {
308         clusterSize = MultiProc_getNumProcsInCluster();
309         baseId = MultiProc_getBaseIdOfCluster();
311         for (clusterId = 0; clusterId < clusterSize; clusterId++) {
312             procId = baseId + clusterId;
314             if (MultiProc_self() == procId) {
315                 continue;
316             }
318             /*  For backward compatibility, we might not be attached to
319              *  all cluster members. Skip unattached processors.
320              */
321             if (!Ipc_isAttached(procId)) {
322                 continue;
323             }
325             status = Ipc_detach(procId);
327             if (status < 0) {
328                 /* Should we keep going or stop? */
329             }
330         }
331     }
333     Ipc_module.transportFactory->deleteFxn();
335     status = MessageQ_destroy();
336     if (status < 0) {
337         printf("Ipc_stop: MessageQ_destroy() failed: %d\n", status);
338         status = Ipc_E_FAIL;
339         goto exit;
340     }
342     status = NameServer_destroy();
343     if (status < 0) {
344         printf("Ipc_stop: NameServer_destroy() failed: %d\n", status);
345         status = Ipc_E_FAIL;
346         goto exit;
347     }
349     ladStatus = LAD_disconnect(ladHandle);
350     if (ladStatus != LAD_SUCCESS) {
351         printf("LAD_disconnect() failed: %d\n", ladStatus);
352         status = Ipc_E_FAIL;
353         goto exit;
354     }
356 exit:
357     pthread_mutex_unlock(&Ipc_module.gate);
359     return (status);
362 /*
363  *  ======== Ipc_transportConfig ========
364  */
365 Int Ipc_transportConfig(Ipc_TransportFactoryFxns *factory)
367     Int status = Ipc_S_SUCCESS;
369     pthread_mutex_lock(&Ipc_module.gate);
371     /*  Only the first caller can actually set the transport factory.
372      *  Subsequent callers (e.g. multi-threaded application) must be
373      *  using the same factory. Otherwise, it is an error.
374      */
375     if (Ipc_module.transportFactory == NULL) {
376         Ipc_module.transportFactory = factory;
377     }
378     else if (Ipc_module.transportFactory != factory) {
379         status = Ipc_E_INVALIDARG;
380         goto exit;
381     }
383 exit:
384     pthread_mutex_unlock(&Ipc_module.gate);
386     return (status);
389 static void cleanup(int arg)
391     printf("Ipc: Caught SIGINT, calling Ipc_stop...\n");
392     Ipc_stop();
393     exit(0);
396 /*
397  *  ======== Ipc_attach ========
398  */
399 Int Ipc_attach(UInt16 procId)
401     Int status = Ipc_S_SUCCESS;
402     UInt16 clusterId;
404     /* cannot attach to yourself */
405     if (MultiProc_self() == procId) {
406         status =  Ipc_E_INVALIDARG;
407         goto done;
408     }
410     /* processor must be a member of the cluster */
411     clusterId = procId - MultiProc_getBaseIdOfCluster();
413     if (clusterId >= MultiProc_getNumProcsInCluster()) {
414         status =  Ipc_E_INVALIDARG;
415         goto done;
416     }
418     /* function must be serialized */
419     pthread_mutex_lock(&Ipc_module.gate);
421     /* if already attached, just increment reference count */
422     if (Ipc_module.attached[clusterId] > 0) {
423         Ipc_module.attached[clusterId]++;
424         goto done;
425     }
427     /* establish name server connection to remote processor */
428     status = NameServer_attach(procId);
430     if (status < 0) {
431         status = Ipc_E_FAIL;
432         goto done;
433     }
435     /* attach the transport to remote processor */
436     status = Ipc_module.transportFactory->attachFxn(procId);
438     if (status < 0) {
439         status = Ipc_E_FAIL;
440         goto done;
441     }
443     /* hack: bind all existing message queues to remote processor */
444     MessageQ_bind(procId);
446     /* getting here means we have successfully attached */
447     Ipc_module.attached[clusterId]++;
449 done:
450     pthread_mutex_unlock(&Ipc_module.gate);
452     return (status);
455 /*
456  *  ======== Ipc_detach ========
457  */
458 Int Ipc_detach(UInt16 procId)
460     Int status = Ipc_S_SUCCESS;
461     UInt16 clusterId;
463     /* cannot detach from yourself */
464     if (MultiProc_self() == procId) {
465         status =  Ipc_E_INVALIDARG;
466         goto done;
467     }
469     /* processor must be a member of the cluster */
470     clusterId = procId - MultiProc_getBaseIdOfCluster();
472     if (clusterId >= MultiProc_getNumProcsInCluster()) {
473         status =  Ipc_E_INVALIDARG;
474         goto done;
475     }
477     /* function must be serialized */
478     pthread_mutex_lock(&Ipc_module.gate);
480     if (Ipc_module.attached[clusterId] == 0) {
481         status = Ipc_E_INVALIDSTATE;
482         goto done;
483     }
485     if (--Ipc_module.attached[clusterId] > 0) {
486         goto done;
487     }
489     /* hack: unbind all existing message queues from remote processor */
490     MessageQ_unbind(procId);
492     /* detach transport from remote processor */
493     status = Ipc_module.transportFactory->detachFxn(procId);
495     if (status < 0) {
496         status = Ipc_E_FAIL;
497         /* report the error */
498         goto done;
499     }
501     /* remove connection to remote processor */
502     status = NameServer_detach(procId);
504     if (status < 0) {
505         status = Ipc_E_FAIL;
506         /* report the error */
507         goto done;
508     }
510 done:
511     if (status < 0) {
512         /* report error */
513         printf("Ipc_detach: Error %d, procId %d\n", status, procId);
514     }
515     pthread_mutex_unlock(&Ipc_module.gate);
517     return (status);
520 /*
521  *  ======== Ipc_isAttached ========
522  */
523 Bool Ipc_isAttached(UInt16 procId)
525     Bool attached;
526     UInt16 clusterId;
528     /* cannot be attached to yourself */
529     if (MultiProc_self() == procId) {
530         return (FALSE);
531     }
533     /* processor must be a member of the cluster */
534     clusterId = procId - MultiProc_getBaseIdOfCluster();
536     if (clusterId >= MultiProc_getNumProcsInCluster()) {
537         return (FALSE);
538     }
540     attached = (Ipc_module.attached[clusterId] > 0 ? TRUE : FALSE);
541     return (attached);
544 /*
545  *  ======== Ipc_getConfig ========
546  *  Get the run-time configuration for the Ipc module
547  *
548  *  This is an IPC internal function. It is used to acquire
549  *  the global Ipc module configuration values from LAD.
550  */
551 Void Ipc_getConfig(Ipc_Config *cfg)
553     Int status;
554     LAD_ClientHandle handle;
555     struct LAD_CommandObj cmd;
556     union LAD_ResponseObj rsp;
558     handle = LAD_findHandle();
559     if (handle == LAD_MAXNUMCLIENTS) {
560         PRINTVERBOSE0("Ipc_getConfig: no connection to LAD\n");
561         return;
562     }
564     cmd.cmd = LAD_IPC_GETCONFIG;
565     cmd.clientId = handle;
567     if ((status = LAD_putCommand(&cmd)) != LAD_SUCCESS) {
568         PRINTVERBOSE1("Ipc_getConfig: sending LAD command failed, "
569                 "status=%d\n", status);
570         return;
571     }
573     if ((status = LAD_getResponse(handle, &rsp)) != LAD_SUCCESS) {
574         PRINTVERBOSE1("Ipc_getConfig: no LAD response, status=%d\n", status);
575         return;
576     }
578     memcpy(cfg, &rsp.ipcConfig, sizeof(Ipc_Config));
579     return;