1/*
2 * Copyright (c) 2010, 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/**
34 *  @file  omx_rpc_stub.c
35 *         This file contains methods that provides the functionality for
36 *         the OpenMAX1.1 DOMX Framework RPC Stub implementations.
37 *
38 *  @path \WTSD_DucatiMMSW\framework\domx\omx_rpc\src
39 *
40 *  @rev 1.0
41 */
42
43
44/******************************************************************
45 *   INCLUDE FILES
46 ******************************************************************/
47/* ----- system and platform files ----------------------------*/
48#include <errno.h>
49//#include <errno-base.h>
50
51#include <fcntl.h>
52#include <sys/ioctl.h>
53#include <sys/eventfd.h>
54#include <unistd.h>
55#include <string.h>
56#include <stdio.h>
57
58#include <OMX_Types.h>
59#include <timm_osal_interfaces.h>
60#include <timm_osal_trace.h>
61
62
63/*-------program files ----------------------------------------*/
64#include "omx_rpc.h"
65#include "omx_proxy_common.h"
66#include "omx_rpc_stub.h"
67#include "omx_rpc_skel.h"
68#include "omx_rpc_internal.h"
69#include "omx_rpc_utils.h"
70
71#include "rpmsg_omx_defs.h"
72
73#define RPC_MSGPIPE_SIZE (4)
74#define RPC_MSG_SIZE_FOR_PIPE (sizeof(OMX_PTR))
75#define MAX_ATTEMPTS 15
76
77#define RPC_getPacket(nPacketSize, pPacket) do { \
78    pPacket = TIMM_OSAL_Malloc(nPacketSize, TIMM_OSAL_TRUE, 0, TIMMOSAL_MEM_SEGMENT_INT); \
79    RPC_assert(pPacket != NULL, RPC_OMX_ErrorInsufficientResources, \
80           "Error Allocating RCM Message Frame"); \
81    } while(0)
82
83#define RPC_freePacket(pPacket) do { \
84    if(pPacket != NULL) TIMM_OSAL_Free(pPacket); \
85    } while(0)
86
87OMX_U8 pBufferError[RPC_PACKET_SIZE];
88
89void *RPC_CallbackThread(void *data);
90
91
92/* ===========================================================================*/
93/**
94* @name RPC_InstanceInit()
95* @brief RPC instance init is used for initialization. It is called once for
96*        every instance of an OMX component.
97* @param cComponentName [IN] : Name of the component.
98* @param phRPCCtx [OUT] : RPC Context structure - to be filled in and returned
99* @return RPC_OMX_ErrorNone = Successful
100*/
101/* ===========================================================================*/
102
103RPC_OMX_ERRORTYPE RPC_InstanceInit(OMX_STRING cComponentName,
104    OMX_HANDLETYPE * phRPCCtx)
105{
106	RPC_OMX_ERRORTYPE eRPCError = RPC_OMX_ErrorNone;
107	RPC_OMX_CONTEXT *pRPCCtx = NULL;
108	OMX_S32 status = 0;
109	struct omx_conn_req sReq = { .name = "OMX" };
110	TIMM_OSAL_ERRORTYPE eError = TIMM_OSAL_ERR_NONE;
111	OMX_U32 i = 0, nAttempts = 0;
112
113	*(RPC_OMX_CONTEXT **) phRPCCtx = NULL;
114
115	//pthread_t cbThread;
116
117//	sReq.name = "OMX";
118
119	/*pRPCCtx contains context information for each instance */
120	pRPCCtx =
121	    (RPC_OMX_CONTEXT *) TIMM_OSAL_Malloc(sizeof(RPC_OMX_CONTEXT),
122	    TIMM_OSAL_TRUE, 0, TIMMOSAL_MEM_SEGMENT_INT);
123	RPC_assert(pRPCCtx != NULL, RPC_OMX_ErrorInsufficientResources,
124	    "Malloc failed");
125	TIMM_OSAL_Memset(pRPCCtx, 0, sizeof(RPC_OMX_CONTEXT));
126
127	/*Assuming that open maintains an internal count for multi instance */
128	DOMX_DEBUG("Calling open on the device");
129	while (1)
130	{
131		pRPCCtx->fd_omx = open("/dev/rpmsg-omx1", O_RDWR);
132		if(pRPCCtx->fd_omx >= 0 || errno != ENOENT || nAttempts == MAX_ATTEMPTS)
133			break;
134		DOMX_DEBUG("errno from open= %d, REATTEMPTING OPEN!!!!",errno);
135		nAttempts++;
136		usleep(1000000);
137	}
138	if(pRPCCtx->fd_omx < 0)
139	{
140		DOMX_ERROR("Can't open device, errorno from open = %d",errno);
141		eError = RPC_OMX_ErrorInsufficientResources;
142		goto EXIT;
143	}
144	DOMX_DEBUG("Open was successful, pRPCCtx->fd_omx = %d",
145	    pRPCCtx->fd_omx);
146//AD
147//      strncpy(sReq.name, cComponentName, OMX_MAX_STRINGNAME_SIZE);
148
149	DOMX_DEBUG("Calling ioctl");
150	status = ioctl(pRPCCtx->fd_omx, OMX_IOCCONNECT, &sReq);
151	RPC_assert(status >= 0, RPC_OMX_ErrorInsufficientResources,
152	    "Can't connect");
153
154	for (i = 0; i < RPC_OMX_MAX_FUNCTION_LIST; i++)
155	{
156		eError =
157		    TIMM_OSAL_CreatePipe(&(pRPCCtx->pMsgPipe[i]),
158		    RPC_MSGPIPE_SIZE, RPC_MSG_SIZE_FOR_PIPE, 1);
159		RPC_assert(eError == TIMM_OSAL_ERR_NONE,
160		    RPC_OMX_ErrorInsufficientResources,
161		    "Pipe creation failed");
162	}
163
164	DOMX_DEBUG("Creating event fd");
165	pRPCCtx->fd_killcb = eventfd(0, 0);
166	RPC_assert(pRPCCtx->fd_killcb >= 0,
167	    RPC_OMX_ErrorInsufficientResources, "Can't create kill fd");
168	/*Create a listener/server thread to listen for Ducati callbacks */
169	DOMX_DEBUG("Create listener thread");
170	status =
171	    pthread_create(&(pRPCCtx->cbThread), NULL, RPC_CallbackThread,
172	    pRPCCtx);
173	RPC_assert(status == 0, RPC_OMX_ErrorInsufficientResources,
174	    "Can't create cb thread");
175
176      EXIT:
177	if (eRPCError != RPC_OMX_ErrorNone)
178	{
179		RPC_InstanceDeInit(pRPCCtx);
180	}
181	else
182	{
183		*(RPC_OMX_CONTEXT **) phRPCCtx = pRPCCtx;
184	}
185	return eRPCError;
186}
187
188
189
190/* ===========================================================================*/
191/**
192* @name RPC_InstanceDeInit()
193* @brief RPC instance deinit is used for tear down. It is called once when an
194*        instance of OMX component is going down.
195* @param phRPCCtx [IN] : RPC Context structure.
196* @return RPC_OMX_ErrorNone = Successful
197*/
198/* ===========================================================================*/
199RPC_OMX_ERRORTYPE RPC_InstanceDeInit(OMX_HANDLETYPE hRPCCtx)
200{
201	RPC_OMX_ERRORTYPE eRPCError = RPC_OMX_ErrorNone;
202	TIMM_OSAL_ERRORTYPE eError = TIMM_OSAL_ERR_NONE;
203	RPC_OMX_CONTEXT *pRPCCtx = (RPC_OMX_CONTEXT *) hRPCCtx;
204	OMX_S32 status = 0;
205	OMX_U32 i = 0;
206	OMX_U64 nKillEvent = 1;
207
208	RPC_assert(hRPCCtx != NULL, RPC_OMX_ErrorUndefined,
209	    "NULL context handle supplied to RPC Deinit");
210
211	if (pRPCCtx->fd_killcb)
212	{
213		status =
214		    write(pRPCCtx->fd_killcb, &nKillEvent, sizeof(OMX_U64));
215		if (status <= 0)
216		{
217			DOMX_ERROR
218			    ("Write to kill fd failed - cb thread may not exit");
219			eRPCError = RPC_OMX_ErrorUndefined;
220		} else if (pRPCCtx->cbThread)
221		{
222			DOMX_DEBUG("Waiting for cb thread to exit");
223			status = pthread_join(pRPCCtx->cbThread, NULL);
224			if (status != 0)
225			{
226				DOMX_ERROR("Join for cb thread failed");
227				eRPCError = RPC_OMX_ErrorUndefined;
228			}
229		}
230		DOMX_DEBUG("Closing the kill fd");
231		status = close(pRPCCtx->fd_killcb);
232		pRPCCtx->fd_killcb = 0;
233		if (status != 0)
234		{
235			DOMX_ERROR("Close failed on kill fd");
236			eRPCError = RPC_OMX_ErrorUndefined;
237		}
238	}
239
240	for (i = 0; i < RPC_OMX_MAX_FUNCTION_LIST; i++)
241	{
242		if (pRPCCtx->pMsgPipe[i])
243		{
244			eError = TIMM_OSAL_DeletePipe(pRPCCtx->pMsgPipe[i]);
245			pRPCCtx->pMsgPipe[i] = NULL;
246			if (eError != TIMM_OSAL_ERR_NONE)
247			{
248				DOMX_ERROR("Pipe deletion failed");
249				eRPCError = RPC_OMX_ErrorUndefined;
250			}
251		}
252	}
253
254	DOMX_DEBUG("Closing the omx fd");
255	if (pRPCCtx->fd_omx)
256	{
257		status = close(pRPCCtx->fd_omx);
258		pRPCCtx->fd_omx = 0;
259		if (status != 0)
260		{
261			DOMX_ERROR("Close failed on omx fd");
262			eRPCError = RPC_OMX_ErrorUndefined;
263		}
264	}
265
266	TIMM_OSAL_Free(pRPCCtx);
267
268	EXIT:
269		return eRPCError;
270}
271
272
273
274/* ===========================================================================*/
275/**
276* @name RPC_CallbackThread()
277* @brief This is the entry function of the thread which keeps spinning, waiting
278*        for messages from Ducati.
279* @param data [IN] : The RPC Context structure is passed here.
280* @return RPC_OMX_ErrorNone = Successful
281*/
282/* ===========================================================================*/
283void *RPC_CallbackThread(void *data)
284{
285	OMX_PTR pBuffer = NULL;
286	RPC_OMX_CONTEXT *pRPCCtx = (RPC_OMX_CONTEXT *) data;
287	fd_set readfds;
288	OMX_S32 maxfd = 0, status = 0;
289	OMX_U32 nFxnIdx = 0, nPacketSize = RPC_PACKET_SIZE, nPos = 0;
290	RPC_OMX_ERRORTYPE eRPCError = RPC_OMX_ErrorNone;
291	TIMM_OSAL_ERRORTYPE eError = TIMM_OSAL_ERR_NONE;
292	OMX_COMPONENTTYPE *hComp = NULL;
293	PROXY_COMPONENT_PRIVATE *pCompPrv = NULL;
294        OMX_PTR pBuff = pBufferError;
295
296	maxfd =
297	    (pRPCCtx->fd_killcb >
298	    pRPCCtx->fd_omx ? pRPCCtx->fd_killcb : pRPCCtx->fd_omx) + 1;
299	while (1)
300	{
301		FD_ZERO(&readfds);
302		FD_SET(pRPCCtx->fd_omx, &readfds);
303		FD_SET(pRPCCtx->fd_killcb, &readfds);
304
305		DOMX_DEBUG("Waiting for messages from remote core");
306		status = select(maxfd, &readfds, NULL, NULL, NULL);
307		RPC_assert(status > 0, RPC_OMX_ErrorUndefined,
308		    "select failed");
309
310		if (FD_ISSET(pRPCCtx->fd_killcb, &readfds))
311		{
312			DOMX_DEBUG("Recd. kill message - exiting the thread");
313			break;
314		}
315
316		if (FD_ISSET(pRPCCtx->fd_omx, &readfds))
317		{
318			DOMX_DEBUG("Recd. omx message");
319			RPC_getPacket(nPacketSize, pBuffer);
320			status = read(pRPCCtx->fd_omx, pBuffer, nPacketSize);
321            if(status < 0)
322            {
323                if(errno == ENXIO)
324                {
325		    for(nFxnIdx = 0; nFxnIdx < RPC_OMX_MAX_FUNCTION_LIST; nFxnIdx++)
326		    {
327			((struct omx_packet *) pBufferError)->result = OMX_ErrorHardware;
328			TIMM_OSAL_WriteToPipe(pRPCCtx->pMsgPipe[nFxnIdx], &pBuff, RPC_MSG_SIZE_FOR_PIPE, TIMM_OSAL_SUSPEND);
329			if(eError != TIMM_OSAL_ERR_NONE)
330				DOMX_ERROR("Write to pipe failed");
331		    }
332                    /*Indicate fatal error and exit*/
333                    RPC_assert(0, RPC_OMX_ErrorHardware,
334                    "Remote processor fatal error");
335                }
336                else
337                {
338                    RPC_assert(0, RPC_OMX_ErrorUndefined,
339                    "read failed");
340                }
341            }
342
343			nPos = 0;
344			nFxnIdx = ((struct omx_packet *) pBuffer)->fxn_idx;
345			/*Indices from static table will have bit 31 set */
346			if (nFxnIdx & 0x80000000)
347				nFxnIdx &= 0x0FFFFFFF;
348			RPC_assert(nFxnIdx < RPC_OMX_MAX_FUNCTION_LIST,
349			    RPC_OMX_ErrorUndefined,
350			    "Bad function index recd");
351			switch (nFxnIdx)
352			{
353			case RPC_OMX_FXN_IDX_EVENTHANDLER:
354				RPC_SKEL_EventHandler(((struct omx_packet *)
355					pBuffer)->data);
356				RPC_freePacket(pBuffer);
357				pBuffer = NULL;
358				break;
359			case RPC_OMX_FXN_IDX_EMPTYBUFFERDONE:
360				RPC_SKEL_EmptyBufferDone(((struct omx_packet *)
361					pBuffer)->data);
362				RPC_freePacket(pBuffer);
363				pBuffer = NULL;
364				break;
365			case RPC_OMX_FXN_IDX_FILLBUFFERDONE:
366				RPC_SKEL_FillBufferDone(((struct omx_packet *)
367					pBuffer)->data);
368				RPC_freePacket(pBuffer);
369				pBuffer = NULL;
370				break;
371			default:
372				eError =
373				    TIMM_OSAL_WriteToPipe(pRPCCtx->
374				    pMsgPipe[nFxnIdx], &pBuffer,
375				    RPC_MSG_SIZE_FOR_PIPE, TIMM_OSAL_SUSPEND);
376				RPC_assert(eError == TIMM_OSAL_ERR_NONE,
377				    RPC_OMX_ErrorUndefined,
378				    "Write to pipe failed");
379				break;
380			}
381		}
382EXIT:
383		if (eRPCError != RPC_OMX_ErrorNone)
384		{
385			//AD TODO: Send error CB to client and then go back in loop to wait for killfd
386			if (pBuffer != NULL)
387			{
388				RPC_freePacket(pBuffer);
389				pBuffer = NULL;
390			}
391			/*Report all hardware errors as fatal and exit from listener thread*/
392			if (eRPCError == RPC_OMX_ErrorHardware)
393			{
394				/*Implicit detail: pAppData is proxy component handle updated during
395                  RPC_GetHandle*/
396				hComp = (OMX_COMPONENTTYPE *) pRPCCtx->pAppData;
397                if(hComp != NULL)
398				{
399					pCompPrv = (PROXY_COMPONENT_PRIVATE *) hComp->pComponentPrivate;
400                    /*Indicate fatal error. Users are expected to cleanup the OMX instance
401                    to ensure all resources are cleaned up.*/
402					pCompPrv->proxyEventHandler(hComp, pCompPrv->pILAppData, OMX_EventError,
403												OMX_ErrorHardware, 0, NULL);
404				}
405				break;
406			}
407		}
408	}
409        return (void*)0;
410}
411