/*
* Copyright (C) 2016 The Android Open Source Project
* Copyright (C) 2016 Mopria Alliance, Inc.
* Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include "wprint_msgq.h"
#include "wprint_debug.h"
#define TAG "wprint_msgq"
#define _SEM_NAME_LENGTH 16
typedef struct {
msg_q_id msgq_id;
char name[_SEM_NAME_LENGTH];
int max_msgs;
int max_msg_length;
int num_msgs;
sem_t sem_count;
sem_t *sem_ptr;
pthread_mutex_t mutex;
pthread_mutexattr_t mutexattr;
unsigned long read_offset;
unsigned long write_offset;
} _msgq_hdr_t;
msg_q_id msgQCreate(int max_msgs, int max_msg_length) {
_msgq_hdr_t *msgq;
int msgq_size;
msgq_size = sizeof(_msgq_hdr_t) + max_msgs * max_msg_length;
msgq = (_msgq_hdr_t *) malloc((size_t)msgq_size);
if (msgq) {
memset((char *) msgq, 0, (size_t)msgq_size);
msgq->msgq_id = (msg_q_id) msgq;
msgq->max_msgs = max_msgs;
msgq->max_msg_length = max_msg_length;
msgq->num_msgs = 0;
// create a mutex to protect access to this structure
pthread_mutexattr_init(&(msgq->mutexattr));
pthread_mutexattr_settype(&(msgq->mutexattr), PTHREAD_MUTEX_RECURSIVE_NP);
pthread_mutex_init(&msgq->mutex, &msgq->mutexattr);
// create a counting semaphore
msgq->sem_ptr = &msgq->sem_count;
sem_init(msgq->sem_ptr, 0, 0); // PRIVATE, EMPTY
msgq->read_offset = 0;
msgq->write_offset = 0;
}
return ((msg_q_id) msgq);
}
status_t msgQDelete(msg_q_id msgQ) {
_msgq_hdr_t *msgq = (msg_q_id) msgQ;
if (msgq) {
pthread_mutex_lock(&(msgq->mutex));
if (msgq->num_msgs) {
LOGE("Warning msgQDelete() called on queue with %d messages", msgq->num_msgs);
}
sem_destroy(&(msgq->sem_count));
pthread_mutex_unlock(&(msgq->mutex));
pthread_mutex_destroy(&(msgq->mutex));
free((void *) msgq);
}
return (msgq ? OK : ERROR);
}
status_t msgQSend(msg_q_id msgQ, const char *buffer, unsigned long nbytes, int timeout,
int priority) {
_msgq_hdr_t *msgq = (msg_q_id) msgQ;
char *msg_loc;
status_t result = ERROR;
// validate function arguments
if (msgq && (timeout == NO_WAIT) && (priority == MSG_Q_FIFO)) {
pthread_mutex_lock(&(msgq->mutex));
// ensure the message conforms to size limits and there is room in the msgQ
if ((nbytes <= msgq->max_msg_length) && (msgq->num_msgs < msgq->max_msgs)) {
msg_loc = (char *) msgq + sizeof(_msgq_hdr_t) +
(msgq->write_offset * msgq->max_msg_length);
memcpy(msg_loc, buffer, nbytes);
msgq->write_offset = (msgq->write_offset + 1) % msgq->max_msgs;
msgq->num_msgs++;
sem_post(msgq->sem_ptr);
result = OK;
}
pthread_mutex_unlock(&(msgq->mutex));
}
return result;
}
status_t msgQReceive(msg_q_id msgQ, char *buffer, unsigned long max_nbytes, int timeout) {
_msgq_hdr_t *msgq = (msg_q_id) msgQ;
char *msg_loc;
status_t result = ERROR;
if (msgq && buffer && ((timeout == WAIT_FOREVER) || (timeout == NO_WAIT))) {
if (timeout == WAIT_FOREVER) {
result = (status_t) sem_wait(msgq->sem_ptr);
} else {
/* timeout is NO_WAIT */
result = (status_t) sem_trywait(msgq->sem_ptr);
}
if (result == 0) {
pthread_mutex_lock(&(msgq->mutex));
msg_loc = (char *) msgq + sizeof(_msgq_hdr_t) +
(msgq->read_offset * msgq->max_msg_length);
memcpy(buffer, msg_loc, max_nbytes);
msgq->read_offset = (msgq->read_offset + 1) % msgq->max_msgs;
msgq->num_msgs--;
pthread_mutex_unlock(&(msgq->mutex));
}
}
return result;
}
int msgQNumMsgs(msg_q_id msgQ) {
_msgq_hdr_t *msgq = (msg_q_id) msgQ;
int num_msgs = -1;
if (msgq) {
pthread_mutex_lock(&(msgq->mutex));
num_msgs = msgq->num_msgs;
pthread_mutex_unlock(&(msgq->mutex));
}
return num_msgs;
}