blob: d3e457aed72640fe5880d70ba58f8c72375896e3 [file] [log] [blame]
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2004-2007 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 <sys/socket.h>
#include <sys/un.h>
#include <alsa/asoundlib.h>
#include <alsa/pcm_external.h>
#include "ipc.h"
#ifndef UNIX_PATH_MAX
#define UNIX_PATH_MAX 108
#endif
#define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg)
#define SOCKET_NAME "/org/bluez/audio"
struct bluetooth_data {
snd_pcm_ioplug_t io;
snd_pcm_sframes_t hw_ptr;
int sock;
};
static int bluetooth_start(snd_pcm_ioplug_t *io)
{
DBG("io %p", io);
return 0;
}
static int bluetooth_stop(snd_pcm_ioplug_t *io)
{
DBG("io %p", io);
return 0;
}
static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io)
{
struct bluetooth_data *data = io->private_data;
//DBG("io %p", io);
//DBG("hw_ptr=%lu", data->hw_ptr);
return data->hw_ptr;
}
static int bluetooth_close(snd_pcm_ioplug_t *io)
{
struct bluetooth_data *data = io->private_data;
DBG("io %p", io);
free(data);
return 0;
}
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 = {
.start = bluetooth_start,
.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]))
static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io)
{
snd_pcm_access_t access_list[] = {
SND_PCM_ACCESS_RW_INTERLEAVED,
/* Mmap access is really useless fo this driver, but we
* support it because some pieces of software out there
* insist on using it */
SND_PCM_ACCESS_MMAP_INTERLEAVED
};
unsigned int format_list[] = {
SND_PCM_FORMAT_S16_LE
};
int err;
err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,
ARRAY_NELEMS(access_list), access_list);
if (err < 0)
return err;
err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,
ARRAY_NELEMS(format_list), format_list);
if (err < 0)
return err;
err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, 1, 1);
if (err < 0)
return err;
err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, 8000, 8000);
if (err < 0)
return err;
err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, 48, 48);
if (err < 0)
return err;
err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, 2, 200);
if (err < 0)
return err;
return 0;
}
SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)
{
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;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)
continue;
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;
}
SNDERR("Unknown field %s", id);
return -EINVAL;
}
id = abs(getpid() * rand());
sk = socket(PF_LOCAL, SOCK_DGRAM, 0);
if (sk < 0) {
SNDERR("Can't open socket");
return -errno;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s/%d", SOCKET_NAME, id);
if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
SNDERR("Can't bind socket");
close(sk);
return -errno;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", SOCKET_NAME);
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
SNDERR("Can't connect socket");
close(sk);
return -errno;
}
data = malloc(sizeof(*data));
if (!data) {
close(sk);
return -ENOMEM;
}
memset(data, 0, sizeof(*data));
data->sock = sk;
data->io.version = SND_PCM_IOPLUG_VERSION;
data->io.name = "Bluetooth Audio";
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;
err = snd_pcm_ioplug_create(&data->io, name, stream, mode);
if (err < 0)
goto error;
err = bluetooth_hw_constraint(&data->io);
if (err < 0) {
snd_pcm_ioplug_delete(&data->io);
goto error;
}
*pcmp = data->io.pcm;
return 0;
error:
close(sk);
free(data);
return err;
}
SND_PCM_PLUGIN_SYMBOL(bluetooth);