blob: 957bdf35a7007778149d80b47bb4e5e3558ca2aa [file] [log] [blame]
/* Copyright (c) 2012 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 <assert.h>
#include <stdlib.h>
#include <syslog.h>
#include "audio_thread.h"
#include "cras_config.h"
#include "cras_dsp.h"
#include "cras_iodev.h"
#include "cras_iodev_list.h"
#include "cras_messages.h"
#include "cras_rclient.h"
#include "cras_rstream.h"
#include "cras_system_state.h"
#include "cras_types.h"
#include "cras_util.h"
#include "stream_list.h"
#include "utlist.h"
/* An attached client.
* id - The id of the client.
* fd - Connection for client communication.
*/
struct cras_rclient {
size_t id;
int fd;
};
/* Handles a message from the client to connect a new stream */
static int handle_client_stream_connect(struct cras_rclient *client,
const struct cras_connect_message *msg,
int aud_fd)
{
struct cras_rstream *stream;
struct cras_client_stream_connected reply;
struct cras_audio_format remote_fmt;
struct cras_rstream_config stream_config;
int rc;
unpack_cras_audio_format(&remote_fmt, &msg->format);
/* check the aud_fd is valid. */
if (aud_fd < 0) {
syslog(LOG_ERR, "Invalid fd in stream connect.\n");
rc = -EINVAL;
goto reply_err;
}
/* When full, getting an error is preferable to blocking. */
cras_make_fd_nonblocking(aud_fd);
/* Create the stream with the specified parameters. */
stream_config.stream_id = msg->stream_id;
stream_config.stream_type = msg->stream_type;
stream_config.direction = msg->direction;
stream_config.dev_idx = msg->dev_idx;
stream_config.flags = msg->flags;
stream_config.format = &remote_fmt;
stream_config.buffer_frames = msg->buffer_frames;
stream_config.cb_threshold = msg->cb_threshold;
stream_config.audio_fd = aud_fd;
stream_config.client = client;
rc = stream_list_add(cras_iodev_list_get_stream_list(),
&stream_config, &stream);
if (rc) {
rc = -ENOMEM;
goto reply_err;
}
/* Tell client about the stream setup. */
syslog(LOG_DEBUG, "Send connected for stream %x\n", msg->stream_id);
cras_fill_client_stream_connected(
&reply,
0, /* No error. */
msg->stream_id,
&remote_fmt,
cras_rstream_input_shm_key(stream),
cras_rstream_output_shm_key(stream),
cras_rstream_get_total_shm_size(stream));
rc = cras_rclient_send_message(client, &reply.header);
if (rc < 0) {
syslog(LOG_ERR, "Failed to send connected messaged\n");
stream_list_rm(cras_iodev_list_get_stream_list(),
stream->stream_id);
goto reply_err;
}
return 0;
reply_err:
/* Send the error code to the client. */
cras_fill_client_stream_connected(&reply, rc, msg->stream_id,
&remote_fmt, 0, 0, 0);
cras_rclient_send_message(client, &reply.header);
if (aud_fd >= 0)
close(aud_fd);
return rc;
}
/* Handles messages from the client requesting that a stream be removed from the
* server. */
static int handle_client_stream_disconnect(
struct cras_rclient *client,
const struct cras_disconnect_stream_message *msg)
{
return stream_list_rm(cras_iodev_list_get_stream_list(),
msg->stream_id);
}
/* Handles dumping audio thread debug info back to the client. */
static void dump_audio_thread_info(struct cras_rclient *client)
{
struct cras_client_audio_debug_info_ready msg;
struct cras_server_state *state;
cras_fill_client_audio_debug_info_ready(&msg);
state = cras_system_state_get_no_lock();
audio_thread_dump_thread_info(cras_iodev_list_get_audio_thread(),
&state->audio_debug_info);
cras_rclient_send_message(client, &msg.header);
}
/*
* Exported Functions.
*/
/* Creates a client structure and sends a message back informing the client that
* the conneciton has succeeded. */
struct cras_rclient *cras_rclient_create(int fd, size_t id)
{
struct cras_rclient *client;
struct cras_client_connected msg;
client = calloc(1, sizeof(struct cras_rclient));
if (!client)
return NULL;
client->fd = fd;
client->id = id;
cras_fill_client_connected(&msg, client->id, cras_sys_state_shm_key());
cras_rclient_send_message(client, &msg.header);
return client;
}
/* Removes all streams that the client owns and destroys it. */
void cras_rclient_destroy(struct cras_rclient *client)
{
stream_list_rm_all_client_streams(
cras_iodev_list_get_stream_list(), client);
free(client);
}
/* Entry point for handling a message from the client. Called from the main
* server context. */
int cras_rclient_message_from_client(struct cras_rclient *client,
const struct cras_server_message *msg,
int fd) {
assert(client && msg);
/* Most messages should not have a file descriptor. */
switch (msg->id) {
case CRAS_SERVER_CONNECT_STREAM:
break;
default:
if (fd != -1) {
syslog(LOG_ERR,
"Message %d should not have fd attached.",
msg->id);
close(fd);
return -1;
}
break;
}
switch (msg->id) {
case CRAS_SERVER_CONNECT_STREAM:
handle_client_stream_connect(client,
(const struct cras_connect_message *)msg, fd);
break;
case CRAS_SERVER_DISCONNECT_STREAM:
handle_client_stream_disconnect(client,
(const struct cras_disconnect_stream_message *)msg);
break;
case CRAS_SERVER_SET_SYSTEM_VOLUME:
cras_system_set_volume(
((const struct cras_set_system_volume *)msg)->volume);
break;
case CRAS_SERVER_SET_SYSTEM_MUTE:
cras_system_set_mute(
((const struct cras_set_system_mute *)msg)->mute);
break;
case CRAS_SERVER_SET_USER_MUTE:
cras_system_set_user_mute(
((const struct cras_set_system_mute *)msg)->mute);
break;
case CRAS_SERVER_SET_SYSTEM_MUTE_LOCKED:
cras_system_set_mute_locked(
((const struct cras_set_system_mute *)msg)->mute);
break;
case CRAS_SERVER_SET_SYSTEM_CAPTURE_GAIN: {
const struct cras_set_system_capture_gain *m =
(const struct cras_set_system_capture_gain *)msg;
cras_system_set_capture_gain(m->gain);
break;
}
case CRAS_SERVER_SET_SYSTEM_CAPTURE_MUTE:
cras_system_set_capture_mute(
((const struct cras_set_system_mute *)msg)->mute);
break;
case CRAS_SERVER_SET_SYSTEM_CAPTURE_MUTE_LOCKED:
cras_system_set_capture_mute_locked(
((const struct cras_set_system_mute *)msg)->mute);
break;
case CRAS_SERVER_SET_NODE_ATTR: {
const struct cras_set_node_attr *m =
(const struct cras_set_node_attr *)msg;
cras_iodev_list_set_node_attr(m->node_id, m->attr, m->value);
break;
}
case CRAS_SERVER_SELECT_NODE: {
const struct cras_select_node *m =
(const struct cras_select_node *)msg;
cras_iodev_list_select_node(m->direction, m->node_id);
break;
}
case CRAS_SERVER_ADD_ACTIVE_NODE: {
const struct cras_add_active_node *m =
(const struct cras_add_active_node *)msg;
cras_iodev_list_add_active_node(m->direction, m->node_id);
break;
}
case CRAS_SERVER_RM_ACTIVE_NODE: {
const struct cras_rm_active_node *m =
(const struct cras_rm_active_node *)msg;
cras_iodev_list_rm_active_node(m->direction, m->node_id);
break;
}
case CRAS_SERVER_RELOAD_DSP:
cras_dsp_reload_ini();
break;
case CRAS_SERVER_DUMP_DSP_INFO:
cras_dsp_dump_info();
break;
case CRAS_SERVER_DUMP_AUDIO_THREAD:
dump_audio_thread_info(client);
break;
case CRAS_SERVER_ADD_TEST_DEV: {
const struct cras_add_test_dev *m =
(const struct cras_add_test_dev *)msg;
cras_iodev_list_add_test_dev(m->type);
break;
}
case CRAS_SERVER_TEST_DEV_COMMAND: {
const struct cras_test_dev_command *m =
(const struct cras_test_dev_command *)msg;
cras_iodev_list_test_dev_command(m->iodev_idx, m->command,
m->data_len, m->data);
break;
}
case CRAS_SERVER_SUSPEND:
cras_system_set_suspended(1);
break;
case CRAS_SERVER_RESUME:
cras_system_set_suspended(0);
break;
default:
break;
}
return 0;
}
/* Sends a message to the client. */
int cras_rclient_send_message(const struct cras_rclient *client,
const struct cras_client_message *msg)
{
return write(client->fd, msg, msg->length);
}