summaryrefslogtreecommitdiffstats
path: root/alsa/pcm_a2dp.c
diff options
context:
space:
mode:
Diffstat (limited to 'alsa/pcm_a2dp.c')
-rw-r--r--alsa/pcm_a2dp.c267
1 files changed, 203 insertions, 64 deletions
diff --git a/alsa/pcm_a2dp.c b/alsa/pcm_a2dp.c
index fcacce75..f5dff96a 100644
--- a/alsa/pcm_a2dp.c
+++ b/alsa/pcm_a2dp.c
@@ -29,7 +29,6 @@
#include <errno.h>
#include <malloc.h>
#include <signal.h>
-#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@@ -50,6 +49,7 @@ static void a2dp_exit(void) __attribute__ ((destructor));
typedef struct snd_pcm_a2dp {
snd_pcm_ioplug_t io;
int refcnt;
+ int timeout;
unsigned long state;
bdaddr_t src;
bdaddr_t dst;
@@ -61,6 +61,20 @@ typedef struct snd_pcm_a2dp {
unsigned int frame_bytes;
} snd_pcm_a2dp_t;
+static void inline a2dp_get(snd_pcm_a2dp_t *a2dp)
+{
+ a2dp->refcnt++;
+ a2dp->timeout = 0;
+}
+
+static void inline a2dp_put(snd_pcm_a2dp_t *a2dp)
+{
+ a2dp->refcnt--;
+
+ if (a2dp->refcnt <= 0)
+ a2dp->timeout = 2;
+}
+
static int a2dp_start(snd_pcm_ioplug_t *io)
{
snd_pcm_a2dp_t *a2dp = io->private_data;
@@ -100,20 +114,18 @@ static snd_pcm_sframes_t a2dp_transfer(snd_pcm_ioplug_t *io,
buf = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8;
- size *= a2dp->frame_bytes;
-
- len = sbc_encode(&a2dp->sbc, buf, size);
+ len = sbc_encode(&a2dp->sbc, buf, size * a2dp->frame_bytes);
if (len <= 0)
return len;
- memcpy(a2dp->buf + a2dp->len, a2dp->sbc.data, a2dp->sbc.len);
- a2dp->len += a2dp->sbc.len;
-
- if (a2dp->len > 700) {
+ if (a2dp->len + a2dp->sbc.len > sizeof(a2dp->buf)) {
write(a2dp->sk, a2dp->buf, a2dp->len);
a2dp->len = 0;
}
+ memcpy(a2dp->buf + a2dp->len, a2dp->sbc.data, a2dp->sbc.len);
+ a2dp->len += a2dp->sbc.len;
+
if (a2dp->state == BT_CONNECTED)
a2dp->num += len / a2dp->frame_bytes;
@@ -128,7 +140,7 @@ static int a2dp_close(snd_pcm_ioplug_t *io)
a2dp->len = 0;
- a2dp->refcnt--;
+ a2dp_put(a2dp);
return 0;
}
@@ -180,40 +192,64 @@ static int a2dp_drain(snd_pcm_ioplug_t *io)
return 0;
}
-static int a2dp_poll(snd_pcm_ioplug_t *io, struct pollfd *ufds,
- unsigned int nfds, unsigned short *revents)
+static int a2dp_descriptors_count(snd_pcm_ioplug_t *io)
{
snd_pcm_a2dp_t *a2dp = io->private_data;
- struct timeval tv;
- *revents = ufds[0].revents;
+ if (a2dp->state == BT_CLOSED)
+ return 0;
+
+ return 1;
+}
+
+static int a2dp_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfds, unsigned int space)
+{
+ snd_pcm_a2dp_t *a2dp = io->private_data;
if (a2dp->state == BT_CLOSED)
return 0;
- if (ufds[0].revents & POLLHUP) {
- a2dp->state = BT_CLOSED;
- a2dp->io.poll_fd = -1;
- a2dp->io.poll_events = POLLIN;
- snd_pcm_ioplug_reinit_status(&a2dp->io);
+ if (space < 1) {
+ SNDERR("Can't fill in descriptors");
+ return 0;
}
- if (gettimeofday(&tv, NULL) < 0)
+ pfds[0].fd = a2dp->sk;
+ pfds[0].events = POLLOUT;
+
+ return 1;
+}
+
+static int a2dp_poll(snd_pcm_ioplug_t *io, struct pollfd *pfds,
+ unsigned int nfds, unsigned short *revents)
+{
+ snd_pcm_a2dp_t *a2dp = io->private_data;
+
+ *revents = pfds[0].revents;
+
+ if (a2dp->state == BT_CLOSED)
return 0;
+ if (pfds[0].revents & POLLHUP) {
+ a2dp->state = BT_CLOSED;
+ snd_pcm_ioplug_reinit_status(&a2dp->io);
+ }
+
return 0;
}
static snd_pcm_ioplug_callback_t a2dp_callback = {
- .start = a2dp_start,
- .stop = a2dp_stop,
- .pointer = a2dp_pointer,
- .transfer = a2dp_transfer,
- .close = a2dp_close,
- .hw_params = a2dp_params,
- .prepare = a2dp_prepare,
- .drain = a2dp_drain,
- .poll_revents = a2dp_poll,
+ .start = a2dp_start,
+ .stop = a2dp_stop,
+ .pointer = a2dp_pointer,
+ .transfer = a2dp_transfer,
+ .close = a2dp_close,
+ .hw_params = a2dp_params,
+ .prepare = a2dp_prepare,
+ .drain = a2dp_drain,
+ .poll_descriptors_count = a2dp_descriptors_count,
+ .poll_descriptors = a2dp_descriptors,
+ .poll_revents = a2dp_poll,
};
static int a2dp_connect(snd_pcm_a2dp_t *a2dp)
@@ -257,6 +293,8 @@ static int a2dp_connect(snd_pcm_a2dp_t *a2dp)
bacpy(&a2dp->src, &addr.rc_bdaddr);
+ fcntl(sk, F_SETFL, fcntl(sk, F_GETFL) | O_NONBLOCK);
+
a2dp->sk = sk;
return 0;
@@ -298,7 +336,7 @@ static int a2dp_constraint(snd_pcm_a2dp_t *a2dp)
if (err < 0)
return err;
- err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, 2048, 2048);
+ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, 8192, 8192);
if (err < 0)
return err;
@@ -306,8 +344,6 @@ static int a2dp_constraint(snd_pcm_a2dp_t *a2dp)
if (err < 0)
return err;
- sbc_init(&a2dp->sbc, SBC_NULL);
-
return 0;
}
@@ -315,9 +351,45 @@ static int a2dp_constraint(snd_pcm_a2dp_t *a2dp)
static snd_pcm_a2dp_t *connections[MAX_CONNECTIONS];
+static snd_timer_t *timer = NULL;
+
+static volatile sig_atomic_t __locked = 0;
+
+static inline void a2dp_lock(void)
+{
+ while (__locked)
+ usleep(100);
+
+ __locked = 1;
+}
+
+static inline void a2dp_unlock(void)
+{
+ __locked = 0;
+}
+
+static inline snd_pcm_a2dp_t *a2dp_alloc(void)
+{
+ snd_pcm_a2dp_t *a2dp;
+
+ a2dp = malloc(sizeof(*a2dp));
+ if (!a2dp)
+ return NULL;
+
+ memset(a2dp, 0, sizeof(*a2dp));
+
+ a2dp->refcnt = 1;
+
+ a2dp->state = BT_OPEN;
+
+ sbc_init(&a2dp->sbc, SBC_NULL);
+
+ return a2dp;
+}
+
static inline void a2dp_free(snd_pcm_a2dp_t *a2dp)
{
- if (a2dp->sk >= 0)
+ if (a2dp->sk > fileno(stderr))
close(a2dp->sk);
sbc_finish(&a2dp->sbc);
@@ -325,37 +397,111 @@ static inline void a2dp_free(snd_pcm_a2dp_t *a2dp)
free(a2dp);
}
-static inline void a2dp_delete(snd_pcm_a2dp_t *a2dp)
+static void a2dp_timer(snd_async_handler_t *async)
{
- int i;
+ snd_timer_t *handle = snd_async_handler_get_timer(async);
+ snd_timer_read_t tr;
+ int i, ticks = 0;
- for (i = 0; i < MAX_CONNECTIONS; i++)
- if (connections[i] == a2dp) {
- connections[i] = NULL;
- a2dp_free(a2dp);
+ while (snd_timer_read(handle, &tr, sizeof(tr)) == sizeof(tr))
+ ticks += tr.ticks;
+
+ a2dp_lock();
+
+ for (i = 0; i < MAX_CONNECTIONS; i++) {
+ snd_pcm_a2dp_t *a2dp = connections[i];
+
+ if (a2dp && a2dp->refcnt <= 0) {
+ a2dp->timeout = ((a2dp->timeout * 1000) - ticks) / 1000;
+ if (a2dp->timeout <= 0) {
+ connections[i] = NULL;
+ a2dp_free(a2dp);
+ }
}
+ }
+
+ a2dp_unlock();
}
static void a2dp_init(void)
{
- int i;
+ snd_async_handler_t *async;
+ snd_timer_info_t *info;
+ snd_timer_params_t *params;
+ long resolution;
+ char timername[64];
+ int err, i;
+
+ a2dp_lock();
for (i = 0; i < MAX_CONNECTIONS; i++)
connections[i] = NULL;
+
+ a2dp_unlock();
+
+ snd_timer_info_alloca(&info);
+ snd_timer_params_alloca(&params);
+
+ sprintf(timername, "hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i",
+ SND_TIMER_CLASS_GLOBAL, SND_TIMER_CLASS_NONE, 0,
+ SND_TIMER_GLOBAL_SYSTEM, 0);
+
+ err = snd_timer_open(&timer, timername, SND_TIMER_OPEN_NONBLOCK);
+ if (err < 0) {
+ SNDERR("Can't open global timer");
+ return;
+ }
+
+ err = snd_timer_info(timer, info);
+ if (err < 0) {
+ SNDERR("Can't get global timer info");
+ return;
+ }
+
+ snd_timer_params_set_auto_start(params, 1);
+
+ resolution = snd_timer_info_get_resolution(info);
+ snd_timer_params_set_ticks(params, 1000000000 / resolution);
+ if (snd_timer_params_get_ticks(params) < 1)
+ snd_timer_params_set_ticks(params, 1);
+
+ err = snd_timer_params(timer, params);
+ if (err < 0) {
+ SNDERR("Can't set global timer parameters");
+ snd_timer_close(timer);
+ return;
+ }
+
+ err = snd_async_add_timer_handler(&async, timer, a2dp_timer, NULL);
+ if (err < 0) {
+ SNDERR("Can't create global async callback");
+ snd_timer_close(timer);
+ return;
+ }
+
+ err = snd_timer_start(timer);
}
static void a2dp_exit(void)
{
- int i;
+ int err, i;
+
+ err = snd_timer_stop(timer);
+
+ err = snd_timer_close(timer);
+
+ a2dp_lock();
for (i = 0; i < MAX_CONNECTIONS; i++) {
snd_pcm_a2dp_t *a2dp = connections[i];
- if (!a2dp)
- continue;
- connections[i] = NULL;
- a2dp_free(a2dp);
+ if (a2dp) {
+ connections[i] = NULL;
+ a2dp_free(a2dp);
+ }
}
+
+ a2dp_unlock();
}
SND_PCM_PLUGIN_DEFINE_FUNC(a2dp)
@@ -402,13 +548,15 @@ SND_PCM_PLUGIN_DEFINE_FUNC(a2dp)
return -EINVAL;
}
+ a2dp_lock();
+
for (n = 0; n < MAX_CONNECTIONS; n++) {
if (connections[n]) {
if (!bacmp(&connections[n]->dst, &dst) &&
(!bacmp(&connections[n]->src, &src) ||
!bacmp(&src, BDADDR_ANY))) {
a2dp = connections[n];
- a2dp->refcnt++;
+ a2dp_get(a2dp);
break;
}
} else if (pos < 0)
@@ -421,42 +569,36 @@ SND_PCM_PLUGIN_DEFINE_FUNC(a2dp)
return -ENOMEM;
}
- a2dp = malloc(sizeof(*a2dp));
+ a2dp = a2dp_alloc();
if (!a2dp) {
- SNDERR("Cannot allocate");
+ SNDERR("Can't allocate");
return -ENOMEM;
}
- memset(a2dp, 0, sizeof(*a2dp));
-
connections[pos] = a2dp;
- a2dp->refcnt = 1;
a2dp->state = BT_CONNECT;
bacpy(&a2dp->src, &src);
bacpy(&a2dp->dst, &dst);
}
+ a2dp_unlock();
+
if (a2dp->state != BT_CONNECTED) {
err = a2dp_connect(a2dp);
if (err < 0) {
- SNDERR("Cannot connect");
+ SNDERR("Can't connect");
goto error;
}
a2dp->state = BT_CONNECTED;
}
-#ifdef SND_PCM_IOPLUG_VERSION
- a2dp->io.version = SND_PCM_IOPLUG_VERSION;
-#endif
-
- a2dp->io.name = "Bluetooth Advanced Audio Distribution";
- a2dp->io.poll_fd = a2dp->sk;
- a2dp->io.poll_events = POLLOUT;
- a2dp->io.mmap_rw = 0;
- a2dp->io.callback = &a2dp_callback;
+ a2dp->io.version = SND_PCM_IOPLUG_VERSION;
+ a2dp->io.name = "Bluetooth Advanced Audio Distribution";
+ a2dp->io.mmap_rw = 0;
+ a2dp->io.callback = &a2dp_callback;
a2dp->io.private_data = a2dp;
err = snd_pcm_ioplug_create(&a2dp->io, name, stream, mode);
@@ -473,10 +615,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(a2dp)
return 0;
error:
- a2dp->refcnt--;
-
- if (!a2dp->refcnt)
- a2dp_delete(a2dp);
+ a2dp_put(a2dp);
return err;
}