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  timm_osal_events.c
35*   This file contains methods that provides the functionality
36*   for creating/using events.
37*
38*  @path \
39*
40*/
41/* -------------------------------------------------------------------------- */
42/* =========================================================================
43 *!
44 *! Revision History
45 *! ===================================
46 *! 06-Nov-2008 Maiya ShreeHarsha: Linux specific changes
47 *! 0.1: Created the first draft version, ksrini@ti.com
48 * ========================================================================= */
49
50/******************************************************************************
51* Includes
52******************************************************************************/
53#include <stdio.h>
54#include <pthread.h>		/*for POSIX calls */
55#include <sys/time.h>
56#include <errno.h>
57
58#include "timm_osal_types.h"
59#include "timm_osal_trace.h"
60#include "timm_osal_error.h"
61#include "timm_osal_memory.h"
62#include "timm_osal_events.h"
63
64
65typedef struct
66{
67	TIMM_OSAL_BOOL bSignaled;
68	TIMM_OSAL_U32 eFlags;
69	pthread_mutex_t mutex;
70	pthread_cond_t condition;
71} TIMM_OSAL_THREAD_EVENT;
72
73
74/* ========================================================================== */
75/**
76* @fn TIMM_OSAL_EventCreate function
77*
78*
79*/
80/* ========================================================================== */
81TIMM_OSAL_ERRORTYPE TIMM_OSAL_EventCreate(TIMM_OSAL_PTR * pEvents)
82{
83	TIMM_OSAL_ERRORTYPE bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
84	TIMM_OSAL_THREAD_EVENT *plEvent = NULL;
85
86	plEvent =
87	    (TIMM_OSAL_THREAD_EVENT *)
88	    TIMM_OSAL_Malloc(sizeof(TIMM_OSAL_THREAD_EVENT), 0, 0, 0);
89
90	if (TIMM_OSAL_NULL == plEvent)
91	{
92		bReturnStatus = TIMM_OSAL_ERR_ALLOC;
93		goto EXIT;
94	}
95	plEvent->bSignaled = TIMM_OSAL_FALSE;
96	plEvent->eFlags = 0;
97
98	if (SUCCESS != pthread_mutex_init(&(plEvent->mutex), NULL))
99	{
100		TIMM_OSAL_Error("Event Create:Mutex Init failed !");
101		goto EXIT;	/*bReturnStatus = TIMM_OSAL_ERR_UNKNOWN */
102	}
103
104	if (SUCCESS != pthread_cond_init(&(plEvent->condition), NULL))
105	{
106		TIMM_OSAL_Error
107		    ("Event Create:Conditional Variable  Init failed !");
108		pthread_mutex_destroy(&(plEvent->mutex));
109		/*TIMM_OSAL_Free(plEvent); */
110	} else
111	{
112		*pEvents = (TIMM_OSAL_PTR) plEvent;
113		bReturnStatus = TIMM_OSAL_ERR_NONE;
114	}
115      EXIT:
116	if ((TIMM_OSAL_ERR_NONE != bReturnStatus) &&
117	    (TIMM_OSAL_NULL != plEvent))
118	{
119		TIMM_OSAL_Free(plEvent);
120	}
121	return bReturnStatus;
122}
123
124/* ========================================================================== */
125/**
126* @fn TIMM_OSAL_EventDelete function
127*
128*
129*/
130/* ========================================================================== */
131TIMM_OSAL_ERRORTYPE TIMM_OSAL_EventDelete(TIMM_OSAL_PTR pEvents)
132{
133	TIMM_OSAL_ERRORTYPE bReturnStatus = TIMM_OSAL_ERR_NONE;
134	TIMM_OSAL_THREAD_EVENT *plEvent = (TIMM_OSAL_THREAD_EVENT *) pEvents;
135
136	if (TIMM_OSAL_NULL == plEvent)
137	{
138		bReturnStatus = TIMM_OSAL_ERR_PARAMETER;
139		goto EXIT;
140	}
141
142	if (SUCCESS != pthread_mutex_lock(&(plEvent->mutex)))
143	{
144		TIMM_OSAL_Error("Event Delete: Mutex Lock failed !");
145		bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
146	}
147	if (SUCCESS != pthread_cond_destroy(&(plEvent->condition)))
148	{
149		TIMM_OSAL_Error
150		    ("Event Delete: Conditional Variable Destroy failed !");
151		bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
152	}
153
154	if (SUCCESS != pthread_mutex_unlock(&(plEvent->mutex)))
155	{
156		TIMM_OSAL_Error("Event Delete: Mutex Unlock failed !");
157		bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
158	}
159
160	if (SUCCESS != pthread_mutex_destroy(&(plEvent->mutex)))
161	{
162		TIMM_OSAL_Error("Event Delete: Mutex Destory failed !");
163		bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
164	}
165
166	TIMM_OSAL_Free(plEvent);
167      EXIT:
168	return bReturnStatus;
169}
170
171
172/* ========================================================================== */
173/**
174* @fn TIMM_OSAL_EventSet function
175*
176*
177*/
178/* ========================================================================== */
179TIMM_OSAL_ERRORTYPE TIMM_OSAL_EventSet(TIMM_OSAL_PTR pEvents,
180    TIMM_OSAL_U32 uEventFlags, TIMM_OSAL_EVENT_OPERATION eOperation)
181{
182
183	TIMM_OSAL_ERRORTYPE bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
184	TIMM_OSAL_THREAD_EVENT *plEvent = (TIMM_OSAL_THREAD_EVENT *) pEvents;
185
186	if (TIMM_OSAL_NULL == plEvent)
187	{
188		bReturnStatus = TIMM_OSAL_ERR_PARAMETER;
189		goto EXIT;
190	}
191
192	if (SUCCESS != pthread_mutex_lock(&(plEvent->mutex)))
193	{
194		TIMM_OSAL_Error("Event Set: Mutex Lock failed !");
195		bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
196		goto EXIT;
197	}
198
199	switch (eOperation)
200	{
201	case TIMM_OSAL_EVENT_AND:
202		plEvent->eFlags = plEvent->eFlags & uEventFlags;
203		break;
204	case TIMM_OSAL_EVENT_OR:
205		plEvent->eFlags = plEvent->eFlags | uEventFlags;
206		break;
207	default:
208		TIMM_OSAL_Error("Event Set: Bad eOperation !");
209		bReturnStatus = TIMM_OSAL_ERR_PARAMETER;
210		pthread_mutex_unlock(&plEvent->mutex);
211		goto EXIT;
212	}
213
214	plEvent->bSignaled = TIMM_OSAL_TRUE;
215
216	if (SUCCESS != pthread_cond_signal(&plEvent->condition))
217	{
218		TIMM_OSAL_Error
219		    ("Event Set: Condition Variable Signal failed !");
220		bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
221		pthread_mutex_unlock(&plEvent->mutex);
222		goto EXIT;
223	}
224
225	if (SUCCESS != pthread_mutex_unlock(&plEvent->mutex))
226	{
227		TIMM_OSAL_Error("Event Set: Mutex Unlock failed !");
228		bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
229	} else
230		bReturnStatus = TIMM_OSAL_ERR_NONE;
231
232      EXIT:
233	return bReturnStatus;
234
235
236}
237
238/* ========================================================================== */
239/**
240* @fn TIMM_OSAL_EventRetrieve function
241*
242*Spurious  wakeups  from  the  pthread_cond_timedwait() or pthread_cond_wait() functions  may  occur.
243*
244*A representative sequence for using condition variables is shown below
245*
246*Thread A (Retrieve Events)							|Thread B (Set Events)
247*------------------------------------------------------------------------------------------------------------
248*1) Do work up to the point where a certain condition 	|1)Do work
249*  must occur (such as "count" must reach a specified 	|2)Lock associated mutex
250*  value)											|3)Change the value of the global variable
251*2) Lock associated mutex and check value of a global 	|  that Thread-A is waiting upon.
252*  variable										|4)Check value of the global Thread-A wait
253*3) Call pthread_cond_wait() to perform a blocking wait 	|  variable. If it fulfills the desired
254*  for signal from Thread-B. Note that a call to 			|  condition, signal Thread-A.
255*  pthread_cond_wait() automatically and atomically 		|5)Unlock mutex.
256*  unlocks the associated mutex variable so that it can 	|6)Continue
257*  be used by Thread-B.							|
258*4) When signalled, wake up. Mutex is automatically and 	|
259*  atomically locked.								|
260*5) Explicitly unlock mutex							|
261*6) Continue										|
262*
263*/
264/* ========================================================================== */
265TIMM_OSAL_ERRORTYPE TIMM_OSAL_EventRetrieve(TIMM_OSAL_PTR pEvents,
266    TIMM_OSAL_U32 uRequestedEvents,
267    TIMM_OSAL_EVENT_OPERATION eOperation,
268    TIMM_OSAL_U32 * pRetrievedEvents, TIMM_OSAL_U32 uTimeOutMsec)
269{
270	TIMM_OSAL_ERRORTYPE bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
271	struct timespec timeout;
272	struct timeval now;
273	TIMM_OSAL_U32 timeout_us;
274	TIMM_OSAL_U32 isolatedFlags;
275	int status = -1;
276	int and_operation;
277	TIMM_OSAL_THREAD_EVENT *plEvent = (TIMM_OSAL_THREAD_EVENT *) pEvents;
278
279	if (TIMM_OSAL_NULL == plEvent)
280	{
281		bReturnStatus = TIMM_OSAL_ERR_PARAMETER;
282		goto EXIT;
283	}
284
285	/* Lock the mutex for access to the eFlags global variable */
286	if (SUCCESS != pthread_mutex_lock(&(plEvent->mutex)))
287	{
288		TIMM_OSAL_Error("Event Retrieve: Mutex Lock failed !");
289		bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
290		goto EXIT;
291	}
292
293	/*Check the eOperation and put it in a variable */
294	and_operation = ((TIMM_OSAL_EVENT_AND == eOperation) ||
295	    (TIMM_OSAL_EVENT_AND_CONSUME == eOperation));
296
297	/* Isolate the flags. The & operation is suffice for an TIMM_OSAL_EVENT_OR eOperation */
298	isolatedFlags = plEvent->eFlags & uRequestedEvents;
299
300	/*Check if it is the AND operation. If yes then, all the flags must match */
301	if (and_operation)
302	{
303		isolatedFlags = (isolatedFlags == uRequestedEvents);
304	}
305
306
307	if (isolatedFlags)
308	{
309
310		/*We have got required combination of the eFlags bits and will return it back */
311		*pRetrievedEvents = plEvent->eFlags;
312		bReturnStatus = TIMM_OSAL_ERR_NONE;
313	} else
314	{
315
316		/*Required combination of bits is not yet available */
317		if (TIMM_OSAL_NO_SUSPEND == uTimeOutMsec)
318		{
319			*pRetrievedEvents = 0;
320			bReturnStatus = TIMM_OSAL_ERR_NONE;
321		}
322
323		else if (TIMM_OSAL_SUSPEND == uTimeOutMsec)
324		{
325
326			/*Wait till we get the required combination of bits. We we get the required
327			 *bits then we go out of the while loop
328			 */
329			while (!isolatedFlags)
330			{
331
332				/*Wait on the conditional variable for another thread to set the eFlags and signal */
333				pthread_cond_wait(&(plEvent->condition),
334				    &(plEvent->mutex));
335
336				/* eFlags set by some thread. Now, isolate the flags.
337				 * The & operation is suffice for an TIMM_OSAL_EVENT_OR eOperation
338				 */
339				isolatedFlags =
340				    plEvent->eFlags & uRequestedEvents;
341
342				/*Check if it is the AND operation. If yes then, all the flags must match */
343				if (and_operation)
344				{
345					isolatedFlags =
346					    (isolatedFlags ==
347					    uRequestedEvents);
348				}
349			}
350
351			/* Obtained the requested combination of bits on eFlags */
352			*pRetrievedEvents = plEvent->eFlags;
353			bReturnStatus = TIMM_OSAL_ERR_NONE;
354
355		} else
356		{
357
358			/* Calculate uTimeOutMsec in terms of the absolute time. uTimeOutMsec is in milliseconds */
359			gettimeofday(&now, NULL);
360			timeout_us = now.tv_usec + 1000 * uTimeOutMsec;
361			timeout.tv_sec = now.tv_sec + timeout_us / 1000000;
362			timeout.tv_nsec = (timeout_us % 1000000) * 1000;
363
364			while (!isolatedFlags)
365			{
366
367				/* Wait till uTimeOutMsec for a thread to signal on the conditional variable */
368				status =
369				    pthread_cond_timedwait(&(plEvent->
370					condition), &(plEvent->mutex),
371				    &timeout);
372
373				/*Timedout or error and returned without being signalled */
374				if (SUCCESS != status)
375				{
376					if (ETIMEDOUT == status)
377						bReturnStatus =
378						    TIMM_OSAL_ERR_NONE;
379					*pRetrievedEvents = 0;
380					break;
381				}
382
383				/* eFlags set by some thread. Now, isolate the flags.
384				 * The & operation is suffice for an TIMM_OSAL_EVENT_OR eOperation
385				 */
386				isolatedFlags =
387				    plEvent->eFlags & uRequestedEvents;
388
389				/*Check if it is the AND operation. If yes then, all the flags must match */
390				if (and_operation)
391				{
392					isolatedFlags =
393					    (isolatedFlags ==
394					    uRequestedEvents);
395				}
396
397			}
398		}
399	}
400
401	/*If we have got the required combination of bits, we will have to reset the eFlags if CONSUME is mentioned
402	 *in the eOperations
403	 */
404	if (isolatedFlags && ((eOperation == TIMM_OSAL_EVENT_AND_CONSUME) ||
405		(eOperation == TIMM_OSAL_EVENT_OR_CONSUME)))
406	{
407		plEvent->eFlags = 0;
408	}
409
410	/*Manually unlock the mutex */
411	if (SUCCESS != pthread_mutex_unlock(&(plEvent->mutex)))
412	{
413		TIMM_OSAL_Error("Event Retrieve: Mutex Unlock failed !");
414		bReturnStatus = TIMM_OSAL_ERR_UNKNOWN;
415	}
416
417      EXIT:
418	return bReturnStatus;
419
420}
421