1/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * met:
6 *     * Redistributions of source code must retain the above copyright
7 *       notice, this list of conditions and the following disclaimer.
8 *     * Redistributions in binary form must reproduce the above
9 *       copyright notice, this list of conditions and the following
10 *       disclaimer in the documentation and/or other materials provided
11 *       with the distribution.
12 *     * Neither the name of The Linux Foundation, nor the names of its
13 *       contributors may be used to endorse or promote products derived
14 *       from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "msg_q.h"
30
31#define LOG_TAG "LocSvc_utils_q"
32#include "log_util.h"
33#include "platform_lib_includes.h"
34#include "linked_list.h"
35#include <stdio.h>
36#include <stdlib.h>
37#include <pthread.h>
38
39typedef struct msg_q {
40   void* msg_list;                  /* Linked list to store information */
41   pthread_cond_t  list_cond;       /* Condition variable for waiting on msg queue */
42   pthread_mutex_t list_mutex;      /* Mutex for exclusive access to message queue */
43   int unblocked;                   /* Has this message queue been unblocked? */
44} msg_q;
45
46/*===========================================================================
47FUNCTION    convert_linked_list_err_type
48
49DESCRIPTION
50   Converts from one set of enum values to another.
51
52   linked_list_val: Value to convert to msg_q_enum_type
53
54DEPENDENCIES
55   N/A
56
57RETURN VALUE
58   Corresponding linked_list_enum_type in msg_q_enum_type
59
60SIDE EFFECTS
61   N/A
62
63===========================================================================*/
64static msq_q_err_type convert_linked_list_err_type(linked_list_err_type linked_list_val)
65{
66   switch( linked_list_val )
67   {
68   case eLINKED_LIST_SUCCESS:
69      return eMSG_Q_SUCCESS;
70   case eLINKED_LIST_INVALID_PARAMETER:
71      return eMSG_Q_INVALID_PARAMETER;
72   case eLINKED_LIST_INVALID_HANDLE:
73      return eMSG_Q_INVALID_HANDLE;
74   case eLINKED_LIST_UNAVAILABLE_RESOURCE:
75      return eMSG_Q_UNAVAILABLE_RESOURCE;
76   case eLINKED_LIST_INSUFFICIENT_BUFFER:
77      return eMSG_Q_INSUFFICIENT_BUFFER;
78
79   case eLINKED_LIST_FAILURE_GENERAL:
80   default:
81      return eMSG_Q_FAILURE_GENERAL;
82   }
83}
84
85/* ----------------------- END INTERNAL FUNCTIONS ---------------------------------------- */
86
87/*===========================================================================
88
89  FUNCTION:   msg_q_init
90
91  ===========================================================================*/
92msq_q_err_type msg_q_init(void** msg_q_data)
93{
94   if( msg_q_data == NULL )
95   {
96      LOC_LOGE("%s: Invalid msg_q_data parameter!\n", __FUNCTION__);
97      return eMSG_Q_INVALID_PARAMETER;
98   }
99
100   msg_q* tmp_msg_q;
101   tmp_msg_q = (msg_q*)calloc(1, sizeof(msg_q));
102   if( tmp_msg_q == NULL )
103   {
104      LOC_LOGE("%s: Unable to allocate space for message queue!\n", __FUNCTION__);
105      return eMSG_Q_FAILURE_GENERAL;
106   }
107
108   if( linked_list_init(&tmp_msg_q->msg_list) != 0 )
109   {
110      LOC_LOGE("%s: Unable to initialize storage list!\n", __FUNCTION__);
111      free(tmp_msg_q);
112      return eMSG_Q_FAILURE_GENERAL;
113   }
114
115   if( pthread_mutex_init(&tmp_msg_q->list_mutex, NULL) != 0 )
116   {
117      LOC_LOGE("%s: Unable to initialize list mutex!\n", __FUNCTION__);
118      linked_list_destroy(&tmp_msg_q->msg_list);
119      free(tmp_msg_q);
120      return eMSG_Q_FAILURE_GENERAL;
121   }
122
123   if( pthread_cond_init(&tmp_msg_q->list_cond, NULL) != 0 )
124   {
125      LOC_LOGE("%s: Unable to initialize msg q cond var!\n", __FUNCTION__);
126      linked_list_destroy(&tmp_msg_q->msg_list);
127      pthread_mutex_destroy(&tmp_msg_q->list_mutex);
128      free(tmp_msg_q);
129      return eMSG_Q_FAILURE_GENERAL;
130   }
131
132   tmp_msg_q->unblocked = 0;
133
134   *msg_q_data = tmp_msg_q;
135
136   return eMSG_Q_SUCCESS;
137}
138
139/*===========================================================================
140
141  FUNCTION:   msg_q_init2
142
143  ===========================================================================*/
144const void* msg_q_init2()
145{
146  void* q = NULL;
147  if (eMSG_Q_SUCCESS != msg_q_init(&q)) {
148    q = NULL;
149  }
150  return q;
151}
152
153/*===========================================================================
154
155  FUNCTION:   msg_q_destroy
156
157  ===========================================================================*/
158msq_q_err_type msg_q_destroy(void** msg_q_data)
159{
160   if( msg_q_data == NULL )
161   {
162      LOC_LOGE("%s: Invalid msg_q_data parameter!\n", __FUNCTION__);
163      return eMSG_Q_INVALID_HANDLE;
164   }
165
166   msg_q* p_msg_q = (msg_q*)*msg_q_data;
167
168   linked_list_destroy(&p_msg_q->msg_list);
169   pthread_mutex_destroy(&p_msg_q->list_mutex);
170   pthread_cond_destroy(&p_msg_q->list_cond);
171
172   p_msg_q->unblocked = 0;
173
174   free(*msg_q_data);
175   *msg_q_data = NULL;
176
177   return eMSG_Q_SUCCESS;
178}
179
180/*===========================================================================
181
182  FUNCTION:   msg_q_snd
183
184  ===========================================================================*/
185msq_q_err_type msg_q_snd(void* msg_q_data, void* msg_obj, void (*dealloc)(void*))
186{
187   msq_q_err_type rv;
188   if( msg_q_data == NULL )
189   {
190      LOC_LOGE("%s: Invalid msg_q_data parameter!\n", __FUNCTION__);
191      return eMSG_Q_INVALID_HANDLE;
192   }
193   if( msg_obj == NULL )
194   {
195      LOC_LOGE("%s: Invalid msg_obj parameter!\n", __FUNCTION__);
196      return eMSG_Q_INVALID_PARAMETER;
197   }
198
199   msg_q* p_msg_q = (msg_q*)msg_q_data;
200
201   pthread_mutex_lock(&p_msg_q->list_mutex);
202   LOC_LOGD("%s: Sending message with handle = 0x%08X\n", __FUNCTION__, msg_obj);
203
204   if( p_msg_q->unblocked )
205   {
206      LOC_LOGE("%s: Message queue has been unblocked.\n", __FUNCTION__);
207      pthread_mutex_unlock(&p_msg_q->list_mutex);
208      return eMSG_Q_UNAVAILABLE_RESOURCE;
209   }
210
211   rv = convert_linked_list_err_type(linked_list_add(p_msg_q->msg_list, msg_obj, dealloc));
212
213   /* Show data is in the message queue. */
214   pthread_cond_signal(&p_msg_q->list_cond);
215
216   pthread_mutex_unlock(&p_msg_q->list_mutex);
217
218   LOC_LOGD("%s: Finished Sending message with handle = 0x%08X\n", __FUNCTION__, msg_obj);
219
220   return rv;
221}
222
223/*===========================================================================
224
225  FUNCTION:   msg_q_rcv
226
227  ===========================================================================*/
228msq_q_err_type msg_q_rcv(void* msg_q_data, void** msg_obj)
229{
230   msq_q_err_type rv;
231   if( msg_q_data == NULL )
232   {
233      LOC_LOGE("%s: Invalid msg_q_data parameter!\n", __FUNCTION__);
234      return eMSG_Q_INVALID_HANDLE;
235   }
236
237   if( msg_obj == NULL )
238   {
239      LOC_LOGE("%s: Invalid msg_obj parameter!\n", __FUNCTION__);
240      return eMSG_Q_INVALID_PARAMETER;
241   }
242
243   msg_q* p_msg_q = (msg_q*)msg_q_data;
244
245   LOC_LOGD("%s: Waiting on message\n", __FUNCTION__);
246
247   pthread_mutex_lock(&p_msg_q->list_mutex);
248
249   if( p_msg_q->unblocked )
250   {
251      LOC_LOGE("%s: Message queue has been unblocked.\n", __FUNCTION__);
252      pthread_mutex_unlock(&p_msg_q->list_mutex);
253      return eMSG_Q_UNAVAILABLE_RESOURCE;
254   }
255
256   /* Wait for data in the message queue */
257   while( linked_list_empty(p_msg_q->msg_list) && !p_msg_q->unblocked )
258   {
259      pthread_cond_wait(&p_msg_q->list_cond, &p_msg_q->list_mutex);
260   }
261
262   rv = convert_linked_list_err_type(linked_list_remove(p_msg_q->msg_list, msg_obj));
263
264   pthread_mutex_unlock(&p_msg_q->list_mutex);
265
266   LOC_LOGD("%s: Received message 0x%08X rv = %d\n", __FUNCTION__, *msg_obj, rv);
267
268   return rv;
269}
270
271/*===========================================================================
272
273  FUNCTION:   msg_q_flush
274
275  ===========================================================================*/
276msq_q_err_type msg_q_flush(void* msg_q_data)
277{
278   msq_q_err_type rv;
279   if ( msg_q_data == NULL )
280   {
281      LOC_LOGE("%s: Invalid msg_q_data parameter!\n", __FUNCTION__);
282      return eMSG_Q_INVALID_HANDLE;
283   }
284
285   msg_q* p_msg_q = (msg_q*)msg_q_data;
286
287   LOC_LOGD("%s: Flushing Message Queue\n", __FUNCTION__);
288
289   pthread_mutex_lock(&p_msg_q->list_mutex);
290
291   /* Remove all elements from the list */
292   rv = convert_linked_list_err_type(linked_list_flush(p_msg_q->msg_list));
293
294   pthread_mutex_unlock(&p_msg_q->list_mutex);
295
296   LOC_LOGD("%s: Message Queue flushed\n", __FUNCTION__);
297
298   return rv;
299}
300
301/*===========================================================================
302
303  FUNCTION:   msg_q_unblock
304
305  ===========================================================================*/
306msq_q_err_type msg_q_unblock(void* msg_q_data)
307{
308   if ( msg_q_data == NULL )
309   {
310      LOC_LOGE("%s: Invalid msg_q_data parameter!\n", __FUNCTION__);
311      return eMSG_Q_INVALID_HANDLE;
312   }
313
314   msg_q* p_msg_q = (msg_q*)msg_q_data;
315   pthread_mutex_lock(&p_msg_q->list_mutex);
316
317   if( p_msg_q->unblocked )
318   {
319      LOC_LOGE("%s: Message queue has been unblocked.\n", __FUNCTION__);
320      pthread_mutex_unlock(&p_msg_q->list_mutex);
321      return eMSG_Q_UNAVAILABLE_RESOURCE;
322   }
323
324   LOC_LOGD("%s: Unblocking Message Queue\n", __FUNCTION__);
325   /* Unblocking message queue */
326   p_msg_q->unblocked = 1;
327
328   /* Allow all the waiters to wake up */
329   pthread_cond_broadcast(&p_msg_q->list_cond);
330
331   pthread_mutex_unlock(&p_msg_q->list_mutex);
332
333   LOC_LOGD("%s: Message Queue unblocked\n", __FUNCTION__);
334
335   return eMSG_Q_SUCCESS;
336}
337