diff options
Diffstat (limited to 'alsa/pcm_a2dp.c')
| -rw-r--r-- | alsa/pcm_a2dp.c | 623 | 
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(¶ms); - -	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); | 
