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 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);
279 }
281 /*
282 * ======== Ipc_stop ========
283 */
284 Int Ipc_stop(Void)
285 {
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);
360 }
362 /*
363 * ======== Ipc_transportConfig ========
364 */
365 Int Ipc_transportConfig(Ipc_TransportFactoryFxns *factory)
366 {
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);
387 }
389 static void cleanup(int arg)
390 {
391 printf("Ipc: Caught SIGINT, calling Ipc_stop...\n");
392 Ipc_stop();
393 exit(0);
394 }
396 /*
397 * ======== Ipc_attach ========
398 */
399 Int Ipc_attach(UInt16 procId)
400 {
401 Int status = Ipc_S_SUCCESS;
402 UInt16 clusterId;
403 #if defined(GATEMP_SUPPORT)
404 Int ret;
405 #endif
407 /* cannot attach to yourself */
408 if (MultiProc_self() == procId) {
409 status = Ipc_E_INVALIDARG;
410 goto done;
411 }
413 /* processor must be a member of the cluster */
414 clusterId = procId - MultiProc_getBaseIdOfCluster();
416 if (clusterId >= MultiProc_getNumProcsInCluster()) {
417 status = Ipc_E_INVALIDARG;
418 goto done;
419 }
421 /* function must be serialized */
422 pthread_mutex_lock(&Ipc_module.gate);
424 /* if already attached, just increment reference count */
425 if (Ipc_module.attached[clusterId] > 0) {
426 Ipc_module.attached[clusterId]++;
427 goto done;
428 }
430 /* establish name server connection to remote processor */
431 status = NameServer_attach(procId);
433 if (status < 0) {
434 status = Ipc_E_FAIL;
435 goto done;
436 }
438 /* attach the transport to remote processor */
439 status = Ipc_module.transportFactory->attachFxn(procId);
441 if (status < 0) {
442 status = Ipc_E_FAIL;
443 goto done;
444 }
446 /* hack: bind all existing message queues to remote processor */
447 MessageQ_bind(procId);
449 #if defined(GATEMP_SUPPORT)
450 if (GateMP_isSetup()) {
451 /* establish GateMP connection to remote processor */
452 ret = GateMP_attach(procId);
454 if (ret < 0) {
455 PRINTVERBOSE1("Ipc_attach: failed to GateMP_attach to procId %d\n",
456 procId);
457 }
458 }
459 #endif
461 /* getting here means we have successfully attached */
462 Ipc_module.attached[clusterId]++;
464 done:
465 pthread_mutex_unlock(&Ipc_module.gate);
467 return (status);
468 }
470 /*
471 * ======== Ipc_detach ========
472 */
473 Int Ipc_detach(UInt16 procId)
474 {
475 Int status = Ipc_S_SUCCESS;
476 UInt16 clusterId;
477 #if defined(GATEMP_SUPPORT)
478 Int ret;
479 #endif
481 /* cannot detach from yourself */
482 if (MultiProc_self() == procId) {
483 status = Ipc_E_INVALIDARG;
484 goto done;
485 }
487 /* processor must be a member of the cluster */
488 clusterId = procId - MultiProc_getBaseIdOfCluster();
490 if (clusterId >= MultiProc_getNumProcsInCluster()) {
491 status = Ipc_E_INVALIDARG;
492 goto done;
493 }
495 /* function must be serialized */
496 pthread_mutex_lock(&Ipc_module.gate);
498 if (Ipc_module.attached[clusterId] == 0) {
499 status = Ipc_E_INVALIDSTATE;
500 goto done;
501 }
503 if (--Ipc_module.attached[clusterId] > 0) {
504 goto done;
505 }
507 #if defined(GATEMP_SUPPORT)
508 if (GateMP_isSetup()) {
509 /* establish GateMP connection to remote processor */
510 ret = GateMP_detach(procId);
512 if (ret < 0) {
513 PRINTVERBOSE1("Ipc_detach: failed to GateMP_detach from procId %d\n",
514 procId);
515 }
516 }
517 #endif
519 /* hack: unbind all existing message queues from remote processor */
520 MessageQ_unbind(procId);
522 /* detach transport from remote processor */
523 status = Ipc_module.transportFactory->detachFxn(procId);
525 if (status < 0) {
526 status = Ipc_E_FAIL;
527 /* report the error */
528 goto done;
529 }
531 /* remove connection to remote processor */
532 status = NameServer_detach(procId);
534 if (status < 0) {
535 status = Ipc_E_FAIL;
536 /* report the error */
537 goto done;
538 }
540 done:
541 if (status < 0) {
542 /* report error */
543 printf("Ipc_detach: Error %d, procId %d\n", status, procId);
544 }
545 pthread_mutex_unlock(&Ipc_module.gate);
547 return (status);
548 }
550 /*
551 * ======== Ipc_isAttached ========
552 */
553 Bool Ipc_isAttached(UInt16 procId)
554 {
555 Bool attached;
556 UInt16 clusterId;
558 /* cannot be attached to yourself */
559 if (MultiProc_self() == procId) {
560 return (FALSE);
561 }
563 /* processor must be a member of the cluster */
564 clusterId = procId - MultiProc_getBaseIdOfCluster();
566 if (clusterId >= MultiProc_getNumProcsInCluster()) {
567 return (FALSE);
568 }
570 attached = (Ipc_module.attached[clusterId] > 0 ? TRUE : FALSE);
571 return (attached);
572 }
574 /*
575 * ======== Ipc_getConfig ========
576 * Get the run-time configuration for the Ipc module
577 *
578 * This is an IPC internal function. It is used to acquire
579 * the global Ipc module configuration values from LAD.
580 */
581 Void Ipc_getConfig(Ipc_Config *cfg)
582 {
583 Int status;
584 LAD_ClientHandle handle;
585 struct LAD_CommandObj cmd;
586 union LAD_ResponseObj rsp;
588 handle = LAD_findHandle();
589 if (handle == LAD_MAXNUMCLIENTS) {
590 PRINTVERBOSE0("Ipc_getConfig: no connection to LAD\n");
591 return;
592 }
594 cmd.cmd = LAD_IPC_GETCONFIG;
595 cmd.clientId = handle;
597 if ((status = LAD_putCommand(&cmd)) != LAD_SUCCESS) {
598 PRINTVERBOSE1("Ipc_getConfig: sending LAD command failed, "
599 "status=%d\n", status);
600 return;
601 }
603 if ((status = LAD_getResponse(handle, &rsp)) != LAD_SUCCESS) {
604 PRINTVERBOSE1("Ipc_getConfig: no LAD response, status=%d\n", status);
605 return;
606 }
608 memcpy(cfg, &rsp.ipcConfig, sizeof(Ipc_Config));
609 return;
610 }