summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
Diffstat (limited to 'audio')
-rw-r--r--audio/ipc.h10
-rw-r--r--audio/pcm_bluetooth.c317
-rw-r--r--audio/unix.c34
3 files changed, 307 insertions, 54 deletions
diff --git a/audio/ipc.h b/audio/ipc.h
index e6859a50..0cd9e620 100644
--- a/audio/ipc.h
+++ b/audio/ipc.h
@@ -40,10 +40,10 @@
/* Packet types */
#define PKT_TYPE_CFG_REQ 0
#define PKT_TYPE_CFG_RSP 1
-#define PKT_TYPE_STATUS_REQ 3
-#define PKT_TYPE_STATUS_RSP 4
-#define PKT_TYPE_CTL_REQ 5
-#define PKT_TYPE_CTL_RSP 6
+#define PKT_TYPE_STATUS_REQ 2
+#define PKT_TYPE_STATUS_RSP 3
+#define PKT_TYPE_CTL_REQ 4
+#define PKT_TYPE_CTL_RSP 5
/* Errors codes */
#define PKT_ERROR_NONE 0
@@ -68,6 +68,8 @@ struct ipc_data_cfg {
uint8_t encoding; /* Stream encoding */
uint8_t bitpool; /* Encoding bitpool */
uint8_t channels; /* Number of audio channel */
+ uint8_t pkt_len; /* Stream packet length */
+ uint8_t sample_size; /* Sample size in bytes */
uint16_t rate; /* Stream sample rate */
} __attribute__ ((packed));
diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c
index 838dcd39..29583940 100644
--- a/audio/pcm_bluetooth.c
+++ b/audio/pcm_bluetooth.c
@@ -31,14 +31,28 @@
#include <alsa/asoundlib.h>
#include <alsa/pcm_external.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sco.h>
+
#include "ipc.h"
#define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg)
+#ifndef SCO_TXBUFS
+#define SCO_TXBUFS 0x03
+#endif
+
+#ifndef SCO_RXBUFS
+#define SCO_RXBUFS 0x04
+#endif
+
struct bluetooth_data {
snd_pcm_ioplug_t io;
snd_pcm_sframes_t hw_ptr;
- int sock;
+ struct ipc_data_cfg cfg; /* Bluetooth device config */
+ int sock; /* Daemon unix socket */
+ uint8_t *buffer; /* Transfer buffer */
+ uint8_t count; /* Transfer buffer counter */
};
static int bluetooth_start(snd_pcm_ioplug_t *io)
@@ -59,9 +73,9 @@ static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io)
{
struct bluetooth_data *data = io->private_data;
- //DBG("io %p", io);
+ DBG("io %p", io);
- //DBG("hw_ptr=%lu", data->hw_ptr);
+ DBG("hw_ptr=%lu", data->hw_ptr);
return data->hw_ptr;
}
@@ -77,16 +91,171 @@ static int bluetooth_close(snd_pcm_ioplug_t *io)
return 0;
}
+
+static int bluetooth_prepare(snd_pcm_ioplug_t *io)
+{
+ struct bluetooth_data *data = io->private_data;
+
+ DBG("Preparing with io->period_size = %lu, io->buffer_size = %lu", io->period_size, io->buffer_size);
+
+ if (io->stream == SND_PCM_STREAM_PLAYBACK) {
+ /* If not null for playback, xmms doesn't display time correctly */
+ data->hw_ptr = 0;
+ }
+ else {
+ /* ALSA library is really picky on the fact hw_ptr is not null. If it is, capture won't start */
+ data->hw_ptr = io->period_size;
+ }
+ return 0;
+}
+
+static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params)
+{
+ struct bluetooth_data *data = io->private_data;
+ struct ipc_data_cfg cfg = data->cfg;
+ uint32_t period_count = io->buffer_size / io->period_size;
+
+ DBG("period_count = %d", period_count);
+
+ if(setsockopt(cfg.fd, SOL_SCO,
+ io->stream == SND_PCM_STREAM_PLAYBACK ? SCO_TXBUFS : SCO_RXBUFS,
+ &period_count,
+ sizeof(period_count)) == 0) {
+ return 0;
+ } else if(setsockopt(cfg.fd, SOL_SCO,
+ io->stream == SND_PCM_STREAM_PLAYBACK ? SO_SNDBUF : SO_RCVBUF,
+ &period_count,
+ sizeof(period_count)) == 0) {
+ return 0;
+ } else {
+ SNDERR("Unable to set number of SCO buffers : please upgrade your Kernel !");
+ return -EINVAL;
+ }
+}
+
+static snd_pcm_sframes_t bluetooth_read(snd_pcm_ioplug_t *io,
+ const snd_pcm_channel_area_t *areas,
+ snd_pcm_uframes_t offset,
+ snd_pcm_uframes_t size)
+{
+ struct bluetooth_data *data = io->private_data;
+ struct ipc_data_cfg cfg = data->cfg;
+
+ snd_pcm_sframes_t ret = 0;
+
+ DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu, io->nonblock=%u",
+ areas->step, areas->first, offset, size, io->nonblock);
+
+ if (data->count == 0) {
+ int nrecv;
+
+ nrecv = recv(cfg.fd, data->buffer, cfg.pkt_len,
+ MSG_WAITALL | (io->nonblock ? MSG_DONTWAIT : 0 ));
+
+ if (nrecv == cfg.pkt_len) {
+ ret = 0;
+ /* Increment hardware transmition pointer */
+ data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % io->buffer_size;
+ }
+ else if (nrecv > 0) {
+ ret = -EIO;
+ SNDERR(strerror(-ret));
+ }
+ else if (nrecv == -1 && errno == EAGAIN) {
+ ret = -EAGAIN;
+ }
+ else { /* nrecv < 0 */
+ /* EPIPE means device underrun in ALSA world. But we mean we lost contact
+ with server, so we have to find another error code */
+ ret = (errno == EPIPE ? -EIO : -errno);
+ SYSERR("Lost contact with headsetd");
+ }
+ }
+ if(ret == 0) { /* Still ok, proceed */
+ snd_pcm_uframes_t frames_to_write;
+ unsigned char *buff;
+
+ buff = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8;
+
+ if((data->count + cfg.sample_size * size) <= cfg.pkt_len)
+ frames_to_write = size;
+ else
+ frames_to_write = (cfg.pkt_len - data->count) / cfg.sample_size;
+
+ memcpy(buff, data->buffer + data->count, areas->step / 8 * frames_to_write);
+ data->count += (areas->step / 8 * frames_to_write);
+ data->count %= cfg.pkt_len;
+ /* Return written frames count */
+ ret = frames_to_write;
+ }
+
+ DBG("returning %d", (int)ret);
+ return ret;
+}
+
+static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io,
+ const snd_pcm_channel_area_t *areas,
+ snd_pcm_uframes_t offset,
+ snd_pcm_uframes_t size)
+{
+ struct bluetooth_data *data = io->private_data;
+ struct ipc_data_cfg cfg = data->cfg;
+ snd_pcm_sframes_t ret = 0;
+ snd_pcm_uframes_t frames_to_read;
+ unsigned char *buff;
+
+ DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu, io->nonblock=%u",
+ areas->step, areas->first, offset, size, io->nonblock);
+
+ if ((data->count + cfg.sample_size * size) <= cfg.pkt_len)
+ frames_to_read = size;
+ else
+ frames_to_read = (cfg.pkt_len - data->count) / cfg.sample_size;
+
+ /* Ready for more data */
+ buff = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8;
+ memcpy(data->buffer + data->count, buff, areas->step / 8 * frames_to_read);
+
+ if ((data->count + areas->step / 8 * frames_to_read) == cfg.pkt_len) {
+ int rsend;
+ /* Actually send packet */
+ rsend = send(cfg.fd, data->buffer, cfg.pkt_len, io->nonblock ? MSG_DONTWAIT : 0);
+ if (rsend > 0) {
+ /* Reset count pointer */
+ data->count = 0;
+
+ /* Increment hardware transmition pointer */
+ data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % io->buffer_size;
+
+ ret = frames_to_read;
+ }
+ else {
+ /* EPIPE means device underrun in ALSA world. But we mean we lost contact
+ with server, so we have to find another error code */
+ ret = (errno == EPIPE ? -EIO : -errno);
+ if(errno == EPIPE)
+ SYSERR("Lost contact with headsetd");
+ }
+ }
+ else {
+ /* Remember we have some frame in the pipe now */
+ data->count += areas->step / 8 * frames_to_read;
+ /* Ask for more */
+ ret = frames_to_read;
+ }
+
+ DBG("returning %d", (int)ret);
+ return ret;
+}
+
static snd_pcm_ioplug_callback_t bluetooth_playback_callback = {
.start = bluetooth_start,
.stop = bluetooth_stop,
.pointer = bluetooth_pointer,
.close = bluetooth_close,
-#if 0
.hw_params = bluetooth_hw_params,
.prepare = bluetooth_prepare,
.transfer = bluetooth_write,
-#endif
};
static snd_pcm_ioplug_callback_t bluetooth_capture_callback = {
@@ -94,11 +263,9 @@ static snd_pcm_ioplug_callback_t bluetooth_capture_callback = {
.stop = bluetooth_stop,
.pointer = bluetooth_pointer,
.close = bluetooth_close,
-#if 0
.hw_params = bluetooth_hw_params,
.prepare = bluetooth_prepare,
.transfer = bluetooth_read,
-#endif
};
#define ARRAY_NELEMS(a) (sizeof((a)) / sizeof((a)[0]))
@@ -146,42 +313,66 @@ static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io)
return 0;
}
-SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)
+static int bluetooth_cfg(struct bluetooth_data *data)
{
- snd_config_iterator_t iter, next;
- struct bluetooth_data *data;
- struct sockaddr_un addr;
- unsigned int id;
- int sk, err;
-
- DBG("Bluetooth PCM plugin (%s)",
- stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture");
-
- snd_config_for_each(iter, next, conf) {
- snd_config_t *n = snd_config_iterator_entry(iter);
- const char *id;
+ struct ipc_packet pkt;
+ int res;
+
+ DBG("Sending PKT_TYPE_CFG_REQ...");
+ pkt.type = PKT_TYPE_CFG_REQ;
+ pkt.role = PKT_ROLE_NONE;
+ res = send(data->sock, &pkt, sizeof(struct ipc_packet), 0);
+ if (res < 0)
+ return errno;
+ DBG("OK - %d bytes sent", res);
+
+ DBG("Waiting for response...");
+ do {
+ int len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg);
+ struct ipc_packet *pkt_ptr;
+
+ pkt_ptr = malloc(sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg));
+ res = recv(data->sock, pkt_ptr, len, MSG_WAITALL | (data->io.nonblock ? MSG_DONTWAIT : 0 ));
+ } while ((res < 0) && (errno == EINTR));
+ if (res < 0)
+ return -errno;
+ DBG("OK - %d bytes received", res);
- if (snd_config_get_id(n, &id) < 0)
- continue;
+ if (pkt.type != PKT_TYPE_CFG_RSP) {
+ SNDERR("Unexpected packet type received: type = %d", pkt.type);
+ return -EINVAL;
+ }
- if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)
- continue;
+ if (pkt.error != PKT_ERROR_NONE) {
+ SNDERR("Error while configuring device: error = %d", pkt.error);
+ return pkt.error;
+ }
- if (strcmp(id, "bdaddr") == 0) {
- const char *str;
- if (snd_config_get_string(n, &str) < 0) {
- SNDERR("Invalid type for %s", id);
- return -EINVAL;
- }
- printf("bdaddr %s\n", str);
- continue;
- }
+ if (pkt.length != sizeof(struct ipc_data_cfg)) {
+ SNDERR("Error while configuring device: packet size doesn't match");
+ return -EINVAL;
+ }
- SNDERR("Unknown field %s", id);
+ memcpy(&data->cfg, &pkt.data, pkt.length);
+ if (data->cfg.fd == -1) {
+ SNDERR("Error while configuring device: could not acquire audio socket");
return -EINVAL;
}
+ DBG("Device configuration:");
+ DBG("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u, sample_size=%u, rate=%u",
+ data->cfg.fd, data->cfg.fd_opt, data->cfg.channels,
+ data->cfg.pkt_len, data->cfg.sample_size, data->cfg.rate);
+
+ return 0;
+}
+
+static int bluetooth_init(struct bluetooth_data *data)
+{
+ int sk, err, id;
+ struct sockaddr_un addr;
+
id = abs(getpid() * rand());
sk = socket(PF_LOCAL, SOCK_DGRAM, 0);
@@ -195,6 +386,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)
snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s/%d",
IPC_SOCKET_NAME, id);
+ DBG("Binding address: %s", addr.sun_path + 1);
if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
SNDERR("Can't bind socket");
close(sk);
@@ -205,6 +397,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", IPC_SOCKET_NAME);
+ DBG("Connecting to address: %s", addr.sun_path + 1);
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
SNDERR("Can't connect socket");
close(sk);
@@ -221,34 +414,62 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)
data->sock = sk;
- data->io.version = SND_PCM_IOPLUG_VERSION;
- data->io.name = "Bluetooth Audio";
- data->io.mmap_rw = 0; /* No direct mmap communication */
+ if ((err = bluetooth_cfg(data)) < 0)
+ return err;
+
+ data->buffer = malloc(data->cfg.pkt_len);
+
+ memset(data->buffer, 0, data->cfg.pkt_len);
- data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ?
+ return 0;
+}
+
+SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)
+{
+// snd_config_iterator_t iter, next;
+ struct bluetooth_data data;
+ int err;
+
+ DBG("Bluetooth PCM plugin blablabla (%s)",
+ stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture");
+
+// snd_config_for_each(iter, next, conf) {
+// }
+
+ DBG("Initing Bluetooth...");
+ err = bluetooth_init(&data);
+ if (err < 0)
+ goto error;
+ DBG("Done");
+
+ data.io.version = SND_PCM_IOPLUG_VERSION;
+ data.io.name = "Bluetooth Audio Device";
+ data.io.mmap_rw = 0; /* No direct mmap communication */
+
+ data.io.callback = stream == SND_PCM_STREAM_PLAYBACK ?
&bluetooth_playback_callback : &bluetooth_capture_callback;
- data->io.poll_fd = sk;
- data->io.poll_events = POLLIN;
- data->io.private_data = data;
+ data.io.poll_fd = data.cfg.fd;
+ data.io.poll_events = POLLIN;
+ data.io.private_data = &data;
- err = snd_pcm_ioplug_create(&data->io, name, stream, mode);
+ err = snd_pcm_ioplug_create(&data.io, name, stream, mode);
if (err < 0)
goto error;
- err = bluetooth_hw_constraint(&data->io);
+ err = bluetooth_hw_constraint(&data.io);
if (err < 0) {
- snd_pcm_ioplug_delete(&data->io);
- goto error;
+ snd_pcm_ioplug_delete(&data.io);
+ goto error;
}
- *pcmp = data->io.pcm;
+ *pcmp = data.io.pcm;
return 0;
error:
- close(sk);
+ close(data.sock);
- free(data);
+ free(&data);
return err;
}
diff --git a/audio/unix.c b/audio/unix.c
index ce2041b2..8cd1affc 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -48,7 +48,8 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data)
{
struct sockaddr_un addr;
socklen_t addrlen;
- unsigned char buf[128];
+ struct ipc_packet pkt;
+ struct ipc_data_cfg cfg;
int sk, len;
debug("chan %p cond %td data %p", chan, cond, data);
@@ -66,10 +67,37 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data)
memset(&addr, 0, sizeof(addr));
addrlen = sizeof(addr);
- len = recvfrom(sk, buf, sizeof(buf), 0, (struct sockaddr *) &addr, &addrlen);
+ len = recvfrom(sk, &pkt, sizeof(pkt), 0, (struct sockaddr *) &addr, &addrlen);
debug("path %s len %d", addr.sun_path + 1, len);
+ switch (pkt.type) {
+ case PKT_TYPE_CFG_REQ:
+ info("Package PKT_TYPE_CFG_REQ:%u", pkt.role);
+ struct ipc_data_cfg *cfg_ptr;
+
+ cfg.fd = -1;
+ cfg.fd_opt = CFG_FD_OPT_READWRITE;
+ cfg.encoding = 0;
+ cfg.bitpool = 0;
+ cfg.channels = 1;
+ cfg.pkt_len = 48;
+ cfg.sample_size = 2;
+ cfg.rate = 8000;
+
+ cfg_ptr = (struct ipc_data_cfg *) &pkt.data;
+ pkt.type = PKT_TYPE_CFG_RSP;
+ cfg_ptr[0] = cfg;
+ pkt.length = sizeof(struct ipc_data_cfg);
+ len = send(sk, &pkt, sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg), 0);
+ break;
+ case PKT_TYPE_STATUS_REQ:
+ info("Package PKT_TYPE_STATUS_REQ");
+ break;
+ case PKT_TYPE_CTL_REQ:
+ info("Package PKT_TYPE_CTL_REQ");
+ break;
+ }
return TRUE;
}
@@ -106,6 +134,8 @@ int unix_init(void)
g_io_channel_unref(io);
+ info("Unix socket created: %d", sk);
+
return 0;
}