summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuiz Augusto von Dentz <luiz.dentz@openbossa.org>2007-12-03 22:41:29 +0000
committerLuiz Augusto von Dentz <luiz.dentz@openbossa.org>2007-12-03 22:41:29 +0000
commit2934e194f3ffe754e18477113c870a7b98f88454 (patch)
tree8af6231fad9f933710e04d0f5ca7434ccf236d70
parentae100c4c1395e1b306d2e91b5fed85f7b8d6dfbf (diff)
Handle new ipc messages properly and adapt the plugins.
-rw-r--r--audio/a2dp.c712
-rw-r--r--audio/a2dp.h26
-rw-r--r--audio/avdtp.c37
-rw-r--r--audio/avdtp.h2
-rw-r--r--audio/gsta2dpsink.c73
-rw-r--r--audio/ipc.h8
-rw-r--r--audio/pcm_bluetooth.c154
-rw-r--r--audio/sink.c212
-rw-r--r--audio/unix.c581
9 files changed, 1225 insertions, 580 deletions
diff --git a/audio/a2dp.c b/audio/a2dp.c
index 83441617..bf644359 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -43,12 +43,10 @@
#include "sink.h"
#include "a2dp.h"
-#define MAX_BITPOOL 64
-#define MIN_BITPOOL 2
-
/* The duration that streams without users are allowed to stay in
* STREAMING state. */
#define SUSPEND_TIMEOUT 5000
+#define RECONFIGURE_TIMEOUT 500
#ifndef MIN
# define MIN(x, y) ((x) < (y) ? (x) : (y))
@@ -69,20 +67,23 @@ struct a2dp_sep {
gboolean starting;
};
-struct a2dp_stream_cb {
- a2dp_stream_cb_t cb;
+struct a2dp_setup_cb {
+ a2dp_config_cb_t config_cb;
+ a2dp_stream_cb_t resume_cb;
+ a2dp_stream_cb_t suspend_cb;
void *user_data;
int id;
};
-struct a2dp_stream_setup {
+struct a2dp_setup {
struct avdtp *session;
struct a2dp_sep *sep;
struct avdtp_stream *stream;
- struct avdtp_service_capability *media_codec;
- gboolean start;
+ GSList *client_caps;
+ gboolean reconfigure;
gboolean canceled;
GSList *cb;
+ int ref;
};
static DBusConnection *connection = NULL;
@@ -94,9 +95,20 @@ static uint32_t source_record_id = 0;
static uint32_t sink_record_id = 0;
static GSList *setups = NULL;
+static unsigned int cb_id = 0;
+
+static struct a2dp_setup *setup_ref(struct a2dp_setup *setup)
+{
+ setup->ref++;
+
+ debug("setup_ref(%p): ref=%d", setup, setup->ref);
+
+ return setup;
+}
-static void stream_setup_free(struct a2dp_stream_setup *s)
+static void setup_free(struct a2dp_setup *s)
{
+ debug("setup_free(%p)", s);
setups = g_slist_remove(setups, s);
if (s->session)
avdtp_unref(s->session);
@@ -105,6 +117,16 @@ static void stream_setup_free(struct a2dp_stream_setup *s)
g_free(s);
}
+static void setup_unref(struct a2dp_setup *setup)
+{
+ setup->ref--;
+
+ debug("setup_unref(%p): ref=%d", setup, setup->ref);
+
+ if (setup->ref <= 0)
+ setup_free(setup);
+}
+
static struct device *a2dp_get_dev(struct avdtp *session)
{
bdaddr_t addr;
@@ -114,35 +136,97 @@ static struct device *a2dp_get_dev(struct avdtp *session)
return manager_device_connected(&addr, A2DP_SOURCE_UUID);
}
-static gboolean finalize_stream_setup(struct a2dp_stream_setup *s, struct avdtp_error *err)
+static gboolean finalize_config(struct a2dp_setup *s, struct avdtp_error *err)
{
GSList *l;
+ setup_ref(s);
for (l = s->cb; l != NULL; l = l->next) {
- struct a2dp_stream_cb *cb = l->data;
-
- cb->cb(s->session, s->sep, s->stream, cb->user_data, err);
+ struct a2dp_setup_cb *cb = l->data;
+
+ if (cb->config_cb) {
+ cb->config_cb(s->session, s->sep, s->stream, err,
+ cb->user_data);
+ cb->config_cb = NULL;
+ setup_unref(s);
+ }
}
- stream_setup_free(s);
+ setup_unref(s);
return FALSE;
}
-static gboolean finalize_stream_setup_errno(struct a2dp_stream_setup *s, int err)
+static gboolean finalize_config_errno(struct a2dp_setup *s, int err)
{
struct avdtp_error avdtp_err;
avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err);
- return finalize_stream_setup(s, err ? &avdtp_err : NULL);
+ return finalize_config(s, err ? &avdtp_err : NULL);
}
-static struct a2dp_stream_setup *find_setup_by_session(struct avdtp *session)
+static gboolean finalize_resume(struct a2dp_setup *s, struct avdtp_error *err)
+{
+ GSList *l;
+
+ setup_ref(s);
+ for (l = s->cb; l != NULL; l = l->next) {
+ struct a2dp_setup_cb *cb = l->data;
+
+ if (cb->resume_cb) {
+ cb->resume_cb(s->session, err, cb->user_data);
+ cb->resume_cb = NULL;
+ setup_unref(s);
+ }
+ }
+
+ setup_unref(s);
+ return FALSE;
+}
+
+static gboolean finalize_resume_errno(struct a2dp_setup *s, int err)
+{
+ struct avdtp_error avdtp_err;
+
+ avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err);
+
+ return finalize_resume(s, err ? &avdtp_err : NULL);
+}
+
+static gboolean finalize_suspend(struct a2dp_setup *s, struct avdtp_error *err)
+{
+ GSList *l;
+
+ setup_ref(s);
+ for (l = s->cb; l != NULL; l = l->next) {
+ struct a2dp_setup_cb *cb = l->data;
+
+ if (cb->suspend_cb) {
+ cb->suspend_cb(s->session, err, cb->user_data);
+ cb->suspend_cb = NULL;
+ setup_unref(s);
+ }
+ }
+
+ setup_unref(s);
+ return FALSE;
+}
+
+static gboolean finalize_suspend_errno(struct a2dp_setup *s, int err)
+{
+ struct avdtp_error avdtp_err;
+
+ avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err);
+
+ return finalize_suspend(s, err ? &avdtp_err : NULL);
+}
+
+static struct a2dp_setup *find_setup_by_session(struct avdtp *session)
{
GSList *l;
for (l = setups; l != NULL; l = l->next) {
- struct a2dp_stream_setup *setup = l->data;
+ struct a2dp_setup *setup = l->data;
if (setup->session == session)
return setup;
@@ -151,12 +235,12 @@ static struct a2dp_stream_setup *find_setup_by_session(struct avdtp *session)
return NULL;
}
-static struct a2dp_stream_setup *find_setup_by_dev(struct device *dev)
+static struct a2dp_setup *find_setup_by_dev(struct device *dev)
{
GSList *l;
for (l = setups; l != NULL; l = l->next) {
- struct a2dp_stream_setup *setup = l->data;
+ struct a2dp_setup *setup = l->data;
struct device *setup_dev = a2dp_get_dev(setup->session);
if (setup_dev == dev)
@@ -186,197 +270,9 @@ static void stream_state_changed(struct avdtp_stream *stream,
avdtp_unref(sep->session);
sep->session = NULL;
}
-
+
sep->stream = NULL;
-}
-
-static uint8_t default_bitpool(uint8_t freq, uint8_t mode)
-{
- switch (freq) {
- case A2DP_SAMPLING_FREQ_16000:
- case A2DP_SAMPLING_FREQ_32000:
- return 53;
- case A2DP_SAMPLING_FREQ_44100:
- switch (mode) {
- case A2DP_CHANNEL_MODE_MONO:
- case A2DP_CHANNEL_MODE_DUAL_CHANNEL:
- return 31;
- case A2DP_CHANNEL_MODE_STEREO:
- case A2DP_CHANNEL_MODE_JOINT_STEREO:
- return 53;
- default:
- error("Invalid channel mode %u", mode);
- return 53;
- }
- case A2DP_SAMPLING_FREQ_48000:
- switch (mode) {
- case A2DP_CHANNEL_MODE_MONO:
- case A2DP_CHANNEL_MODE_DUAL_CHANNEL:
- return 29;
- case A2DP_CHANNEL_MODE_STEREO:
- case A2DP_CHANNEL_MODE_JOINT_STEREO:
- return 51;
- default:
- error("Invalid channel mode %u", mode);
- return 51;
- }
- default:
- error("Invalid sampling freq %u", freq);
- return 53;
- }
-}
-
-static gboolean select_sbc_params(struct sbc_codec_cap *cap,
- struct sbc_codec_cap *supported)
-{
- uint max_bitpool, min_bitpool;
-
- memset(cap, 0, sizeof(struct sbc_codec_cap));
-
- cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
- cap->cap.media_codec_type = A2DP_CODEC_SBC;
-
- if (supported->frequency & A2DP_SAMPLING_FREQ_44100)
- cap->frequency = A2DP_SAMPLING_FREQ_44100;
- else if (supported->frequency & A2DP_SAMPLING_FREQ_48000)
- cap->frequency = A2DP_SAMPLING_FREQ_48000;
- else if (supported->frequency & A2DP_SAMPLING_FREQ_32000)
- cap->frequency = A2DP_SAMPLING_FREQ_32000;
- else if (supported->frequency & A2DP_SAMPLING_FREQ_16000)
- cap->frequency = A2DP_SAMPLING_FREQ_16000;
- else {
- error("No supported frequencies");
- return FALSE;
- }
-
- if (supported->channel_mode & A2DP_CHANNEL_MODE_JOINT_STEREO)
- cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO;
- else if (supported->channel_mode & A2DP_CHANNEL_MODE_STEREO)
- cap->channel_mode = A2DP_CHANNEL_MODE_STEREO;
- else if (supported->channel_mode & A2DP_CHANNEL_MODE_DUAL_CHANNEL)
- cap->channel_mode = A2DP_CHANNEL_MODE_DUAL_CHANNEL;
- else if (supported->channel_mode & A2DP_CHANNEL_MODE_MONO)
- cap->channel_mode = A2DP_CHANNEL_MODE_MONO;
- else {
- error("No supported channel modes");
- return FALSE;
- }
-
- if (supported->block_length & A2DP_BLOCK_LENGTH_16)
- cap->block_length = A2DP_BLOCK_LENGTH_16;
- else if (supported->block_length & A2DP_BLOCK_LENGTH_12)
- cap->block_length = A2DP_BLOCK_LENGTH_12;
- else if (supported->block_length & A2DP_BLOCK_LENGTH_8)
- cap->block_length = A2DP_BLOCK_LENGTH_8;
- else if (supported->block_length & A2DP_BLOCK_LENGTH_4)
- cap->block_length = A2DP_BLOCK_LENGTH_4;
- else {
- error("No supported block lengths");
- return FALSE;
- }
-
- if (supported->subbands & A2DP_SUBBANDS_8)
- cap->subbands = A2DP_SUBBANDS_8;
- else if (supported->subbands & A2DP_SUBBANDS_4)
- cap->subbands = A2DP_SUBBANDS_4;
- else {
- error("No supported subbands");
- return FALSE;
- }
-
- if (supported->allocation_method & A2DP_ALLOCATION_LOUDNESS)
- cap->allocation_method = A2DP_ALLOCATION_LOUDNESS;
- else if (supported->allocation_method & A2DP_ALLOCATION_SNR)
- cap->allocation_method = A2DP_ALLOCATION_SNR;
-
- min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool);
- max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode),
- supported->max_bitpool);
-
- cap->min_bitpool = min_bitpool;
- cap->max_bitpool = max_bitpool;
-
- return TRUE;
-}
-static gboolean a2dp_select_capabilities(struct avdtp *session,
- struct avdtp_remote_sep *rsep,
- GSList **caps)
-{
- struct avdtp_service_capability *media_transport, *media_codec;
- struct sbc_codec_cap sbc_cap;
- struct a2dp_stream_setup *setup;
-
- setup = find_setup_by_session(session);
- if (!setup)
- return FALSE;
-
- if (setup->media_codec) {
- memcpy(&sbc_cap, setup->media_codec->data, sizeof(sbc_cap));
- } else {
- media_codec = avdtp_get_codec(rsep);
- if (!media_codec)
- return FALSE;
-
- select_sbc_params(&sbc_cap,
- (struct sbc_codec_cap *) media_codec->data);
- }
-
- media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
- NULL, 0);
-
- *caps = g_slist_append(*caps, media_transport);
-
- media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
- sizeof(sbc_cap));
-
- *caps = g_slist_append(*caps, media_codec);
-
-
- return TRUE;
-}
-
-static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err,
- void *user_data)
-{
- struct avdtp_local_sep *lsep;
- struct avdtp_remote_sep *rsep;
- struct a2dp_stream_setup *setup;
- GSList *caps = NULL;
- int posix_err;
-
- setup = find_setup_by_session(session);
-
- if (!setup)
- return;
-
- if (err || setup->canceled) {
- setup->stream = NULL;
- finalize_stream_setup(setup, err);
- return;
- }
-
- debug("Discovery complete");
-
- if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
- A2DP_CODEC_SBC, &lsep, &rsep) < 0) {
- error("No matching ACP and INT SEPs found");
- finalize_stream_setup_errno(setup, -EINVAL);
- return;
- }
-
- if (!a2dp_select_capabilities(session, rsep, &caps)) {
- error("Unable to select remote SEP capabilities");
- finalize_stream_setup_errno(setup, -EINVAL);
- return;
- }
-
- posix_err = avdtp_set_configuration(session, rsep, lsep, caps,
- &setup->stream);
- if (posix_err < 0) {
- error("avdtp_set_configuration: %s", strerror(-posix_err));
- finalize_stream_setup_errno(setup, posix_err);
- }
}
static gboolean setconf_ind(struct avdtp *session,
@@ -490,7 +386,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_error *err, void *user_data)
{
struct a2dp_sep *a2dp_sep = user_data;
- struct a2dp_stream_setup *setup;
+ struct a2dp_setup *setup;
struct device *dev;
int ret;
@@ -503,7 +399,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
if (err) {
if (setup)
- finalize_stream_setup(setup, err);
+ finalize_config(setup, err);
return;
}
@@ -523,7 +419,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
error("Error on avdtp_open %s (%d)", strerror(-ret),
-ret);
setup->stream = NULL;
- finalize_stream_setup_errno(setup, ret);
+ finalize_config_errno(setup, ret);
}
}
@@ -569,8 +465,7 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
void *user_data)
{
struct a2dp_sep *a2dp_sep = user_data;
- struct a2dp_stream_setup *setup;
- int posix_err;
+ struct a2dp_setup *setup;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
debug("SBC Sink: Open_Cfm");
@@ -584,28 +479,19 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
if (setup->canceled) {
if (!err)
avdtp_close(session, stream);
- stream_setup_free(setup);
+ setup_unref(setup);
return;
}
+ if (setup->reconfigure)
+ setup->reconfigure = FALSE;
+
if (err) {
setup->stream = NULL;
- finalize_stream_setup(setup, err);
- return;
- }
-
- if (setup->start) {
- posix_err = avdtp_start(session, stream);
- if (posix_err == 0)
- return;
-
- error("avdtp_start failed");
- setup->stream = NULL;
+ finalize_config(setup, err);
}
else
- posix_err = 0;
-
- finalize_stream_setup_errno(setup, -posix_err);
+ finalize_config(setup, 0);
}
static gboolean suspend_timeout(struct a2dp_sep *sep)
@@ -647,7 +533,7 @@ static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
void *user_data)
{
struct a2dp_sep *a2dp_sep = user_data;
- struct a2dp_stream_setup *setup;
+ struct a2dp_setup *setup;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
debug("SBC Sink: Start_Cfm");
@@ -661,16 +547,16 @@ static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
if (setup->canceled) {
if (!err)
avdtp_close(session, stream);
- stream_setup_free(setup);
+ setup_unref(setup);
return;
}
if (err) {
setup->stream = NULL;
- finalize_stream_setup(setup, err);
+ finalize_resume(setup, err);
}
else
- finalize_stream_setup_errno(setup, 0);
+ finalize_resume_errno(setup, 0);
}
static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep,
@@ -699,8 +585,7 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
void *user_data)
{
struct a2dp_sep *a2dp_sep = user_data;
- struct a2dp_stream_setup *setup;
- int posix_err;
+ struct a2dp_setup *setup;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
debug("SBC Sink: Suspend_Cfm");
@@ -714,15 +599,11 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
return;
if (err) {
- finalize_stream_setup(setup, err);
- return;
- }
-
- if (setup->start) {
- posix_err = avdtp_start(session, stream);
- if (posix_err < 0)
- finalize_stream_setup_errno(setup, posix_err);
+ setup->stream = NULL;
+ finalize_suspend(setup, err);
}
+ else
+ finalize_suspend_errno(setup, 0);
}
static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
@@ -739,13 +620,39 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
return TRUE;
}
+static gboolean reconfigure(gpointer data)
+{
+ struct a2dp_setup *setup = data;
+ struct avdtp_local_sep *lsep;
+ struct avdtp_remote_sep *rsep;
+ int posix_err;
+
+ posix_err = avdtp_get_seps(setup->session, AVDTP_SEP_TYPE_SINK,
+ AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_SBC,
+ &lsep, &rsep);
+ if (posix_err < 0) {
+ error("No matching ACP and INT SEPs found");
+ finalize_config_errno(setup, posix_err);
+ }
+
+ posix_err = avdtp_set_configuration(setup->session, rsep, lsep,
+ setup->client_caps,
+ &setup->stream);
+ if (posix_err < 0) {
+ error("avdtp_set_configuration: %s",
+ strerror(-posix_err));
+ finalize_config_errno(setup, posix_err);
+ }
+
+ return FALSE;
+}
+
static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream, struct avdtp_error *err,
void *user_data)
{
struct a2dp_sep *a2dp_sep = user_data;
- struct a2dp_stream_setup *setup;
- int posix_err;
+ struct a2dp_setup *setup;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
debug("SBC Sink: Close_Cfm");
@@ -757,28 +664,18 @@ static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
return;
if (setup->canceled) {
- stream_setup_free(setup);
+ setup_unref(setup);
return;
}
if (err) {
setup->stream = NULL;
- finalize_stream_setup(setup, err);
+ finalize_config(setup, err);
return;
}
- if (setup->start) {
- posix_err = avdtp_discover(session, discovery_complete, setup);
- if (posix_err == 0)
- return;
-
- error("avdtp_discover failed");
- setup->stream = NULL;
- }
- else
- posix_err = 0;
-
- finalize_stream_setup_errno(setup, -posix_err);
+ if (setup->reconfigure)
+ g_timeout_add(RECONFIGURE_TIMEOUT, reconfigure, setup);
}
static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
@@ -802,11 +699,18 @@ static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
void *user_data)
{
struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
debug("SBC Sink: Abort_Cfm");
else
debug("SBC Source: Abort_Cfm");
+
+ setup = find_setup_by_session(session);
+ if (!setup)
+ return;
+
+ setup_unref(setup);
}
static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,
@@ -826,8 +730,7 @@ static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
void *user_data)
{
struct a2dp_sep *a2dp_sep = user_data;
- struct a2dp_stream_setup *setup;
- int posix_err;
+ struct a2dp_setup *setup;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
debug("SBC Sink: ReConfigure_Cfm");
@@ -841,28 +744,16 @@ static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
if (setup->canceled) {
if (!err)
avdtp_close(session, stream);
- stream_setup_free(setup);
+ setup_unref(setup);
return;
}
if (err) {
setup->stream = NULL;
- finalize_stream_setup(setup, err);
- return;
- }
-
- if (setup->start) {
- posix_err = avdtp_start(session, stream);
- if (posix_err == 0)
- return;
-
- error("avdtp_start failed");
- setup->stream = NULL;
+ finalize_config(setup, err);
}
else
- posix_err = 0;
-
- finalize_stream_setup_errno(setup, -posix_err);
+ finalize_config(setup, 0);
}
static struct avdtp_sep_cfm cfm = {
@@ -1062,10 +953,10 @@ void a2dp_exit()
dbus_connection_unref(connection);
}
-gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id)
+gboolean a2dp_source_cancel(struct device *dev, unsigned int id)
{
- struct a2dp_stream_cb *cb_data;
- struct a2dp_stream_setup *setup;
+ struct a2dp_setup_cb *cb_data;
+ struct a2dp_setup *setup;
GSList *l;
setup = find_setup_by_dev(dev);
@@ -1073,7 +964,7 @@ gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id)
return FALSE;
for (cb_data = NULL, l = setup->cb; l != NULL; l = g_slist_next(l)) {
- struct a2dp_stream_cb *cb = l->data;
+ struct a2dp_setup_cb *cb = l->data;
if (cb->id == id) {
cb_data = cb;
@@ -1095,17 +986,16 @@ gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id)
return TRUE;
}
-unsigned int a2dp_source_request_stream(struct avdtp *session,
- gboolean start,
- a2dp_stream_cb_t cb,
- void *user_data,
- struct avdtp_service_capability *media_codec)
+unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb,
+ GSList *caps, void *user_data)
{
- struct a2dp_stream_cb *cb_data;
- static unsigned int cb_id = 0;
+ struct a2dp_setup_cb *cb_data;
GSList *l;
- struct a2dp_stream_setup *setup;
+ struct a2dp_setup *setup;
struct a2dp_sep *sep = NULL;
+ struct avdtp_local_sep *lsep;
+ struct avdtp_remote_sep *rsep;
+ int posix_err;
for (l = sources; l != NULL; l = l->next) {
struct a2dp_sep *tmp = l->data;
@@ -1120,91 +1010,173 @@ unsigned int a2dp_source_request_stream(struct avdtp *session,
}
if (!sep) {
- error("a2dp_source_request_stream: no available SEP found");
+ error("a2dp_source_cfg: no available SEP found");
return 0;
}
- setup = find_setup_by_session(session);
-
- debug("a2dp_source_request_stream: selected SEP %p", sep);
+ debug("a2dp_source_config: selected SEP %p", sep);
- cb_data = g_new(struct a2dp_stream_cb, 1);
- cb_data->cb = cb;
+ cb_data = g_new(struct a2dp_setup_cb, 1);
+ cb_data->config_cb = cb;
cb_data->user_data = user_data;
cb_data->id = ++cb_id;
- if (setup) {
- setup->canceled = FALSE;
- setup->sep = sep;
- setup->cb = g_slist_append(setup->cb, cb_data);
- if (start)
- setup->start = TRUE;
- return cb_data->id;
+ setup = find_setup_by_session(session);
+ if (!setup) {
+ setup = g_new0(struct a2dp_setup, 1);
+ setup->session = avdtp_ref(session);
+ setups = g_slist_append(setups, setup);
}
- setup = g_new0(struct a2dp_stream_setup, 1);
- setup->session = avdtp_ref(session);
- setup->sep = sep;
+ setup_ref(setup);
setup->cb = g_slist_append(setup->cb, cb_data);
- setup->start = start;
+ setup->sep = sep;
setup->stream = sep->stream;
- setup->media_codec = media_codec;
+ setup->client_caps = caps;
switch (avdtp_sep_get_state(sep->sep)) {
case AVDTP_STATE_IDLE:
- if (avdtp_discover(session, discovery_complete, setup) < 0) {
- error("avdtp_discover failed");
+ if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK,
+ AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_SBC,
+ &lsep, &rsep) < 0) {
+ error("No matching ACP and INT SEPs found");
+ goto failed;
+ }
+
+ posix_err = avdtp_set_configuration(session, rsep, lsep,
+ caps, &setup->stream);
+ if (posix_err < 0) {
+ error("avdtp_set_configuration: %s",
+ strerror(-posix_err));
goto failed;
}
break;
case AVDTP_STATE_OPEN:
- if (!start) {
- g_idle_add((GSourceFunc) finalize_stream_setup, setup);
- break;
- }
- if (sep->starting)
- break;
- if (setup->media_codec) {
- if (avdtp_stream_has_capability(setup->stream,
- setup->media_codec)) {
- if (avdtp_start(session, sep->stream) < 0) {
- error("avdtp_start failed");
- goto failed;
- }
- } else {
- if (avdtp_close(session, sep->stream) < 0) {
- error("avdtp_close failed");
- goto failed;
- }
+ case AVDTP_STATE_STREAMING:
+ if (avdtp_stream_has_capabilities(setup->stream, caps))
+ g_idle_add((GSourceFunc) finalize_config, setup);
+ else if (!setup->reconfigure) {
+ setup->reconfigure = TRUE;
+ if (avdtp_close(session, sep->stream) < 0) {
+ error("avdtp_close failed");
+ goto failed;
}
}
- else if (avdtp_start(session, sep->stream) < 0) {
+ break;
+ default:
+ error("SEP in bad state for requesting a new stream");
+ goto failed;
+ }
+
+ return cb_data->id;
+
+failed:
+ setup_unref(setup);
+ cb_id--;
+ return 0;
+}
+
+unsigned int a2dp_source_resume(struct avdtp *session, struct a2dp_sep *sep,
+ a2dp_stream_cb_t cb, void *user_data)
+{
+ struct a2dp_setup_cb *cb_data;
+ struct a2dp_setup *setup;
+
+ cb_data = g_new(struct a2dp_setup_cb, 1);
+ cb_data->resume_cb = cb;
+ cb_data->user_data = user_data;
+ cb_data->id = ++cb_id;
+
+ setup = find_setup_by_session(session);
+ if (!setup) {
+ setup = g_new0(struct a2dp_setup, 1);
+ setup->session = avdtp_ref(session);
+ setups = g_slist_append(setups, setup);
+ }
+
+ setup_ref(setup);
+ setup->cb = g_slist_append(setup->cb, cb_data);
+ setup->sep = sep;
+ setup->stream = sep->stream;
+
+ switch (avdtp_sep_get_state(sep->sep)) {
+ case AVDTP_STATE_IDLE:
+ goto failed;
+ break;
+ case AVDTP_STATE_OPEN:
+ if (avdtp_start(session, sep->stream) < 0) {
error("avdtp_start failed");
goto failed;
}
break;
case AVDTP_STATE_STREAMING:
- if (!start || !sep->suspending) {
- if (sep->suspend_timer) {
- g_source_remove(sep->suspend_timer);
- sep->suspend_timer = 0;
- avdtp_unref(sep->session);
- sep->session = NULL;
- }
- g_idle_add((GSourceFunc) finalize_stream_setup, setup);
+ if (!sep->suspending && sep->suspend_timer) {
+ g_source_remove(sep->suspend_timer);
+ sep->suspend_timer = 0;
+ avdtp_unref(sep->session);
+ sep->session = NULL;
}
+ g_idle_add((GSourceFunc) finalize_resume, setup);
break;
default:
- error("SEP in bad state for requesting a new stream");
+ error("SEP in bad state");
goto failed;
}
- setups = g_slist_append(setups, setup);
+ return cb_data->id;
+
+failed:
+ setup_unref(setup);
+ cb_id--;
+ return 0;
+}
+
+unsigned int a2dp_source_suspend(struct avdtp *session, struct a2dp_sep *sep,
+ a2dp_stream_cb_t cb, void *user_data)
+{
+ struct a2dp_setup_cb *cb_data;
+ struct a2dp_setup *setup;
+
+ cb_data = g_new(struct a2dp_setup_cb, 1);
+ cb_data->suspend_cb = cb;
+ cb_data->user_data = user_data;
+ cb_data->id = ++cb_id;
+
+ setup = find_setup_by_session(session);
+ if (!setup) {
+ setup = g_new0(struct a2dp_setup, 1);
+ setup->session = avdtp_ref(session);
+ setups = g_slist_append(setups, setup);
+ }
+
+ setup_ref(setup);
+ setup->cb = g_slist_append(setup->cb, cb_data);
+ setup->sep = sep;
+ setup->stream = sep->stream;
+
+ switch (avdtp_sep_get_state(sep->sep)) {
+ case AVDTP_STATE_IDLE:
+ error("a2dp_source_suspend: no stream to suspend");
+ goto failed;
+ break;
+ case AVDTP_STATE_OPEN:
+ g_idle_add((GSourceFunc) finalize_suspend, setup);
+ break;
+ case AVDTP_STATE_STREAMING:
+ if (avdtp_start(session, sep->stream) < 0) {
+ error("avdtp_start failed");
+ goto failed;
+ }
+ break;
+ default:
+ error("SEP in bad state for resume");
+ goto failed;
+ }
return cb_data->id;
failed:
- stream_setup_free(setup);
+ setup_unref(setup);
cb_id--;
return 0;
}
@@ -1248,67 +1220,3 @@ gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session)
return TRUE;
}
-gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session)
-{
- avdtp_state_t state;
- GSList *l;
- struct a2dp_sep *sep = NULL;
-
- for (l = sources; l != NULL; l = l->next) {
- struct a2dp_sep *tmp = l->data;
-
- if (tmp->session && tmp->session == session) {
- sep = tmp;
- break;
- }
- }
-
- if (!sep)
- return FALSE;
-
- state = avdtp_sep_get_state(sep->sep);
-
- if (!sep->stream || state != AVDTP_STATE_STREAMING)
- return TRUE;
-
- if (avdtp_suspend(session, sep->stream) == 0) {
- sep->suspending = TRUE;
- return TRUE;
- }
-
- return FALSE;
-}
-
-gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session)
-{
- avdtp_state_t state;
- GSList *l;
- struct a2dp_sep *sep = NULL;
-
- for (l = sources; l != NULL; l = l->next) {
- struct a2dp_sep *tmp = l->data;
-
- if (tmp->session && tmp->session == session) {
- sep = tmp;
- break;
- }
- }
-
- if (!sep)
- return FALSE;
-
- state = avdtp_sep_get_state(sep->sep);
-
- if (state < AVDTP_STATE_OPEN) {
- error("a2dp_source_start_stream: no stream open");
- return FALSE;
- }
-
- if (state == AVDTP_STATE_STREAMING)
- return TRUE;
-
- if (avdtp_start(session, sep->stream) < 0)
- return FALSE;
-
- return TRUE;
-}
diff --git a/audio/a2dp.h b/audio/a2dp.h
index 6579f64c..8227296f 100644
--- a/audio/a2dp.h
+++ b/audio/a2dp.h
@@ -48,6 +48,9 @@
#define A2DP_ALLOCATION_SNR (1 << 1)
#define A2DP_ALLOCATION_LOUDNESS 1
+#define MAX_BITPOOL 64
+#define MIN_BITPOOL 2
+
#if __BYTE_ORDER == __LITTLE_ENDIAN
struct sbc_codec_cap {
@@ -80,21 +83,24 @@ struct sbc_codec_cap {
struct a2dp_sep;
-typedef void (*a2dp_stream_cb_t) (struct avdtp *session, struct a2dp_sep *sep,
+typedef void (*a2dp_config_cb_t) (struct avdtp *session, struct a2dp_sep *sep,
struct avdtp_stream *stream,
- void *user_data,
- struct avdtp_error *err);
+ struct avdtp_error *err,
+ void *user_data);
+typedef void (*a2dp_stream_cb_t) (struct avdtp *session,
+ struct avdtp_error *err,
+ void *user_data);
int a2dp_init(DBusConnection *conn, int sources, int sinks);
void a2dp_exit(void);
-unsigned int a2dp_source_request_stream(struct avdtp *session,
- gboolean start, a2dp_stream_cb_t cb,
- void *user_data,
- struct avdtp_service_capability *media_codec);
-gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id);
+unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb,
+ GSList *caps, void *user_data);
+unsigned int a2dp_source_resume(struct avdtp *session, struct a2dp_sep *sep,
+ a2dp_stream_cb_t cb, void *user_data);
+unsigned int a2dp_source_suspend(struct avdtp *session, struct a2dp_sep *sep,
+ a2dp_stream_cb_t cb, void *user_data);
+gboolean a2dp_source_cancel(struct device *dev, unsigned int id);
gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session);
gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session);
-gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session);
-gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session);
diff --git a/audio/avdtp.c b/audio/avdtp.c
index f68561e9..3acd4128 100644
--- a/audio/avdtp.c
+++ b/audio/avdtp.c
@@ -1825,16 +1825,20 @@ static gboolean avdtp_discover_resp(struct avdtp *session,
resp->seps[i].media_type, resp->seps[i].inuse);
/* Skip SEP's which are in use */
+/*
if (resp->seps[i].inuse)
continue;
+*/
sep = find_remote_sep(session->seps, resp->seps[i].seid);
if (!sep) {
sep = g_new0(struct avdtp_remote_sep, 1);
session->seps = g_slist_append(session->seps, sep);
}
+/*
else if (sep && sep->stream)
continue;
+*/
sep->seid = resp->seps[i].seid;
sep->type = resp->seps[i].type;
@@ -2275,6 +2279,21 @@ gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,
return FALSE;
}
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+ GSList *caps)
+{
+ GSList *l;
+
+ for (l = caps; l; l = g_slist_next(l)) {
+ struct avdtp_service_capability *cap = l->data;
+
+ if (!avdtp_stream_has_capability(stream, cap))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
uint16_t *imtu, uint16_t *omtu,
GSList **caps)
@@ -2331,6 +2350,9 @@ struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
{
struct avdtp_service_capability *cap;
+ if (category < AVDTP_MEDIA_TRANSPORT || category > AVDTP_MEDIA_CODEC)
+ return NULL;
+
cap = g_malloc(sizeof(struct avdtp_service_capability) + length);
cap->category = category;
cap->length = length;
@@ -2457,6 +2479,18 @@ int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream)
return send_request(session, FALSE, stream, &req, sizeof(req));
}
+static void copy_capabilities(gpointer data, gpointer user_data)
+{
+ struct avdtp_service_capability *src_cap = data;
+ struct avdtp_service_capability *dst_cap;
+ GSList **l = user_data;
+
+ dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data,
+ src_cap->length);
+
+ *l = g_slist_append(*l, dst_cap);
+}
+
int avdtp_set_configuration(struct avdtp *session,
struct avdtp_remote_sep *rsep,
struct avdtp_local_sep *lsep,
@@ -2484,7 +2518,8 @@ int avdtp_set_configuration(struct avdtp *session,
new_stream->session = session;
new_stream->lsep = lsep;
new_stream->rseid = rsep->seid;
- new_stream->caps = caps;
+
+ g_slist_foreach(caps, copy_capabilities, &new_stream->caps);
/* Calculate total size of request */
for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) {
diff --git a/audio/avdtp.h b/audio/avdtp.h
index 88684f28..241fa713 100644
--- a/audio/avdtp.h
+++ b/audio/avdtp.h
@@ -221,6 +221,8 @@ gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,
struct avdtp_service_capability *cap);
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+ GSList *caps);
int avdtp_set_configuration(struct avdtp *session,
struct avdtp_remote_sep *rsep,
diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c
index b24dc8b8..1b893cc1 100644
--- a/audio/gsta2dpsink.c
+++ b/audio/gsta2dpsink.c
@@ -45,6 +45,7 @@ GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug);
#define GST_CAT_DEFAULT a2dp_sink_debug
#define BUFFER_SIZE 2048
+#define TEMPLATE_MAX_BITPOOL_VALUE 64
#define GST_A2DP_SINK_MUTEX_LOCK(s) G_STMT_START { \
g_mutex_lock (s->sink_lock); \
@@ -56,8 +57,8 @@ GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug);
struct bluetooth_data {
- struct bt_getcapabilities_rsp cfg; /* Bluetooth device config */
- gint link_mtu;
+ struct bt_getcapabilities_rsp caps; /* Bluetooth device capabilities */
+ struct bt_setconfiguration_rsp cfg; /* Bluetooth device configuration */
int samples; /* Number of encoded samples */
gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */
gsize count; /* Codec transfer buffer counter */
@@ -67,6 +68,7 @@ struct bluetooth_data {
int frame_count; /* Current frames in buffer*/
};
+
#define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0)
#define IS_MPEG(n) (strcmp((n), "audio/mpeg") == 0)
@@ -92,6 +94,7 @@ static GstStaticPadTemplate a2dp_sink_factory =
"blocks = (int) { 4, 8, 12, 16 }, "
"subbands = (int) { 4, 8 }, "
"allocation = (string) { snr, loudness },"
+ /* FIXME use constant here */
"bitpool = (int) [ 2, 64 ]; "
"audio/mpeg, "
"mpegversion = (int) 1, "
@@ -230,9 +233,10 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink,
GstCaps *caps,
sbc_capabilities_t *pkt)
{
- sbc_capabilities_t *cfg = &sink->data->cfg.sbc_capabilities;
+ sbc_capabilities_t *cfg = &sink->data->caps.sbc_capabilities;
const GValue *value = NULL;
const char *pref, *name;
+ gint rate, blocks, subbands;
GstStructure *structure = gst_caps_get_structure(caps, 0);
name = gst_structure_get_name(structure);
@@ -243,7 +247,19 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink,
}
value = gst_structure_get_value(structure, "rate");
- cfg->frequency = g_value_get_int(value);
+ rate = g_value_get_int(value);
+ if (rate == 44100)
+ cfg->frequency = BT_A2DP_SAMPLING_FREQ_44100;
+ else if (rate == 48000)
+ cfg->frequency = BT_A2DP_SAMPLING_FREQ_48000;
+ else if (rate == 32000)
+ cfg->frequency = BT_A2DP_SAMPLING_FREQ_32000;
+ else if (rate == 16000)
+ cfg->frequency = BT_A2DP_SAMPLING_FREQ_16000;
+ else {
+ GST_ERROR_OBJECT(sink, "Invalid rate while setting caps");
+ return FALSE;
+ }
value = gst_structure_get_value(structure, "mode");
pref = g_value_get_string(value);
@@ -276,13 +292,34 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink,
}
value = gst_structure_get_value(structure, "subbands");
- cfg->subbands = g_value_get_int(value);
+ subbands = g_value_get_int(value);
+ if (subbands == 8)
+ cfg->subbands = BT_A2DP_SUBBANDS_8;
+ else if (subbands == 4)
+ cfg->subbands = BT_A2DP_SUBBANDS_4;
+ else {
+ GST_ERROR_OBJECT(sink, "Invalid subbands %d", subbands);
+ return FALSE;
+ }
value = gst_structure_get_value(structure, "blocks");
- cfg->block_length = g_value_get_int(value);
+ blocks = g_value_get_int(value);
+ if (blocks == 16)
+ cfg->block_length = BT_A2DP_BLOCK_LENGTH_16;
+ else if (blocks == 12)
+ cfg->block_length = BT_A2DP_BLOCK_LENGTH_12;
+ else if (blocks == 8)
+ cfg->block_length = BT_A2DP_BLOCK_LENGTH_8;
+ else if (blocks == 4)
+ cfg->block_length = BT_A2DP_BLOCK_LENGTH_4;
+ else {
+ GST_ERROR_OBJECT(sink, "Invalid blocks %d", blocks);
+ return FALSE;
+ }
/* FIXME min and max ??? */
value = gst_structure_get_value(structure, "bitpool");
+
cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value);
memcpy(pkt, cfg, sizeof(*pkt));
@@ -375,7 +412,7 @@ static gboolean server_callback(GIOChannel *chan,
static gboolean gst_a2dp_sink_update_caps(GstA2dpSink *self)
{
- sbc_capabilities_t *sbc = &self->data->cfg.sbc_capabilities;
+ sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities;
GstStructure *structure;
GValue *value;
GValue *list;
@@ -519,7 +556,9 @@ static gboolean gst_a2dp_sink_update_caps(GstA2dpSink *self)
/* bitpool */
value = g_value_init(value, GST_TYPE_INT_RANGE);
- gst_value_set_int_range(value, sbc->min_bitpool, sbc->max_bitpool);
+ gst_value_set_int_range(value,
+ MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL_VALUE),
+ MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL_VALUE));
gst_structure_set_value(structure, "bitpool", value);
/* channels */
@@ -528,6 +567,8 @@ static gboolean gst_a2dp_sink_update_caps(GstA2dpSink *self)
g_free(value);
+ if (self->dev_caps != NULL)
+ gst_caps_unref(self->dev_caps);
self->dev_caps = gst_caps_new_full(structure, NULL);
tmp = gst_caps_to_string(self->dev_caps);
@@ -549,8 +590,6 @@ static gboolean gst_a2dp_sink_get_capabilities(GstA2dpSink *self)
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");
@@ -570,12 +609,7 @@ static gboolean gst_a2dp_sink_get_capabilities(GstA2dpSink *self)
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));
+ memcpy(&self->data->caps, rsp, sizeof(*rsp));
if (!gst_a2dp_sink_update_caps(self)) {
GST_WARNING_OBJECT(self, "failed to update capabilities");
return FALSE;
@@ -685,6 +719,7 @@ static gboolean gst_a2dp_sink_configure(GstA2dpSink *self, GstCaps *caps)
memset (req, 0, sizeof(buf));
req->h.msg_type = BT_SETCONFIGURATION_REQ;
+ req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;
strncpy(req->device, self->device, 18);
ret = gst_a2dp_sink_init_pkt_conf(self, caps, &req->sbc_capabilities);
if (!ret) {
@@ -716,6 +751,7 @@ static gboolean gst_a2dp_sink_configure(GstA2dpSink *self, GstCaps *caps)
return FALSE;
}
+ memcpy(&self->data->cfg, rsp, sizeof(*rsp));
GST_DEBUG_OBJECT(self, "configuration set");
return TRUE;
@@ -801,7 +837,10 @@ 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;
+ if (self->dev_caps)
+ return gst_caps_ref(self->dev_caps);
+
+ return gst_caps_copy(gst_pad_get_pad_template_caps(GST_BASE_SINK_PAD(self)));
}
static gboolean gst_a2dp_sink_set_caps(GstBaseSink *basesink, GstCaps *caps)
diff --git a/audio/ipc.h b/audio/ipc.h
index 0384cfd6..096c29f1 100644
--- a/audio/ipc.h
+++ b/audio/ipc.h
@@ -115,7 +115,6 @@ 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));
/* BT_GETCAPABILITIES_RSP */
@@ -165,8 +164,6 @@ 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 */
@@ -176,6 +173,8 @@ struct bt_getcapabilities_rsp {
struct bt_setconfiguration_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 */
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 */
@@ -184,6 +183,9 @@ struct bt_setconfiguration_req {
/* BT_SETCONFIGURATION_RSP */
struct bt_setconfiguration_rsp {
bt_audio_msg_header_t h;
+ uint8_t transport; /* Granted transport */
+ uint8_t access_mode; /* Granted access mode */
+ uint16_t link_mtu; /* Max length that transport supports */
uint8_t posix_errno;
} __attribute__ ((packed));
diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c
index a5bfcdf9..a177274a 100644
--- a/audio/pcm_bluetooth.c
+++ b/audio/pcm_bluetooth.c
@@ -69,6 +69,17 @@
#define SCO_RXBUFS 0x04
#endif
+#ifndef MIN
+# define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#define MAX_BITPOOL 64
+#define MIN_BITPOOL 2
+
/* adapted from glibc sys/time.h timersub() macro */
#define priv_timespecsub(a, b, result) \
do { \
@@ -411,6 +422,120 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io)
return write(data->pipefd[1], &c, 1);
}
+static uint8_t default_bitpool(uint8_t freq, uint8_t mode)
+{
+ switch (freq) {
+ case BT_A2DP_SAMPLING_FREQ_16000:
+ case BT_A2DP_SAMPLING_FREQ_32000:
+ return 53;
+ case BT_A2DP_SAMPLING_FREQ_44100:
+ switch (mode) {
+ case BT_A2DP_CHANNEL_MODE_MONO:
+ case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+ return 31;
+ case BT_A2DP_CHANNEL_MODE_STEREO:
+ case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+ return 53;
+ default:
+ DBG("Invalid channel mode %u", mode);
+ return 53;
+ }
+ case BT_A2DP_SAMPLING_FREQ_48000:
+ switch (mode) {
+ case BT_A2DP_CHANNEL_MODE_MONO:
+ case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+ return 29;
+ case BT_A2DP_CHANNEL_MODE_STEREO:
+ case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+ return 51;
+ default:
+ DBG("Invalid channel mode %u", mode);
+ return 51;
+ }
+ default:
+ DBG("Invalid sampling freq %u", freq);
+ return 53;
+ }
+}
+
+static int select_sbc_params(sbc_capabilities_t *cap, unsigned int rate,
+ unsigned int channels)
+{
+ unsigned int max_bitpool, min_bitpool;
+
+ switch (rate) {
+ case 48000:
+ cap->frequency = BT_A2DP_SAMPLING_FREQ_48000;
+ break;
+ case 44100:
+ cap->frequency = BT_A2DP_SAMPLING_FREQ_44100;
+ break;
+ case 32000:
+ cap->frequency = BT_A2DP_SAMPLING_FREQ_32000;
+ break;
+ case 16000:
+ cap->frequency = BT_A2DP_SAMPLING_FREQ_16000;
+ break;
+ default:
+ DBG("Rate %d not supported", rate);
+ return -1;
+ }
+
+ if (channels == 2) {
+ if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
+ else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
+ else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+ } else {
+ if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+ }
+
+ if (!cap->channel_mode) {
+ DBG("No supported channel modes");
+ return -1;
+ }
+
+ if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_16;
+ else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_12;
+ else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_8;
+ else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4)
+ cap->block_length = BT_A2DP_BLOCK_LENGTH_4;
+ else {
+ DBG("No supported block lengths");
+ return -1;
+ }
+
+ if (cap->subbands & BT_A2DP_SUBBANDS_8)
+ cap->subbands = BT_A2DP_SUBBANDS_8;
+ else if (cap->subbands & BT_A2DP_SUBBANDS_4)
+ cap->subbands = BT_A2DP_SUBBANDS_4;
+ else {
+ DBG("No supported subbands");
+ return -1;
+ }
+
+ if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS)
+ cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
+ else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR)
+ cap->allocation_method = BT_A2DP_ALLOCATION_SNR;
+
+ min_bitpool = MAX(MIN_BITPOOL, cap->min_bitpool);
+ max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode),
+ cap->max_bitpool);
+
+ cap->min_bitpool = min_bitpool;
+ cap->max_bitpool = max_bitpool;
+
+ return 0;
+}
+
+
static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io,
snd_pcm_hw_params_t *params)
{
@@ -419,7 +544,8 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io,
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;
+ unsigned int rate, channels;
+ int err, dir;
sbc_capabilities_t active_capabilities;
DBG("Preparing with io->period_size=%lu io->buffer_size=%lu",
@@ -428,11 +554,20 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io,
/* 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 */
+ snd_pcm_hw_params_get_rate(params, &rate, &dir);
+ snd_pcm_hw_params_get_channels(params, &channels);
+ err = select_sbc_params(&a2dp->sbc_capabilities, rate, channels);
+ if (err < 0)
+ return err;
+
active_capabilities = a2dp->sbc_capabilities;
memset(setconf_req, 0, BT_AUDIO_IPC_PACKET_SIZE);
setconf_req->h.msg_type = BT_SETCONFIGURATION_REQ;
setconf_req->sbc_capabilities = active_capabilities;
+ setconf_req->access_mode = (io->stream == SND_PCM_STREAM_PLAYBACK ?
+ BT_CAPABILITIES_ACCESS_MODE_WRITE :
+ BT_CAPABILITIES_ACCESS_MODE_READ);
err = audioservice_send(data->server.fd, &setconf_req->h);
if (err < 0)
@@ -450,13 +585,16 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io,
return -setconf_rsp->posix_errno;
}
+ data->transport = setconf_rsp->transport;
+ data->link_mtu = setconf_rsp->link_mtu;
+
/* 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);
+ sbc_reinit(&a2dp->sbc, 0);
+ else
+ sbc_init(&a2dp->sbc, 0);
a2dp->sbc_initialized = 1;
+
if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000)
a2dp->sbc.rate = 16000;
@@ -1327,11 +1465,11 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream,
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;
@@ -1348,7 +1486,9 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream,
}
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;
diff --git a/audio/sink.c b/audio/sink.c
index b1fc5b2b..259abf8f 100644
--- a/audio/sink.c
+++ b/audio/sink.c
@@ -159,7 +159,7 @@ static gboolean stream_setup_retry(gpointer user_data)
static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
struct avdtp_stream *stream,
- void *user_data, struct avdtp_error *err)
+ struct avdtp_error *err, void *user_data)
{
struct sink *sink = user_data;
struct pending_request *pending;
@@ -190,23 +190,212 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
}
}
+static uint8_t default_bitpool(uint8_t freq, uint8_t mode)
+{
+ switch (freq) {
+ case A2DP_SAMPLING_FREQ_16000:
+ case A2DP_SAMPLING_FREQ_32000:
+ return 53;
+ case A2DP_SAMPLING_FREQ_44100:
+ switch (mode) {
+ case A2DP_CHANNEL_MODE_MONO:
+ case A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+ return 31;
+ case A2DP_CHANNEL_MODE_STEREO:
+ case A2DP_CHANNEL_MODE_JOINT_STEREO:
+ return 53;
+ default:
+ error("Invalid channel mode %u", mode);
+ return 53;
+ }
+ case A2DP_SAMPLING_FREQ_48000:
+ switch (mode) {
+ case A2DP_CHANNEL_MODE_MONO:
+ case A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+ return 29;
+ case A2DP_CHANNEL_MODE_STEREO:
+ case A2DP_CHANNEL_MODE_JOINT_STEREO:
+ return 51;
+ default:
+ error("Invalid channel mode %u", mode);
+ return 51;
+ }
+ default:
+ error("Invalid sampling freq %u", freq);
+ return 53;
+ }
+}
+
+static gboolean select_sbc_params(struct sbc_codec_cap *cap,
+ struct sbc_codec_cap *supported)
+{
+ unsigned int max_bitpool, min_bitpool;
+
+ memset(cap, 0, sizeof(struct sbc_codec_cap));
+
+ cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ cap->cap.media_codec_type = A2DP_CODEC_SBC;
+
+ if (supported->frequency & A2DP_SAMPLING_FREQ_44100)
+ cap->frequency = A2DP_SAMPLING_FREQ_44100;
+ else if (supported->frequency & A2DP_SAMPLING_FREQ_48000)
+ cap->frequency = A2DP_SAMPLING_FREQ_48000;
+ else if (supported->frequency & A2DP_SAMPLING_FREQ_32000)
+ cap->frequency = A2DP_SAMPLING_FREQ_32000;
+ else if (supported->frequency & A2DP_SAMPLING_FREQ_16000)
+ cap->frequency = A2DP_SAMPLING_FREQ_16000;
+ else {
+ error("No supported frequencies");
+ return FALSE;
+ }
+
+ if (supported->channel_mode & A2DP_CHANNEL_MODE_JOINT_STEREO)
+ cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO;
+ else if (supported->channel_mode & A2DP_CHANNEL_MODE_STEREO)
+ cap->channel_mode = A2DP_CHANNEL_MODE_STEREO;
+ else if (supported->channel_mode & A2DP_CHANNEL_MODE_DUAL_CHANNEL)
+ cap->channel_mode = A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+ else if (supported->channel_mode & A2DP_CHANNEL_MODE_MONO)
+ cap->channel_mode = A2DP_CHANNEL_MODE_MONO;
+ else {
+ error("No supported channel modes");
+ return FALSE;
+ }
+
+ if (supported->block_length & A2DP_BLOCK_LENGTH_16)
+ cap->block_length = A2DP_BLOCK_LENGTH_16;
+ else if (supported->block_length & A2DP_BLOCK_LENGTH_12)
+ cap->block_length = A2DP_BLOCK_LENGTH_12;
+ else if (supported->block_length & A2DP_BLOCK_LENGTH_8)
+ cap->block_length = A2DP_BLOCK_LENGTH_8;
+ else if (supported->block_length & A2DP_BLOCK_LENGTH_4)
+ cap->block_length = A2DP_BLOCK_LENGTH_4;
+ else {
+ error("No supported block lengths");
+ return FALSE;
+ }
+
+ if (supported->subbands & A2DP_SUBBANDS_8)
+ cap->subbands = A2DP_SUBBANDS_8;
+ else if (supported->subbands & A2DP_SUBBANDS_4)
+ cap->subbands = A2DP_SUBBANDS_4;
+ else {
+ error("No supported subbands");
+ return FALSE;
+ }
+
+ if (supported->allocation_method & A2DP_ALLOCATION_LOUDNESS)
+ cap->allocation_method = A2DP_ALLOCATION_LOUDNESS;
+ else if (supported->allocation_method & A2DP_ALLOCATION_SNR)
+ cap->allocation_method = A2DP_ALLOCATION_SNR;
+
+ min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool);
+ max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode),
+ supported->max_bitpool);
+
+ cap->min_bitpool = min_bitpool;
+ cap->max_bitpool = max_bitpool;
+
+ return TRUE;
+}
+
+static gboolean select_capabilities(struct avdtp *session,
+ struct avdtp_remote_sep *rsep,
+ GSList **caps)
+{
+ struct avdtp_service_capability *media_transport, *media_codec;
+ struct sbc_codec_cap sbc_cap;
+
+ media_codec = avdtp_get_codec(rsep);
+ if (!media_codec)
+ return FALSE;
+
+ select_sbc_params(&sbc_cap, (struct sbc_codec_cap *) media_codec->data);
+
+ media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+ NULL, 0);
+
+ *caps = g_slist_append(*caps, media_transport);
+
+ media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
+ sizeof(sbc_cap));
+
+ *caps = g_slist_append(*caps, media_codec);
+
+
+ return TRUE;
+}
+
+static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err,
+ void *user_data)
+{
+ struct sink *sink = user_data;
+ struct pending_request *pending;
+ struct avdtp_local_sep *lsep;
+ struct avdtp_remote_sep *rsep;
+ GSList *caps = NULL;
+ int id;
+
+ pending = sink->connect;
+
+ if (err) {
+ avdtp_unref(sink->session);
+ sink->session = NULL;
+ if (avdtp_error_type(err) == AVDTP_ERROR_ERRNO
+ && avdtp_error_posix_errno(err) != EHOSTDOWN) {
+ debug("connect:connect XCASE detected");
+ g_timeout_add(STREAM_SETUP_RETRY_TIMER,
+ stream_setup_retry, sink);
+ } else
+ goto failed;
+ return;
+ }
+
+ debug("Discovery complete");
+
+ if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
+ A2DP_CODEC_SBC, &lsep, &rsep) < 0) {
+ error("No matching ACP and INT SEPs found");
+ goto failed;
+ }
+
+ if (!select_capabilities(session, rsep, &caps)) {
+ error("Unable to select remote SEP capabilities");
+ goto failed;
+ }
+
+ id = a2dp_source_config(sink->session, stream_setup_complete,
+ caps, sink);
+ if (id == 0)
+ goto failed;
+
+ pending->id = id;
+ return;
+
+failed:
+ pending_request_free(pending);
+ sink->connect = NULL;
+ avdtp_unref(sink->session);
+ sink->session = NULL;
+ error_failed(pending->conn, pending->msg, "Stream setup failed");
+}
+
static DBusHandlerResult sink_connect(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct device *dev = data;
struct sink *sink = dev->sink;
struct pending_request *pending;
- unsigned int id;
if (!sink->session)
sink->session = avdtp_get(&dev->src, &dev->dst);
if (!sink->session)
- return error_failed(conn, msg,
- "Unable to get a session");
+ return error_failed(conn, msg, "Unable to get a session");
if (sink->connect || sink->disconnect)
- return error_in_progress(conn, msg, "Device connection already in progress");
+ return error_in_progress(conn, msg, "Device connection"
+ "already in progress");
if (sink->state >= AVDTP_STATE_OPEN)
return error_already_connected(conn, msg);
@@ -216,21 +405,10 @@ static DBusHandlerResult sink_connect(DBusConnection *conn,
pending->msg = dbus_message_ref(msg);
sink->connect = pending;
- id = a2dp_source_request_stream(sink->session, FALSE,
- stream_setup_complete, sink,
- NULL);
- if (id == 0) {
- pending_request_free(pending);
- sink->connect = NULL;
- avdtp_unref(sink->session);
- sink->session = NULL;
- return error_failed(conn, msg, "Failed to request a stream");
- }
+ avdtp_discover(sink->session, discovery_complete, sink);
debug("stream creation in progress");
- pending->id = id;
-
return DBUS_HANDLER_RESULT_HANDLED;
}
diff --git a/audio/unix.c b/audio/unix.c
index ea96bd4f..c321b99b 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -71,7 +71,7 @@ struct headset_data {
struct unix_client {
struct device *dev;
- struct avdtp_service_capability *media_codec;
+ GSList *caps;
service_type_t type;
char *interface;
union {
@@ -83,18 +83,13 @@ struct unix_client {
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);
+ gboolean (*cancel) (struct device *dev, unsigned int id);
};
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;
@@ -118,8 +113,10 @@ static void client_free(struct unix_client *client)
if (client->sock >= 0)
close(client->sock);
- if (client->media_codec)
- g_free(client->media_codec);
+ if (client->caps) {
+ g_slist_foreach(client->caps, (GFunc) g_free, NULL);
+ g_slist_free(client->caps);
+ }
g_free(client->interface);
g_free(client);
@@ -152,6 +149,27 @@ static int unix_sendmsg_fd(int sock, int fd)
return sendmsg(sock, &msgh, MSG_NOSIGNAL);
}
+static void unix_ipc_sendmsg(struct unix_client *client,
+ const bt_audio_msg_header_t *msg)
+{
+ 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);
+}
+
+static void unix_ipc_error(struct unix_client *client, int type, int err)
+{
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_getcapabilities_rsp *rsp = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+ rsp->h.msg_type = type;
+ rsp->posix_errno = err;
+
+ unix_ipc_sendmsg(client, &rsp->h);
+}
+
static service_type_t select_service(struct device *dev, const char *interface)
{
if (!interface) {
@@ -198,8 +216,8 @@ static void stream_state_changed(struct avdtp_stream *stream,
break;
}
}
-
-static void headset_setup_complete(struct device *dev, void *user_data)
+/*
+static void headset_discovery_complete(struct device *dev, void *user_data)
{
struct unix_client *client = user_data;
char buf[BT_AUDIO_IPC_PACKET_SIZE];
@@ -209,7 +227,7 @@ static void headset_setup_complete(struct device *dev, void *user_data)
client->req_id = 0;
if (!dev) {
- send_getcapabilities_rsp_error(client, EIO);
+ unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO);
client->dev = NULL;
return;
}
@@ -231,7 +249,7 @@ static void headset_setup_complete(struct device *dev, void *user_data)
if (!headset_lock(dev, hs->lock)) {
error("Unable to lock headset");
- send_getcapabilities_rsp_error(client, EIO);
+ unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO);
client->dev = NULL;
return;
}
@@ -240,25 +258,131 @@ static void headset_setup_complete(struct device *dev, void *user_data)
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;
client->data_fd = headset_get_sco_fd(dev);
unix_ipc_sendmsg(client, &rsp->h);
}
+*/
+static void headset_setup_complete(struct device *dev, void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_setconfiguration_rsp *rsp = (void *) buf;
+ struct headset_data *hs = &client->d.hs;
-static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
- struct avdtp_stream *stream,
- void *user_data, struct avdtp_error *err)
+ client->req_id = 0;
+
+ if (!dev) {
+ unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO);
+ client->dev = NULL;
+ return;
+ }
+
+ switch (client->access_mode) {
+ case BT_CAPABILITIES_ACCESS_MODE_READ:
+ hs->lock = HEADSET_LOCK_READ;
+ break;
+ case BT_CAPABILITIES_ACCESS_MODE_WRITE:
+ hs->lock = HEADSET_LOCK_WRITE;
+ break;
+ case BT_CAPABILITIES_ACCESS_MODE_READWRITE:
+ hs->lock = HEADSET_LOCK_READ | HEADSET_LOCK_WRITE;
+ break;
+ default:
+ hs->lock = 0;
+ break;
+ }
+
+ if (!headset_lock(dev, hs->lock)) {
+ error("Unable to lock headset");
+ unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO);
+ client->dev = NULL;
+ return;
+ }
+
+ memset(buf, 0, sizeof(buf));
+
+ rsp->h.msg_type = BT_SETCONFIGURATION_RSP;
+ rsp->transport = BT_CAPABILITIES_TRANSPORT_SCO;
+ rsp->access_mode = client->access_mode;
+
+ client->data_fd = headset_get_sco_fd(dev);
+
+ unix_ipc_sendmsg(client, &rsp->h);
+}
+
+static void a2dp_discovery_complete(struct avdtp *session, GSList *seps,
+ struct avdtp_error *err,
+ void *user_data)
{
struct unix_client *client = user_data;
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 a2dp_data *a2dp = &client->d.a2dp;
+ struct sbc_codec_cap *sbc_cap = NULL;
+ GSList *l;
+
+ if (err)
+ goto failed;
+
+ memset(buf, 0, sizeof(buf));
+ client->req_id = 0;
+
+ rsp->h.msg_type = BT_GETCAPABILITIES_RSP;
+ rsp->transport = BT_CAPABILITIES_TRANSPORT_A2DP;
+
+ for (l = seps; l; l = g_slist_next(l)) {
+ struct avdtp_remote_sep *rsep = l->data;
+ struct avdtp_service_capability *cap;
+ struct avdtp_media_codec_capability *codec_cap;
+
+ cap = avdtp_get_codec(rsep);
+
+ if (cap->category != AVDTP_MEDIA_CODEC)
+ continue;
+
+ codec_cap = (void *) cap->data;
+
+ if (codec_cap->media_codec_type == A2DP_CODEC_SBC && !sbc_cap)
+ sbc_cap = (void *) codec_cap;
+
+ }
+
+ /* endianess prevent direct cast */
+ if (sbc_cap) {
+ 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;
+ }
+
+ unix_ipc_sendmsg(client, &rsp->h);
+
+ return;
+
+failed:
+ error("discovery failed");
+ unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO);
+
+ avdtp_unref(a2dp->session);
+
+ a2dp->session = NULL;
+ a2dp->stream = NULL;
+}
+
+static void a2dp_config_complete(struct avdtp *session, struct a2dp_sep *sep,
+ struct avdtp_stream *stream,
+ struct avdtp_error *err,
+ void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_setconfiguration_rsp *rsp = (void *) buf;
struct a2dp_data *a2dp = &client->d.a2dp;
uint16_t imtu, omtu;
GSList *caps;
@@ -282,40 +406,13 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
goto failed;
}
- for (codec_cap = NULL; caps; caps = g_slist_next(caps)) {
- cap = caps->data;
- if (cap->category == AVDTP_MEDIA_CODEC) {
- codec_cap = (void *) cap->data;
- break;
- }
- }
-
- if (codec_cap == NULL ||
- codec_cap->media_codec_type != A2DP_CODEC_SBC) {
- error("Unable to find matching codec capability");
- goto failed;
- }
-
- rsp->h.msg_type = BT_GETCAPABILITIES_RSP;
+ rsp->h.msg_type = BT_SETCONFIGURATION_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 */
rsp->link_mtu = omtu;
- sbc_cap = (void *) codec_cap;
-
- /* 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;
-
unix_ipc_sendmsg(client, &rsp->h);
client->cb_id = avdtp_stream_add_cb(session, stream,
@@ -324,12 +421,86 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
return;
failed:
- error("stream setup failed");
+ error("setup failed");
+
+ if (a2dp->sep) {
+ a2dp_sep_unlock(a2dp->sep, a2dp->session);
+ a2dp->sep = NULL;
+ }
+ unix_ipc_error(client, BT_SETCONFIGURATION_RSP, EIO);
+
+ avdtp_unref(a2dp->session);
+
+ a2dp->session = NULL;
+ a2dp->stream = NULL;
+}
+
+static void a2dp_resume_complete(struct avdtp *session,
+ struct avdtp_error *err, void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_streamstart_rsp *rsp = (void *) buf;
+ struct bt_datafd_ind *ind = (void *) buf;
+ struct a2dp_data *a2dp = &client->d.a2dp;
+
+ memset(buf, 0, sizeof(buf));
+ rsp->h.msg_type = BT_STREAMSTART_RSP;
+ rsp->posix_errno = 0;
+ unix_ipc_sendmsg(client, &rsp->h);
+
+ memset(buf, 0, sizeof(buf));
+ ind->h.msg_type = BT_STREAMFD_IND;
+ unix_ipc_sendmsg(client, &ind->h);
+
+ if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) {
+ error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno);
+ goto failed;
+ }
+
+ return;
+
+failed:
+ error("resume failed");
+
+ if (a2dp->sep) {
+ a2dp_sep_unlock(a2dp->sep, a2dp->session);
+ a2dp->sep = NULL;
+ }
+ unix_ipc_error(client, BT_STREAMSTART_REQ, EIO);
+
+ avdtp_unref(a2dp->session);
+
+ a2dp->session = NULL;
+ a2dp->stream = NULL;
+}
+
+static void a2dp_suspend_complete(struct avdtp *session,
+ struct avdtp_error *err, void *user_data)
+{
+ struct unix_client *client = user_data;
+ char buf[BT_AUDIO_IPC_PACKET_SIZE];
+ struct bt_streamstart_rsp *rsp = (void *) buf;
+ struct a2dp_data *a2dp = &client->d.a2dp;
+
+ if (err)
+ goto failed;
+
+ memset(buf, 0, sizeof(buf));
+ rsp->h.msg_type = BT_STREAMSTOP_RSP;
+ rsp->posix_errno = 0;
+ unix_ipc_sendmsg(client, &rsp->h);
+
+ return;
+
+failed:
+ error("suspend failed");
+
if (a2dp->sep) {
a2dp_sep_unlock(a2dp->sep, a2dp->session);
a2dp->sep = NULL;
}
- send_getcapabilities_rsp_error(client, EIO);
+ unix_ipc_error(client, BT_STREAMSTOP_REQ, EIO);
avdtp_unref(a2dp->session);
@@ -337,10 +508,11 @@ failed:
a2dp->stream = NULL;
}
-static void create_stream(struct device *dev, struct unix_client *client)
+static void start_discovery(struct device *dev, struct unix_client *client)
{
struct a2dp_data *a2dp;
unsigned int id;
+ int err = 0;
client->type = select_service(dev, client->interface);
@@ -356,18 +528,55 @@ static void create_stream(struct device *dev, struct unix_client *client)
goto failed;
}
- /* FIXME: The provided media_codec breaks bitpool
- selection. So disable it. This needs fixing */
- id = a2dp_source_request_stream(a2dp->session,
- TRUE, a2dp_setup_complete,
- client,
- NULL/*client->media_codec*/);
- client->cancel_stream = a2dp_source_cancel_stream;
+ err = avdtp_discover(a2dp->session, a2dp_discovery_complete,
+ client);
+ if (err)
+ goto failed;
break;
case TYPE_HEADSET:
id = headset_request_stream(dev, headset_setup_complete, client);
- client->cancel_stream = headset_cancel_stream;
+ client->cancel = headset_cancel_stream;
+ break;
+
+ default:
+ error("No known services for device");
+ goto failed;
+ }
+
+ return;
+
+failed:
+ unix_ipc_error(client, BT_GETCAPABILITIES_RSP, err ? : EIO);
+}
+
+static void start_config(struct device *dev, struct unix_client *client)
+{
+ struct a2dp_data *a2dp;
+ unsigned int id;
+
+ client->type = select_service(dev, client->interface);
+
+ switch (client->type) {
+ case TYPE_SINK:
+ a2dp = &client->d.a2dp;
+
+ if (!a2dp->session)
+ a2dp->session = avdtp_get(&dev->src, &dev->dst);
+
+ if (!a2dp->session) {
+ error("Unable to get a session");
+ goto failed;
+ }
+
+ id = a2dp_source_config(a2dp->session, a2dp_config_complete,
+ client->caps, client);
+ client->cancel = a2dp_source_cancel;
+ break;
+
+ case TYPE_HEADSET:
+ id = headset_request_stream(dev, headset_setup_complete, client);
+ client->cancel = headset_cancel_stream;
break;
default:
@@ -376,7 +585,7 @@ static void create_stream(struct device *dev, struct unix_client *client)
}
if (id == 0) {
- error("request_stream failed");
+ error("config failed");
goto failed;
}
@@ -386,37 +595,117 @@ static void create_stream(struct device *dev, struct unix_client *client)
return;
failed:
- send_getcapabilities_rsp_error(client, EIO);
+ unix_ipc_error(client, BT_SETCONFIGURATION_RSP, EIO);
}
-static void create_cb(struct device *dev, void *user_data)
+static void start_resume(struct device *dev, struct unix_client *client)
{
- struct unix_client *client = user_data;
+ struct a2dp_data *a2dp;
+ unsigned int id;
- if (!dev)
- send_getcapabilities_rsp_error(client, EIO);
- else
- create_stream(dev, client);
+ client->type = select_service(dev, client->interface);
+
+ switch (client->type) {
+ case TYPE_SINK:
+ a2dp = &client->d.a2dp;
+
+ if (!a2dp->session)
+ a2dp->session = avdtp_get(&dev->src, &dev->dst);
+
+ if (!a2dp->session) {
+ error("Unable to get a session");
+ goto failed;
+ }
+
+ if (!a2dp->sep) {
+ error("Unable to get a sep");
+ goto failed;
+ }
+
+ id = a2dp_source_resume(a2dp->session, a2dp->sep,
+ a2dp_resume_complete, client);
+ client->cancel = a2dp_source_cancel;
+ break;
+
+ case TYPE_HEADSET:
+ id = headset_request_stream(dev, headset_setup_complete, client);
+ client->cancel = headset_cancel_stream;
+ break;
+
+ default:
+ error("No known services for device");
+ goto failed;
+ }
+
+ if (id == 0) {
+ error("resume failed");
+ goto failed;
+ }
+
+ return;
+
+failed:
+ unix_ipc_error(client, BT_STREAMSTART_RSP, EIO);
}
-static void unix_ipc_sendmsg(struct unix_client *client,
- const bt_audio_msg_header_t *msg)
+static void start_suspend(struct device *dev, struct unix_client *client)
{
- 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);
+ struct a2dp_data *a2dp;
+ unsigned int id;
+
+ client->type = select_service(dev, client->interface);
+
+ switch (client->type) {
+ case TYPE_SINK:
+ a2dp = &client->d.a2dp;
+
+ if (!a2dp->session)
+ a2dp->session = avdtp_get(&dev->src, &dev->dst);
+
+ if (!a2dp->session) {
+ error("Unable to get a session");
+ goto failed;
+ }
+
+ if (!a2dp->sep) {
+ error("Unable to get a sep");
+ goto failed;
+ }
+
+ id = a2dp_source_suspend(a2dp->session, a2dp->sep,
+ a2dp_suspend_complete, client);
+ client->cancel = a2dp_source_cancel;
+ break;
+
+ case TYPE_HEADSET:
+ id = headset_request_stream(dev, headset_setup_complete, client);
+ client->cancel = headset_cancel_stream;
+ break;
+
+ default:
+ error("No known services for device");
+ goto failed;
+ }
+
+ if (id == 0) {
+ error("suspend failed");
+ goto failed;
+ }
+
+ return;
+
+failed:
+ unix_ipc_error(client, BT_STREAMSTOP_RSP, EIO);
}
-static void send_getcapabilities_rsp_error(struct unix_client *client, int err)
+static void create_cb(struct device *dev, void *user_data)
{
- char buf[BT_AUDIO_IPC_PACKET_SIZE];
- struct bt_getcapabilities_rsp *rsp = (void *) buf;
-
- memset(buf, 0, sizeof(buf));
- rsp->h.msg_type = BT_GETCAPABILITIES_RSP;
- rsp->posix_errno = err;
+ struct unix_client *client = user_data;
- unix_ipc_sendmsg(client, &rsp->h);
+ if (!dev)
+ unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO);
+ else
+ start_discovery(dev, client);
}
static void handle_getcapabilities_req(struct unix_client *client,
@@ -427,13 +716,6 @@ static void handle_getcapabilities_req(struct unix_client *client,
str2ba(req->device, &bdaddr);
- if (!req->access_mode) {
- send_getcapabilities_rsp_error(client, EINVAL);
- return;
- }
-
- client->access_mode = req->access_mode;
-
if (client->interface) {
g_free(client->interface);
client->interface = NULL;
@@ -444,8 +726,6 @@ static void handle_getcapabilities_req(struct unix_client *client,
else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
client->interface = g_strdup(AUDIO_SINK_INTERFACE);
- client->media_codec = 0;
-
if (!manager_find_device(&bdaddr, NULL, FALSE)) {
if (!bacmp(&bdaddr, BDADDR_ANY))
goto failed;
@@ -461,63 +741,118 @@ static void handle_getcapabilities_req(struct unix_client *client,
if (!dev)
goto failed;
- create_stream(dev, client);
+ start_discovery(dev, client);
return;
failed:
- send_getcapabilities_rsp_error(client, EIO);
+ unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO);
}
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;
+ struct avdtp_service_capability *media_transport, *media_codec;
+ struct sbc_codec_cap sbc_cap;
+ struct device *dev;
+ bdaddr_t bdaddr;
+ int err = 0;
- memset(buf, 0, sizeof(buf));
- rsp->h.msg_type = BT_SETCONFIGURATION_RSP;
- rsp->posix_errno = 0;
+ if (!req->access_mode) {
+ err = EINVAL;
+ goto failed;
+ }
- unix_ipc_sendmsg(client, &rsp->h);
+ str2ba(req->device, &bdaddr);
+
+ if (client->interface) {
+ g_free(client->interface);
+ client->interface = NULL;
+ }
+
+ if (req->transport == BT_CAPABILITIES_TRANSPORT_SCO)
+ client->interface = g_strdup(AUDIO_HEADSET_INTERFACE);
+ else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
+ client->interface = g_strdup(AUDIO_SINK_INTERFACE);
+
+ if (!manager_find_device(&bdaddr, NULL, FALSE)) {
+ if (!bacmp(&bdaddr, BDADDR_ANY))
+ goto failed;
+ if (!manager_create_device(&bdaddr, create_cb, client))
+ goto failed;
+ return;
+ }
+
+ dev = manager_find_device(&bdaddr, client->interface, TRUE);
+ if (!dev)
+ dev = manager_find_device(&bdaddr, client->interface, FALSE);
+
+ if (!dev)
+ goto failed;
+
+ client->access_mode = req->access_mode;
+
+ if (client->caps) {
+ g_slist_foreach(client->caps, (GFunc) g_free, NULL);
+ g_slist_free(client->caps);
+ }
+
+ media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+ NULL, 0);
+
+ client->caps = g_slist_append(client->caps, media_transport);
+
+ memset(&sbc_cap, 0, sizeof(sbc_cap));
+
+ sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+ sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC;
+ sbc_cap.channel_mode = req->sbc_capabilities.channel_mode;
+ sbc_cap.frequency = req->sbc_capabilities.frequency;
+ sbc_cap.allocation_method = req->sbc_capabilities.allocation_method;
+ sbc_cap.subbands = req->sbc_capabilities.subbands;
+ sbc_cap.block_length = req->sbc_capabilities.block_length ;
+ sbc_cap.min_bitpool = req->sbc_capabilities.min_bitpool;
+ sbc_cap.max_bitpool = req->sbc_capabilities.max_bitpool;
+
+ media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
+ sizeof(sbc_cap));
+
+ client->caps = g_slist_append(client->caps, media_codec);
+
+ start_config(dev, client);
+
+ return;
+
+failed:
+ unix_ipc_error(client, BT_SETCONFIGURATION_RSP, err ? : EIO);
}
static void handle_streamstart_req(struct unix_client *client,
struct bt_streamstart_req *req)
{
- /* 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;
-
- memset(buf, 0, sizeof(buf));
- rsp->h.msg_type = BT_STREAMSTART_RSP;
- rsp->posix_errno = 0;
- unix_ipc_sendmsg(client, &rsp->h);
+ if (!client->dev)
+ goto failed;
- memset(buf, 0, sizeof(buf));
- ind->h.msg_type = BT_STREAMFD_IND;
- unix_ipc_sendmsg(client, &ind->h);
+ start_resume(client->dev, client);
- if (unix_sendmsg_fd(client->sock, client->data_fd) < 0)
- error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno);
+ return;
+failed:
+ unix_ipc_error(client, BT_STREAMSTART_REQ, EIO);
}
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;
+ if (!client->dev)
+ goto failed;
- memset(buf, 0, sizeof(buf));
- rsp->h.msg_type = BT_STREAMSTOP_RSP;
- rsp->posix_errno = 0;
+ start_suspend(client->dev, client);
- unix_ipc_sendmsg(client, &rsp->h);
+ return;
+
+failed:
+ unix_ipc_error(client, BT_STREAMSTOP_REQ, EIO);
}
static void handle_control_req(struct unix_client *client,
@@ -565,8 +900,8 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
break;
}
- if (client->cancel_stream && client->req_id > 0)
- client->cancel_stream(client->dev, client->req_id);
+ if (client->cancel && client->req_id > 0)
+ client->cancel(client->dev, client->req_id);
goto failed;
}