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.c623
1 files changed, 0 insertions, 623 deletions
diff --git a/alsa/pcm_a2dp.c b/alsa/pcm_a2dp.c
deleted file mode 100644
index fef13941..00000000
--- a/alsa/pcm_a2dp.c
+++ /dev/null
@@ -1,623 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2004-2006 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <errno.h>
-#include <malloc.h>
-#include <signal.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/rfcomm.h>
-
-#include <alsa/asoundlib.h>
-#include <alsa/pcm_external.h>
-
-#include "sbc.h"
-
-//#define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg)
-#define DBG(D...)
-
-static void a2dp_init(void) __attribute__ ((constructor));
-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;
- int sk;
- sbc_t sbc;
- snd_pcm_sframes_t num;
- unsigned char buf[1024];
- unsigned int len;
- 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;
-
- DBG("a2dp %p", a2dp);
-
- a2dp->len = 0;
-
- return 0;
-}
-
-static int a2dp_stop(snd_pcm_ioplug_t *io)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
-
- DBG("a2dp %p", a2dp);
-
- a2dp->len = 0;
-
- return 0;
-}
-
-static snd_pcm_sframes_t a2dp_pointer(snd_pcm_ioplug_t *io)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
-
- return a2dp->num;
-}
-
-static snd_pcm_sframes_t a2dp_transfer(snd_pcm_ioplug_t *io,
- const snd_pcm_channel_area_t *areas,
- snd_pcm_uframes_t offset, snd_pcm_uframes_t size)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
- unsigned char *buf;
- int err, len;
-
- buf = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8;
-
- len = sbc_encode(&a2dp->sbc, buf, size * a2dp->frame_bytes);
- if (len <= 0)
- return len;
-
- if (a2dp->len + a2dp->sbc.len > sizeof(a2dp->buf)) {
- err = 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;
-
- return len / a2dp->frame_bytes;
-}
-
-static int a2dp_close(snd_pcm_ioplug_t *io)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
-
- DBG("a2dp %p", a2dp);
-
- a2dp->len = 0;
-
- a2dp_put(a2dp);
-
- return 0;
-}
-
-static int a2dp_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
- unsigned int period_bytes;
-
- DBG("a2dp %p", a2dp);
-
- a2dp->frame_bytes = (snd_pcm_format_physical_width(io->format) * io->channels) / 8;
-
- period_bytes = io->period_size * a2dp->frame_bytes;
-
- DBG("format %s rate %d channels %d", snd_pcm_format_name(io->format),
- io->rate, io->channels);
-
- DBG("frame_bytes %d period_bytes %d period_size %ld buffer_size %ld",
- a2dp->frame_bytes, period_bytes, io->period_size, io->buffer_size);
-
- return 0;
-}
-
-static int a2dp_prepare(snd_pcm_ioplug_t *io)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
-
- DBG("a2dp %p", a2dp);
-
- a2dp->len = 0;
-
- a2dp->num = 0;
-
- a2dp->sbc.rate = io->rate;
- a2dp->sbc.channels = io->channels;
-
- return 0;
-}
-
-static int a2dp_drain(snd_pcm_ioplug_t *io)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
-
- DBG("a2dp %p", a2dp);
-
- a2dp->len = 0;
-
- return 0;
-}
-
-static int a2dp_descriptors_count(snd_pcm_ioplug_t *io)
-{
- snd_pcm_a2dp_t *a2dp = io->private_data;
-
- 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 (space < 1) {
- SNDERR("Can't fill in descriptors");
- return 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_descriptors_count = a2dp_descriptors_count,
- .poll_descriptors = a2dp_descriptors,
- .poll_revents = a2dp_poll,
-};
-
-static int a2dp_connect(snd_pcm_a2dp_t *a2dp)
-{
- struct sockaddr_rc addr;
- socklen_t len;
- int sk;
-
- DBG("a2dp %p", a2dp);
-
- sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
- if (sk < 0)
- return -errno;
-
- memset(&addr, 0, sizeof(addr));
- addr.rc_family = AF_BLUETOOTH;
- bacpy(&addr.rc_bdaddr, &a2dp->src);
-
- if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- close(sk);
- return -errno;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.rc_family = AF_BLUETOOTH;
- bacpy(&addr.rc_bdaddr, &a2dp->dst);
- addr.rc_channel = 1;
-
- if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- close(sk);
- return -errno;
- }
-
- memset(&addr, 0, sizeof(addr));
- len = sizeof(addr);
-
- if (getsockname(sk, (struct sockaddr *) &addr, &len) < 0) {
- close(sk);
- return -errno;
- }
-
- bacpy(&a2dp->src, &addr.rc_bdaddr);
-
- fcntl(sk, F_SETFL, fcntl(sk, F_GETFL) | O_NONBLOCK);
-
- a2dp->sk = sk;
-
- return 0;
-}
-
-static int a2dp_constraint(snd_pcm_a2dp_t *a2dp)
-{
- snd_pcm_ioplug_t *io = &a2dp->io;
- snd_pcm_access_t access_list[] = {
- SND_PCM_ACCESS_RW_INTERLEAVED,
- SND_PCM_ACCESS_MMAP_INTERLEAVED,
- };
- unsigned int format[2], channel[2], rate[2];
- int err;
-
- DBG("a2dp %p", a2dp);
-
- err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, 2, access_list);
- if (err < 0)
- return err;
-
- format[0] = SND_PCM_FORMAT_S16_LE;
-
- err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT, 1, format);
- if (err < 0)
- return err;
-
- channel[0] = 1;
- channel[1] = 2;
-
- err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_CHANNELS, 2, channel);
- if (err < 0)
- return err;
-
- rate[0] = 44100;
- rate[1] = 48000;
-
- err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_RATE, 2, rate);
- if (err < 0)
- return err;
-
- err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, 8192, 8192);
- if (err < 0)
- return err;
-
- err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, 2, 2);
- if (err < 0)
- return err;
-
- return 0;
-}
-
-#define MAX_CONNECTIONS 10
-
-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 > fileno(stderr))
- close(a2dp->sk);
-
- sbc_finish(&a2dp->sbc);
-
- free(a2dp);
-}
-
-static void a2dp_timer(snd_async_handler_t *async)
-{
- snd_timer_t *handle = snd_async_handler_get_timer(async);
- snd_timer_read_t tr;
- int i, ticks = 0;
-
- 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)
-{
- 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 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) {
- connections[i] = NULL;
- a2dp_free(a2dp);
- }
- }
-
- a2dp_unlock();
-}
-
-SND_PCM_PLUGIN_DEFINE_FUNC(a2dp)
-{
- snd_pcm_a2dp_t *a2dp = NULL;
- snd_config_iterator_t i, next;
- bdaddr_t src, dst;
- int err, n, pos = -1;
-
- DBG("name %s mode %d", name, mode);
-
- bacpy(&src, BDADDR_ANY);
- bacpy(&dst, BDADDR_ANY);
-
- snd_config_for_each(i, next, conf) {
- snd_config_t *n = snd_config_iterator_entry(i);
- const char *id, *addr;
-
- if (snd_config_get_id(n, &id) < 0)
- continue;
-
- if (!strcmp(id, "comment") || !strcmp(id, "type"))
- continue;
-
- if (!strcmp(id, "bdaddr") || !strcmp(id, "dst")) {
- if (snd_config_get_string(n, &addr) < 0) {
- SNDERR("Invalid type for %s", id);
- return -EINVAL;
- }
- str2ba(addr, &dst);
- continue;
- }
-
- if (!strcmp(id, "local") || !strcmp(id, "src")) {
- if (snd_config_get_string(n, &addr) < 0) {
- SNDERR("Invalid type for %s", id);
- return -EINVAL;
- }
- str2ba(addr, &src);
- continue;
- }
-
- SNDERR("Unknown field %s", id);
- 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_get(a2dp);
- break;
- }
- } else if (pos < 0)
- pos = n;
- }
-
- if (!a2dp) {
- if (pos < 0) {
- SNDERR("Too many connections");
- return -ENOMEM;
- }
-
- a2dp = a2dp_alloc();
- if (!a2dp) {
- SNDERR("Can't allocate");
- return -ENOMEM;
- }
-
- connections[pos] = a2dp;
-
- 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("Can't connect");
- goto error;
- }
-
- a2dp->state = BT_CONNECTED;
- }
-
- 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);
- if (err < 0)
- goto error;
-
- err = a2dp_constraint(a2dp);
- if (err < 0) {
- snd_pcm_ioplug_delete(&a2dp->io);
- goto error;
- }
-
- *pcmp = a2dp->io.pcm;
- return 0;
-
-error:
- a2dp_put(a2dp);
-
- return err;
-}
-
-SND_PCM_PLUGIN_SYMBOL(a2dp);