1/*
2 * Copyright (C) Texas Instruments - http://www.ti.com/
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18#include <errno.h>
19#include <string.h>
20#include <sys/types.h>
21#include <sys/poll.h>
22#include <unistd.h>
23#include <utils/Errors.h>
24
25
26
27#define LOG_TAG "MessageQueue"
28#include <utils/Log.h>
29
30#include "MessageQueue.h"
31
32namespace TIUTILS {
33
34/**
35   @brief Constructor for the message queue class
36
37   @param none
38   @return none
39 */
40MessageQueue::MessageQueue()
41{
42    LOG_FUNCTION_NAME;
43
44    int fds[2] = {-1,-1};
45    android::status_t stat;
46
47    stat = pipe(fds);
48
49    if ( 0 > stat )
50        {
51        MSGQ_LOGEB("Error while openning pipe: %s", strerror(stat) );
52        this->fd_read = 0;
53        this->fd_write = 0;
54        mHasMsg = false;
55        }
56    else
57        {
58        this->fd_read = fds[0];
59        this->fd_write = fds[1];
60
61        mHasMsg = false;
62        }
63
64    LOG_FUNCTION_NAME_EXIT;
65}
66
67/**
68   @brief Destructor for the semaphore class
69
70   @param none
71   @return none
72 */
73MessageQueue::~MessageQueue()
74{
75    LOG_FUNCTION_NAME;
76
77    if(this->fd_read >= 0)
78        {
79        close(this->fd_read);
80        }
81
82    if(this->fd_write >= 0)
83        {
84        close(this->fd_write);
85        }
86
87    LOG_FUNCTION_NAME_EXIT;
88}
89
90/**
91   @brief Get a message from the queue
92
93   @param msg Message structure to hold the message to be retrieved
94   @return android::NO_ERROR On success
95   @return android::BAD_VALUE if the message pointer is NULL
96   @return android::NO_INIT If the file read descriptor is not set
97   @return android::UNKNOWN_ERROR if the read operation fromthe file read descriptor fails
98 */
99android::status_t MessageQueue::get(Message* msg)
100{
101    LOG_FUNCTION_NAME;
102
103    if(!msg)
104        {
105        MSGQ_LOGEA("msg is NULL");
106        LOG_FUNCTION_NAME_EXIT;
107        return android::BAD_VALUE;
108        }
109
110    if(!this->fd_read)
111        {
112        MSGQ_LOGEA("read descriptor not initialized for message queue");
113        LOG_FUNCTION_NAME_EXIT;
114        return android::NO_INIT;
115        }
116
117    char* p = (char*) msg;
118    size_t read_bytes = 0;
119
120    while( read_bytes  < sizeof(*msg) )
121        {
122        int err = read(this->fd_read, p, sizeof(*msg) - read_bytes);
123
124        if( err < 0 )
125            {
126            MSGQ_LOGEB("read() error: %s", strerror(errno));
127            return android::UNKNOWN_ERROR;
128            }
129        else
130            {
131            read_bytes += err;
132            }
133        }
134
135    MSGQ_LOGDB("MQ.get(%d,%p,%p,%p,%p)", msg->command, msg->arg1,msg->arg2,msg->arg3,msg->arg4);
136
137    mHasMsg = false;
138
139    LOG_FUNCTION_NAME_EXIT;
140
141    return 0;
142}
143
144/**
145   @brief Get the input file descriptor of the message queue
146
147   @param none
148   @return file read descriptor
149 */
150
151int MessageQueue::getInFd()
152{
153    return this->fd_read;
154}
155
156/**
157   @brief Constructor for the message queue class
158
159   @param fd file read descriptor
160   @return none
161 */
162
163void MessageQueue::setInFd(int fd)
164{
165    LOG_FUNCTION_NAME;
166
167    if ( -1 != this->fd_read )
168        {
169        close(this->fd_read);
170        }
171
172    this->fd_read = fd;
173
174    LOG_FUNCTION_NAME_EXIT;
175}
176
177/**
178   @brief Queue a message
179
180   @param msg Message structure to hold the message to be retrieved
181   @return android::NO_ERROR On success
182   @return android::BAD_VALUE if the message pointer is NULL
183   @return android::NO_INIT If the file write descriptor is not set
184   @return android::UNKNOWN_ERROR if the write operation fromthe file write descriptor fails
185 */
186
187android::status_t MessageQueue::put(Message* msg)
188{
189    LOG_FUNCTION_NAME;
190
191    char* p = (char*) msg;
192    size_t bytes = 0;
193
194    if(!msg)
195        {
196        MSGQ_LOGEA("msg is NULL");
197        LOG_FUNCTION_NAME_EXIT;
198        return android::BAD_VALUE;
199        }
200
201    if(!this->fd_write)
202        {
203        MSGQ_LOGEA("write descriptor not initialized for message queue");
204        LOG_FUNCTION_NAME_EXIT;
205        return android::NO_INIT;
206        }
207
208
209    MSGQ_LOGDB("MQ.put(%d,%p,%p,%p,%p)", msg->command, msg->arg1,msg->arg2,msg->arg3,msg->arg4);
210
211    while( bytes  < sizeof(msg) )
212        {
213        int err = write(this->fd_write, p, sizeof(*msg) - bytes);
214
215        if( err < 0 )
216            {
217            MSGQ_LOGEB("write() error: %s", strerror(errno));
218            LOG_FUNCTION_NAME_EXIT;
219            return android::UNKNOWN_ERROR;
220            }
221        else
222            {
223            bytes += err;
224            }
225        }
226
227    MSGQ_LOGDA("MessageQueue::put EXIT");
228
229    LOG_FUNCTION_NAME_EXIT;
230    return 0;
231}
232
233
234/**
235   @brief Returns if the message queue is empty or not
236
237   @param none
238   @return true If the queue is empty
239   @return false If the queue has at least one message
240 */
241bool MessageQueue::isEmpty()
242{
243    LOG_FUNCTION_NAME;
244
245    struct pollfd pfd;
246
247    pfd.fd = this->fd_read;
248    pfd.events = POLLIN;
249    pfd.revents = 0;
250
251    if(!this->fd_read)
252        {
253        MSGQ_LOGEA("read descriptor not initialized for message queue");
254        LOG_FUNCTION_NAME_EXIT;
255        return android::NO_INIT;
256        }
257
258
259    if( -1 == poll(&pfd,1,0) )
260        {
261        MSGQ_LOGEB("poll() error: %s", strerror(errno));
262        LOG_FUNCTION_NAME_EXIT;
263        return false;
264        }
265
266    if(pfd.revents & POLLIN)
267        {
268        mHasMsg = true;
269        }
270    else
271        {
272        mHasMsg = false;
273        }
274
275    LOG_FUNCTION_NAME_EXIT;
276    return !mHasMsg;
277}
278
279void MessageQueue::clear()
280{
281    if(!this->fd_read)
282        {
283        MSGQ_LOGEA("read descriptor not initialized for message queue");
284        LOG_FUNCTION_NAME_EXIT;
285        return;
286        }
287
288    Message msg;
289    while(!isEmpty())
290        {
291        get(&msg);
292        }
293
294}
295
296
297/**
298   @brief Force whether the message queue has message or not
299
300   @param hasMsg Whether the queue has a message or not
301   @return none
302 */
303void MessageQueue::setMsg(bool hasMsg)
304    {
305    mHasMsg = hasMsg;
306    }
307
308
309/**
310   @briefWait for message in maximum three different queues with a timeout
311
312   @param queue1 First queue. At least this should be set to a valid queue pointer
313   @param queue2 Second queue. Optional.
314   @param queue3 Third queue. Optional.
315   @param timeout The timeout value (in micro secs) to wait for a message in any of the queues
316   @return android::NO_ERROR On success
317   @return android::BAD_VALUE If queue1 is NULL
318   @return android::NO_INIT If the file read descriptor of any of the provided queues is not set
319 */
320android::status_t MessageQueue::waitForMsg(MessageQueue *queue1, MessageQueue *queue2, MessageQueue *queue3, int timeout)
321    {
322    LOG_FUNCTION_NAME;
323
324    int n =1;
325    struct pollfd pfd[3];
326
327    if(!queue1)
328        {
329        MSGQ_LOGEA("queue1 pointer is NULL");
330        LOG_FUNCTION_NAME_EXIT;
331        return android::BAD_VALUE;
332        }
333
334    pfd[0].fd = queue1->getInFd();
335    if(!pfd[0].fd)
336        {
337        MSGQ_LOGEA("read descriptor not initialized for message queue1");
338        LOG_FUNCTION_NAME_EXIT;
339        return android::NO_INIT;
340        }
341    pfd[0].events = POLLIN;
342    pfd[0].revents = 0;
343    if(queue2)
344        {
345        MSGQ_LOGDA("queue2 not-null");
346        pfd[1].fd = queue2->getInFd();
347        if(!pfd[1].fd)
348            {
349            MSGQ_LOGEA("read descriptor not initialized for message queue2");
350            LOG_FUNCTION_NAME_EXIT;
351            return android::NO_INIT;
352            }
353
354        pfd[1].events = POLLIN;
355        pfd[1].revents = 0;
356        n++;
357        }
358
359    if(queue3)
360        {
361        MSGQ_LOGDA("queue3 not-null");
362        pfd[2].fd = queue3->getInFd();
363        if(!pfd[2].fd)
364            {
365            MSGQ_LOGEA("read descriptor not initialized for message queue3");
366            LOG_FUNCTION_NAME_EXIT;
367            return android::NO_INIT;
368            }
369
370        pfd[2].events = POLLIN;
371        pfd[2].revents = 0;
372        n++;
373        }
374
375
376    int ret = poll(pfd, n, timeout);
377    if(ret==0)
378        {
379        LOG_FUNCTION_NAME_EXIT;
380        return ret;
381        }
382
383    if(ret<android::NO_ERROR)
384        {
385        MSGQ_LOGEB("Message queue returned error %d", ret);
386        LOG_FUNCTION_NAME_EXIT;
387        return ret;
388        }
389
390    if (pfd[0].revents & POLLIN)
391        {
392        queue1->setMsg(true);
393        }
394
395    if(queue2)
396        {
397        if (pfd[1].revents & POLLIN)
398            {
399            queue2->setMsg(true);
400            }
401        }
402
403    if(queue3)
404        {
405        if (pfd[2].revents & POLLIN)
406            {
407            queue3->setMsg(true);
408            }
409        }
410
411    LOG_FUNCTION_NAME_EXIT;
412    return ret;
413    }
414
415};
416