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)
112 {
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 fprintf(stderr, "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 fprintf(stderr, "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 fprintf(stderr, "Ipc_start: GateHWSpinlock_start failed: %d\n",
258 status);
259 status = Ipc_E_FAIL;
260 goto exit;
261 }
263 status = GateMP_start();
264 if (status < 0) {
265 fprintf(stderr, "Ipc_start: GateMP_start failed: %d\n", status);
266 status = Ipc_E_FAIL;
267 GateHWSpinlock_stop();
268 goto exit;
269 }
270 }
271 #endif
273 /* getting here means we have successfully started */
274 Ipc_module.refCount++;
276 exit:
277 pthread_mutex_unlock(&Ipc_module.gate);
279 return (status);
280 }
282 /*
283 * ======== Ipc_stop ========
284 */
285 Int Ipc_stop(Void)
286 {
287 Int32 status = Ipc_S_SUCCESS;
288 LAD_Status ladStatus;
289 UInt16 procId;
290 UInt16 clusterSize;
291 UInt16 baseId;
292 UInt16 clusterId;
294 /* function must be serialized */
295 pthread_mutex_lock(&Ipc_module.gate);
297 if (Ipc_module.refCount == 0) {
298 status = Ipc_E_INVALIDSTATE;
299 goto exit;
300 }
302 /* ensure only last thread performs stop procedure */
303 if (--Ipc_module.refCount > 0) {
304 goto exit;
305 }
307 /* if using ProcSync_ALL, then detach from all processors in the cluster */
308 if (Ipc_module.config.procSync == Ipc_ProcSync_ALL) {
309 clusterSize = MultiProc_getNumProcsInCluster();
310 baseId = MultiProc_getBaseIdOfCluster();
312 for (clusterId = 0; clusterId < clusterSize; clusterId++) {
313 procId = baseId + clusterId;
315 if (MultiProc_self() == procId) {
316 continue;
317 }
319 /* For backward compatibility, we might not be attached to
320 * all cluster members. Skip unattached processors.
321 */
322 if (!Ipc_isAttached(procId)) {
323 continue;
324 }
326 status = Ipc_detach(procId);
328 if (status < 0) {
329 /* Should we keep going or stop? */
330 }
331 }
332 }
334 Ipc_module.transportFactory->deleteFxn();
336 status = MessageQ_destroy();
337 if (status < 0) {
338 fprintf(stderr, "Ipc_stop: MessageQ_destroy() failed: %d\n", status);
339 status = Ipc_E_FAIL;
340 goto exit;
341 }
343 status = NameServer_destroy();
344 if (status < 0) {
345 fprintf(stderr, "Ipc_stop: NameServer_destroy() failed: %d\n", status);
346 status = Ipc_E_FAIL;
347 goto exit;
348 }
350 ladStatus = LAD_disconnect(ladHandle);
351 if (ladStatus != LAD_SUCCESS) {
352 fprintf(stderr, "LAD_disconnect() failed: %d\n", ladStatus);
353 status = Ipc_E_FAIL;
354 goto exit;
355 }
357 exit:
358 pthread_mutex_unlock(&Ipc_module.gate);
360 return (status);
361 }
363 /*
364 * ======== Ipc_transportConfig ========
365 */
366 Int Ipc_transportConfig(Ipc_TransportFactoryFxns *factory)
367 {
368 Int status = Ipc_S_SUCCESS;
370 pthread_mutex_lock(&Ipc_module.gate);
372 /* Only the first caller can actually set the transport factory.
373 * Subsequent callers (e.g. multi-threaded application) must be
374 * using the same factory. Otherwise, it is an error.
375 */
376 if (Ipc_module.transportFactory == NULL) {
377 Ipc_module.transportFactory = factory;
378 }
379 else if (Ipc_module.transportFactory != factory) {
380 status = Ipc_E_INVALIDARG;
381 goto exit;
382 }
384 exit:
385 pthread_mutex_unlock(&Ipc_module.gate);
387 return (status);
388 }
390 static void cleanup(int arg)
391 {
392 printf("Ipc: Caught SIGINT, calling Ipc_stop...\n");
393 Ipc_stop();
394 exit(0);
395 }
397 /*
398 * ======== Ipc_attach ========
399 */
400 Int Ipc_attach(UInt16 procId)
401 {
402 Int status = Ipc_S_SUCCESS;
403 UInt16 clusterId;
404 #if defined(GATEMP_SUPPORT)
405 Int ret;
406 #endif
408 /* cannot attach to yourself */
409 if (MultiProc_self() == procId) {
410 status = Ipc_E_INVALIDARG;
411 goto done;
412 }
414 /* processor must be a member of the cluster */
415 clusterId = procId - MultiProc_getBaseIdOfCluster();
417 if (clusterId >= MultiProc_getNumProcsInCluster()) {
418 status = Ipc_E_INVALIDARG;
419 goto done;
420 }
422 /* function must be serialized */
423 pthread_mutex_lock(&Ipc_module.gate);
425 /* if already attached, just increment reference count */
426 if (Ipc_module.attached[clusterId] > 0) {
427 Ipc_module.attached[clusterId]++;
428 goto done;
429 }
431 /* establish name server connection to remote processor */
432 status = NameServer_attach(procId);
434 if (status < 0) {
435 status = Ipc_E_FAIL;
436 goto done;
437 }
439 /* attach the transport to remote processor */
440 status = Ipc_module.transportFactory->attachFxn(procId);
442 if (status < 0) {
443 status = Ipc_E_FAIL;
444 goto done;
445 }
447 /* hack: bind all existing message queues to remote processor */
448 MessageQ_bind(procId);
450 #if defined(GATEMP_SUPPORT)
451 if (GateMP_isSetup()) {
452 /* establish GateMP connection to remote processor */
453 ret = GateMP_attach(procId);
455 if (ret < 0) {
456 PRINTVERBOSE1("Ipc_attach: failed to GateMP_attach to procId %d\n",
457 procId);
458 }
459 }
460 #endif
462 /* getting here means we have successfully attached */
463 Ipc_module.attached[clusterId]++;
465 done:
466 pthread_mutex_unlock(&Ipc_module.gate);
468 return (status);
469 }
471 /*
472 * ======== Ipc_detach ========
473 */
474 Int Ipc_detach(UInt16 procId)
475 {
476 Int status = Ipc_S_SUCCESS;
477 UInt16 clusterId;
478 #if defined(GATEMP_SUPPORT)
479 Int ret;
480 #endif
482 /* cannot detach from yourself */
483 if (MultiProc_self() == procId) {
484 status = Ipc_E_INVALIDARG;
485 goto done;
486 }
488 /* processor must be a member of the cluster */
489 clusterId = procId - MultiProc_getBaseIdOfCluster();
491 if (clusterId >= MultiProc_getNumProcsInCluster()) {
492 status = Ipc_E_INVALIDARG;
493 goto done;
494 }
496 /* function must be serialized */
497 pthread_mutex_lock(&Ipc_module.gate);
499 if (Ipc_module.attached[clusterId] == 0) {
500 status = Ipc_E_INVALIDSTATE;
501 goto done;
502 }
504 if (--Ipc_module.attached[clusterId] > 0) {
505 goto done;
506 }
508 #if defined(GATEMP_SUPPORT)
509 if (GateMP_isSetup()) {
510 /* establish GateMP connection to remote processor */
511 ret = GateMP_detach(procId);
513 if (ret < 0) {
514 PRINTVERBOSE1("Ipc_detach: failed to GateMP_detach from procId %d\n",
515 procId);
516 }
517 }
518 #endif
520 /* hack: unbind all existing message queues from remote processor */
521 MessageQ_unbind(procId);
523 /* detach transport from remote processor */
524 status = Ipc_module.transportFactory->detachFxn(procId);
526 if (status < 0) {
527 status = Ipc_E_FAIL;
528 /* report the error */
529 goto done;
530 }
532 /* remove connection to remote processor */
533 status = NameServer_detach(procId);
535 if (status < 0) {
536 status = Ipc_E_FAIL;
537 /* report the error */
538 goto done;
539 }
541 done:
542 if (status < 0) {
543 /* report error */
544 fprintf(stderr, "Ipc_detach: Error %d, procId %d\n", status, procId);
545 }
546 pthread_mutex_unlock(&Ipc_module.gate);
548 return (status);
549 }
551 /*
552 * ======== Ipc_isAttached ========
553 */
554 Bool Ipc_isAttached(UInt16 procId)
555 {
556 Bool attached;
557 UInt16 clusterId;
559 /* cannot be attached to yourself */
560 if (MultiProc_self() == procId) {
561 return (FALSE);
562 }
564 /* processor must be a member of the cluster */
565 clusterId = procId - MultiProc_getBaseIdOfCluster();
567 if (clusterId >= MultiProc_getNumProcsInCluster()) {
568 return (FALSE);
569 }
571 attached = (Ipc_module.attached[clusterId] > 0 ? TRUE : FALSE);
572 return (attached);
573 }
575 /*
576 * ======== Ipc_getConfig ========
577 * Get the run-time configuration for the Ipc module
578 *
579 * This is an IPC internal function. It is used to acquire
580 * the global Ipc module configuration values from LAD.
581 */
582 Void Ipc_getConfig(Ipc_Config *cfg)
583 {
584 Int status;
585 LAD_ClientHandle handle;
586 struct LAD_CommandObj cmd;
587 union LAD_ResponseObj rsp;
589 handle = LAD_findHandle();
590 if (handle == LAD_MAXNUMCLIENTS) {
591 PRINTVERBOSE0("Ipc_getConfig: no connection to LAD\n");
592 return;
593 }
595 cmd.cmd = LAD_IPC_GETCONFIG;
596 cmd.clientId = handle;
598 if ((status = LAD_putCommand(&cmd)) != LAD_SUCCESS) {
599 PRINTVERBOSE1("Ipc_getConfig: sending LAD command failed, "
600 "status=%d\n", status);
601 return;
602 }
604 if ((status = LAD_getResponse(handle, &rsp)) != LAD_SUCCESS) {
605 PRINTVERBOSE1("Ipc_getConfig: no LAD response, status=%d\n", status);
606 return;
607 }
609 memcpy(cfg, &rsp.ipcConfig, sizeof(Ipc_Config));
610 return;
611 }