summaryrefslogtreecommitdiffstats
path: root/audio/unix.c
diff options
context:
space:
mode:
Diffstat (limited to 'audio/unix.c')
-rw-r--r--audio/unix.c227
1 files changed, 159 insertions, 68 deletions
diff --git a/audio/unix.c b/audio/unix.c
index fbda7ed9..0880f4ff 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -37,11 +37,28 @@
#include "logging.h"
#include "dbus.h"
-
#include "manager.h"
+#include "ipc.h"
+#include "unix.h"
+
+struct unix_client {
+ struct device *dev;
+ int sock;
+};
+
+static GSList *clients = NULL;
static int unix_sock = -1;
+static int unix_send_state(int sock, struct ipc_packet *pkt);
+
+static void client_free(struct unix_client *client)
+{
+ if (client->sock >= 0)
+ close(client->sock);
+ g_free(client);
+}
+
/* Pass file descriptor through local domain sockets (AF_LOCAL, formerly AF_UNIX)
and the sendmsg() system call with the cmsg_type field of a "struct cmsghdr" set
to SCM_RIGHTS and the data being an integer value equal to the handle of the
@@ -75,52 +92,120 @@ static int unix_sendmsg_fd(int sock, int fd, struct ipc_packet *pkt)
return sendmsg(sock, &msgh, MSG_NOSIGNAL);
}
-static void cfg_event(int clisk, struct ipc_packet *pkt)
+static void cfg_event(struct unix_client *client, struct ipc_packet *pkt,
+ int len)
{
- struct ipc_data_cfg *cfg = (struct ipc_data_cfg *) pkt->data;
- struct device *device;
+ struct ipc_data_cfg *rsp;
+ struct device *dev;
+ int ret;
- memset(cfg, 0, sizeof(struct ipc_data_cfg));
+ dev = manager_get_connected_device();
+ if (dev)
+ goto proceed;
- if ((device = manager_default_device())) {
- if (device->headset)
- headset_get_config(device, clisk, pkt);
- }
- else
- cfg->fd = -1;
+ dev = manager_default_device();
+ if (!dev)
+ goto failed;
- if (cfg->fd != 0)
- unix_send_cfg(clisk, pkt);
+proceed:
+ ret = device_get_config(dev, client->sock, pkt, len, &rsp);
+ if (ret < 0)
+ goto failed;
+
+ client->dev = dev;
+
+ /* Connecting in progress */
+ if (ret == 1)
+ return;
+
+ unix_send_cfg(client->sock, rsp);
+ g_free(rsp);
+
+ return;
+
+failed:
+ unix_send_cfg(client->sock, NULL);
}
-static void ctl_event(int clisk, struct ipc_packet *pkt)
+static void ctl_event(struct unix_client *client, struct ipc_packet *pkt,
+ int len)
{
}
-static void state_event(int clisk, struct ipc_packet *pkt)
+static void state_event(struct unix_client *client, struct ipc_packet *pkt,
+ int len)
{
struct ipc_data_state *state = (struct ipc_data_state *) pkt->data;
- struct device *device;
+ struct device *dev = client->dev;
- if (!(device = manager_default_device()))
- return;
+ if (len > sizeof(struct ipc_packet))
+ device_set_state(dev, state->state);
+ else
+ state->state = device_get_state(dev);
- if (device->headset)
- headset_set_state(device, state->state);
+ unix_send_state(client->sock, pkt);
+}
+
+static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ char buf[IPC_MTU];
+ struct ipc_packet *pkt = (void *) buf;
+ struct unix_client *client = data;
+ int len, len_check;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ if (cond & (G_IO_HUP | G_IO_ERR)) {
+ debug("Unix client disconnected");
+ device_set_state(client->dev, STATE_CONNECTED);
+ goto failed;
+ }
+
+ memset(buf, 0, sizeof(buf));
+
+ len = recv(client->sock, buf, sizeof(buf), 0);
+ if (len < 0) {
+ error("recv: %s (%d)", strerror(errno), errno);
+ goto failed;
+ }
+
+ len_check = pkt->length + sizeof(struct ipc_packet);
+ if (len != len_check) {
+ error("Packet lenght doesn't match");
+ goto failed;
+ }
+
+ switch (pkt->type) {
+ case PKT_TYPE_CFG_REQ:
+ info("Package PKT_TYPE_CFG_REQ:%u", pkt->role);
+ cfg_event(client, pkt, len);
+ break;
+ case PKT_TYPE_STATE_REQ:
+ info("Package PKT_TYPE_STATE_REQ");
+ state_event(client, pkt, len);
+ break;
+ case PKT_TYPE_CTL_REQ:
+ info("Package PKT_TYPE_CTL_REQ");
+ ctl_event(client, pkt, len);
+ break;
+ }
- unix_send_status(clisk, pkt);
+ return TRUE;
- g_free(pkt);
+failed:
+ clients = g_slist_remove(clients, client);
+ client_free(client);
+ return FALSE;
}
-static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data)
+static gboolean server_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
{
struct sockaddr_un addr;
socklen_t addrlen;
- struct ipc_packet *pkt;
- int sk, clisk, len;
-
- debug("chan %p cond %td data %p", chan, cond, data);
+ int sk, cli_sk;
+ struct unix_client *client;
+ GIOChannel *io;
if (cond & G_IO_NVAL)
return FALSE;
@@ -135,32 +220,22 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data)
memset(&addr, 0, sizeof(addr));
addrlen = sizeof(addr);
- clisk = accept(sk, (struct sockaddr *) &addr, &addrlen);
- if (clisk < 0) {
+ cli_sk = accept(sk, (struct sockaddr *) &addr, &addrlen);
+ if (cli_sk < 0) {
error("accept: %s (%d)", strerror(errno), errno);
return TRUE;
}
- len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg);
- pkt = g_malloc0(len);
- len = recv(clisk, pkt, len, 0);
+ debug("Accepted new client connection on unix socket");
- debug("path %s len %d", addr.sun_path + 1, len);
+ client = g_new(struct unix_client, 1);
+ client->sock = cli_sk;
+ clients = g_slist_append(clients, client);
- switch (pkt->type) {
- case PKT_TYPE_CFG_REQ:
- info("Package PKT_TYPE_CFG_REQ:%u", pkt->role);
- cfg_event(clisk, pkt);
- break;
- case PKT_TYPE_STATE_REQ:
- info("Package PKT_TYPE_STATE_REQ");
- state_event(clisk, pkt);
- break;
- case PKT_TYPE_CTL_REQ:
- info("Package PKT_TYPE_CTL_REQ");
- ctl_event(clisk, pkt);
- break;
- }
+ io = g_io_channel_unix_new(cli_sk);
+ g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ client_cb, client);
+ g_io_channel_unref(io);
return TRUE;
}
@@ -194,10 +269,8 @@ int unix_init(void)
listen(sk, 1);
io = g_io_channel_unix_new(sk);
-
g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- unix_event, NULL);
-
+ server_cb, NULL);
g_io_channel_unref(io);
info("Unix socket created: %d", sk);
@@ -207,59 +280,77 @@ int unix_init(void)
void unix_exit(void)
{
+ g_slist_foreach(clients, (GFunc) client_free, NULL);
+ g_slist_free(clients);
close(unix_sock);
unix_sock = -1;
}
-int unix_send_cfg(int sock, struct ipc_packet *pkt)
+int unix_send_cfg(int sock, struct ipc_data_cfg *cfg)
{
- struct ipc_data_cfg *cfg = (struct ipc_data_cfg *) pkt->data;
- int len;
+ char buf[IPC_MTU];
+ struct ipc_packet *pkt = (void *) buf;
+ int len, codec_len;
- info("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u, sample_size=%u,"
- "rate=%u", cfg->fd, cfg->fd_opt, cfg->channels,
- cfg->pkt_len, cfg->sample_size, cfg->rate);
+ memset(buf, 0, sizeof(buf));
pkt->type = PKT_TYPE_CFG_RSP;
- pkt->length = sizeof(struct ipc_data_cfg);
+
+ if (!cfg) {
+ pkt->error = EINVAL;
+ len = send(sock, pkt, sizeof(struct ipc_packet), 0);
+ if (len < 0)
+ error("send: %s (%d)", strerror(errno), errno);
+ return len;
+ }
+
+ debug("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u,"
+ "sample_size=%u, rate=%u", cfg->fd, cfg->fd_opt,
+ cfg->channels, cfg->pkt_len, cfg->sample_size, cfg->rate);
+
+ if (cfg->codec == CFG_CODEC_SBC)
+ codec_len = sizeof(struct ipc_codec_sbc);
+ else
+ codec_len = 0;
+
pkt->error = PKT_ERROR_NONE;
+ pkt->length = sizeof(struct ipc_data_cfg) + codec_len;
+ memcpy(pkt->data, cfg, pkt->length);
- len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg);
+ len = sizeof(struct ipc_packet) + pkt->length;
len = send(sock, pkt, len, 0);
if (len < 0)
- info("Error %s(%d)", strerror(errno), errno);
+ error("Error %s(%d)", strerror(errno), errno);
- info("%d bytes sent", len);
+ debug("%d bytes sent", len);
if (cfg->fd != -1) {
len = unix_sendmsg_fd(sock, cfg->fd, pkt);
if (len < 0)
- info("Error %s(%d)", strerror(errno), errno);
- info("%d bytes sent", len);
+ error("Error %s(%d)", strerror(errno), errno);
+ debug("%d bytes sent", len);
}
- g_free(pkt);
return 0;
}
-int unix_send_status(int sock, struct ipc_packet *pkt)
+static int unix_send_state(int sock, struct ipc_packet *pkt)
{
struct ipc_data_state *state = (struct ipc_data_state *) pkt->data;
int len;
info("status=%u", state->state);
- pkt->type = PKT_TYPE_CFG_RSP;
+ pkt->type = PKT_TYPE_STATE_RSP;
pkt->length = sizeof(struct ipc_data_state);
pkt->error = PKT_ERROR_NONE;
len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_state);
len = send(sock, pkt, len, 0);
if (len < 0)
- info("Error %s(%d)", strerror(errno), errno);
+ error("Error %s(%d)", strerror(errno), errno);
- info("%d bytes sent", len);
+ debug("%d bytes sent", len);
- g_free(pkt);
return 0;
}