summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuiz Augusto von Dentz <luiz.dentz@openbossa.org>2007-11-21 20:24:11 +0000
committerLuiz Augusto von Dentz <luiz.dentz@openbossa.org>2007-11-21 20:24:11 +0000
commitd4e24bf6a3d8af6479abce92fbbf1869a59669aa (patch)
tree8ba8c084bb1eb1a3e1dd598127ca33553dbc6d23
parent47e2c26cc95d761099c367593bbbd4bc581bf0ac (diff)
Integrate new ipc API implementation.
-rw-r--r--audio/Makefile.am14
-rw-r--r--audio/ctl_bluetooth.c134
-rw-r--r--audio/device.c2
-rw-r--r--audio/gsta2dpsink.c746
-rw-r--r--audio/gsta2dpsink.h8
-rw-r--r--audio/gstsbcenc.c276
-rw-r--r--audio/gstsbcenc.h4
-rw-r--r--audio/gstsbcparse.c49
-rw-r--r--audio/gstsbcutil.c174
-rw-r--r--audio/gstsbcutil.h9
-rw-r--r--audio/ipc.c119
-rw-r--r--audio/ipc.h367
-rw-r--r--audio/manager.c4
-rw-r--r--audio/pcm_bluetooth.c786
-rw-r--r--audio/unix.c433
15 files changed, 1843 insertions, 1282 deletions
diff --git a/audio/Makefile.am b/audio/Makefile.am
index 48c2a80f..b04fc302 100644
--- a/audio/Makefile.am
+++ b/audio/Makefile.am
@@ -3,7 +3,7 @@ if AUDIOSERVICE
if CONFIGFILES
confdir = $(sysconfdir)/bluetooth
-conf_DATA = audio.service
+conf_DATA = audio.service audio.conf
endif
servicedir = $(libdir)/bluetooth
@@ -11,7 +11,7 @@ servicedir = $(libdir)/bluetooth
service_PROGRAMS = bluetoothd-service-audio
bluetoothd_service_audio_SOURCES = main.c \
- manager.h manager.c headset.h headset.c ipc.h unix.h unix.c \
+ manager.h manager.c headset.h headset.c ipc.h ipc.c unix.h unix.c \
error.h error.c device.h device.c gateway.h gateway.c \
sink.c sink.h avdtp.c avdtp.h a2dp.c a2dp.h control.c control.h
@@ -23,12 +23,12 @@ alsadir = $(libdir)/alsa-lib
alsa_LTLIBRARIES = libasound_module_pcm_bluetooth.la libasound_module_ctl_bluetooth.la
-libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c ipc.h rtp.h
+libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c rtp.h ipc.h ipc.c
libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex [_]*snd_pcm_.*
libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @ALSA_LIBS@
libasound_module_pcm_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ @SBC_CFLAGS@
-libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c ipc.h
+libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c rtp.h ipc.h ipc.c
libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex [_]*snd_ctl_.*
libasound_module_ctl_bluetooth_la_LIBADD = @ALSA_LIBS@
libasound_module_ctl_bluetooth_la_CFLAGS = @ALSA_CFLAGS@
@@ -39,12 +39,13 @@ gstreamerdir = $(libdir)/gstreamer-0.10
gstreamer_LTLIBRARIES = libgstbluetooth.la
-libgstbluetooth_la_SOURCES = gstbluetooth.c ipc.h rtp.h \
+libgstbluetooth_la_SOURCES = gstbluetooth.c \
gstsbcenc.h gstsbcenc.c \
gstsbcdec.h gstsbcdec.c \
gstsbcparse.h gstsbcparse.c \
gsta2dpsink.h gsta2dpsink.c \
- gstsbcutil.h gstsbcutil.c
+ gstsbcutil.h gstsbcutil.c \
+ rtp.h ipc.h ipc.c
libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex gst_plugin_desc
libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10
libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@
@@ -58,3 +59,4 @@ INCLUDES = -I$(top_srcdir)/common
EXTRA_DIST = audio.service audio.conf audio-api.txt test-audio asound.conf
MAINTAINERCLEANFILES = Makefile.in
+
diff --git a/audio/ctl_bluetooth.c b/audio/ctl_bluetooth.c
index 9e1c320c..5c198b1a 100644
--- a/audio/ctl_bluetooth.c
+++ b/audio/ctl_bluetooth.c
@@ -65,7 +65,7 @@ static void bluetooth_exit(struct bluetooth_data *data)
return;
if (data->sock >= 0)
- close(data->sock);
+ bt_audio_service_close(data->sock);
free(data);
}
@@ -141,32 +141,49 @@ static int bluetooth_get_integer_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
}
static int bluetooth_send_ctl(struct bluetooth_data *data,
- struct ipc_packet *pkt, int len)
+ uint8_t mode, uint8_t key, struct bt_control_rsp *ctl_rsp)
{
int ret;
+ struct bt_control_req *ctl_req = (void *) ctl_rsp;
+ const char *type;
- ret = send(data->sock, pkt, len, MSG_NOSIGNAL);
+ memset(ctl_req, 0, BT_AUDIO_IPC_PACKET_SIZE);
+ ctl_req->h.msg_type = BT_CONTROL_REQ;
+ ctl_req->mode = mode;
+ ctl_req->key = key;
+
+ ret = send(data->sock, ctl_req, BT_AUDIO_IPC_PACKET_SIZE, MSG_NOSIGNAL);
if (ret <= 0) {
SYSERR("Unable to request new volume value to server");
return -errno;
}
- ret = recv(data->sock, pkt, len, 0);
+ ret = recv(data->sock, ctl_rsp, BT_AUDIO_IPC_PACKET_SIZE, 0);
if (ret <= 0) {
- SYSERR("Unable to receive new volume value from server");
+ SNDERR("Unable to receive new volume value from server");
return -errno;
}
- if(pkt->type != PKT_TYPE_CTL_RSP) {
- SNDERR("Unexpected packet type %d received", pkt->type);
+ type = bt_audio_strmsg(ctl_rsp->h.msg_type);
+ if (!type) {
+ SNDERR("Bogus message type %d "
+ "received from audio service",
+ ctl_rsp->h.msg_type);
return -EINVAL;
}
- if(pkt->length != sizeof(struct ipc_data_ctl)) {
- SNDERR("Unexpected packet length %d received", pkt->length);
+ if (ctl_rsp->h.msg_type != BT_CONTROL_RSP) {
+ SNDERR("Unexpected message %s received", type);
return -EINVAL;
}
+ if (ctl_rsp->posix_errno != 0) {
+ SNDERR("BT_CONTROL failed : %s (%d)",
+ strerror(ctl_rsp->posix_errno),
+ ctl_rsp->posix_errno);
+ return -ctl_rsp->posix_errno;
+ }
+
return 0;
}
@@ -174,28 +191,21 @@ static int bluetooth_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
long *value)
{
struct bluetooth_data *data = ext->private_data;
- struct ipc_packet *pkt;
- struct ipc_data_ctl *ctl;
- int len, ret;
+ int ret;
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_control_rsp *rsp = (void *) buf;
DBG("ext %p key %ld", ext, key);
- len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_ctl);
- pkt = malloc(len);
- memset(pkt, 0, len);
+ memset(buf, 0, sizeof(buf));
*value = 0;
- pkt->type = PKT_TYPE_CTL_REQ;
- pkt->length = sizeof(struct ipc_data_ctl);
- ctl = (struct ipc_data_ctl *) pkt->data;
- ctl->mode = key;
-
- if ((ret = bluetooth_send_ctl(data, pkt, len)) < 0)
+ ret = bluetooth_send_ctl(data, key, 0, rsp);
+ if (ret < 0)
goto done;
- *value = ctl->key;
+ *value = rsp->key;
done:
- free(pkt);
return ret;
}
@@ -203,38 +213,31 @@ static int bluetooth_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
long *value)
{
struct bluetooth_data *data = ext->private_data;
- struct ipc_packet *pkt;
- struct ipc_data_ctl *ctl;
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_control_rsp *rsp = (void *) buf;
long current;
- int len, ret;
+ int ret, keyvalue;
DBG("ext %p key %ld", ext, key);
- if ((ret = bluetooth_read_integer(ext, key, &current)) < 0)
+ ret = bluetooth_read_integer(ext, key, &current);
+ if (ret < 0)
return ret;
if (*value == current)
return 0;
- len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_ctl);
- pkt = malloc(len);
- memset(pkt, 0, len);
-
- pkt->length = sizeof(struct ipc_data_ctl);
- ctl = (struct ipc_data_ctl *) pkt->data;
- ctl->mode = key;
-
while (*value != current) {
- pkt->type = PKT_TYPE_CTL_REQ;
- ctl->key = (*value > current) ? CTL_KEY_VOL_UP : CTL_KEY_VOL_DOWN;
+ keyvalue = (*value > current) ? BT_CONTROL_KEY_VOL_UP :
+ BT_CONTROL_KEY_VOL_DOWN;
- if ((ret = bluetooth_send_ctl(data, pkt, len)) < 0)
+ ret = bluetooth_send_ctl(data, key, keyvalue, rsp);
+ if (ret < 0)
break;
- current = ctl->key;
+ current = keyvalue;
}
- free(pkt);
return ret;
}
@@ -242,33 +245,31 @@ static int bluetooth_read_event(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id,
unsigned int *event_mask)
{
struct bluetooth_data *data = ext->private_data;
- struct ipc_packet *pkt;
- struct ipc_data_ctl *ctl;
- int len, ret;
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_control_ind *ind = (void *) buf;
+ int ret;
+ const char *type;
DBG("ext %p id %p", ext, id);
- len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_ctl);
- pkt = malloc(len);
- memset(pkt, 0, len);
-
- ret = recv(data->sock, pkt, len, MSG_DONTWAIT);
- if (ret <= 0)
- return -errno;
+ memset(buf, 0, sizeof(buf));
- if(pkt->type != PKT_TYPE_CTL_NTFY) {
- SNDERR("Unexpected packet type %d received!", pkt->type);
+ ret = recv(data->sock, ind, BT_AUDIO_IPC_PACKET_SIZE, MSG_DONTWAIT);
+ type = bt_audio_strmsg(ind->h.msg_type);
+ if (!type) {
+ SNDERR("Bogus message type %d "
+ "received from audio service",
+ ind->h.msg_type);
return -EAGAIN;
}
- if(pkt->length != sizeof(struct ipc_data_ctl)) {
- SNDERR("Unexpected packet length %d received", pkt->length);
+ if (ind->h.msg_type != BT_CONTROL_IND) {
+ SNDERR("Unexpected message %s received", type);
return -EAGAIN;
}
- ctl = (struct ipc_data_ctl *) pkt->data;
snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
- snd_ctl_elem_id_set_name(id, ctl->mode == BLUETOOTH_PLAYBACK ?
+ snd_ctl_elem_id_set_name(id, ind->mode == BLUETOOTH_PLAYBACK ?
vol_devices[BLUETOOTH_PLAYBACK] :
vol_devices[BLUETOOTH_CAPTURE]);
*event_mask = SND_CTL_EVENT_MASK_VALUE;
@@ -290,10 +291,7 @@ static snd_ctl_ext_callback_t bluetooth_callback = {
static int bluetooth_init(struct bluetooth_data *data)
{
- int sk, err, id;
- struct sockaddr_un addr = {
- AF_UNIX, IPC_SOCKET_NAME
- };
+ int sk;
if (!data)
return -EINVAL;
@@ -302,21 +300,9 @@ static int bluetooth_init(struct bluetooth_data *data)
data->sock = -1;
- id = abs(getpid() * rand());
-
- if ((sk = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
- err = -errno;
- SNDERR("Can't open socket");
+ sk = bt_audio_service_open();
+ if (sk < 0)
return -errno;
- }
-
- DBG("Connecting to address: %s", addr.sun_path + 1);
- if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- err = -errno;
- SNDERR("Can't connect socket");
- close(sk);
- return err;
- }
data->sock = sk;
diff --git a/audio/device.c b/audio/device.c
index c86f711d..a5b9b0a9 100644
--- a/audio/device.c
+++ b/audio/device.c
@@ -450,7 +450,6 @@ static headset_state_t ipc_to_hs_state(uint8_t ipc_state)
return HEADSET_STATE_DISCONNECTED;
}
}
-#endif
static uint8_t avdtp_to_ipc_state(avdtp_state_t state)
{
@@ -504,6 +503,7 @@ uint8_t device_get_state(struct device *dev)
return STATE_DISCONNECTED;
}
+#endif
gboolean device_is_connected(struct device *dev, const char *interface)
{
diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c
index e605158f..535f6c5e 100644
--- a/audio/gsta2dpsink.c
+++ b/audio/gsta2dpsink.c
@@ -41,14 +41,6 @@
#include "gsta2dpsink.h"
-enum {
- NOT_CONFIGURED,
- CONFIGURING_INIT,
- CONFIGURING_SENT_CONF,
- CONFIGURING_RCVD_DEV_CONF,
- CONFIGURED
-};
-
GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug);
#define GST_CAT_DEFAULT a2dp_sink_debug
@@ -62,24 +54,10 @@ GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug);
g_mutex_unlock (s->sink_lock); \
} G_STMT_END
-#define GST_A2DP_SINK_WAIT_CON_END(s) G_STMT_START { \
- s->waiting_con_conf = TRUE; \
- g_cond_wait (s->con_conf_end, s->sink_lock); \
- s->waiting_con_conf = FALSE; \
- } G_STMT_END
-
-#define GST_A2DP_SINK_CONFIGURATION_FAIL(s) G_STMT_START { \
- s->con_state = NOT_CONFIGURED; \
- g_cond_signal (s->con_conf_end); \
- } G_STMT_END
-
-#define GST_A2DP_SINK_CONFIGURATION_SUCCESS(s) G_STMT_START { \
- s->con_state = CONFIGURED; \
- g_cond_signal (s->con_conf_end); \
- } G_STMT_END
struct bluetooth_data {
- struct ipc_data_cfg cfg; /* Bluetooth device config */
+ struct bt_getcapabilities_rsp cfg; /* Bluetooth device config */
+ gint link_mtu;
int samples; /* Number of encoded samples */
gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */
gsize count; /* Codec transfer buffer counter */
@@ -121,6 +99,13 @@ static GstStaticPadTemplate a2dp_sink_factory =
"rate = (int) { 16000, 22050, 24000, 32000, 44100, 48000 }, "
"channels = (int) [ 1, 2 ]"));
+static GIOError gst_a2dp_sink_audioservice_send(GstA2dpSink *self,
+ const bt_audio_msg_header_t *msg);
+static GIOError gst_a2dp_sink_audioservice_expect(GstA2dpSink *self,
+ bt_audio_msg_header_t *outmsg,
+ int expected_type);
+
+
static void gst_a2dp_sink_base_init(gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
@@ -137,8 +122,6 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink)
GST_INFO_OBJECT(self, "stop");
- self->con_state = NOT_CONFIGURED;
-
if (self->watch_id != 0) {
g_source_remove(self->watch_id);
self->watch_id = 0;
@@ -152,7 +135,7 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink)
}
if (self->server) {
- g_io_channel_close(self->server);
+ bt_audio_service_close(g_io_channel_unix_get_fd(self->server));
g_io_channel_unref(self->server);
self->server = NULL;
}
@@ -162,9 +145,9 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink)
self->data = NULL;
}
- if (self->sbc) {
- g_free(self->sbc);
- self->sbc = NULL;
+ if (self->stream_caps) {
+ gst_caps_unref(self->stream_caps);
+ self->stream_caps = NULL;
}
if (self->dev_caps) {
@@ -185,12 +168,6 @@ static void gst_a2dp_sink_finalize(GObject *object)
if (self->device)
g_free(self->device);
- /* unlock any thread waiting for this signal */
- GST_A2DP_SINK_MUTEX_LOCK(self);
- GST_A2DP_SINK_CONFIGURATION_FAIL(self);
- GST_A2DP_SINK_MUTEX_UNLOCK(self);
-
- g_cond_free(self->con_conf_end);
g_mutex_free(self->sink_lock);
G_OBJECT_CLASS(parent_class)->finalize(object);
@@ -232,19 +209,10 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id,
static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink)
{
- char cmsg_b[CMSG_SPACE(sizeof(int))], m;
- int err, ret, stream_fd;
- struct iovec iov = { &m, sizeof(m) };
- struct msghdr msgh;
- struct cmsghdr *cmsg;
-
- memset(&msgh, 0, sizeof(msgh));
- msgh.msg_iov = &iov;
- msgh.msg_iovlen = 1;
- msgh.msg_control = &cmsg_b;
- msgh.msg_controllen = CMSG_LEN(sizeof(int));
-
- ret = recvmsg(g_io_channel_unix_get_fd(sink->server), &msgh, 0);
+ int err, ret;
+
+ ret = bt_audio_service_get_data_fd(g_io_channel_unix_get_fd(sink->server));
+
if (ret < 0) {
err = errno;
GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)",
@@ -252,76 +220,20 @@ static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink)
return -err;
}
- /* Receive auxiliary data in msgh */
- for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;
- cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
- if (cmsg->cmsg_level == SOL_SOCKET
- && cmsg->cmsg_type == SCM_RIGHTS) {
- stream_fd = (*(int *) CMSG_DATA(cmsg));
- sink->stream = g_io_channel_unix_new(stream_fd);
-
- GST_DEBUG_OBJECT(sink, "stream_fd=%d", stream_fd);
- return 0;
- }
- }
-
- return -EINVAL;
-}
-
-static void gst_a2dp_sink_check_dev_caps(GstA2dpSink *self)
-{
- GstStructure *structure;
- GstCaps *dev_caps;
- gint channels;
-
- structure = gst_caps_get_structure(self->dev_caps, 0);
- if (!gst_structure_get_int(structure, "channels", &channels))
- channels = 2; /* FIXME how to get channels */
- dev_caps = gst_sbc_caps_from_sbc(&(self->data->cfg), self->sbc,
- channels);
-
- self->new_dev_caps = TRUE;
- gst_caps_unref(self->dev_caps);
- self->dev_caps = gst_caps_ref(dev_caps);
-
-
-}
-
-static int gst_a2dp_sink_bluetooth_a2dp_init(GstA2dpSink *self,
- struct ipc_codec_sbc *sbc)
-{
- struct bluetooth_data *data = self->data;
- struct ipc_data_cfg *cfg = &data->cfg;
-
- if (cfg == NULL) {
- GST_ERROR_OBJECT(self, "Error getting codec parameters");
- return -1;
- }
-
- if (cfg->codec != CFG_CODEC_SBC)
- return -1;
-
- data->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
-
- GST_DEBUG_OBJECT(self, "Codec parameters: "
- "\tallocation=%u\n\tsubbands=%u\n "
- "\tblocks=%u\n\tbitpool=%u\n",
- sbc->allocation, sbc->subbands,
- sbc->blocks, sbc->bitpool);
+ sink->stream = g_io_channel_unix_new(ret);
+ GST_DEBUG_OBJECT(sink, "stream_fd=%d", ret);
return 0;
}
static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink,
GstCaps *caps,
- struct ipc_packet *pkt)
+ sbc_capabilities_t *pkt)
{
-
- struct ipc_data_cfg *cfg = (void *) pkt->data;
- struct ipc_codec_sbc *sbc = (void *) cfg->data;
+ sbc_capabilities_t *cfg = &sink->data->cfg.sbc_capabilities;
const GValue *value = NULL;
const char *pref, *name;
- GstStructure *structure = gst_caps_get_structure(caps,0);
+ GstStructure *structure = gst_caps_get_structure(caps, 0);
name = gst_structure_get_name(structure);
/* FIXME only sbc supported here, should suport mp3 */
@@ -330,26 +242,21 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink,
return FALSE;
}
- if (sink->device)
- strncpy(pkt->device, sink->device, 18);
-
- pkt->role = PKT_ROLE_HIFI;
-
value = gst_structure_get_value(structure, "rate");
- cfg->rate = g_value_get_int(value);
+ cfg->frequency = g_value_get_int(value);
value = gst_structure_get_value(structure, "mode");
pref = g_value_get_string(value);
if (strcmp(pref, "auto") == 0)
- cfg->mode = CFG_MODE_AUTO;
+ cfg->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO;
else if (strcmp(pref, "mono") == 0)
- cfg->mode = CFG_MODE_MONO;
+ cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
else if (strcmp(pref, "dual") == 0)
- cfg->mode = CFG_MODE_DUAL_CHANNEL;
+ cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
else if (strcmp(pref, "stereo") == 0)
- cfg->mode = CFG_MODE_STEREO;
+ cfg->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
else if (strcmp(pref, "joint") == 0)
- cfg->mode = CFG_MODE_JOINT_STEREO;
+ cfg->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
else {
GST_ERROR_OBJECT(sink, "Invalid mode %s", pref);
return FALSE;
@@ -358,107 +265,27 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink,
value = gst_structure_get_value(structure, "allocation");
pref = g_value_get_string(value);
if (strcmp(pref, "auto") == 0)
- sbc->allocation = CFG_ALLOCATION_AUTO;
+ cfg->allocation_method = BT_A2DP_ALLOCATION_AUTO;
else if (strcmp(pref, "loudness") == 0)
- sbc->allocation = CFG_ALLOCATION_LOUDNESS;
+ cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
else if (strcmp(pref, "snr") == 0)
- sbc->allocation = CFG_ALLOCATION_SNR;
+ cfg->allocation_method = BT_A2DP_ALLOCATION_SNR;
else {
GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref);
return FALSE;
}
value = gst_structure_get_value(structure, "subbands");
- sbc->subbands = g_value_get_int(value);
+ cfg->subbands = g_value_get_int(value);
value = gst_structure_get_value(structure, "blocks");
- sbc->blocks = g_value_get_int(value);
+ cfg->block_length = g_value_get_int(value);
+ /* FIXME min and max ??? */
value = gst_structure_get_value(structure, "bitpool");
- sbc->bitpool = g_value_get_int(value);
-
- pkt->length = sizeof(*cfg) + sizeof(*sbc);
- pkt->type = PKT_TYPE_CFG_REQ;
- pkt->error = PKT_ERROR_NONE;
-
- return TRUE;
-}
-
-static gboolean gst_a2dp_sink_conf_resp(GstA2dpSink *sink)
-{
- gchar buf[IPC_MTU];
- GIOError io_error;
- gsize ret;
- gint total;
- struct ipc_packet *pkt = (void *) buf;
- struct ipc_data_cfg *cfg = (void *) pkt->data;
- struct ipc_codec_sbc *sbc = (void *) cfg->data;
-
- memset(buf, 0, sizeof(buf));
-
- io_error = g_io_channel_read(sink->server, (gchar *) buf,
- sizeof(*pkt) + sizeof(*cfg), &ret);
- if (io_error != G_IO_ERROR_NONE && ret > 0) {
- GST_ERROR_OBJECT(sink, "Error ocurred while receiving "
- "configurarion packet answer");
- return FALSE;
- }
-
- total = ret;
- if (pkt->type != PKT_TYPE_CFG_RSP) {
- GST_ERROR_OBJECT(sink, "Unexpected packet type %d "
- "received", pkt->type);
- return FALSE;
- }
-
- if (pkt->error != PKT_ERROR_NONE) {
- GST_ERROR_OBJECT(sink, "Error %d while configuring "
- "device", pkt->error);
- return FALSE;
- }
-
- if (cfg->codec != CFG_CODEC_SBC) {
- GST_ERROR_OBJECT(sink, "Unsupported format");
- return FALSE;
- }
-
- io_error = g_io_channel_read(sink->server, (gchar *) sbc,
- sizeof(*sbc), &ret);
- if (io_error != G_IO_ERROR_NONE) {
- GST_ERROR_OBJECT(sink, "Error while reading data from socket "
- "%s (%d)", strerror(errno), errno);
- return FALSE;
- } else if (ret == 0) {
- GST_ERROR_OBJECT(sink, "Read 0 bytes from socket");
- return FALSE;
- }
-
- total += ret;
- GST_DEBUG_OBJECT(sink, "OK - %d bytes received", total);
-
- if (pkt->length != (total - sizeof(struct ipc_packet))) {
- GST_ERROR_OBJECT(sink, "Error while configuring device: "
- "packet size doesn't match");
- return FALSE;
- }
-
- memcpy(&sink->data->cfg, cfg, sizeof(*cfg));
- memcpy(sink->sbc, sbc, sizeof(struct ipc_codec_sbc));
+ cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value);
- gst_a2dp_sink_check_dev_caps(sink);
-
- GST_DEBUG_OBJECT(sink, "Device configuration:\n\tchannel=%p\n\t"
- "fd_opt=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u",
- sink->stream, sink->data->cfg.fd_opt,
- sink->data->cfg.pkt_len, sink->data->cfg.sample_size,
- sink->data->cfg.rate);
-
- if (sink->data->cfg.codec == CFG_CODEC_SBC) {
- /* FIXME is this necessary? */
- ret = gst_a2dp_sink_bluetooth_a2dp_init(sink, sbc);
- if (ret < 0)
- return FALSE;
- }
+ memcpy(pkt, cfg, sizeof(*pkt));
return TRUE;
}
@@ -504,7 +331,7 @@ static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *self)
GST_LOG_OBJECT(self, "emptying stream pipe");
while (1) {
err = g_io_channel_read(self->stream, data->buffer,
- (gsize) data->cfg.pkt_len,
+ (gsize) data->cfg.link_mtu,
&read);
if (err != G_IO_ERROR_NONE || read <= 0)
break;
@@ -531,33 +358,6 @@ static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *self)
return TRUE;
}
-static void gst_a2dp_sink_conf_recv_data(GstA2dpSink *sink)
-{
- /*
- * We hold the lock, since we can send a signal.
- * It is a good practice, according to the glib api.
- */
- GST_A2DP_SINK_MUTEX_LOCK(sink);
-
- switch (sink->con_state) {
- case CONFIGURING_SENT_CONF:
- if (gst_a2dp_sink_conf_resp(sink))
- sink->con_state = CONFIGURING_RCVD_DEV_CONF;
- else
- GST_A2DP_SINK_CONFIGURATION_FAIL(sink);
- break;
- case CONFIGURING_RCVD_DEV_CONF:
- if (gst_a2dp_sink_conf_recv_stream_fd(sink))
- GST_A2DP_SINK_CONFIGURATION_SUCCESS(sink);
- else
- GST_A2DP_SINK_CONFIGURATION_FAIL(sink);
- break;
- }
-
- GST_A2DP_SINK_MUTEX_UNLOCK(sink);
-}
-
-
static gboolean server_callback(GIOChannel *chan,
GIOCondition cond, gpointer data)
{
@@ -568,16 +368,217 @@ static gboolean server_callback(GIOChannel *chan,
else if (cond & G_IO_ERR) {
sink = GST_A2DP_SINK(data);
GST_WARNING_OBJECT(sink, "Untreated callback G_IO_ERR");
- } else if (cond & G_IO_IN) {
- sink = GST_A2DP_SINK(data);
- if (sink->con_state != NOT_CONFIGURED &&
- sink->con_state != CONFIGURED)
- gst_a2dp_sink_conf_recv_data(sink);
- else
- GST_WARNING_OBJECT(sink, "Unexpected data received");
+ }
+
+ return TRUE;
+}
+
+static gboolean gst_a2dp_sink_update_caps(GstA2dpSink *self)
+{
+ sbc_capabilities_t *sbc = &self->data->cfg.sbc_capabilities;
+ GstStructure *structure;
+ GValue *value;
+ GValue *list;
+ gchar *tmp;
+
+ GST_LOG_OBJECT(self, "updating device caps");
+
+ structure = gst_structure_empty_new("audio/x-sbc");
+ value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING);
+
+ /* mode */
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) {
+ g_value_set_static_string(value, "joint");
+ gst_value_list_prepend_value(list, value);
+ g_value_set_static_string(value, "stereo");
+ gst_value_list_prepend_value(list, value);
+ g_value_set_static_string(value, "mono");
+ gst_value_list_prepend_value(list, value);
+ g_value_set_static_string(value, "dual");
+ gst_value_list_prepend_value(list, value);
} else {
- sink = GST_A2DP_SINK(data);
- GST_WARNING_OBJECT(sink, "Unexpected callback call");
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
+ g_value_set_static_string(value, "mono");
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) {
+ g_value_set_static_string(value, "stereo");
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) {
+ g_value_set_static_string(value, "dual");
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) {
+ g_value_set_static_string(value, "joint");
+ gst_value_list_prepend_value(list, value);
+ }
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "mode", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* subbands */
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ value = g_value_init(value, G_TYPE_INT);
+ if (sbc->subbands & BT_A2DP_SUBBANDS_4) {
+ g_value_set_int(value, 4);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->subbands & BT_A2DP_SUBBANDS_8) {
+ g_value_set_int(value, 8);
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "subbands", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* blocks */
+ value = g_value_init(value, G_TYPE_INT);
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) {
+ g_value_set_int(value, 16);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) {
+ g_value_set_int(value, 12);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) {
+ g_value_set_int(value, 8);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) {
+ g_value_set_int(value, 4);
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "blocks", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* allocation */
+ g_value_init(value, G_TYPE_STRING);
+ list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST);
+ if (sbc->allocation_method == BT_A2DP_ALLOCATION_AUTO) {
+ g_value_set_static_string(value, "loudness");
+ gst_value_list_prepend_value(list, value);
+ g_value_set_static_string(value, "snr");
+ gst_value_list_prepend_value(list, value);
+ } else {
+ if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) {
+ g_value_set_static_string(value, "loudness");
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) {
+ g_value_set_static_string(value, "snr");
+ gst_value_list_prepend_value(list, value);
+ }
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "allocation", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* rate */
+ g_value_init(value, G_TYPE_INT);
+ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+ if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_48000) {
+ g_value_set_int(value, 48000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_44100) {
+ g_value_set_int(value, 44100);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_32000) {
+ g_value_set_int(value, 32000);
+ gst_value_list_prepend_value(list, value);
+ }
+ if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_16000) {
+ g_value_set_int(value, 16000);
+ gst_value_list_prepend_value(list, value);
+ }
+ g_value_unset(value);
+ if (list) {
+ gst_structure_set_value(structure, "rate", list);
+ g_free(list);
+ list = NULL;
+ }
+
+ /* bitpool */
+ value = g_value_init(value, GST_TYPE_INT_RANGE);
+ gst_value_set_int_range(value, sbc->min_bitpool, sbc->max_bitpool);
+ gst_structure_set_value(structure, "bitpool", value);
+
+ /* channels */
+ gst_value_set_int_range(value, 1, 2);
+ gst_structure_set_value(structure, "channels", value);
+
+ g_free(value);
+
+ self->dev_caps = gst_caps_new_full(structure, NULL);
+
+ tmp = gst_caps_to_string(self->dev_caps);
+ GST_DEBUG_OBJECT(self, "Device capabilities: %s", tmp);
+ g_free(tmp);
+
+ return TRUE;
+}
+
+static gboolean gst_a2dp_sink_get_capabilities(GstA2dpSink *self)
+{
+ gchar *buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_getcapabilities_req *req = (void *) buf;
+ struct bt_getcapabilities_rsp *rsp = (void *) buf;
+ GIOError io_error;
+
+ memset(req, 0, BT_AUDIO_IPC_PACKET_SIZE);
+
+ req->h.msg_type = BT_GETCAPABILITIES_REQ;
+ strncpy(req->device, self->device, 18);
+
+ req->transport = BT_CAPABILITIES_TRANSPORT_A2DP;
+ req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;
+ io_error = gst_a2dp_sink_audioservice_send(self, &req->h);
+ if (io_error != G_IO_ERROR_NONE) {
+ GST_ERROR_OBJECT(self, "Error while asking device caps");
+ }
+
+ io_error = gst_a2dp_sink_audioservice_expect(self, &rsp->h,
+ BT_GETCAPABILITIES_RSP);
+ if (io_error != G_IO_ERROR_NONE) {
+ GST_ERROR_OBJECT(self, "Error while getting device caps");
+ return FALSE;
+ }
+
+ if (rsp->posix_errno != 0) {
+ GST_ERROR_OBJECT(self, "BT_GETCAPABILITIES failed : %s(%d)",
+ strerror(rsp->posix_errno),
+ rsp->posix_errno);
+ return FALSE;
+ }
+
+ if (rsp->transport != BT_CAPABILITIES_TRANSPORT_A2DP) {
+ GST_ERROR_OBJECT(self, "Non a2dp answer from device");
+ return FALSE;
+ }
+
+ memcpy(&self->data->cfg, rsp, sizeof(*rsp));
+ if (!gst_a2dp_sink_update_caps(self)) {
+ GST_WARNING_OBJECT(self, "failed to update capabilities");
+ return FALSE;
}
return TRUE;
@@ -586,7 +587,6 @@ static gboolean server_callback(GIOChannel *chan,
static gboolean gst_a2dp_sink_start(GstBaseSink *basesink)
{
GstA2dpSink *self = GST_A2DP_SINK(basesink);
- struct sockaddr_un addr = { AF_UNIX, IPC_SOCKET_NAME };
gint sk;
gint err;
@@ -594,107 +594,146 @@ static gboolean gst_a2dp_sink_start(GstBaseSink *basesink)
self->watch_id = 0;
- sk = socket(PF_LOCAL, SOCK_STREAM, 0);
- if (sk < 0) {
+ sk = bt_audio_service_open();
+ if (sk <= 0) {
err = errno;
- GST_ERROR_OBJECT(self, "Cannot open socket: %s (%d)",
- strerror(err), err);
- return FALSE;
- }
-
- if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- err = errno;
- GST_ERROR_OBJECT(self, "Connection fail %s (%d)",
- strerror(err), err);
- close(sk);
- return FALSE;
+ GST_ERROR_OBJECT(self, "Cannot open connection to bt "
+ "audio service: %s %d", strerror(err), err);
+ goto failed;
}
self->server = g_io_channel_unix_new(sk);
-
- self->watch_id = g_io_add_watch(self->server, G_IO_IN | G_IO_HUP |
- G_IO_ERR | G_IO_NVAL, server_callback, self);
+ self->watch_id = g_io_add_watch(self->server, G_IO_HUP | G_IO_ERR |
+ G_IO_NVAL, server_callback, self);
self->data = g_new0(struct bluetooth_data, 1);
memset(self->data, 0, sizeof(struct bluetooth_data));
- self->sbc = g_new0(struct ipc_codec_sbc, 1);
-
self->stream = NULL;
- self->con_state = NOT_CONFIGURED;
- self->new_dev_caps = FALSE;
- self->dev_caps = NULL;
+ self->stream_caps = NULL;
- self->waiting_con_conf = FALSE;
+ if (!gst_a2dp_sink_get_capabilities(self))
+ goto failed;
return TRUE;
+
+failed:
+ bt_audio_service_close(sk);
+ return FALSE;
}
-static gboolean gst_a2dp_sink_send_conf_pkt(GstA2dpSink *sink, GstCaps *caps)
+static gboolean gst_a2dp_sink_stream_start(GstA2dpSink *self)
{
- gchar buf[IPC_MTU];
- struct ipc_packet *pkt = (void *) buf;
- gboolean ret;
- gsize bytes_sent;
+ gchar buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_streamstart_req *req = (void *) buf;
+ struct bt_streamstart_rsp *rsp = (void *) buf;
+ struct bt_datafd_ind *ind = (void*) buf;
GIOError io_error;
- g_assert(sink->con_state == NOT_CONFIGURED);
+ GST_DEBUG_OBJECT(self, "stream start");
- memset (pkt, 0, sizeof(buf));
- ret = gst_a2dp_sink_init_pkt_conf(sink, caps, pkt);
- if (!ret) {
- GST_ERROR_OBJECT(sink, "Couldn't initialize parse caps "
- "to packet configuration");
+ memset (req, 0, sizeof(buf));
+ req->h.msg_type = BT_STREAMSTART_REQ;
+
+ io_error = gst_a2dp_sink_audioservice_send(self, &req->h);
+ if (io_error != G_IO_ERROR_NONE) {
+ GST_ERROR_OBJECT(self, "Error ocurred while sending "
+ "start packet");
return FALSE;
}
- sink->con_state = CONFIGURING_INIT;
+ GST_DEBUG_OBJECT(self, "stream start packet sent");
- io_error = g_io_channel_write(sink->server, (gchar *) pkt,
- sizeof(*pkt) + pkt->length, &bytes_sent);
+ io_error = gst_a2dp_sink_audioservice_expect(self, &rsp->h,
+ BT_STREAMSTART_RSP);
if (io_error != G_IO_ERROR_NONE) {
- GST_ERROR_OBJECT(sink, "Error ocurred while sending "
- "configurarion packet");
- sink->con_state = NOT_CONFIGURED;
+ GST_ERROR_OBJECT(self, "Error while stream start confirmation");
+ return FALSE;
+ }
+
+ if (rsp->posix_errno != 0) {
+ GST_ERROR_OBJECT(self, "BT_STREAMSTART_RSP failed : %s(%d)",
+ strerror(rsp->posix_errno),
+ rsp->posix_errno);
+ return FALSE;
+ }
+
+ GST_DEBUG_OBJECT(self, "stream started");
+
+ io_error = gst_a2dp_sink_audioservice_expect(self, &ind->h,
+ BT_STREAMFD_IND);
+ if (io_error != G_IO_ERROR_NONE) {
+ GST_ERROR_OBJECT(self, "Error while receiving stream fd");
return FALSE;
}
- GST_DEBUG_OBJECT(sink, "%d bytes sent", bytes_sent);
- sink->con_state = CONFIGURING_SENT_CONF;
+ if (!gst_a2dp_sink_conf_recv_stream_fd(self))
+ return FALSE;
return TRUE;
}
-static gboolean gst_a2dp_sink_start_dev_conf(GstA2dpSink *sink, GstCaps *caps)
+static gboolean gst_a2dp_sink_configure(GstA2dpSink *self, GstCaps *caps)
{
+ gchar buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_setconfiguration_req *req = (void *) buf;
+ struct bt_setconfiguration_rsp *rsp = (void *) buf;
gboolean ret;
+ GIOError io_error;
+
+ GST_DEBUG_OBJECT(self, "configuring device");
+
+ memset (req, 0, sizeof(buf));
+ req->h.msg_type = BT_SETCONFIGURATION_REQ;
+ strncpy(req->device, self->device, 18);
+ ret = gst_a2dp_sink_init_pkt_conf(self, caps, &req->sbc_capabilities);
+ if (!ret) {
+ GST_ERROR_OBJECT(self, "Couldn't parse caps "
+ "to packet configuration");
+ return FALSE;
+ }
- g_assert(sink->con_state == NOT_CONFIGURED);
+ io_error = gst_a2dp_sink_audioservice_send(self, &req->h);
+ if (io_error != G_IO_ERROR_NONE) {
+ GST_ERROR_OBJECT(self, "Error ocurred while sending "
+ "configurarion packet");
+ return FALSE;
+ }
- GST_DEBUG_OBJECT(sink, "starting device configuration");
+ GST_DEBUG_OBJECT(self, "configuration packet sent");
- ret = gst_a2dp_sink_send_conf_pkt(sink, caps);
+ io_error = gst_a2dp_sink_audioservice_expect(self, &rsp->h,
+ BT_SETCONFIGURATION_RSP);
+ if (io_error != G_IO_ERROR_NONE) {
+ GST_ERROR_OBJECT(self, "Error while receiving device confirmation");
+ return FALSE;
+ }
- return ret;
+ if (rsp->posix_errno != 0) {
+ GST_ERROR_OBJECT(self, "BT_SETCONFIGURATION_RSP failed : %s(%d)",
+ strerror(rsp->posix_errno),
+ rsp->posix_errno);
+ return FALSE;
+ }
+
+ GST_DEBUG_OBJECT(self, "configuration set");
+
+ return TRUE;
}
static GstFlowReturn gst_a2dp_sink_preroll(GstBaseSink *basesink,
GstBuffer *buffer)
{
GstA2dpSink *sink = GST_A2DP_SINK(basesink);
+ gboolean ret;
GST_A2DP_SINK_MUTEX_LOCK(sink);
- if (sink->con_state == NOT_CONFIGURED)
- gst_a2dp_sink_start_dev_conf(sink, GST_BUFFER_CAPS(buffer));
-
- /* wait for the connection process to finish */
- if (sink->con_state != CONFIGURED)
- GST_A2DP_SINK_WAIT_CON_END(sink);
+ ret = gst_a2dp_sink_stream_start(sink);
GST_A2DP_SINK_MUTEX_UNLOCK(sink);
- if (sink->con_state != CONFIGURED)
+ if (!ret)
return GST_FLOW_ERROR;
return GST_FLOW_OK;
@@ -745,7 +784,7 @@ static GstFlowReturn gst_a2dp_sink_render(GstBaseSink *basesink,
encoded = GST_BUFFER_SIZE(buffer);
- if (data->count + encoded >= data->cfg.pkt_len) {
+ if (data->count + encoded >= data->cfg.link_mtu) {
ret = gst_a2dp_sink_avdtp_write(self);
if (ret < 0)
return GST_FLOW_ERROR;
@@ -758,24 +797,28 @@ static GstFlowReturn gst_a2dp_sink_render(GstBaseSink *basesink,
return GST_FLOW_OK;
}
+static GstCaps* gst_a2dp_sink_get_caps(GstBaseSink *basesink)
+{
+ GstA2dpSink *self = GST_A2DP_SINK(basesink);
+
+ return self->dev_caps ? gst_caps_ref(self->dev_caps): NULL;
+}
+
static gboolean gst_a2dp_sink_set_caps(GstBaseSink *basesink, GstCaps *caps)
{
GstA2dpSink *self = GST_A2DP_SINK(basesink);
+ gboolean ret;
GST_A2DP_SINK_MUTEX_LOCK(self);
- if (self->con_state == NOT_CONFIGURED) {
- gst_a2dp_sink_start_dev_conf(self, caps);
+ ret = gst_a2dp_sink_configure(self, caps);
- if (self->dev_caps)
- gst_caps_unref(self->dev_caps);
- self->dev_caps = gst_caps_ref(caps);
+ if (self->stream_caps)
+ gst_caps_unref(self->stream_caps);
+ self->stream_caps = gst_caps_ref(caps);
- /* we suppose the device will accept this caps */
- self->new_dev_caps = FALSE;
- }
GST_A2DP_SINK_MUTEX_UNLOCK(self);
- return TRUE;
+ return ret;
}
static gboolean gst_a2dp_sink_unlock(GstBaseSink *basesink)
@@ -800,12 +843,7 @@ static GstFlowReturn gst_a2dp_sink_buffer_alloc(GstBaseSink *basesink,
return GST_FLOW_ERROR;
}
- if (self->new_dev_caps && self->dev_caps) {
- GST_INFO_OBJECT(self, "new caps from device");
- gst_buffer_set_caps(*buf, self->dev_caps);
- self->new_dev_caps = FALSE;
- } else
- gst_buffer_set_caps(*buf, caps);
+ gst_buffer_set_caps(*buf, caps);
GST_BUFFER_OFFSET(*buf) = offset;
@@ -830,6 +868,7 @@ static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass)
basesink_class->render = GST_DEBUG_FUNCPTR(gst_a2dp_sink_render);
basesink_class->preroll = GST_DEBUG_FUNCPTR(gst_a2dp_sink_preroll);
basesink_class->set_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps);
+ basesink_class->get_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_get_caps);
basesink_class->unlock = GST_DEBUG_FUNCPTR(gst_a2dp_sink_unlock);
basesink_class->buffer_alloc =
GST_DEBUG_FUNCPTR(gst_a2dp_sink_buffer_alloc);
@@ -847,12 +886,79 @@ static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass)
{
self->device = NULL;
self->data = NULL;
- self->sbc = NULL;
self->stream = NULL;
- self->con_state = NOT_CONFIGURED;
- self->con_conf_end = g_cond_new();
- self->waiting_con_conf = FALSE;
+ self->dev_caps = NULL;
+
self->sink_lock = g_mutex_new();
}
+
+static GIOError gst_a2dp_sink_audioservice_send(GstA2dpSink *self,
+ const bt_audio_msg_header_t *msg)
+{
+ gint err;
+ GIOError error;
+ gsize written;
+
+ GST_DEBUG_OBJECT(self, "sending %s", bt_audio_strmsg(msg->msg_type));
+
+ error = g_io_channel_write(self->server, (const gchar*) msg,
+ BT_AUDIO_IPC_PACKET_SIZE, &written);
+ if (error != G_IO_ERROR_NONE) {
+ err = errno;
+ GST_ERROR_OBJECT(self, "Error sending data to audio service:"
+ " %s(%d)", strerror(err), err);
+ }
+
+ return error;
+}
+
+static GIOError gst_a2dp_sink_audioservice_recv(GstA2dpSink *self,
+ bt_audio_msg_header_t *inmsg)
+{
+ GIOError status;
+ gsize bytes_read;
+ const char *type;
+
+ status = g_io_channel_read(self->server, (gchar*) inmsg,
+ BT_AUDIO_IPC_PACKET_SIZE, &bytes_read);
+ if (status != G_IO_ERROR_NONE) {
+ GST_ERROR_OBJECT(self, "Error receiving data from service");
+ return status;
+ }
+
+ type = bt_audio_strmsg(inmsg->msg_type);
+ if (!type) {
+ GST_ERROR_OBJECT(self, "Bogus message type %d "
+ "received from audio service",
+ inmsg->msg_type);
+ return G_IO_ERROR_INVAL;
+ }
+
+ GST_DEBUG_OBJECT(self, "Received %s", type);
+
+ return status;
+}
+
+static GIOError gst_a2dp_sink_audioservice_expect(GstA2dpSink *self,
+ bt_audio_msg_header_t *outmsg,
+ int expected_type)
+{
+ GIOError status;
+
+ status = gst_a2dp_sink_audioservice_recv(self, outmsg);
+ if (status != G_IO_ERROR_NONE)
+ return status;
+
+ if (outmsg->msg_type != expected_type) {
+ GST_ERROR_OBJECT(self, "Bogus message %s "
+ "received while %s was expected",
+ bt_audio_strmsg(outmsg->msg_type),
+ bt_audio_strmsg(expected_type));
+ return G_IO_ERROR_INVAL;
+ }
+
+ return status;
+}
+
diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h
index ea750406..f5b9b69b 100644
--- a/audio/gsta2dpsink.h
+++ b/audio/gsta2dpsink.h
@@ -49,15 +49,13 @@ struct _GstA2dpSink {
GIOChannel *stream;
struct bluetooth_data *data;
- struct ipc_codec_sbc *sbc;
GIOChannel *server;
- gint con_state;
+ /* stream connection data */
+ GstCaps *stream_caps;
+
GstCaps *dev_caps;
- gboolean new_dev_caps;
- GCond *con_conf_end;
- gboolean waiting_con_conf;
GMutex *sink_lock;
guint watch_id;
diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c
index 54bb7f8a..021ecacf 100644
--- a/audio/gstsbcenc.c
+++ b/audio/gstsbcenc.c
@@ -27,14 +27,17 @@
#include <string.h>
+#include "ipc.h"
#include "gstsbcenc.h"
#include "gstsbcutil.h"
-#define SBC_ENC_DEFAULT_MODE CFG_MODE_AUTO
-#define SBC_ENC_DEFAULT_BLOCKS 16
-#define SBC_ENC_DEFAULT_SUB_BANDS 8
-#define SBC_ENC_DEFAULT_BITPOOL 53
-#define SBC_ENC_DEFAULT_ALLOCATION CFG_ALLOCATION_AUTO
+#define SBC_ENC_DEFAULT_MODE BT_A2DP_CHANNEL_MODE_AUTO
+#define SBC_ENC_DEFAULT_BLOCKS 0
+#define SBC_ENC_DEFAULT_SUB_BANDS 0
+#define SBC_ENC_DEFAULT_BITPOOL 0
+#define SBC_ENC_DEFAULT_ALLOCATION BT_A2DP_ALLOCATION_AUTO
+#define SBC_ENC_DEFAULT_RATE 0
+#define SBC_ENC_DEFAULT_CHANNELS 0
GST_DEBUG_CATEGORY_STATIC(sbc_enc_debug);
#define GST_CAT_DEFAULT sbc_enc_debug
@@ -65,9 +68,9 @@ static GType gst_sbc_allocation_get_type(void)
{
static GType sbc_allocation_type = 0;
static GEnumValue sbc_allocations[] = {
- { CFG_ALLOCATION_AUTO, "Auto", "auto" },
- { CFG_ALLOCATION_LOUDNESS, "Loudness", "loudness" },
- { CFG_ALLOCATION_SNR, "SNR", "snr" },
+ { BT_A2DP_ALLOCATION_AUTO, "Auto", "auto" },
+ { BT_A2DP_ALLOCATION_LOUDNESS, "Loudness", "loudness" },
+ { BT_A2DP_ALLOCATION_SNR, "SNR", "snr" },
{ -1, NULL, NULL}
};
@@ -115,73 +118,193 @@ static GstStaticPadTemplate sbc_enc_src_factory =
"allocation = (string) { snr, loudness },"
"bitpool = (int) [ 2, 64 ]"));
-static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc, GstCaps *caps)
+gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps);
+
+static void sbc_enc_set_structure_int_param(GstSbcEnc *enc,
+ GstStructure *structure, const gchar* field,
+ gint field_value)
+{
+ GValue *value;
+
+ value = g_new0(GValue,1);
+ value = g_value_init(value, G_TYPE_INT);
+ g_value_set_int(value, field_value);
+ gst_structure_set_value(structure, field, value);
+ g_free(value);
+}
+
+static void sbc_enc_set_structure_string_param(GstSbcEnc *enc,
+ GstStructure *structure, const gchar* field,
+ const gchar* field_value)
+{
+ GValue *value;
+
+ value = g_new0(GValue,1);
+ value = g_value_init(value, G_TYPE_STRING);
+ g_value_set_string(value, field_value);
+ gst_structure_set_value(structure, field, value);
+ g_free(value);
+}
+
+static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc)
{
- gint rate;
- gint channels;
GstCaps* src_caps;
GstStructure *structure;
- const gchar *mode;
- const gchar *allocation;
+ GEnumValue *enum_value;
+ GEnumClass *enum_class;
+ gchar* temp;
+
+ src_caps = gst_caps_copy(gst_pad_get_pad_template_caps(enc->srcpad));
+ structure = gst_caps_get_structure(src_caps, 0);
+
+ if (enc->rate != 0)
+ sbc_enc_set_structure_int_param(enc, structure, "rate",
+ enc->rate);
+
+ if (enc->channels != 0)
+ sbc_enc_set_structure_int_param(enc, structure, "channels",
+ enc->channels);
+
+ if (enc->subbands != 0)
+ sbc_enc_set_structure_int_param(enc, structure, "subbands",
+ enc->subbands);
+
+ if (enc->blocks != 0)
+ sbc_enc_set_structure_int_param(enc, structure, "blocks",
+ enc->blocks);
+
+ if (enc->mode != BT_A2DP_CHANNEL_MODE_AUTO) {
+ enum_class = g_type_class_ref(GST_TYPE_SBC_MODE);
+ enum_value = g_enum_get_value(enum_class, enc->mode);
+ sbc_enc_set_structure_string_param(enc, structure, "mode",
+ enum_value->value_nick);
+ g_type_class_unref(enum_class);
+ }
- structure = gst_caps_get_structure(caps, 0);
+ if (enc->allocation != BT_A2DP_ALLOCATION_AUTO) {
+ enum_class = g_type_class_ref(GST_TYPE_SBC_ALLOCATION);
+ enum_value = g_enum_get_value(enum_class, enc->allocation);
+ sbc_enc_set_structure_string_param(enc, structure, "allocation",
+ enum_value->value_nick);
+ g_type_class_unref(enum_class);
+ }
- if (!gst_structure_get_int (structure, "rate", &rate))
- return NULL;
- if (!gst_structure_get_int (structure, "channels", &channels))
+ temp = gst_caps_to_string(src_caps);
+ GST_DEBUG_OBJECT(enc, "Srcpad caps: %s", temp);
+ g_free(temp);
+
+ return src_caps;
+}
+
+static GstCaps* sbc_enc_src_getcaps (GstPad * pad)
+{
+ GstSbcEnc *enc;
+
+ enc = GST_SBC_ENC(GST_PAD_PARENT(pad));
+
+ return sbc_enc_generate_srcpad_caps(enc);
+}
+
+static gboolean sbc_enc_src_setcaps (GstPad *pad, GstCaps *caps)
+{
+ GstCaps* srcpad_caps;
+ GstCaps* temp_caps;
+ gboolean res = TRUE;
+ GstSbcEnc *enc = GST_SBC_ENC(GST_PAD_PARENT(pad));
+
+ GST_LOG_OBJECT(enc, "setting srcpad caps");
+
+ srcpad_caps = sbc_enc_generate_srcpad_caps(enc);
+ temp_caps = gst_caps_intersect(srcpad_caps, caps);
+ if (temp_caps == GST_CAPS_NONE)
+ res = FALSE;
+
+ gst_caps_unref(temp_caps);
+ gst_caps_unref(srcpad_caps);
+
+ g_return_val_if_fail(res, FALSE);
+
+ return gst_sbc_enc_fill_sbc_params(enc, caps);
+}
+
+static GstCaps* sbc_enc_src_caps_fixate(GstSbcEnc *enc, GstCaps *caps)
+{
+
+ gchar *error_message = NULL;
+ GstCaps* result;
+
+ result = gst_sbc_util_caps_fixate(caps, &error_message);
+
+ if (!result) {
+ GST_ERROR_OBJECT (enc, "Invalid input caps caused parsing "
+ "error: %s", error_message);
+ g_free(error_message);
return NULL;
+ }
- enc->sbc.rate = rate;
- enc->sbc.channels = channels;
+ return result;
+}
- if (enc->mode == CFG_MODE_AUTO)
- enc->mode = CFG_MODE_JOINT_STEREO;
+static GstCaps* sbc_enc_get_fixed_srcpad_caps(GstSbcEnc *enc)
+{
+ GstCaps *peer_caps;
+ GstCaps *src_caps;
+ GstCaps *caps;
+ gboolean res = TRUE;
+ GstCaps *result_caps = NULL;
+
+ peer_caps = gst_pad_peer_get_caps(enc->srcpad);
+ if (!peer_caps)
+ return NULL;
- if (enc->mode == CFG_MODE_MONO || enc->mode == CFG_MODE_JOINT_STEREO)
- enc->sbc.joint = 1;
+ src_caps = sbc_enc_generate_srcpad_caps(enc);
+ caps = gst_caps_intersect(src_caps, peer_caps);
- enc->sbc.blocks = enc->blocks;
- enc->sbc.subbands = enc->subbands;
- if (enc->allocation == 0)
- enc->sbc.allocation = CFG_ALLOCATION_LOUDNESS;
- else
- enc->sbc.allocation = enc->allocation;
+ if (caps == GST_CAPS_NONE || gst_caps_is_empty(caps)) {
+ res = FALSE;
+ goto done;
+ }
- enc->sbc.bitpool = SBC_ENC_DEFAULT_BITPOOL;
+ result_caps = sbc_enc_src_caps_fixate(enc, caps);
- mode = gst_sbc_get_mode_string(enc->sbc.joint);
- allocation = gst_sbc_get_allocation_string(enc->sbc.allocation);
+done:
- src_caps = gst_caps_new_simple("audio/x-sbc",
- "rate", G_TYPE_INT, enc->sbc.rate,
- "channels", G_TYPE_INT, enc->sbc.channels,
- "mode", G_TYPE_STRING, mode,
- "subbands", G_TYPE_INT, enc->sbc.subbands,
- "blocks", G_TYPE_INT, enc->sbc.blocks,
- "allocation", G_TYPE_STRING, allocation,
- "bitpool", G_TYPE_INT, enc->sbc.bitpool,
- NULL);
+ gst_caps_unref(src_caps);
+ gst_caps_unref(peer_caps);
+ gst_caps_unref(caps);
- return src_caps;
+ if (!res)
+ return NULL;
+
+ return result_caps;
}
static gboolean sbc_enc_sink_setcaps (GstPad * pad, GstCaps * caps)
{
GstSbcEnc *enc;
GstStructure *structure;
- GstCaps *othercaps;
+ GstCaps *src_caps;
+ gint rate, channels;
+ gboolean res;
enc = GST_SBC_ENC(GST_PAD_PARENT (pad));
structure = gst_caps_get_structure(caps, 0);
- othercaps = sbc_enc_generate_srcpad_caps(enc, caps);
- if (othercaps == NULL)
+ if (!gst_structure_get_int(structure, "rate", &rate))
+ goto error;
+ if (!gst_structure_get_int(structure, "channels", &channels))
goto error;
- gst_pad_set_caps (enc->srcpad, othercaps);
- gst_caps_unref(othercaps);
+ enc->rate = rate;
+ enc->channels = channels;
- return TRUE;
+ src_caps = sbc_enc_get_fixed_srcpad_caps(enc);
+ if (!src_caps)
+ goto error;
+ res = gst_pad_set_caps(enc->srcpad, src_caps);
+ gst_caps_unref(src_caps);
+
+ return res;
error:
GST_ERROR_OBJECT (enc, "invalid input caps");
@@ -215,14 +338,13 @@ gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps)
if (!(allocation = gst_structure_get_string(structure, "allocation")))
return FALSE;
- sbc_reinit(&enc->sbc, 0);
- enc->sbc.rate = rate;
- enc->sbc.channels = channels;
- enc->blocks = blocks;
- enc->sbc.subbands = subbands;
+ enc->rate = enc->sbc.rate = rate;
+ enc->channels = enc->sbc.channels = channels;
+ enc->blocks = enc->sbc.blocks = blocks;
+ enc->subbands = enc->sbc.subbands = subbands;
enc->sbc.bitpool = bitpool;
- enc->mode = gst_sbc_get_mode_int(mode);
- enc->allocation = gst_sbc_get_allocation_mode_int(allocation);
+ enc->mode = enc->sbc.joint = gst_sbc_get_mode_int(mode);
+ enc->allocation = enc->sbc.allocation = gst_sbc_get_allocation_mode_int(allocation);
return TRUE;
}
@@ -291,11 +413,8 @@ static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer)
GST_BUFFER_TIMESTAMP(output) = GST_BUFFER_TIMESTAMP(buffer);
res = gst_pad_push(enc->srcpad, output);
- if (res != GST_FLOW_OK) {
- GST_ERROR_OBJECT(enc, "pad pushing failed");
+ if (res != GST_FLOW_OK)
goto done;
- }
-
}
done:
@@ -350,12 +469,30 @@ static void gst_sbc_enc_base_init(gpointer g_class)
gst_element_class_set_details(element_class, &sbc_enc_details);
}
+static gboolean sbc_enc_set_blocks(GstSbcEnc *enc, gint value)
+{
+ if (value != 4 && value != 8 && value != 12 &&
+ value != 16 && value != 0)
+ return FALSE;
+ enc->blocks = value;
+ return TRUE;
+}
+
+static gboolean sbc_enc_set_subbands(GstSbcEnc *enc, gint value)
+{
+ if (value != 4 && value != 8 && value != 0)
+ return FALSE;
+ enc->subbands = value;
+ return TRUE;
+}
+
static void gst_sbc_enc_set_property(GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
GstSbcEnc *enc = GST_SBC_ENC(object);
- /* TODO - CAN ONLY BE CHANGED ON READY AND BELOW */
+ /* changes to those properties will only happen on the next caps
+ * negotiation */
switch (prop_id) {
case PROP_MODE:
@@ -365,12 +502,14 @@ static void gst_sbc_enc_set_property(GObject *object, guint prop_id,
enc->allocation = g_value_get_enum(value);
break;
case PROP_BLOCKS:
- /* TODO - verify consistency */
- enc->blocks = g_value_get_int(value);
+ if (!sbc_enc_set_blocks(enc, g_value_get_int(value)))
+ GST_WARNING_OBJECT(enc, "invalid value %d for "
+ "blocks property", g_value_get_int(value));
break;
case PROP_SUBBANDS:
- /* TODO - verify consistency */
- enc->subbands = g_value_get_int(value);
+ if (!sbc_enc_set_subbands(enc, g_value_get_int(value)))
+ GST_WARNING_OBJECT(enc, "invalid value %d for "
+ "subbands property", g_value_get_int(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
@@ -428,7 +567,7 @@ static void gst_sbc_enc_class_init(GstSbcEncClass *klass)
g_object_class_install_property(object_class, PROP_BLOCKS,
g_param_spec_int("blocks", "Blocks",
"Blocks", 0, G_MAXINT,
- SBC_ENC_DEFAULT_BLOCKS, G_PARAM_READWRITE));
+ SBC_ENC_DEFAULT_BLOCKS, G_PARAM_READWRITE));
g_object_class_install_property(object_class, PROP_SUBBANDS,
g_param_spec_int("subbands", "Sub Bands",
@@ -447,13 +586,18 @@ static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass)
gst_element_add_pad(GST_ELEMENT(self), self->sinkpad);
self->srcpad = gst_pad_new_from_static_template(&sbc_enc_src_factory, "src");
- gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_enc_chain));
+ gst_pad_set_getcaps_function(self->srcpad, GST_DEBUG_FUNCPTR(sbc_enc_src_getcaps));
+ gst_pad_set_setcaps_function(self->srcpad, GST_DEBUG_FUNCPTR(sbc_enc_src_setcaps));
gst_element_add_pad(GST_ELEMENT(self), self->srcpad);
+ gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_enc_chain));
+
self->subbands = SBC_ENC_DEFAULT_SUB_BANDS;
self->blocks = SBC_ENC_DEFAULT_BLOCKS;
self->mode = SBC_ENC_DEFAULT_MODE;
self->allocation = SBC_ENC_DEFAULT_ALLOCATION;
+ self->rate = SBC_ENC_DEFAULT_RATE;
+ self->channels = SBC_ENC_DEFAULT_CHANNELS;
self->adapter = gst_adapter_new();
}
diff --git a/audio/gstsbcenc.h b/audio/gstsbcenc.h
index c5fc6bcc..f65bcc94 100644
--- a/audio/gstsbcenc.h
+++ b/audio/gstsbcenc.h
@@ -25,8 +25,6 @@
#include <gst/base/gstadapter.h>
#include "sbc.h"
-#include "ipc.h"
-
G_BEGIN_DECLS
@@ -51,6 +49,8 @@ struct _GstSbcEnc {
GstPad *srcpad;
GstAdapter *adapter;
+ gint rate;
+ gint channels;
gint mode;
gint blocks;
gint allocation;
diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c
index 185cda03..bae7d623 100644
--- a/audio/gstsbcparse.c
+++ b/audio/gstsbcparse.c
@@ -57,6 +57,7 @@ static GstStaticPadTemplate sbc_parse_src_factory =
"bitpool = (int) [ 2, 64 ]"));
/* Creates a fixed caps from the caps given. */
+/* FIXME use gstsbcutil caps fixating function */
static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps)
{
GstCaps *result;
@@ -67,6 +68,11 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps)
const gchar* allocation = NULL;
const gchar* mode = NULL;
const gchar* error_message = NULL;
+ gchar* str;
+
+ str = gst_caps_to_string(caps);
+ GST_DEBUG_OBJECT(parse, "Parsing caps: %s", str);
+ g_free(str);
structure = gst_caps_get_structure(caps, 0);
@@ -76,11 +82,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps)
goto error;
} else {
value = gst_structure_get_value(structure, "rate");
- if (GST_VALUE_HOLDS_LIST(value)) {
+ if (GST_VALUE_HOLDS_LIST(value))
temp = gst_sbc_select_rate_from_list(value);
- } else {
+ else
temp = g_value_get_int(value);
- }
rate = temp;
}
@@ -90,11 +95,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps)
goto error;
} else {
value = gst_structure_get_value(structure, "channels");
- if (GST_VALUE_HOLDS_INT_RANGE(value)) {
+ if (GST_VALUE_HOLDS_INT_RANGE(value))
temp = gst_sbc_select_channels_from_range(value);
- } else {
+ else
temp = g_value_get_int(value);
- }
channels = temp;
}
@@ -104,11 +108,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps)
goto error;
} else {
value = gst_structure_get_value(structure, "blocks");
- if (GST_VALUE_HOLDS_LIST(value)) {
+ if (GST_VALUE_HOLDS_LIST(value))
temp = gst_sbc_select_blocks_from_list(value);
- } else {
+ else
temp = g_value_get_int(value);
- }
blocks = temp;
}
@@ -118,11 +121,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps)
goto error;
} else {
value = gst_structure_get_value(structure, "subbands");
- if (GST_VALUE_HOLDS_LIST(value)) {
+ if (GST_VALUE_HOLDS_LIST(value))
temp = gst_sbc_select_subbands_from_list(value);
- } else {
+ else
temp = g_value_get_int(value);
- }
subbands = temp;
}
@@ -132,11 +134,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps)
goto error;
} else {
value = gst_structure_get_value(structure, "bitpool");
- if (GST_VALUE_HOLDS_INT_RANGE(value)) {
+ if (GST_VALUE_HOLDS_INT_RANGE(value))
temp = gst_sbc_select_bitpool_from_range(value);
- } else {
+ else
temp = g_value_get_int(value);
- }
bitpool = temp;
}
@@ -146,11 +147,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps)
goto error;
} else {
value = gst_structure_get_value(structure, "allocation");
- if (GST_VALUE_HOLDS_LIST(value)) {
+ if (GST_VALUE_HOLDS_LIST(value))
allocation = gst_sbc_get_allocation_from_list(value);
- } else {
+ else
allocation = g_value_get_string(value);
- }
}
if (!gst_structure_has_field(structure, "mode")) {
@@ -159,11 +159,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps)
goto error;
} else {
value = gst_structure_get_value(structure, "mode");
- if (GST_VALUE_HOLDS_LIST(value)) {
+ if (GST_VALUE_HOLDS_LIST(value))
mode = gst_sbc_get_mode_from_list(value);
- } else {
+ else
mode = g_value_get_string(value);
- }
}
error:
@@ -205,9 +204,15 @@ static gboolean sbc_parse_sink_setcaps(GstPad * pad, GstCaps * caps)
other = gst_caps_new_any();
inter = gst_caps_intersect(caps, other);
+ if (gst_caps_is_empty(inter)) {
+ gst_caps_unref(inter);
+ return FALSE;
+ }
srccaps = sbc_parse_select_caps(parse, inter);
- if (srccaps == NULL)
+ if (srccaps == NULL) {
+ gst_caps_unref(inter);
return FALSE;
+ }
gst_pad_set_caps(parse->srcpad, srccaps);
diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c
index 1d7a1227..f2351e6b 100644
--- a/audio/gstsbcutil.c
+++ b/audio/gstsbcutil.c
@@ -99,11 +99,11 @@ const gchar *gst_sbc_get_mode_from_list(const GValue *value)
gint gst_sbc_get_allocation_mode_int(const gchar *allocation)
{
if (g_ascii_strcasecmp(allocation, "loudness") == 0)
- return CFG_ALLOCATION_LOUDNESS;
+ return BT_A2DP_ALLOCATION_LOUDNESS;
else if (g_ascii_strcasecmp(allocation, "snr") == 0)
- return CFG_ALLOCATION_SNR;
+ return BT_A2DP_ALLOCATION_SNR;
else if (g_ascii_strcasecmp(allocation, "auto") == 0)
- return CFG_ALLOCATION_AUTO;
+ return BT_A2DP_ALLOCATION_AUTO;
else
return -1;
}
@@ -111,15 +111,15 @@ gint gst_sbc_get_allocation_mode_int(const gchar *allocation)
gint gst_sbc_get_mode_int(const gchar *mode)
{
if (g_ascii_strcasecmp(mode, "joint") == 0)
- return CFG_MODE_JOINT_STEREO;
+ return BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
else if (g_ascii_strcasecmp(mode, "stereo") == 0)
- return CFG_MODE_STEREO;
+ return BT_A2DP_CHANNEL_MODE_STEREO;
else if (g_ascii_strcasecmp(mode, "dual") == 0)
- return CFG_MODE_DUAL_CHANNEL;
+ return BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
else if (g_ascii_strcasecmp(mode, "mono") == 0)
- return CFG_MODE_MONO;
+ return BT_A2DP_CHANNEL_MODE_MONO;
else if (g_ascii_strcasecmp(mode, "auto") == 0)
- return CFG_MODE_AUTO;
+ return BT_A2DP_CHANNEL_MODE_AUTO;
else
return -1;
}
@@ -127,15 +127,15 @@ gint gst_sbc_get_mode_int(const gchar *mode)
const gchar *gst_sbc_get_mode_string(int joint)
{
switch (joint) {
- case CFG_MODE_MONO:
+ case BT_A2DP_CHANNEL_MODE_MONO:
return "mono";
- case CFG_MODE_DUAL_CHANNEL:
+ case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
return "dual";
- case CFG_MODE_STEREO:
+ case BT_A2DP_CHANNEL_MODE_STEREO:
return "stereo";
- case CFG_MODE_JOINT_STEREO:
+ case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
return "joint";
- case CFG_MODE_AUTO:
+ case BT_A2DP_CHANNEL_MODE_AUTO:
return NULL; /* TODO what should be selected here? */
default:
return NULL;
@@ -145,37 +145,165 @@ const gchar *gst_sbc_get_mode_string(int joint)
const gchar *gst_sbc_get_allocation_string(int alloc)
{
switch (alloc) {
- case CFG_ALLOCATION_LOUDNESS:
+ case BT_A2DP_ALLOCATION_LOUDNESS:
return "loudness";
- case CFG_ALLOCATION_SNR:
+ case BT_A2DP_ALLOCATION_SNR:
return "snr";
- case CFG_ALLOCATION_AUTO:
+ case BT_A2DP_ALLOCATION_AUTO:
return "loudness"; /* TODO what should be selected here? */
default:
return NULL;
}
}
-GstCaps* gst_sbc_caps_from_sbc(struct ipc_data_cfg *cfg,
- struct ipc_codec_sbc *sbc, gint channels)
+GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels)
{
GstCaps *caps;
const gchar *mode_str;
const gchar *allocation_str;
- mode_str = gst_sbc_get_mode_string(cfg->mode);
- allocation_str = gst_sbc_get_allocation_string(sbc->allocation);
+ mode_str = gst_sbc_get_mode_string(sbc->channel_mode);
+ allocation_str = gst_sbc_get_allocation_string(sbc->allocation_method);
caps = gst_caps_new_simple("audio/x-sbc",
- "rate", G_TYPE_INT, cfg->rate,
+ "rate", G_TYPE_INT, sbc->frequency,
"channels", G_TYPE_INT, channels,
"mode", G_TYPE_STRING, mode_str,
"subbands", G_TYPE_INT, sbc->subbands,
- "blocks", G_TYPE_INT, sbc->blocks,
+ "blocks", G_TYPE_INT, sbc->block_length,
"allocation", G_TYPE_STRING, allocation_str,
- "bitpool", G_TYPE_INT, sbc->bitpool,
+ "bitpool", G_TYPE_INT, sbc->max_bitpool,
NULL);
return caps;
}
+/*
+ * Given a GstCaps, this will return a fixed GstCaps on sucessfull conversion.
+ * If an error occurs, it will return NULL and error_message will contain the
+ * error message.
+ *
+ * error_message must be passed NULL, if an error occurs, the caller has the
+ * ownership of the error_message, it must be freed after use.
+ */
+GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message)
+{
+ GstCaps *result;
+ GstStructure *structure;
+ const GValue *value;
+ gboolean error = FALSE;
+ gint temp, rate, channels, blocks, subbands, bitpool;
+ const gchar* allocation = NULL;
+ const gchar* mode = NULL;
+
+ g_assert(*error_message == NULL);
+
+ structure = gst_caps_get_structure(caps, 0);
+
+ if (!gst_structure_has_field(structure, "rate")) {
+ error = TRUE;
+ *error_message = g_strdup("no rate");
+ goto error;
+ } else {
+ value = gst_structure_get_value(structure, "rate");
+ if (GST_VALUE_HOLDS_LIST(value))
+ temp = gst_sbc_select_rate_from_list(value);
+ else
+ temp = g_value_get_int(value);
+ rate = temp;
+ }
+
+ if (!gst_structure_has_field(structure, "channels")) {
+ error = TRUE;
+ *error_message = g_strdup("no channels");
+ goto error;
+ } else {
+ value = gst_structure_get_value(structure, "channels");
+ if (GST_VALUE_HOLDS_INT_RANGE(value))
+ temp = gst_sbc_select_channels_from_range(value);
+ else
+ temp = g_value_get_int(value);
+ channels = temp;
+ }
+
+ if (!gst_structure_has_field(structure, "blocks")) {
+ error = TRUE;
+ *error_message = g_strdup("no blocks.");
+ goto error;
+ } else {
+ value = gst_structure_get_value(structure, "blocks");
+ if (GST_VALUE_HOLDS_LIST(value))
+ temp = gst_sbc_select_blocks_from_list(value);
+ else
+ temp = g_value_get_int(value);
+ blocks = temp;
+ }
+
+ if (!gst_structure_has_field(structure, "subbands")) {
+ error = TRUE;
+ *error_message = g_strdup("no subbands");
+ goto error;
+ } else {
+ value = gst_structure_get_value(structure, "subbands");
+ if (GST_VALUE_HOLDS_LIST(value))
+ temp = gst_sbc_select_subbands_from_list(value);
+ else
+ temp = g_value_get_int(value);
+ subbands = temp;
+ }
+
+ if (!gst_structure_has_field(structure, "bitpool")) {
+ error = TRUE;
+ *error_message = g_strdup("no bitpool");
+ goto error;
+ } else {
+ value = gst_structure_get_value(structure, "bitpool");
+ if (GST_VALUE_HOLDS_INT_RANGE(value))
+ temp = gst_sbc_select_bitpool_from_range(value);
+ else
+ temp = g_value_get_int(value);
+ bitpool = temp;
+ }
+
+ if (!gst_structure_has_field(structure, "allocation")) {
+ error = TRUE;
+ *error_message = g_strdup("no allocation");
+ goto error;
+ } else {
+ value = gst_structure_get_value(structure, "allocation");
+ if (GST_VALUE_HOLDS_LIST(value))
+ allocation = gst_sbc_get_allocation_from_list(value);
+ else
+ allocation = g_value_get_string(value);
+ }
+
+ if (!gst_structure_has_field(structure, "mode")) {
+ error = TRUE;
+ *error_message = g_strdup("no mode");
+ goto error;
+ } else {
+ value = gst_structure_get_value(structure, "mode");
+ if (GST_VALUE_HOLDS_LIST(value))
+ mode = gst_sbc_get_mode_from_list(value);
+ else
+ mode = g_value_get_string(value);
+ }
+
+error:
+ if (error)
+ return NULL;
+
+ result = gst_caps_new_simple("audio/x-sbc",
+ "rate", G_TYPE_INT, rate,
+ "channels", G_TYPE_INT, channels,
+ "mode", G_TYPE_STRING, mode,
+ "blocks", G_TYPE_INT, blocks,
+ "subbands", G_TYPE_INT, subbands,
+ "allocation", G_TYPE_STRING, allocation,
+ "bitpool", G_TYPE_INT, bitpool,
+ NULL);
+
+ return result;
+}
+
+
diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h
index 0c91fe82..4581abf7 100644
--- a/audio/gstsbcutil.h
+++ b/audio/gstsbcutil.h
@@ -22,10 +22,9 @@
*/
#include <gst/gst.h>
-#include "sbc.h"
-struct ipc_data_cfg; /* FIXME can't include ipc.h */
-struct ipc_codec_sbc;
+#include "sbc.h"
+#include "ipc.h"
gint gst_sbc_select_rate_from_list(const GValue *value);
@@ -47,6 +46,6 @@ const gchar *gst_sbc_get_mode_from_list(const GValue *value);
gint gst_sbc_get_mode_int(const gchar *mode);
const gchar *gst_sbc_get_mode_string(int joint);
-GstCaps* gst_sbc_caps_from_sbc(struct ipc_data_cfg *cfg, struct ipc_codec_sbc *sbc,
- gint channels);
+GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels);
+GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message);
diff --git a/audio/ipc.c b/audio/ipc.c
new file mode 100644
index 00000000..05920648
--- /dev/null
+++ b/audio/ipc.c
@@ -0,0 +1,119 @@
+/*
+ *
+ * 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
+ *
+ */
+
+#include "ipc.h"
+
+/* This table contains the string representation for messages */
+static const char *strmsg[] = {
+ "BT_GETCAPABILITIES_REQ",
+ "BT_GETCAPABILITIES_RSP",
+ "BT_SETCONFIGURATION_REQ",
+ "BT_SETCONFIGURATION_RSP",
+ "BT_STREAMSTART_REQ",
+ "BT_STREAMSTART_RSP",
+ "BT_STREAMSTOP_REQ",
+ "BT_STREAMSTOP_RSP",
+ "BT_STREAMSUSPEND_IND",
+ "BT_STREAMRESUME_IND",
+ "BT_CONTROL_REQ",
+ "BT_CONTROL_RSP",
+ "BT_CONTROL_IND",
+ "BT_STREAMFD_IND",
+};
+
+int bt_audio_service_open()
+{
+ int sk;
+ int err;
+ struct sockaddr_un addr = {
+ AF_UNIX, BT_IPC_SOCKET_NAME
+ };
+
+ sk = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (sk < 0) {
+ err = errno;
+ fprintf(stderr, "%s: Cannot open socket: %s (%d)\n",
+ __FUNCTION__, strerror(err), err);
+ errno = err;
+ return -1;
+ }
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ err = errno;
+ fprintf(stderr, "%s: connect() failed: %s (%d)\n",
+ __FUNCTION__, strerror(err), err);
+ close(sk);
+ errno = err;
+ return -1;
+ }
+
+ return sk;
+}
+
+int bt_audio_service_close(int sk)
+{
+ return close(sk);
+}
+
+int bt_audio_service_get_data_fd(int sk)
+{
+ char cmsg_b[CMSG_SPACE(sizeof(int))], m;
+ int err, ret;
+ struct iovec iov = { &m, sizeof(m) };
+ struct msghdr msgh;
+ struct cmsghdr *cmsg;
+
+ memset(&msgh, 0, sizeof(msgh));
+ msgh.msg_iov = &iov;
+ msgh.msg_iovlen = 1;
+ msgh.msg_control = &cmsg_b;
+ msgh.msg_controllen = CMSG_LEN(sizeof(int));
+
+ ret = recvmsg(sk, &msgh, 0);
+ if (ret < 0) {
+ err = errno;
+ fprintf(stderr, "%s: Unable to receive fd: %s (%d)\n",
+ __FUNCTION__, strerror(err), err);
+ errno = err;
+ return -1;
+ }
+
+ /* Receive auxiliary data in msgh */
+ for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET
+ && cmsg->cmsg_type == SCM_RIGHTS)
+ return (*(int *) CMSG_DATA(cmsg));
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+const char *bt_audio_strmsg(int type)
+{
+ if (type < 0 || type > (sizeof(strmsg) / sizeof(strmsg[0])))
+ return NULL;
+
+ return strmsg[type];
+}
+
diff --git a/audio/ipc.h b/audio/ipc.h
index 1c26e304..0384cfd6 100644
--- a/audio/ipc.h
+++ b/audio/ipc.h
@@ -4,132 +4,285 @@
*
* 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 program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
+ * 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 General Public License for more details.
+ * 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 General Public License
- * along with this program; if not, write to the Free Software
+ * 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
*
*/
-#include <stdint.h>
+/*
+ Message sequence chart of streaming sequence for A2DP transport
+
+ Audio daemon User
+ on snd_pcm_open
+ <--BT_GETCAPABILITIES_REQ
+
+ BT_GETCAPABILITIES_RSP-->
+
+ on snd_pcm_hw_params
+ <--BT_SETCONFIGURATION_REQ
+
+ BT_SETCONFIGURATION_RSP-->
+
+ on snd_pcm_prepare
+ <--BT_STREAMSTART_REQ
+
+ <Moves to streaming state>
+ BT_STREAMSTART_RSP-->
-#define IPC_TYPE_CONNECT 0x0001
+ BT_STREAMFD_IND -->
-#define IPC_MTU 128
+ < streams data >
+ ..........
-#define IPC_SOCKET_NAME "\0/org/bluez/audio"
+ on snd_pcm_drop/snd_pcm_drain
-#ifndef UNIX_PATH_MAX
-#define UNIX_PATH_MAX 108
+ <--BT_STREAMSTOP_REQ
+
+ <Moves to open state>
+ BT_STREAMSTOP_RSP-->
+
+ on IPC close or appl crash
+ <Moves to idle>
+
+ */
+
+#ifndef BT_AUDIOCLIENT_H
+#define BT_AUDIOCLIENT_H
+
+#ifdef __cplusplus
+extern "C" {
#endif
-/* Supported roles */
-#define PKT_ROLE_AUTO 0
-#define PKT_ROLE_VOICE 1
-#define PKT_ROLE_HIFI 2
-
-/* Packet types */
-#define PKT_TYPE_CFG_REQ 0
-#define PKT_TYPE_CFG_RSP 1
-#define PKT_TYPE_STATE_REQ 2
-#define PKT_TYPE_STATE_RSP 3
-#define PKT_TYPE_CTL_REQ 4
-#define PKT_TYPE_CTL_RSP 5
-#define PKT_TYPE_CTL_NTFY 6
-
-/* Errors codes */
-#define PKT_ERROR_NONE 0
-
-struct ipc_packet {
- char device[18]; /* Address of the remote Device */
- uint8_t role; /* Audio role eg: voice, wifi, auto... */
- uint8_t type; /* Packet type */
- uint8_t error; /* Packet error code */
- uint8_t length; /* Payload length in bytes */
- uint8_t data[0]; /* Packet payload */
-} __attribute__ ((packed));
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+
+#define BT_AUDIO_IPC_PACKET_SIZE 128
+#define BT_IPC_SOCKET_NAME "\0/org/bluez/audio"
-/* File descriptor options */
-#define CFG_FD_OPT_READ 0
-#define CFG_FD_OPT_WRITE 1
-#define CFG_FD_OPT_READWRITE 2
-
-/* Audio channel mode */
-#define CFG_MODE_AUTO 0
-#define CFG_MODE_MONO 1
-#define CFG_MODE_DUAL_CHANNEL 2
-#define CFG_MODE_STEREO 3
-#define CFG_MODE_JOINT_STEREO 4
-
-/* Allocation method */
-#define CFG_ALLOCATION_AUTO 0
-#define CFG_ALLOCATION_LOUDNESS 1
-#define CFG_ALLOCATION_SNR 2
-
-/* Codec options */
-#define CFG_CODEC_NONE 0
-#define CFG_CODEC_SCO 1
-#define CFG_CODEC_SBC 2
-
-struct ipc_data_cfg {
- uint8_t fd_opt; /* Stream file descriptor options: read,
- write or readwrite */
- uint16_t pkt_len; /* Stream packet length */
- uint8_t sample_size; /* Sample size in bytes */
- uint8_t mode; /* Audio channel mode */
- uint16_t rate; /* Stream sample rate */
- uint8_t codec; /* Stream codec */
- uint8_t data[0]; /* Codec payload */
+/* Generic message header definition */
+typedef struct {
+ uint8_t msg_type;
+} __attribute__ ((packed)) bt_audio_msg_header_t;
+
+/* Messages list */
+#define BT_GETCAPABILITIES_REQ 0
+#define BT_GETCAPABILITIES_RSP 1
+
+#define BT_SETCONFIGURATION_REQ 2
+#define BT_SETCONFIGURATION_RSP 3
+
+#define BT_STREAMSTART_REQ 4
+#define BT_STREAMSTART_RSP 5
+
+#define BT_STREAMSTOP_REQ 6
+#define BT_STREAMSTOP_RSP 7
+
+#define BT_STREAMSUSPEND_IND 8
+#define BT_STREAMRESUME_IND 9
+
+#define BT_CONTROL_REQ 10
+#define BT_CONTROL_RSP 11
+#define BT_CONTROL_IND 12
+
+#define BT_STREAMFD_IND 13
+
+/* BT_GETCAPABILITIES_REQ */
+
+#define BT_CAPABILITIES_TRANSPORT_A2DP 0
+#define BT_CAPABILITIES_TRANSPORT_SCO 1
+#define BT_CAPABILITIES_TRANSPORT_ANY 2
+
+#define BT_CAPABILITIES_ACCESS_MODE_READ 1
+#define BT_CAPABILITIES_ACCESS_MODE_WRITE 2
+#define BT_CAPABILITIES_ACCESS_MODE_READWRITE 3
+
+struct bt_getcapabilities_req {
+ bt_audio_msg_header_t h;
+ char device[18]; /* Address of the remote Device */
+ uint8_t transport; /* Requested transport */
+ uint8_t access_mode; /* Requested access mode */
} __attribute__ ((packed));
-struct ipc_codec_sbc {
- uint8_t allocation;
+/* BT_GETCAPABILITIES_RSP */
+
+/**
+ * SBC Codec parameters as per A2DP profile 1.0 § 4.3
+ */
+
+#define BT_A2DP_SAMPLING_FREQ_16000 (1 << 3)
+#define BT_A2DP_SAMPLING_FREQ_32000 (1 << 2)
+#define BT_A2DP_SAMPLING_FREQ_44100 (1 << 1)
+#define BT_A2DP_SAMPLING_FREQ_48000 1
+
+#define BT_A2DP_CHANNEL_MODE_MONO (1 << 3)
+#define BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL (1 << 2)
+#define BT_A2DP_CHANNEL_MODE_STEREO (1 << 1)
+#define BT_A2DP_CHANNEL_MODE_JOINT_STEREO 1
+#define BT_A2DP_CHANNEL_MODE_AUTO 0
+
+#define BT_A2DP_BLOCK_LENGTH_4 (1 << 3)
+#define BT_A2DP_BLOCK_LENGTH_8 (1 << 2)
+#define BT_A2DP_BLOCK_LENGTH_12 (1 << 1)
+#define BT_A2DP_BLOCK_LENGTH_16 1
+
+#define BT_A2DP_SUBBANDS_4 (1 << 1)
+#define BT_A2DP_SUBBANDS_8 1
+
+#define BT_A2DP_ALLOCATION_SNR (1 << 1)
+#define BT_A2DP_ALLOCATION_LOUDNESS 1
+#define BT_A2DP_ALLOCATION_AUTO 0
+
+typedef struct {
+ uint8_t channel_mode;
+ uint8_t frequency;
+ uint8_t allocation_method;
uint8_t subbands;
- uint8_t blocks;
- uint8_t bitpool;
+ uint8_t block_length;
+ uint8_t min_bitpool;
+ uint8_t max_bitpool;
+} __attribute__ ((packed)) sbc_capabilities_t;
+
+/* To be defined */
+typedef struct {
+} __attribute__ ((packed)) mpeg_capabilities_t;
+
+struct bt_getcapabilities_rsp {
+ bt_audio_msg_header_t h;
+ uint8_t posix_errno;
+ uint8_t transport; /* Granted transport */
+ uint8_t access_mode; /* Granted access mode */
+ uint16_t link_mtu; /* Max length that transport supports */
+ sbc_capabilities_t sbc_capabilities; /* A2DP only */
+ mpeg_capabilities_t mpeg_capabilities; /* A2DP only */
+ uint16_t sampling_rate; /* SCO only */
+} __attribute__ ((packed));
+
+/* BT_SETCONFIGURATION_REQ */
+struct bt_setconfiguration_req {
+ bt_audio_msg_header_t h;
+ char device[18]; /* Address of the remote Device */
+ sbc_capabilities_t sbc_capabilities; /* A2DP only - only one of this field
+ and next one must be filled */
+ mpeg_capabilities_t mpeg_capabilities; /* A2DP only */
+} __attribute__ ((packed));
+
+/* BT_SETCONFIGURATION_RSP */
+struct bt_setconfiguration_rsp {
+ bt_audio_msg_header_t h;
+ uint8_t posix_errno;
+} __attribute__ ((packed));
+
+/* BT_STREAMSTART_REQ */
+#define BT_STREAM_ACCESS_READ 0
+#define BT_STREAM_ACCESS_WRITE 1
+#define BT_STREAM_ACCESS_READWRITE 2
+struct bt_streamstart_req {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+/* BT_STREAMSTART_RSP */
+struct bt_streamstart_rsp {
+ bt_audio_msg_header_t h;
+ uint8_t posix_errno;
+} __attribute__ ((packed));
+
+/* BT_STREAMFD_IND */
+/* This message is followed by one byte of data containing the stream data fd
+ as ancilliary data */
+struct bt_datafd_ind {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+/* BT_STREAMSTOP_REQ */
+struct bt_streamstop_req {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
+
+/* BT_STREAMSTOP_RSP */
+struct bt_streamstop_rsp {
+ bt_audio_msg_header_t h;
+ uint8_t posix_errno;
} __attribute__ ((packed));
-/* Device status */
-#define STATE_DISCONNECTED 0
-#define STATE_CONNECTING 1
-#define STATE_CONNECTED 2
-#define STATE_STREAM_STARTING 3
-#define STATE_STREAMING 4
+/* BT_STREAMSUSPEND_IND */
+struct bt_streamsuspend_ind {
+ bt_audio_msg_header_t h;
+} __attribute__ ((packed));
-struct ipc_data_state {
- uint8_t state; /* Stream state */
+/* BT_STREAMRESUME_IND */
+struct bt_streamresume_ind {
+ bt_audio_msg_header_t h;
} __attribute__ ((packed));
-#define CTL_MODE_PLAYBACK 0
-#define CTL_MODE_CAPTURE 1
-#define CTL_MODE_GENERAL 2
-
-/* Supported control operations */
-#define CTL_KEY_POWER 0x40
-#define CTL_KEY_VOL_UP 0x41
-#define CTL_KEY_VOL_DOWN 0x42
-#define CTL_KEY_MUTE 0x43
-#define CTL_KEY_PLAY 0x44
-#define CTL_KEY_STOP 0x45
-#define CTL_KEY_PAUSE 0x46
-#define CTL_KEY_RECORD 0x47
-#define CTL_KEY_REWIND 0x48
-#define CTL_KEY_FAST_FORWARD 0x49
-#define CTL_KEY_EJECT 0x4A
-#define CTL_KEY_FORWARD 0x4B
-#define CTL_KEY_BACKWARD 0x4C
-
-struct ipc_data_ctl {
- uint8_t mode; /* Control Mode */
- uint8_t key; /* Control Key */
-} __attribute__ ((packed));
+/* BT_CONTROL_REQ */
+
+#define BT_CONTROL_KEY_POWER 0x40
+#define BT_CONTROL_KEY_VOL_UP 0x41
+#define BT_CONTROL_KEY_VOL_DOWN 0x42
+#define BT_CONTROL_KEY_MUTE 0x43
+#define BT_CONTROL_KEY_PLAY 0x44
+#define BT_CONTROL_KEY_STOP 0x45
+#define BT_CONTROL_KEY_PAUSE 0x46
+#define BT_CONTROL_KEY_RECORD 0x47
+#define BT_CONTROL_KEY_REWIND 0x48
+#define BT_CONTROL_KEY_FAST_FORWARD 0x49
+#define BT_CONTROL_KEY_EJECT 0x4A
+#define BT_CONTROL_KEY_FORWARD 0x4B
+#define BT_CONTROL_KEY_BACKWARD 0x4C
+
+struct bt_control_req {
+ bt_audio_msg_header_t h;
+ uint8_t mode; /* Control Mode */
+ uint8_t key; /* Control Key */
+} __attribute__ ((packed));
+
+/* BT_CONTROL_RSP */
+struct bt_control_rsp {
+ bt_audio_msg_header_t h;
+ uint8_t posix_errno;
+ uint8_t mode; /* Control Mode */
+ uint8_t key; /* Control Key */
+} __attribute__ ((packed));
+
+/* BT_CONTROL_IND */
+struct bt_control_ind {
+ bt_audio_msg_header_t h;
+ uint8_t mode; /* Control Mode */
+ uint8_t key; /* Control Key */
+} __attribute__ ((packed));
+
+/* Function declaration */
+
+/* Opens a connection to the audio service: return a socket descriptor */
+int bt_audio_service_open();
+
+/* Closes a connection to the audio service */
+int bt_audio_service_close(int sk);
+
+/* Receives stream data file descriptor : must be called after a
+BT_STREAMFD_IND message is returned */
+int bt_audio_service_get_data_fd(int sk);
+
+/* Human readable message type string */
+const char *bt_audio_strmsg(int type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BT_AUDIOCLIENT_H */
diff --git a/audio/manager.c b/audio/manager.c
index b0cc3340..d80fb962 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -1052,7 +1052,7 @@ static void parse_stored_devices(char *key, char *value, void *data)
return;
str2ba(key, &dst);
- device = manager_find_device(&dst, PKT_ROLE_AUTO, FALSE);
+ device = manager_find_device(&dst, NULL, FALSE);
if (device)
return;
@@ -1111,7 +1111,7 @@ static void register_devices_stored(const char *adapter)
return;
str2ba(addr, &dst);
- device = manager_find_device(&dst, PKT_ROLE_AUTO, FALSE);
+ device = manager_find_device(&dst, NULL, FALSE);
if (device) {
info("Setting %s as default device", addr);
diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c
index efe1d2aa..f4a20015 100644
--- a/audio/pcm_bluetooth.c
+++ b/audio/pcm_bluetooth.c
@@ -76,33 +76,60 @@
#endif
struct bluetooth_a2dp {
- sbc_t sbc; /* Codec data */
- int codesize; /* SBC codesize */
- int samples; /* Number of encoded samples */
- uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */
- int count; /* Codec transfer buffer counter */
-
- int nsamples; /* Cumulative number of codec samples */
- uint16_t seq_num; /* Cumulative packet sequence */
- int frame_count; /* Current frames in buffer*/
+ sbc_capabilities_t sbc_capabilities;
+ sbc_t sbc; /* Codec data */
+ int sbc_initialized; /* Keep track if the encoder is initialized */
+ int codesize; /* SBC codesize */
+ int samples; /* Number of encoded samples */
+ uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */
+ int count; /* Codec transfer buffer counter */
+
+ int nsamples; /* Cumulative number of codec samples */
+ uint16_t seq_num; /* Cumulative packet sequence */
+ int frame_count; /* Current frames in buffer*/
+};
+
+struct bluetooth_alsa_config {
+ char device[18]; /* Address of the remote Device */
+ int has_device;
+ uint8_t transport; /* Requested transport */
+ int has_transport;
+ uint16_t rate;
+ int has_rate;
+ uint8_t channel_mode; /* A2DP only */
+ int has_channel_mode;
+ uint8_t allocation_method; /* A2DP only */
+ int has_allocation_method;
+ uint8_t subbands; /* A2DP only */
+ int has_subbands;
+ uint8_t block_length; /* A2DP only */
+ int has_block_length;
+ uint8_t bitpool; /* A2DP only */
+ int has_bitpool;
};
struct bluetooth_data {
snd_pcm_ioplug_t io;
+ struct bluetooth_alsa_config alsa_config; /* ALSA resource file parameters */
volatile snd_pcm_sframes_t hw_ptr;
- struct ipc_data_cfg cfg; /* Bluetooth device config */
- struct pollfd stream; /* Audio stream filedescriptor */
- struct pollfd server; /* Audio daemon filedescriptor */
- uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */
- int count; /* Transfer buffer counter */
- struct bluetooth_a2dp a2dp; /* A2DP data */
-
- pthread_t hw_thread; /* Makes virtual hw pointer move */
- int pipefd[2]; /* Inter thread communication */
+ int transport; /* chosen transport SCO or AD2P */
+ int link_mtu; /* MTU for selected transport channel */
+ volatile struct pollfd stream; /* Audio stream filedescriptor */
+ struct pollfd server; /* Audio daemon filedescriptor */
+ uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */
+ int count; /* Transfer buffer counter */
+ struct bluetooth_a2dp a2dp; /* A2DP data */
+
+ pthread_t hw_thread; /* Makes virtual hw pointer move */
+ int pipefd[2]; /* Inter thread communication */
int stopped;
sig_atomic_t reset; /* Request XRUN handling */
};
+static int audioservice_send(int sk, const bt_audio_msg_header_t *msg);
+static int audioservice_expect(int sk, bt_audio_msg_header_t *outmsg,
+ int expected_type);
+
static int bluetooth_start(snd_pcm_ioplug_t *io)
{
DBG("bluetooth_start %p", io);
@@ -215,62 +242,6 @@ iter_sleep:
pthread_exit(NULL);
}
-#if 0
-static int bluetooth_state_init(struct ipc_packet *pkt, int newstate)
-{
- struct ipc_data_state *state = (void *) pkt->data;
-
- pkt->length = sizeof(*state);
- pkt->type = PKT_TYPE_STATE_REQ;
- pkt->error = PKT_ERROR_NONE;
- state->state = newstate;
-
- return 0;
-}
-
-static int bluetooth_state(struct bluetooth_data *data, int newstate)
-{
- char buf[IPC_MTU];
- struct ipc_packet *pkt = (void *) buf;
- struct ipc_data_state *state = (void *) pkt->data;
- int ret;
-
- memset(buf, 0, sizeof(buf));
-
- ret = bluetooth_state_init(pkt, newstate);
- if (ret < 0)
- return -ret;
-
- ret = send(data->server.fd, pkt, sizeof(*pkt) + pkt->length, 0);
- if (ret < 0)
- return -errno;
- else if (ret == 0)
- return -EIO;
-
- DBG("OK - %d bytes sent. Waiting for response...", ret);
-
- memset(buf, 0, sizeof(buf));
-
- ret = recv(data->server.fd, buf, sizeof(*pkt) + sizeof(*state), 0);
- if (ret < 0)
- return -errno;
- else if (ret == 0)
- return -EIO;
-
- if (pkt->type != PKT_TYPE_STATE_RSP) {
- SNDERR("Unexpected packet type %d received", pkt->type);
- return -EINVAL;
- }
-
- if (pkt->error != PKT_ERROR_NONE) {
- SNDERR("Error %d while configuring device", pkt->error);
- return -pkt->error;
- }
-
- return 0;
-}
-#endif
-
static int bluetooth_playback_start(snd_pcm_ioplug_t *io)
{
struct bluetooth_data *data = io->private_data;
@@ -278,9 +249,6 @@ static int bluetooth_playback_start(snd_pcm_ioplug_t *io)
DBG("%p", io);
-#if 0
- bluetooth_state(data, STATE_STREAMING);
-#endif
data->stopped = 0;
if (data->hw_thread)
@@ -297,9 +265,6 @@ static int bluetooth_playback_stop(snd_pcm_ioplug_t *io)
DBG("%p", io);
-#if 0
- bluetooth_state(data, STATE_CONNECTED);
-#endif
data->stopped = 1;
return 0;
@@ -317,7 +282,7 @@ static void bluetooth_exit(struct bluetooth_data *data)
struct bluetooth_a2dp *a2dp = &data->a2dp;
if (data->server.fd >= 0)
- close(data->server.fd);
+ bt_audio_service_close(data->server.fd);
if (data->stream.fd >= 0)
close(data->stream.fd);
@@ -327,7 +292,7 @@ static void bluetooth_exit(struct bluetooth_data *data)
pthread_join(data->hw_thread, 0);
}
- if (data->cfg.codec == CFG_CODEC_SBC)
+ if (a2dp->sbc_initialized)
sbc_finish(&a2dp->sbc);
if (data->pipefd[0] > 0)
@@ -354,12 +319,27 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io)
{
struct bluetooth_data *data = io->private_data;
char c = 'w';
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_streamstart_req *start_req = (void*) buf;
+ struct bt_streamstart_rsp *start_rsp = (void*) buf;
+ struct bt_datafd_ind *datafd_ind = (void*) buf;
+ uint32_t period_count = io->buffer_size / io->period_size;
+ int opt_name, err;
+ struct timeval t = { 0, period_count };
DBG("Preparing with io->period_size=%lu io->buffer_size=%lu",
io->period_size, io->buffer_size);
data->reset = 0;
+ /* As we're gonna receive messages on the server socket, we have to stop the
+ hw thread that is polling on it, if any */
+ if (data->hw_thread) {
+ pthread_cancel(data->hw_thread);
+ pthread_join(data->hw_thread, 0);
+ data->hw_thread = 0;
+ }
+
if (io->stream == SND_PCM_STREAM_PLAYBACK)
/* If not null for playback, xmms doesn't display time
* correctly */
@@ -369,63 +349,174 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io)
* If it is, capture won't start */
data->hw_ptr = io->period_size;
- /* wake up any client polling at us */
- return write(data->pipefd[1], &c, 1);
-}
+ /* send start */
+ memset(start_req, 0, BT_AUDIO_IPC_PACKET_SIZE);
+ start_req->h.msg_type = BT_STREAMSTART_REQ;
-static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io,
- snd_pcm_hw_params_t *params)
-{
- struct bluetooth_data *data = io->private_data;
- uint32_t period_count = io->buffer_size / io->period_size;
- int opt_name, err;
+ err = audioservice_send(data->server.fd, &start_req->h);
+ if (err < 0)
+ return err;
+
+ err = audioservice_expect(data->server.fd, &start_rsp->h,
+ BT_STREAMSTART_RSP);
+ if (err < 0)
+ return err;
+
+ if (start_rsp->posix_errno != 0) {
+ SNDERR("BT_START failed : %s(%d)",
+ strerror(start_rsp->posix_errno),
+ start_rsp->posix_errno);
+ return -start_rsp->posix_errno;
+ }
+
+ err = audioservice_expect(data->server.fd, &datafd_ind->h,
+ BT_STREAMFD_IND);
+ if (err < 0)
+ return err;
+
+ if (data->stream.fd >= 0)
+ close(data->stream.fd);
+
+ data->stream.fd = bt_audio_service_get_data_fd(data->server.fd);
+ if (data->stream.fd < 0) {
+ return -errno;
+ }
- DBG("fd=%d period_count=%d", data->stream.fd, period_count);
+ if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+ opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ?
+ SO_SNDTIMEO : SO_RCVTIMEO;
- opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ?
+ if (setsockopt(data->stream.fd, SOL_SOCKET, opt_name, &t,
+ sizeof(t)) < 0)
+ return -errno;
+ } else {
+ opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ?
SCO_TXBUFS : SCO_RXBUFS;
- if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count,
+ if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count,
sizeof(period_count)) == 0)
- return 0;
+ return 0;
- opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ?
+ opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ?
SO_SNDBUF : SO_RCVBUF;
- if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count,
+ if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count,
sizeof(period_count)) == 0)
- return 0;
-
- err = errno;
+ return 0;
- SNDERR("%s (%d)", strerror(err), err);
+ /* FIXME : handle error codes */
+ }
- /* FIXME: We should not ignores errors in the future. */
- return 0;
+ /* wake up any client polling at us */
+ return write(data->pipefd[1], &c, 1);
}
static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io,
snd_pcm_hw_params_t *params)
{
struct bluetooth_data *data = io->private_data;
- uint32_t period_count = io->buffer_size / io->period_size;
- int opt_name, err;
- struct timeval t = { 0, period_count };
+ struct bluetooth_a2dp *a2dp = &data->a2dp;
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_setconfiguration_req *setconf_req = (void*) buf;
+ struct bt_setconfiguration_rsp *setconf_rsp = (void*) buf;
+ int err;
+ sbc_capabilities_t active_capabilities;
- DBG("fd=%d period_count=%d", data->stream.fd, period_count);
+ DBG("Preparing with io->period_size=%lu io->buffer_size=%lu",
+ io->period_size, io->buffer_size);
- opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ?
- SO_SNDTIMEO : SO_RCVTIMEO;
+ /* FIXME: this needs to be really implemented (take into account
+ real asoundrc settings + ALSA hw settings ) once server side sends us
+ more than one possible configuration */
+ active_capabilities = a2dp->sbc_capabilities;
- if (setsockopt(data->stream.fd, SOL_SOCKET, opt_name, &t,
- sizeof(t)) == 0)
- return 0;
+ memset(setconf_req, 0, BT_AUDIO_IPC_PACKET_SIZE);
+ setconf_req->h.msg_type = BT_SETCONFIGURATION_REQ;
+ setconf_req->sbc_capabilities = active_capabilities;
- err = errno;
+ err = audioservice_send(data->server.fd, &setconf_req->h);
+ if (err < 0)
+ return err;
- SNDERR("%s (%d)", strerror(err), err);
+ err = audioservice_expect(data->server.fd, &setconf_rsp->h,
+ BT_SETCONFIGURATION_RSP);
+ if (err < 0)
+ return err;
- return -err;
+ if (setconf_rsp->posix_errno != 0) {
+ SNDERR("BT_SETCONFIGURATION failed : %s(%d)",
+ strerror(setconf_rsp->posix_errno),
+ setconf_rsp->posix_errno);
+ return -setconf_rsp->posix_errno;
+ }
+
+ /* Setup SBC encoder now we agree on parameters */
+ if (a2dp->sbc_initialized)
+ sbc_finish(&a2dp->sbc);
+
+ /* FIXME: init using flags? */
+ sbc_init(&a2dp->sbc, 0);
+ a2dp->sbc_initialized = 1;
+ if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000)
+ a2dp->sbc.rate = 16000;
+
+ if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_32000)
+ a2dp->sbc.rate = 32000;
+
+ if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_44100)
+ a2dp->sbc.rate = 44100;
+
+ if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_48000)
+ a2dp->sbc.rate = 48000;
+
+ if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ a2dp->sbc.channels = 1;
+ else
+ a2dp->sbc.channels = 2;
+
+ if (active_capabilities.channel_mode &
+ (BT_A2DP_CHANNEL_MODE_MONO || BT_A2DP_CHANNEL_MODE_JOINT_STEREO))
+ a2dp->sbc.joint = 1;
+ else
+ a2dp->sbc.joint = 0;
+
+ a2dp->sbc.allocation = active_capabilities.allocation_method
+ == BT_A2DP_ALLOCATION_SNR ? 0x01 : 0x00;
+
+ switch (active_capabilities.subbands) {
+ case BT_A2DP_SUBBANDS_4:
+ a2dp->sbc.subbands = 4;
+ break;
+ case BT_A2DP_SUBBANDS_8:
+ a2dp->sbc.subbands = 8;
+ break;
+ }
+
+ switch (active_capabilities.block_length) {
+ case BT_A2DP_BLOCK_LENGTH_4:
+ a2dp->sbc.blocks = 4;
+ break;
+ case BT_A2DP_BLOCK_LENGTH_8:
+ a2dp->sbc.blocks = 8;
+ break;
+ case BT_A2DP_BLOCK_LENGTH_12:
+ a2dp->sbc.blocks = 12;
+ break;
+ case BT_A2DP_BLOCK_LENGTH_16:
+ a2dp->sbc.blocks = 16;
+ break;
+ }
+
+ a2dp->sbc.bitpool = active_capabilities.max_bitpool;
+ a2dp->codesize = a2dp->sbc.subbands * a2dp->sbc.blocks *
+ a2dp->sbc.channels * 2;
+ a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
+
+ DBG("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
+ a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks,
+ a2dp->sbc.bitpool);
+
+ return 0;
}
static int bluetooth_poll_descriptors(snd_pcm_ioplug_t *io,
@@ -504,7 +595,6 @@ static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io,
snd_pcm_uframes_t size)
{
struct bluetooth_data *data = io->private_data;
- struct ipc_data_cfg cfg = data->cfg;
snd_pcm_uframes_t frames_to_write, ret;
unsigned char *buff;
int nrecv, frame_size = 0;
@@ -517,7 +607,7 @@ static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io,
frame_size = areas->step / 8;
- nrecv = recv(data->stream.fd, data->buffer, cfg.pkt_len,
+ nrecv = recv(data->stream.fd, data->buffer, data->link_mtu,
MSG_WAITALL | (io->nonblock ? MSG_DONTWAIT : 0));
if (nrecv < 0) {
@@ -525,28 +615,28 @@ static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io,
goto done;
}
- if (nrecv != cfg.pkt_len) {
+ if (nrecv != data->link_mtu) {
ret = -EIO;
SNDERR(strerror(-ret));
goto done;
}
/* Increment hardware transmition pointer */
- data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) %
+ data->hw_ptr = (data->hw_ptr + data->link_mtu / frame_size) %
io->buffer_size;
proceed:
buff = (unsigned char *) areas->addr +
(areas->first + areas->step * offset) / 8;
- if ((data->count + size * frame_size) <= cfg.pkt_len)
+ if ((data->count + size * frame_size) <= data->link_mtu)
frames_to_write = size;
else
- frames_to_write = (cfg.pkt_len - data->count) / frame_size;
+ frames_to_write = (data->link_mtu - data->count) / frame_size;
memcpy(buff, data->buffer + data->count, frame_size * frames_to_write);
data->count += (frame_size * frames_to_write);
- data->count %= cfg.pkt_len;
+ data->count %= data->link_mtu;
/* Return written frames count */
ret = frames_to_write;
@@ -562,7 +652,6 @@ static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io,
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;
uint8_t *buff;
@@ -579,10 +668,10 @@ static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io,
}
frame_size = areas->step / 8;
- if ((data->count + size * frame_size) <= cfg.pkt_len)
+ if ((data->count + size * frame_size) <= data->link_mtu)
frames_to_read = size;
else
- frames_to_read = (cfg.pkt_len - data->count) / frame_size;
+ frames_to_read = (data->link_mtu - data->count) / frame_size;
DBG("count=%d frames_to_read=%lu", data->count, frames_to_read);
@@ -593,12 +682,12 @@ static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io,
/* Remember we have some frames in the pipe now */
data->count += frames_to_read * frame_size;
- if (data->count != cfg.pkt_len) {
+ if (data->count != data->link_mtu) {
ret = frames_to_read;
goto done;
}
- rsend = send(data->stream.fd, data->buffer, cfg.pkt_len,
+ rsend = send(data->stream.fd, data->buffer, data->link_mtu,
io->nonblock ? MSG_DONTWAIT : 0);
if (rsend > 0) {
/* Reset count pointer */
@@ -710,7 +799,7 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io,
frames_to_read = (a2dp->codesize - data->count) / frame_size;
DBG("count=%d frames_to_read=%lu", data->count, frames_to_read);
- DBG("a2dp.count=%d cfg.pkt_len=%d", a2dp->count, data->cfg.pkt_len);
+ DBG("a2dp.count=%d data.link_mtu=%d", a2dp->count, data->link_mtu);
/* FIXME: If state is not streaming then return */
@@ -747,10 +836,11 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io,
written, a2dp->count);
/* No space left for another frame then send */
- if (a2dp->count + written >= data->cfg.pkt_len) {
+ if (a2dp->count + written >= data->link_mtu) {
avdtp_write(data);
- DBG("sending packet %d, count %d, pkt_len %u", c,
- old_count, data->cfg.pkt_len);
+ DBG("sending packet %d, count %d, link_mtu %u",
+ a2dp->seq_num, a2dp->count,
+ data->link_mtu);
}
ret += frames_to_read;
@@ -791,7 +881,6 @@ static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = {
.stop = bluetooth_playback_stop,
.pointer = bluetooth_pointer,
.close = bluetooth_close,
- .hw_params = bluetooth_hsp_hw_params,
.prepare = bluetooth_prepare,
.transfer = bluetooth_hsp_write,
.poll_descriptors = bluetooth_playback_poll_descriptors,
@@ -804,7 +893,6 @@ static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = {
.stop = bluetooth_stop,
.pointer = bluetooth_pointer,
.close = bluetooth_close,
- .hw_params = bluetooth_hsp_hw_params,
.prepare = bluetooth_prepare,
.transfer = bluetooth_hsp_read,
.poll_descriptors = bluetooth_poll_descriptors,
@@ -841,7 +929,6 @@ static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = {
static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io)
{
struct bluetooth_data *data = io->private_data;
- struct ipc_data_cfg cfg = data->cfg;
snd_pcm_access_t access_list[] = {
SND_PCM_ACCESS_RW_INTERLEAVED,
/* Mmap access is really useless fo this driver, but we
@@ -852,7 +939,7 @@ static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io)
unsigned int format_list[] = {
SND_PCM_FORMAT_S16_LE
};
- int err, channels;
+ int err;
/* access type */
err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,
@@ -867,21 +954,20 @@ static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io)
return err;
/* supported channels */
- channels = cfg.mode == CFG_MODE_MONO ? 1 : 2;
err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS,
- channels, channels);
+ 1, 1);
if (err < 0)
return err;
/* supported rate */
err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE,
- cfg.rate, cfg.rate);
+ 8000, 8000);
if (err < 0)
return err;
/* supported block size */
err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
- cfg.pkt_len, cfg.pkt_len);
+ data->link_mtu, data->link_mtu);
if (err < 0)
return err;
@@ -896,7 +982,7 @@ static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io)
static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io)
{
struct bluetooth_data *data = io->private_data;
- struct ipc_data_cfg cfg = data->cfg;
+ struct bluetooth_a2dp *a2dp = &data->a2dp;
snd_pcm_access_t access_list[] = {
SND_PCM_ACCESS_RW_INTERLEAVED,
/* Mmap access is really useless fo this driver, but we
@@ -907,10 +993,12 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io)
unsigned int format_list[] = {
SND_PCM_FORMAT_S16_LE
};
+ unsigned int rate_list[4];
+ unsigned int rate_count;
+ int err, min_channels, max_channels;
unsigned int period_list[] = {
4096, /* 23/46ms (stereo/mono 16bit at 44.1kHz) */
};
- int err, channels;
/* access type */
err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,
@@ -925,15 +1013,18 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io)
return err;
/* supported channels */
- channels = cfg.mode == CFG_MODE_MONO ? 1 : 2;
- err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS,
- channels, channels);
- if (err < 0)
- return err;
+ if (a2dp->sbc_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ min_channels = 1;
+ else
+ min_channels = 2;
- /* supported rate */
- err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE,
- cfg.rate, cfg.rate);
+ if (a2dp->sbc_capabilities.channel_mode & (~BT_A2DP_CHANNEL_MODE_MONO))
+ max_channels = 2;
+ else
+ max_channels = 1;
+
+ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS,
+ min_channels, max_channels);
if (err < 0)
return err;
@@ -949,96 +1040,44 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io)
if (err < 0)
return err;
- return 0;
-}
-
-static int bluetooth_recvmsg_fd(struct bluetooth_data *data)
-{
- char cmsg_b[CMSG_SPACE(sizeof(int))], m;
- int err, ret;
- struct iovec iov = { &m, sizeof(m) };
- struct msghdr msgh;
- struct cmsghdr *cmsg;
-
- memset(&msgh, 0, sizeof(msgh));
- msgh.msg_iov = &iov;
- msgh.msg_iovlen = 1;
- msgh.msg_control = &cmsg_b;
- msgh.msg_controllen = CMSG_LEN(sizeof(int));
-
- ret = recvmsg(data->server.fd, &msgh, 0);
- if (ret < 0) {
- err = errno;
- SNDERR("Unable to receive fd: %s (%d)", strerror(err), err);
- return -err;
+ /* supported rates */
+ rate_count = 0;
+ if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000) {
+ rate_list[rate_count] = 16000;
+ rate_count++;
}
- /* Receive auxiliary data in msgh */
- for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;
- cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
- if (cmsg->cmsg_level == SOL_SOCKET
- && cmsg->cmsg_type == SCM_RIGHTS) {
- data->stream.fd = (*(int *) CMSG_DATA(cmsg));
- DBG("stream_fd=%d", data->stream.fd);
- return 0;
- }
+ if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_32000) {
+ rate_list[rate_count] = 32000;
+ rate_count++;
}
- return -EINVAL;
-}
-
-static int bluetooth_a2dp_init(struct bluetooth_data *data,
- struct ipc_codec_sbc *sbc)
-{
- struct bluetooth_a2dp *a2dp = &data->a2dp;
- struct ipc_data_cfg *cfg = &data->cfg;
-
- if (cfg == NULL) {
- SNDERR("Error getting codec parameters");
- return -1;
+ if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_44100) {
+ rate_list[rate_count] = 44100;
+ rate_count++;
}
- if (cfg->codec != CFG_CODEC_SBC)
- return -1;
-
- /* FIXME: init using flags? */
- sbc_init(&a2dp->sbc, 0);
- a2dp->sbc.rate = cfg->rate;
- a2dp->sbc.channels = cfg->mode == CFG_MODE_MONO ? 1 : 2;
- if (cfg->mode == CFG_MODE_MONO || cfg->mode == CFG_MODE_JOINT_STEREO)
- a2dp->sbc.joint = 1;
- a2dp->sbc.allocation = sbc->allocation;
- a2dp->sbc.subbands = sbc->subbands;
- a2dp->sbc.blocks = sbc->blocks;
- a2dp->sbc.bitpool = sbc->bitpool;
- a2dp->codesize = a2dp->sbc.subbands * a2dp->sbc.blocks *
- a2dp->sbc.channels * 2;
- a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
+ if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_48000) {
+ rate_list[rate_count] = 48000;
+ rate_count++;
+ }
- DBG("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
- a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks,
- a2dp->sbc.bitpool);
+ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_RATE,
+ rate_count, rate_list);
+ if (err < 0)
+ return err;
return 0;
}
-static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream,
- snd_config_t *conf)
+static int bluetooth_parse_config(snd_config_t *conf,
+ struct bluetooth_alsa_config *bt_config)
{
- struct ipc_data_cfg *cfg = (void *) pkt->data;
- struct ipc_codec_sbc *sbc = (void *) cfg->data;
snd_config_iterator_t i, next;
const char *addr, *pref;
const char *mode, *allocation, *rate, *subbands, *blocks, *bitpool;
- switch (stream) {
- case SND_PCM_STREAM_PLAYBACK:
- cfg->fd_opt = CFG_FD_OPT_WRITE;
- break;
- case SND_PCM_STREAM_CAPTURE:
- cfg->fd_opt = CFG_FD_OPT_READ;
- break;
- }
+ memset(bt_config, 0, sizeof(struct bluetooth_alsa_config));
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
@@ -1056,7 +1095,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream,
return -EINVAL;
}
- strncpy(pkt->device, addr, 18);
+ bt_config->has_device = 1;
+ strncpy(bt_config->device, addr, 18);
continue;
}
@@ -1066,14 +1106,18 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream,
return -EINVAL;
}
- if (strcmp(pref, "auto") == 0)
- pkt->role = PKT_ROLE_AUTO;
- else if (strcmp(pref, "voice") == 0 ||
+ if (strcmp(pref, "auto") == 0) {
+ bt_config->transport = BT_CAPABILITIES_TRANSPORT_ANY;
+ bt_config->has_transport = 1;
+ } else if (strcmp(pref, "voice") == 0 ||
strcmp(pref, "hfp") == 0) {
- pkt->role = PKT_ROLE_VOICE;
+ bt_config->transport = BT_CAPABILITIES_TRANSPORT_SCO;
+ bt_config->has_transport = 1;
} else if (strcmp(pref, "hifi") == 0 ||
- strcmp(pref, "a2dp") == 0)
- pkt->role = PKT_ROLE_HIFI;
+ strcmp(pref, "a2dp") == 0) {
+ bt_config->transport = BT_CAPABILITIES_TRANSPORT_A2DP;
+ bt_config->has_transport = 1;
+ }
continue;
}
@@ -1083,7 +1127,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream,
return -EINVAL;
}
- cfg->rate = atoi(rate);
+ bt_config->rate = atoi(rate);
+ bt_config->has_rate = 1;
continue;
}
@@ -1093,16 +1138,22 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream,
return -EINVAL;
}
- if (strcmp(pref, "auto") == 0)
- cfg->mode = CFG_MODE_AUTO;
- else if (strcmp(pref, "mono") == 0)
- cfg->mode = CFG_MODE_MONO;
- else if (strcmp(pref, "dual") == 0)
- cfg->mode = CFG_MODE_DUAL_CHANNEL;
- else if (strcmp(pref, "stereo") == 0)
- cfg->mode = CFG_MODE_STEREO;
- else if (strcmp(pref, "joint") == 0)
- cfg->mode = CFG_MODE_JOINT_STEREO;
+ if (strcmp(pref, "auto") == 0) {
+ bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO;
+ bt_config->has_channel_mode = 1;
+ } else if (strcmp(pref, "mono") == 0) {
+ bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+ bt_config->has_channel_mode = 1;
+ } else if (strcmp(pref, "dual") == 0) {
+ bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+ bt_config->has_channel_mode = 1;
+ } else if (strcmp(pref, "stereo") == 0) {
+ bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
+ bt_config->has_channel_mode = 1;
+ } else if (strcmp(pref, "joint") == 0) {
+ bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
+ bt_config->has_channel_mode = 1;
+ }
continue;
}
@@ -1112,12 +1163,16 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream,
return -EINVAL;
}
- if (strcmp(pref, "auto") == 0)
- sbc->allocation = CFG_ALLOCATION_AUTO;
- else if (strcmp(pref, "loudness") == 0)
- sbc->allocation = CFG_ALLOCATION_LOUDNESS;
- else if (strcmp(pref, "snr") == 0)
- sbc->allocation = CFG_ALLOCATION_SNR;
+ if (strcmp(pref, "auto") == 0) {
+ bt_config->allocation_method = BT_A2DP_ALLOCATION_AUTO;
+ bt_config->has_allocation_method = 1;
+ } else if (strcmp(pref, "loudness") == 0) {
+ bt_config->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
+ bt_config->has_allocation_method = 1;
+ } else if (strcmp(pref, "snr") == 0) {
+ bt_config->allocation_method = BT_A2DP_ALLOCATION_SNR;
+ bt_config->has_allocation_method = 1;
+ }
continue;
}
@@ -1127,7 +1182,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream,
return -EINVAL;
}
- sbc->subbands = atoi(subbands);
+ bt_config->subbands = atoi(subbands);
+ bt_config->has_subbands = 1;
continue;
}
@@ -1137,7 +1193,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream,
return -EINVAL;
}
- sbc->blocks = atoi(blocks);
+ bt_config->block_length = atoi(blocks);
+ bt_config->has_block_length = 1;
continue;
}
@@ -1147,7 +1204,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream,
return -EINVAL;
}
- sbc->bitpool = atoi(bitpool);
+ bt_config->bitpool = atoi(bitpool);
+ bt_config->has_bitpool = 1;
continue;
}
@@ -1155,139 +1213,89 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream,
return -EINVAL;
}
- pkt->length = sizeof(*cfg) + sizeof(*sbc);
- pkt->type = PKT_TYPE_CFG_REQ;
- pkt->error = PKT_ERROR_NONE;
-
return 0;
}
-static int bluetooth_cfg(struct bluetooth_data *data, snd_pcm_stream_t stream,
- snd_config_t *conf)
+static int audioservice_send(int sk, const bt_audio_msg_header_t *msg)
{
- int ret, total;
- char buf[IPC_MTU];
- struct ipc_packet *pkt = (void *) buf;
- struct ipc_data_cfg *cfg = (void *) pkt->data;
- struct ipc_codec_sbc *sbc = (void *) cfg->data;
-
- DBG("Sending PKT_TYPE_CFG_REQ...");
-
- memset(buf, 0, sizeof(buf));
-
- ret = bluetooth_cfg_init(pkt, stream, conf);
- if (ret < 0)
- return -ret;
-
- ret = send(data->server.fd, pkt, sizeof(*pkt) + pkt->length, 0);
- if (ret < 0)
- return -errno;
- else if (ret == 0)
- return -EIO;
-
- DBG("OK - %d bytes sent. Waiting for response...", ret);
-
- memset(buf, 0, sizeof(buf));
-
- ret = recv(data->server.fd, buf, sizeof(*pkt) + sizeof(*cfg), 0);
- if (ret < 0)
- return -errno;
- else if (ret == 0)
- return -EIO;
-
- total = ret;
-
- if (pkt->type != PKT_TYPE_CFG_RSP) {
- SNDERR("Unexpected packet type %d received", pkt->type);
- return -EINVAL;
- }
-
- if (pkt->error != PKT_ERROR_NONE) {
- SNDERR("Error %d while configuring device", pkt->error);
- return -pkt->error;
- }
-
- if (cfg->codec != CFG_CODEC_SBC)
- goto done;
-
- ret = recv(data->server.fd, sbc, sizeof(*sbc), 0);
- if (ret < 0)
- return -errno;
- else if (ret == 0)
- return -EIO;
-
- total += ret;
-
-done:
- DBG("OK - %d bytes received", total);
+ int err;
- if (pkt->length != (total - sizeof(struct ipc_packet))) {
- SNDERR("Error while configuring device: packet size doesn't match");
- return -EINVAL;
+ DBG("sending %s", bt_audio_strmsg(msg->msg_type));
+ if (send(sk, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0)
+ err = 0;
+ else {
+ err = -errno;
+ SNDERR("Error sending data to audio service: %s(%d)",
+ strerror(errno), errno);
}
- memcpy(&data->cfg, cfg, sizeof(*cfg));
-
- DBG("Device configuration:");
-
- DBG("\n\tfd=%d\n\tfd_opt=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u",
- data->stream.fd, data->cfg.fd_opt, data->cfg.pkt_len,
- data->cfg.sample_size, data->cfg.rate);
+ return err;
+}
- if (data->cfg.codec == CFG_CODEC_SBC) {
- ret = bluetooth_a2dp_init(data, sbc);
- if (ret < 0)
- return ret;
+static int audioservice_recv(int sk, bt_audio_msg_header_t *inmsg)
+{
+ int err;
+ const char *type;
+
+ DBG("trying to receive msg from audio service...");
+ if (recv(sk, inmsg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0) {
+ type = bt_audio_strmsg(inmsg->msg_type);
+ if (type) {
+ DBG("Received %s", type);
+ err = 0;
+ } else {
+ err = -EINVAL;
+ SNDERR("Bogus message type %d "
+ "received from audio service",
+ inmsg->msg_type);
+ }
+ } else {
+ err = -errno;
+ SNDERR("Error receiving data from audio service: %s(%d)",
+ strerror(errno), errno);
}
- ret = bluetooth_recvmsg_fd(data);
- if (ret < 0)
- return ret;
+ return err;
+}
- if (data->stream.fd == -1) {
- SNDERR("Error while configuring device: could not acquire audio socket");
- return -EINVAL;
+static int audioservice_expect(int sk, bt_audio_msg_header_t *outmsg,
+ int expected_type)
+{
+ int err = audioservice_recv(sk, outmsg);
+ if (err == 0) {
+ if (outmsg->msg_type != expected_type) {
+ err = -EINVAL;
+ SNDERR("Bogus message %s received while "
+ "%s was expected",
+ bt_audio_strmsg(outmsg->msg_type),
+ bt_audio_strmsg(expected_type));
+ }
}
-
- /* It is possible there is some outstanding
- data in the pipe - we have to empty it */
- while (recv(data->stream.fd, data->buffer, data->cfg.pkt_len,
- MSG_DONTWAIT) > 0);
-
- memset(data->buffer, 0, sizeof(data->buffer));
-
- return 0;
+ return err;
}
static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream,
snd_config_t *conf)
{
int sk, err;
- struct sockaddr_un addr = {
- AF_UNIX, IPC_SOCKET_NAME
- };
-
- if (!data)
- return -EINVAL;
+ struct bluetooth_alsa_config *alsa_conf = &data->alsa_config;
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_getcapabilities_req *getcaps_req = (void*) buf;
+ struct bt_getcapabilities_rsp *getcaps_rsp = (void*) buf;
memset(data, 0, sizeof(struct bluetooth_data));
+ err = bluetooth_parse_config(conf, alsa_conf);
+ if (err < 0)
+ return err;
+
data->server.fd = -1;
data->stream.fd = -1;
- sk = socket(PF_LOCAL, SOCK_STREAM, 0);
- if (sk < 0) {
- err = errno;
- SNDERR("Cannot open socket: %s (%d)", strerror(err), err);
- return -err;
- }
-
- DBG("Connecting to address: %s", addr.sun_path + 1);
- if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- err = errno;
- SNDERR("Connection fail", strerror(err), err);
- close(sk);
- return -err;
+ sk = bt_audio_service_open();
+ if(sk <= 0) {
+ err = -errno;
+ goto failed;
}
data->server.fd = sk;
@@ -1296,14 +1304,56 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream,
data->pipefd[0] = -1;
data->pipefd[1] = -1;
- if (pipe(data->pipefd) < 0)
- return -errno;
- if (fcntl(data->pipefd[0], F_SETFL, O_NONBLOCK) < 0)
- return -errno;
- if (fcntl(data->pipefd[1], F_SETFL, O_NONBLOCK) < 0)
- return -errno;
+ if (pipe(data->pipefd) < 0) {
+ err = -errno;
+ goto failed;
+ }
+ if (fcntl(data->pipefd[0], F_SETFL, O_NONBLOCK) < 0) {
+ err = -errno;
+ goto failed;
+ }
+ if (fcntl(data->pipefd[1], F_SETFL, O_NONBLOCK) < 0) {
+ err = -errno;
+ goto failed;
+ }
+
+ memset(getcaps_req, 0, BT_AUDIO_IPC_PACKET_SIZE);
+ getcaps_req->h.msg_type = BT_GETCAPABILITIES_REQ;
+ strncpy(getcaps_req->device, alsa_conf->device, 18);
+ if (alsa_conf->has_transport)
+ getcaps_req->transport = alsa_conf->transport;
+ else
+ getcaps_req->transport = BT_CAPABILITIES_TRANSPORT_ANY;
+
+ getcaps_req->access_mode = (stream == SND_PCM_STREAM_PLAYBACK ?
+ BT_CAPABILITIES_ACCESS_MODE_WRITE :
+ BT_CAPABILITIES_ACCESS_MODE_READ);
+
+ err = audioservice_send(data->server.fd, &getcaps_req->h);
+ if (err < 0)
+ goto failed;
+
+ err = audioservice_expect(data->server.fd, &getcaps_rsp->h, BT_GETCAPABILITIES_RSP);
+ if (err < 0)
+ goto failed;
+
+ if (getcaps_rsp->posix_errno != 0) {
+ SNDERR("BT_GETCAPABILITIES failed : %s(%d)",
+ strerror(getcaps_rsp->posix_errno),
+ getcaps_rsp->posix_errno);
+ return -getcaps_rsp->posix_errno;
+ }
+
+ data->transport = getcaps_rsp->transport;
+ data->link_mtu = getcaps_rsp->link_mtu;
+ if (getcaps_rsp->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
+ data->a2dp.sbc_capabilities = getcaps_rsp->sbc_capabilities;
- return bluetooth_cfg(data, stream, conf);
+ return 0;
+
+failed:
+ bt_audio_service_close(sk);
+ return err;
}
SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)
@@ -1329,7 +1379,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)
data->io.mmap_rw = 0; /* No direct mmap communication */
data->io.private_data = data;
- if (data->cfg.codec == CFG_CODEC_SBC)
+ if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ?
&bluetooth_a2dp_playback :
&bluetooth_a2dp_capture;
@@ -1342,7 +1392,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)
if (err < 0)
goto error;
- if (data->cfg.codec == CFG_CODEC_SBC)
+ if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
err = bluetooth_a2dp_hw_constraint(&data->io);
else
err = bluetooth_hsp_hw_constraint(&data->io);
diff --git a/audio/unix.c b/audio/unix.c
index 71eb570a..ea96bd4f 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -79,7 +79,8 @@ struct unix_client {
struct headset_data hs;
} d;
int sock;
- int fd_opt;
+ int access_mode;
+ int data_fd; /* To be deleted once two phase configuration is fully implemented */
unsigned int req_id;
unsigned int cb_id;
gboolean (*cancel_stream) (struct device *dev, unsigned int id);
@@ -89,6 +90,11 @@ static GSList *clients = NULL;
static int unix_sock = -1;
+static void unix_ipc_sendmsg(struct unix_client *client,
+ const bt_audio_msg_header_t *msg);
+
+static void send_getcapabilities_rsp_error(struct unix_client *client, int err);
+
static void client_free(struct unix_client *client)
{
struct a2dp_data *a2dp;
@@ -121,7 +127,7 @@ static void client_free(struct unix_client *client)
/* Pass file descriptor through local domain sockets (AF_LOCAL, formerly AF_UNIX)
and the sendmsg() system call with the cmsg_type field of a "struct cmsghdr" set
-to SCM_RIGHTS and the data being an integer value equal to the handle of the
+to SCM_RIGHTS and the data being an integer value equal to the handle of the
file descriptor to be passed.*/
static int unix_sendmsg_fd(int sock, int fd)
{
@@ -193,77 +199,29 @@ static void stream_state_changed(struct avdtp_stream *stream,
}
}
-static int unix_send_cfg(int sock, struct ipc_data_cfg *cfg, int fd)
-{
- char buf[IPC_MTU];
- struct ipc_packet *pkt = (void *) buf;
- int len, codec_len;
-
- memset(buf, 0, sizeof(buf));
-
- pkt->type = PKT_TYPE_CFG_RSP;
-
- if (!cfg) {
- pkt->error = EINVAL;
- len = send(sock, pkt, sizeof(struct ipc_packet), 0);
- if (len < 0)
- error("send: %s (%d)", strerror(errno), errno);
- return len;
- }
-
- debug("fd=%d, fd_opt=%u, pkt_len=%u, sample_size=%u, rate=%u",
- fd, cfg->fd_opt, cfg->pkt_len,
- cfg->sample_size, cfg->rate);
-
- if (cfg->codec == CFG_CODEC_SBC)
- codec_len = sizeof(struct ipc_codec_sbc);
- else
- codec_len = 0;
-
- pkt->error = PKT_ERROR_NONE;
- pkt->length = sizeof(struct ipc_data_cfg) + codec_len;
- memcpy(pkt->data, cfg, pkt->length);
-
- len = sizeof(struct ipc_packet) + pkt->length;
- len = send(sock, pkt, len, 0);
- if (len < 0)
- error("Error %s(%d)", strerror(errno), errno);
-
- debug("%d bytes sent", len);
-
- if (fd != -1) {
- len = unix_sendmsg_fd(sock, fd);
- if (len < 0)
- error("Error %s(%d)", strerror(errno), errno);
- debug("%d bytes sent", len);
- }
-
- return 0;
-}
-
static void headset_setup_complete(struct device *dev, void *user_data)
{
struct unix_client *client = user_data;
- struct ipc_data_cfg cfg;
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_getcapabilities_rsp *rsp = (void *) buf;
struct headset_data *hs = &client->d.hs;
- int fd;
client->req_id = 0;
if (!dev) {
- unix_send_cfg(client->sock, NULL, -1);
+ send_getcapabilities_rsp_error(client, EIO);
client->dev = NULL;
return;
}
- switch (client->fd_opt) {
- case CFG_FD_OPT_READ:
+ switch (client->access_mode) {
+ case BT_CAPABILITIES_ACCESS_MODE_READ:
hs->lock = HEADSET_LOCK_READ;
break;
- case CFG_FD_OPT_WRITE:
+ case BT_CAPABILITIES_ACCESS_MODE_WRITE:
hs->lock = HEADSET_LOCK_WRITE;
break;
- case CFG_FD_OPT_READWRITE:
+ case BT_CAPABILITIES_ACCESS_MODE_READWRITE:
hs->lock = HEADSET_LOCK_READ | HEADSET_LOCK_WRITE;
break;
default:
@@ -273,23 +231,22 @@ static void headset_setup_complete(struct device *dev, void *user_data)
if (!headset_lock(dev, hs->lock)) {
error("Unable to lock headset");
- unix_send_cfg(client->sock, NULL, -1);
+ send_getcapabilities_rsp_error(client, EIO);
client->dev = NULL;
return;
}
- memset(&cfg, 0, sizeof(cfg));
+ memset(buf, 0, sizeof(buf));
- cfg.fd_opt = client->fd_opt;
- cfg.codec = CFG_CODEC_SCO;
- cfg.mode = CFG_MODE_MONO;
- cfg.pkt_len = 48;
- cfg.sample_size = 2;
- cfg.rate = 8000;
+ rsp->h.msg_type = BT_GETCAPABILITIES_RSP;
+ rsp->transport = BT_CAPABILITIES_TRANSPORT_SCO;
+ rsp->access_mode = client->access_mode;
+ rsp->link_mtu = 48;
+ rsp->sampling_rate = 8000;
- fd = headset_get_sco_fd(dev);
+ client->data_fd = headset_get_sco_fd(dev);
- unix_send_cfg(client->sock, &cfg, fd);
+ unix_ipc_sendmsg(client, &rsp->h);
}
static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
@@ -297,17 +254,16 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
void *user_data, struct avdtp_error *err)
{
struct unix_client *client = user_data;
- char buf[sizeof(struct ipc_data_cfg) + sizeof(struct ipc_codec_sbc)];
- struct ipc_data_cfg *cfg = (void *) buf;
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_getcapabilities_rsp *rsp = (void *) buf;
struct avdtp_service_capability *cap;
struct avdtp_media_codec_capability *codec_cap;
struct sbc_codec_cap *sbc_cap;
- struct ipc_codec_sbc *sbc = (void *) cfg->data;
struct a2dp_data *a2dp = &client->d.a2dp;
- int fd;
uint16_t imtu, omtu;
GSList *caps;
+ memset(buf, 0, sizeof(buf));
client->req_id = 0;
if (!stream)
@@ -321,7 +277,7 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
a2dp->sep = sep;
a2dp->stream = stream;
- if (!avdtp_stream_get_transport(stream, &fd, &imtu, &omtu, &caps)) {
+ if (!avdtp_stream_get_transport(stream, &client->data_fd, &imtu, &omtu, &caps)) {
error("Unable to get stream transport");
goto failed;
}
@@ -340,66 +296,27 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
goto failed;
}
+ rsp->h.msg_type = BT_GETCAPABILITIES_RSP;
+ rsp->transport = BT_CAPABILITIES_TRANSPORT_A2DP;
+ client->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;
+ rsp->access_mode = client->access_mode;
/* FIXME: Use imtu when fd_opt is CFG_FD_OPT_READ */
- cfg->pkt_len = omtu;
- cfg->fd_opt = CFG_FD_OPT_WRITE;
+ rsp->link_mtu = omtu;
sbc_cap = (void *) codec_cap;
- cfg->sample_size = 2;
- switch (sbc_cap->channel_mode) {
- case A2DP_CHANNEL_MODE_MONO:
- cfg->mode = CFG_MODE_MONO;
- break;
- case A2DP_CHANNEL_MODE_DUAL_CHANNEL:
- cfg->mode = CFG_MODE_DUAL_CHANNEL;
- break;
- case A2DP_CHANNEL_MODE_STEREO:
- cfg->mode = CFG_MODE_STEREO;
- break;
- case A2DP_CHANNEL_MODE_JOINT_STEREO:
- cfg->mode = CFG_MODE_JOINT_STEREO;
- break;
- }
+ /* assignations below are ok as soon as newipc.h and a2dp.h are kept */
+ /* in sync. However it is not possible to cast a struct to another */
+ /* dues to endianess issues */
+ rsp->sbc_capabilities.channel_mode = sbc_cap->channel_mode;
+ rsp->sbc_capabilities.frequency = sbc_cap->frequency;
+ rsp->sbc_capabilities.allocation_method = sbc_cap->allocation_method;
+ rsp->sbc_capabilities.subbands = sbc_cap->subbands;
+ rsp->sbc_capabilities.block_length = sbc_cap->block_length;
+ rsp->sbc_capabilities.min_bitpool = sbc_cap->min_bitpool;
+ rsp->sbc_capabilities.max_bitpool = sbc_cap->max_bitpool;
- switch (sbc_cap->frequency) {
- case A2DP_SAMPLING_FREQ_16000:
- cfg->rate = 16000;
- break;
- case A2DP_SAMPLING_FREQ_32000:
- cfg->rate = 32000;
- break;
- case A2DP_SAMPLING_FREQ_44100:
- cfg->rate = 44100;
- break;
- case A2DP_SAMPLING_FREQ_48000:
- cfg->rate = 48000;
- break;
- }
-
- cfg->codec = CFG_CODEC_SBC;
- sbc->allocation = sbc_cap->allocation_method == A2DP_ALLOCATION_SNR ?
- 0x01 : 0x00;
- sbc->subbands = sbc_cap->subbands == A2DP_SUBBANDS_4 ? 4 : 8;
-
- switch (sbc_cap->block_length) {
- case A2DP_BLOCK_LENGTH_4:
- sbc->blocks = 4;
- break;
- case A2DP_BLOCK_LENGTH_8:
- sbc->blocks = 8;
- break;
- case A2DP_BLOCK_LENGTH_12:
- sbc->blocks = 12;
- break;
- case A2DP_BLOCK_LENGTH_16:
- sbc->blocks = 16;
- break;
- }
-
- sbc->bitpool = sbc_cap->max_bitpool;
-
- unix_send_cfg(client->sock, cfg, fd);
+ unix_ipc_sendmsg(client, &rsp->h);
client->cb_id = avdtp_stream_add_cb(session, stream,
stream_state_changed, client);
@@ -412,7 +329,7 @@ failed:
a2dp_sep_unlock(a2dp->sep, a2dp->session);
a2dp->sep = NULL;
}
- unix_send_cfg(client->sock, NULL, -1);
+ send_getcapabilities_rsp_error(client, EIO);
avdtp_unref(a2dp->session);
@@ -469,7 +386,7 @@ static void create_stream(struct device *dev, struct unix_client *client)
return;
failed:
- unix_send_cfg(client->sock, NULL, -1);
+ send_getcapabilities_rsp_error(client, EIO);
}
static void create_cb(struct device *dev, void *user_data)
@@ -477,135 +394,57 @@ static void create_cb(struct device *dev, void *user_data)
struct unix_client *client = user_data;
if (!dev)
- unix_send_cfg(client->sock, NULL, -1);
+ send_getcapabilities_rsp_error(client, EIO);
else
create_stream(dev, client);
}
-static int cfg_to_caps(struct ipc_data_cfg *cfg, struct sbc_codec_cap *sbc_cap)
+static void unix_ipc_sendmsg(struct unix_client *client,
+ const bt_audio_msg_header_t *msg)
{
- struct ipc_codec_sbc *sbc = (void *) cfg->data;
-
- memset(sbc_cap, 0, sizeof(struct sbc_codec_cap));
-
- sbc_cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
- sbc_cap->cap.media_codec_type = A2DP_CODEC_SBC;
-
- switch (cfg->rate) {
- case 48000:
- sbc_cap->frequency = A2DP_SAMPLING_FREQ_48000;
- break;
- case 44100:
- sbc_cap->frequency = A2DP_SAMPLING_FREQ_44100;
- break;
- case 32000:
- sbc_cap->frequency = A2DP_SAMPLING_FREQ_32000;
- break;
- case 16000:
- sbc_cap->frequency = A2DP_SAMPLING_FREQ_16000;
- break;
- default:
- sbc_cap->frequency = A2DP_SAMPLING_FREQ_44100;
- break;
- }
-
- switch (cfg->mode) {
- case CFG_MODE_MONO:
- sbc_cap->channel_mode = A2DP_CHANNEL_MODE_MONO;
- break;
- case CFG_MODE_DUAL_CHANNEL:
- sbc_cap->channel_mode = A2DP_CHANNEL_MODE_DUAL_CHANNEL;
- break;
- case CFG_MODE_STEREO:
- sbc_cap->channel_mode = A2DP_CHANNEL_MODE_STEREO;
- break;
- case CFG_MODE_JOINT_STEREO:
- sbc_cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO;
- break;
- default:
- sbc_cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO;
- break;
- }
-
- switch (sbc->allocation) {
- case CFG_ALLOCATION_LOUDNESS:
- sbc_cap->allocation_method = A2DP_ALLOCATION_LOUDNESS;
- break;
- case CFG_ALLOCATION_SNR:
- sbc_cap->allocation_method = A2DP_ALLOCATION_LOUDNESS;
- break;
- default:
- sbc_cap->allocation_method = A2DP_ALLOCATION_LOUDNESS;
- break;
- }
-
- switch (sbc->subbands) {
- case 8:
- sbc_cap->subbands = A2DP_SUBBANDS_8;
- break;
- case 4:
- sbc_cap->subbands = A2DP_SUBBANDS_4;
- break;
- default:
- sbc_cap->subbands = A2DP_SUBBANDS_8;
- break;
- }
-
- switch (sbc->blocks) {
- case 16:
- sbc_cap->block_length = A2DP_BLOCK_LENGTH_16;
- break;
- case 12:
- sbc_cap->block_length = A2DP_BLOCK_LENGTH_12;
- break;
- case 8:
- sbc_cap->block_length = A2DP_BLOCK_LENGTH_8;
- break;
- case 4:
- sbc_cap->block_length = A2DP_BLOCK_LENGTH_4;
- break;
- default:
- sbc_cap->block_length = A2DP_BLOCK_LENGTH_16;
- break;
- }
+ info("Audio API: sending %s", bt_audio_strmsg(msg->msg_type));
+ if (send(client->sock, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) < 0)
+ error("Error %s(%d)", strerror(errno), errno);
+}
- if (sbc->bitpool != 0) {
- if (sbc->bitpool > 250)
- return -EINVAL;
+static void send_getcapabilities_rsp_error(struct unix_client *client, int err)
+{
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_getcapabilities_rsp *rsp = (void *) buf;
- sbc_cap->min_bitpool = sbc->bitpool;
- sbc_cap->max_bitpool = sbc->bitpool;
- }
+ memset(buf, 0, sizeof(buf));
+ rsp->h.msg_type = BT_GETCAPABILITIES_RSP;
+ rsp->posix_errno = err;
- return 0;
+ unix_ipc_sendmsg(client, &rsp->h);
}
-static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, int len)
+static void handle_getcapabilities_req(struct unix_client *client,
+ struct bt_getcapabilities_req *req)
{
struct device *dev;
bdaddr_t bdaddr;
- struct ipc_data_cfg *cfg = (void *) pkt->data;
- struct sbc_codec_cap sbc_cap;
- str2ba(pkt->device, &bdaddr);
+ str2ba(req->device, &bdaddr);
+
+ if (!req->access_mode) {
+ send_getcapabilities_rsp_error(client, EINVAL);
+ return;
+ }
- client->fd_opt = cfg->fd_opt;
+ client->access_mode = req->access_mode;
if (client->interface) {
g_free(client->interface);
client->interface = NULL;
}
- if (pkt->role == PKT_ROLE_VOICE)
+ if (req->transport == BT_CAPABILITIES_TRANSPORT_SCO)
client->interface = g_strdup(AUDIO_HEADSET_INTERFACE);
- else if (pkt->role == PKT_ROLE_HIFI)
+ else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
client->interface = g_strdup(AUDIO_SINK_INTERFACE);
- if (cfg_to_caps(cfg, &sbc_cap) < 0)
- goto failed;
-
- client->media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC,
- &sbc_cap, sizeof(sbc_cap));
+ client->media_codec = 0;
if (!manager_find_device(&bdaddr, NULL, FALSE)) {
if (!bacmp(&bdaddr, BDADDR_ANY))
@@ -627,59 +466,83 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, int le
return;
failed:
- unix_send_cfg(client->sock, NULL, -1);
+ send_getcapabilities_rsp_error(client, EIO);
}
-static void ctl_event(struct unix_client *client,
- struct ipc_packet *pkt, int len)
+static void handle_setconfiguration_req(struct unix_client *client,
+ struct bt_setconfiguration_req *req)
{
+ /* FIXME: for now we just blindly assume that we receive is the
+ only valid configuration sent.*/
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_setconfiguration_rsp *rsp = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+ rsp->h.msg_type = BT_SETCONFIGURATION_RSP;
+ rsp->posix_errno = 0;
+
+ unix_ipc_sendmsg(client, &rsp->h);
}
-static int reply_state(int sock, struct ipc_packet *pkt)
+static void handle_streamstart_req(struct unix_client *client,
+ struct bt_streamstart_req *req)
{
- struct ipc_data_state *state = (struct ipc_data_state *) pkt->data;
- int len;
+ /* FIXME : to be really implemented */
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_streamstart_rsp *rsp = (void *) buf;
+ struct bt_datafd_ind *ind = (void *) buf;
- info("status=%u", state->state);
+ memset(buf, 0, sizeof(buf));
+ rsp->h.msg_type = BT_STREAMSTART_RSP;
+ rsp->posix_errno = 0;
+ unix_ipc_sendmsg(client, &rsp->h);
- pkt->type = PKT_TYPE_STATE_RSP;
- pkt->length = sizeof(struct ipc_data_state);
- pkt->error = PKT_ERROR_NONE;
+ memset(buf, 0, sizeof(buf));
+ ind->h.msg_type = BT_STREAMFD_IND;
+ unix_ipc_sendmsg(client, &ind->h);
- len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_state);
- len = send(sock, pkt, len, 0);
- if (len < 0)
- error("Error %s(%d)", strerror(errno), errno);
+ if (unix_sendmsg_fd(client->sock, client->data_fd) < 0)
+ error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno);
+
+}
- debug("%d bytes sent", len);
+static void handle_streamstop_req(struct unix_client *client,
+ struct bt_streamstop_req *req)
+{
+ /* FIXME : to be implemented */
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_streamstop_rsp *rsp = (void *) buf;
- return 0;
+ memset(buf, 0, sizeof(buf));
+ rsp->h.msg_type = BT_STREAMSTOP_RSP;
+ rsp->posix_errno = 0;
+
+ unix_ipc_sendmsg(client, &rsp->h);
}
-static void state_event(struct unix_client *client,
- struct ipc_packet *pkt, int len)
+static void handle_control_req(struct unix_client *client,
+ struct bt_control_req *req)
{
-#if 0
- struct ipc_data_state *state = (struct ipc_data_state *) pkt->data;
- struct device *dev = client->dev;
+ /* FIXME: really implement that */
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_setconfiguration_rsp *rsp = (void *) buf;
- if (len > sizeof(struct ipc_packet))
- device_set_state(dev, state->state);
- else
- state->state = device_get_state(dev);
-#endif
+ memset(buf, 0, sizeof(buf));
+ rsp->h.msg_type = BT_CONTROL_RSP;
+ rsp->posix_errno = 0;
- reply_state(client->sock, pkt);
+ unix_ipc_sendmsg(client, &rsp->h);
}
static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
{
- char buf[IPC_MTU];
- struct ipc_packet *pkt = (void *) buf;
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ bt_audio_msg_header_t *msghdr = (void *) buf;
struct unix_client *client = data;
- int len, len_check;
+ int len;
struct a2dp_data *a2dp = &client->d.a2dp;
struct headset_data *hs = &client->d.hs;
+ const char *type;
if (cond & G_IO_NVAL)
return FALSE;
@@ -709,31 +572,39 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
memset(buf, 0, sizeof(buf));
- len = recv(client->sock, buf, sizeof(buf), 0);
+ len = recv(client->sock, buf, sizeof(buf), MSG_WAITALL);
if (len < 0) {
error("recv: %s (%d)", strerror(errno), errno);
goto failed;
}
- len_check = pkt->length + sizeof(struct ipc_packet);
- if (len != len_check) {
- error("Packet lenght doesn't match");
- goto failed;
- }
+ if ((type = bt_audio_strmsg(msghdr->msg_type)))
+ info("Audio API: received %s", type);
- switch (pkt->type) {
- case PKT_TYPE_CFG_REQ:
- info("Package PKT_TYPE_CFG_REQ:%u", pkt->role);
- cfg_event(client, pkt, len);
+ switch (msghdr->msg_type) {
+ case BT_GETCAPABILITIES_REQ:
+ handle_getcapabilities_req(client,
+ (struct bt_getcapabilities_req *) msghdr);
break;
- case PKT_TYPE_STATE_REQ:
- info("Package PKT_TYPE_STATE_REQ");
- state_event(client, pkt, len);
+ case BT_SETCONFIGURATION_REQ:
+ handle_setconfiguration_req(client,
+ (struct bt_setconfiguration_req *) msghdr);
break;
- case PKT_TYPE_CTL_REQ:
- info("Package PKT_TYPE_CTL_REQ");
- ctl_event(client, pkt, len);
+ case BT_STREAMSTART_REQ:
+ handle_streamstart_req(client,
+ (struct bt_streamstart_req *) msghdr);
break;
+ case BT_STREAMSTOP_REQ:
+ handle_streamstop_req(client,
+ (struct bt_streamstop_req *) msghdr);
+ break;
+ case BT_CONTROL_REQ:
+ handle_control_req(client,
+ (struct bt_control_req *) msghdr);
+ break;
+ default:
+ error("Audio API: received unexpected packet type %d",
+ msghdr->msg_type);
}
return TRUE;
@@ -789,7 +660,7 @@ int unix_init(void)
{
GIOChannel *io;
struct sockaddr_un addr = {
- AF_UNIX, IPC_SOCKET_NAME
+ AF_UNIX, BT_IPC_SOCKET_NAME
};
int sk, err;