summaryrefslogtreecommitdiffstats
path: root/audio/a2dp.c
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 /audio/a2dp.c
parentae100c4c1395e1b306d2e91b5fed85f7b8d6dfbf (diff)
Handle new ipc messages properly and adapt the plugins.
Diffstat (limited to 'audio/a2dp.c')
-rw-r--r--audio/a2dp.c712
1 files changed, 310 insertions, 402 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;
-}