diff options
Diffstat (limited to 'audio/unix.c')
-rw-r--r-- | audio/unix.c | 227 |
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; } |