/* Copyright (c) 2015 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include "cras_main_message.h"
#include "cras_system_state.h"
#include "cras_util.h"
/* Callback to handle specific type of main thread message. */
struct cras_main_msg_callback {
enum CRAS_MAIN_MESSAGE_TYPE type;
cras_message_callback callback;
void *callback_data;
struct cras_main_msg_callback *prev, *next;
};
static int main_msg_fds[2];
static struct cras_main_msg_callback *main_msg_callbacks;
int cras_main_message_add_handler(enum CRAS_MAIN_MESSAGE_TYPE type,
cras_message_callback callback,
void *callback_data)
{
struct cras_main_msg_callback *msg_cb;
DL_FOREACH(main_msg_callbacks, msg_cb) {
if (msg_cb->type == type) {
syslog(LOG_ERR, "Main message type %u already exists",
type);
return -EEXIST;
}
}
msg_cb = (struct cras_main_msg_callback *)calloc(1, sizeof(*msg_cb));
msg_cb->type = type;
msg_cb->callback = callback;
msg_cb->callback_data = callback_data;
DL_APPEND(main_msg_callbacks, msg_cb);
return 0;
}
int cras_main_message_send(struct cras_main_message *msg)
{
int err;
err = write(main_msg_fds[1], msg, msg->length);
if (err < 0) {
syslog(LOG_ERR, "Failed to send main message, type %u",
msg->type);
return err;
}
return 0;
}
static int read_main_message(int msg_fd, uint8_t *buf, size_t max_len) {
int to_read, nread, rc;
struct cras_main_message *msg = (struct cras_main_message *)buf;
nread = read(msg_fd, buf, sizeof(msg->length));
if (nread < 0)
return nread;
if (msg->length > max_len)
return -ENOMEM;
to_read = msg->length - nread;
rc = read(msg_fd, &buf[0] + nread, to_read);
if (rc < 0)
return rc;
return 0;
}
static void handle_main_messages(void *arg)
{
uint8_t buf[256];
int rc;
struct cras_main_msg_callback *main_msg_cb;
struct cras_main_message *msg = (struct cras_main_message *)buf;
rc = read_main_message(main_msg_fds[0], buf, sizeof(buf));
if (rc < 0) {
syslog(LOG_ERR, "Failed to read main message");
return;
}
DL_FOREACH(main_msg_callbacks, main_msg_cb) {
if (main_msg_cb->type == msg->type) {
main_msg_cb->callback(msg, main_msg_cb->callback_data);
break;
}
}
}
void cras_main_message_init() {
int rc;
rc = pipe(main_msg_fds);
if (rc < 0) {
syslog(LOG_ERR, "Fatal: main message init");
exit(-ENOMEM);
}
/* When full it's preferred to get error instead of blocked. */
cras_make_fd_nonblocking(main_msg_fds[0]);
cras_make_fd_nonblocking(main_msg_fds[1]);
cras_system_add_select_fd(main_msg_fds[0],
handle_main_messages,
NULL);
}