1 /*
2 * Copyright (c) 2012-2014, 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 */
32 /*
33 * ======== LAD_client.c ========
34 */
35 #include <ti/ipc/Std.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <sys/stat.h>
42 #include <sys/types.h>
43 #include <unistd.h>
44 #include <time.h>
45 #include <pthread.h>
47 #include <ladclient.h>
48 #include <_lad.h>
51 /* traces in this file are controlled via _LAD_Client_verbose */
52 Bool _LAD_Client_verbose = FALSE;
53 #define verbose _LAD_Client_verbose
55 typedef struct _LAD_ClientInfo {
56 Bool connectedToLAD; /* connection status */
57 UInt PID; /* client's process ID */
58 Char responseFIFOName[LAD_MAXLENGTHFIFONAME]; /* response FIFO name */
59 FILE *responseFIFOFilePtr; /* FIFO file pointer */
60 } _LAD_ClientInfo;
62 static Bool initialized = FALSE;
63 static String commandFIFOFileName = LAD_COMMANDFIFO;
64 static FILE *commandFIFOFilePtr = NULL;
65 static _LAD_ClientInfo clientInfo[LAD_MAXNUMCLIENTS];
67 static LAD_Status initWrappers(Void);
68 static Bool openCommandFIFO(Void);
70 #if defined(IPC_BUILDOS_ANDROID)
71 static pthread_mutex_t modGate = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
72 #else
73 // only _NP (non-portable) type available in CG tools which we're using
74 static pthread_mutex_t modGate = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
75 #endif
78 /*
79 * LAD_findHandle() - finds the LAD_ClientHandle for the calling pid (process ID).
80 *
81 * Assumes that there is only one client per process, which has to be the
82 * case since the pid is used to construct the responseFIFOFileName.
83 *
84 * Multiple threads within a process can all connect since each thread gets
85 * its own pid (which might be an OS-specific thing, some OSes (even some
86 * Linux implementations) use the same process pid for every thread within
87 * a process).
88 *
89 * Returns either the found "handle", or LAD_MAXNUMCLIENTS if the handle
90 * can't be found.
91 */
92 LAD_ClientHandle LAD_findHandle(Void)
93 {
94 Int i;
95 Int pid;
97 pid = getpid();
99 for (i = 0; i < LAD_MAXNUMCLIENTS; i++) {
100 if (clientInfo[i].PID == pid &&
101 clientInfo[i].connectedToLAD == TRUE) {
102 break;
103 }
104 }
106 return i;
107 }
109 /*
110 * ======== LAD_connect ========
111 */
112 LAD_Status LAD_connect(LAD_ClientHandle * handle)
113 {
114 Char responseFIFOName[LAD_MAXLENGTHFIFONAME];
115 LAD_Status status = LAD_SUCCESS;
116 time_t currentTime;
117 time_t startTime;
118 struct stat statBuf;
119 double delta;
120 Int assignedId;
121 FILE * filePtr;
122 Int n;
123 Int pid;
124 struct LAD_CommandObj cmd;
125 union LAD_ResponseObj rsp;
127 /* sanity check arg */
128 if (handle == NULL) {
129 return(LAD_INVALIDARG);
130 }
132 /* check and initialize on first connect request */
133 if (initialized == FALSE) {
135 /* TODO:M does this need to be atomized? */
136 status = initWrappers();
137 if (status != LAD_SUCCESS) {
138 return(status);
139 }
140 initialized = TRUE;
141 }
143 /* get caller's process ID */
144 pid = getpid();
146 /* form name for dedicated response FIFO */
147 sprintf(responseFIFOName, "%s%d", LAD_RESPONSEFIFOPATH, pid);
149 PRINTVERBOSE2("\nLAD_connect: PID = %d, fifoName = %s\n", pid,
150 responseFIFOName)
152 /* check if FIFO already exists; if yes, reject the request */
153 if (stat(responseFIFOName, &statBuf) == 0) {
154 PRINTVERBOSE0("\nLAD_connect: already connected; request denied!\n")
155 return(LAD_ACCESSDENIED);
156 }
158 cmd.cmd = LAD_CONNECT;
159 strcpy(cmd.args.connect.name, responseFIFOName);
160 strcpy(cmd.args.connect.protocol, LAD_PROTOCOLVERSION);
161 cmd.args.connect.pid = pid;
163 if ((status = LAD_putCommand(&cmd)) != LAD_SUCCESS) {
164 return(status);
165 }
167 /* now open the dedicated response FIFO for this client */
168 startTime = time ((time_t *) 0);
169 while ((filePtr = fopen(responseFIFOName, "r")) == NULL) {
170 /* insert wait to yield, so LAD can process connect command sooner */
171 usleep(100);
172 currentTime = time ((time_t *) 0);
173 delta = difftime(currentTime, startTime);
174 if (delta > LAD_CONNECTTIMEOUT) {
175 pthread_mutex_unlock(&modGate);
177 return(LAD_IOFAILURE);
178 }
179 }
181 /* now get LAD's response to the connection request */
182 n = fread(&rsp, LAD_RESPONSELENGTH, 1, filePtr);
184 /* need to unlock mutex obtained by LAD_putCommand() */
185 pthread_mutex_unlock(&modGate);
187 if (n) {
188 PRINTVERBOSE0("\nLAD_connect: got response\n")
190 /* extract LAD's response code and the client ID */
191 status = rsp.connect.status;
193 /* if a successful connect ... */
194 if (status == LAD_SUCCESS) {
195 assignedId = rsp.connect.assignedId;
196 *handle = assignedId;
198 /* setup client info */
199 clientInfo[assignedId].PID = pid;
200 clientInfo[assignedId].responseFIFOFilePtr = filePtr;
201 strcpy(clientInfo[assignedId].responseFIFOName, responseFIFOName);
202 clientInfo[assignedId].connectedToLAD = TRUE;
204 PRINTVERBOSE1(" status == LAD_SUCCESS, assignedId=%d\n",
205 assignedId);
206 }
207 else {
208 PRINTVERBOSE1(" status != LAD_SUCCESS (status=%d)\n", status);
209 }
210 }
211 else {
212 PRINTVERBOSE0(
213 "\nLAD_connect: 0 bytes read when getting LAD response!\n")
214 status = LAD_IOFAILURE;
215 }
217 /* if connect failed, close client side of FIFO (LAD closes its side) */
218 if (status != LAD_SUCCESS) {
219 PRINTVERBOSE0("\nLAD_connect failed: closing client-side of FIFO...\n")
220 fclose(filePtr);
221 }
223 return(status);
224 }
227 /*
228 * ======== LAD_disconnect ========
229 */
230 LAD_Status LAD_disconnect(LAD_ClientHandle handle)
231 {
232 LAD_Status status = LAD_SUCCESS;
233 Bool waiting = TRUE;
234 struct stat statBuf;
235 time_t currentTime;
236 time_t startTime;
237 double delta;
238 struct LAD_CommandObj cmd;
240 /* sanity check args */
241 if (handle >= LAD_MAXNUMCLIENTS) {
242 return (LAD_INVALIDARG);
243 }
245 /* check for initialization and connection */
246 if ((initialized == FALSE) ||
247 (clientInfo[handle].connectedToLAD == FALSE)) {
248 return (LAD_NOTCONNECTED);
249 }
251 cmd.cmd = LAD_DISCONNECT;
252 cmd.clientId = handle;
254 if ((status = LAD_putCommand(&cmd)) != LAD_SUCCESS) {
255 return(status);
256 }
258 /* on success, close the dedicated response FIFO */
259 fclose(clientInfo[handle].responseFIFOFilePtr);
261 /* need to unlock mutex obtained by LAD_putCommand() */
262 pthread_mutex_unlock(&modGate);
264 /* now wait for LAD to close the connection ... */
265 startTime = time ((time_t *) 0);
266 while (waiting == TRUE) {
268 /* do a minimal wait, to yield, so LAD can disconnect */
269 usleep(1);
270 currentTime = time ((time_t *) 0);
272 /* check to see if LAD has shutdown FIFO yet... */
273 if (stat(clientInfo[handle].responseFIFOName, &statBuf) != 0) {
274 waiting = FALSE; /* yes, so done */
275 }
276 /* if not, check for timeout */
277 else {
278 delta = difftime(currentTime, startTime);
279 if (delta > LAD_DISCONNECTTIMEOUT) {
280 PRINTVERBOSE0("\nLAD_disconnect: timeout waiting for LAD!\n")
281 return(LAD_IOFAILURE);
282 }
283 }
284 }
286 /* reset connection status flag */
287 clientInfo[handle].connectedToLAD = FALSE;
289 return(status);
290 }
292 /*
293 * ======== LAD_getResponse ========
294 */
295 LAD_Status LAD_getResponse(LAD_ClientHandle handle, union LAD_ResponseObj *rsp)
296 {
297 LAD_Status status = LAD_SUCCESS;
298 Int n;
300 PRINTVERBOSE1("LAD_getResponse: client = %d\n", handle)
302 n = fread(rsp, LAD_RESPONSELENGTH, 1,
303 clientInfo[handle].responseFIFOFilePtr);
305 pthread_mutex_unlock(&modGate);
307 if (n == 0) {
308 PRINTVERBOSE0("LAD_getResponse: n = 0!\n")
309 status = LAD_IOFAILURE;
310 }
311 else {
312 PRINTVERBOSE0("LAD_getResponse: got response\n")
313 }
315 return(status);
316 }
318 /*
319 * ======== LAD_putCommand ========
320 */
321 LAD_Status LAD_putCommand(struct LAD_CommandObj *cmd)
322 {
323 LAD_Status status = LAD_SUCCESS;
324 Int stat;
325 Int n;
327 PRINTVERBOSE1("\nLAD_putCommand: cmd = %d\n", cmd->cmd);
329 pthread_mutex_lock(&modGate);
331 n = fwrite(cmd, LAD_COMMANDLENGTH, 1, commandFIFOFilePtr);
333 if (n == 0) {
334 PRINTVERBOSE0("\nLAD_putCommand: fwrite returned 0!\n")
335 status = LAD_IOFAILURE;
336 }
337 else {
338 stat = fflush(commandFIFOFilePtr);
340 if (stat == (Int) EOF) {
341 PRINTVERBOSE0("\nLAD_putCommand: stat for fflush = EOF!\n")
342 status = LAD_IOFAILURE;
343 }
344 }
346 if (status != LAD_SUCCESS) {
347 pthread_mutex_unlock(&modGate);
348 }
350 PRINTVERBOSE1("LAD_putCommand: status = %d\n", status)
352 return(status);
353 }
356 /*
357 * ======== initWrappers ========
358 */
359 static LAD_Status initWrappers(Void)
360 {
361 Int i;
363 /* initialize the client info structures */
364 for (i = 0; i < LAD_MAXNUMCLIENTS; i++) {
365 clientInfo[i].connectedToLAD = FALSE;
366 clientInfo[i].responseFIFOFilePtr = NULL;
367 }
369 /* now open LAD's command FIFO */
370 if (openCommandFIFO() == FALSE) {
371 return(LAD_IOFAILURE);
372 }
373 else {
374 return(LAD_SUCCESS);
375 }
376 }
378 /*
379 * ======== openCommandFIFO ========
380 */
381 static Bool openCommandFIFO(Void)
382 {
383 /* open a file for writing to FIFO */
384 commandFIFOFilePtr = fopen(commandFIFOFileName, "w");
386 if (commandFIFOFilePtr == NULL) {
387 PRINTVERBOSE2("\nERROR: failed to open %s, errno = %x\n",
388 commandFIFOFileName, errno)
389 return(FALSE);
390 }
392 return(TRUE);
393 }