summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
Diffstat (limited to 'audio')
-rw-r--r--audio/a2dp.c102
1 files changed, 76 insertions, 26 deletions
diff --git a/audio/a2dp.c b/audio/a2dp.c
index 1505b798..689e9da6 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -61,7 +61,6 @@ struct a2dp_sep {
struct device *used_by;
guint suspend_timer;
gboolean locked;
- gboolean start_requested;
gboolean suspending;
gboolean starting;
};
@@ -91,16 +90,16 @@ static GSList *sources = NULL;
static uint32_t source_record_id = 0;
static uint32_t sink_record_id = 0;
-static struct a2dp_stream_setup *setup = NULL;
+static GSList *setups = NULL;
static void stream_setup_free(struct a2dp_stream_setup *s)
{
+ setups = g_slist_remove(setups, s);
if (s->session)
avdtp_unref(s->session);
g_slist_foreach(s->cb, (GFunc) g_free, NULL);
g_slist_free(s->cb);
g_free(s);
- setup = NULL;
}
static void setup_callback(struct a2dp_stream_cb *cb,
@@ -111,13 +110,41 @@ static void setup_callback(struct a2dp_stream_cb *cb,
static gboolean finalize_stream_setup(struct a2dp_stream_setup *s)
{
- if (!s->stream)
+ if (!s->stream && s->sep)
s->sep->used_by = NULL;
- g_slist_foreach(setup->cb, (GFunc) setup_callback, setup);
- stream_setup_free(setup);
+ g_slist_foreach(s->cb, (GFunc) setup_callback, s);
+ stream_setup_free(s);
return FALSE;
}
+static struct a2dp_stream_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;
+
+ if (setup->session == session)
+ return setup;
+ }
+
+ return NULL;
+}
+
+static struct a2dp_stream_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;
+
+ if (setup->dev == dev)
+ return setup;
+ }
+
+ return NULL;
+}
+
static void stream_state_changed(struct avdtp_stream *stream,
avdtp_state_t old_state,
avdtp_state_t new_state,
@@ -251,12 +278,15 @@ static gboolean select_sbc_params(struct sbc_codec_cap *cap,
return TRUE;
}
-static gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep,
+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;
@@ -290,8 +320,14 @@ static void discovery_complete(struct avdtp *session, GSList *seps, int err,
{
struct avdtp_local_sep *lsep;
struct avdtp_remote_sep *rsep;
+ struct a2dp_stream_setup *setup;
GSList *caps = NULL;
+ setup = find_setup_by_session(session);
+
+ if (!setup)
+ return;
+
if (err < 0 || setup->canceled) {
setup->stream = NULL;
finalize_stream_setup(setup);
@@ -307,7 +343,7 @@ static void discovery_complete(struct avdtp *session, GSList *seps, int err,
return;
}
- if (!a2dp_select_capabilities(rsep, &caps)) {
+ if (!a2dp_select_capabilities(session, rsep, &caps)) {
error("Unable to select remote SEP capabilities");
finalize_stream_setup(setup);
return;
@@ -347,6 +383,7 @@ static gboolean setconf_ind(struct avdtp *session,
avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep);
a2dp_sep->stream = stream;
+ a2dp_sep->used_by = dev;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SOURCE)
sink_new_stream(dev, session, stream);
@@ -414,6 +451,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;
int ret;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
@@ -421,6 +459,8 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
else
debug("SBC Source: Set_Configuration_Cfm");
+ setup = find_setup_by_session(session);
+
if (err) {
if (setup)
finalize_stream_setup(setup);
@@ -487,12 +527,14 @@ 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;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
debug("SBC Sink: Open_Cfm");
else
debug("SBC Source: Open_Cfm");
+ setup = find_setup_by_session(session);
if (!setup)
return;
@@ -557,12 +599,14 @@ 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;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
debug("SBC Sink: Start_Cfm");
else
debug("SBC Source: Start_Cfm");
+ setup = find_setup_by_session(session);
if (!setup)
return;
@@ -597,6 +641,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;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
debug("SBC Sink: Suspend_Cfm");
@@ -605,16 +650,18 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
a2dp_sep->suspending = FALSE;
+ setup = find_setup_by_session(session);
+ if (!setup)
+ return;
+
if (err) {
- a2dp_sep->start_requested = FALSE;
- if (setup)
- finalize_stream_setup(setup);
+ finalize_stream_setup(setup);
return;
}
- if (a2dp_sep->start_requested) {
- avdtp_start(session, stream);
- a2dp_sep->start_requested = FALSE;
+ if (setup->start) {
+ if (avdtp_start(session, stream) < 0)
+ finalize_stream_setup(setup);
}
}
@@ -637,12 +684,14 @@ static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
void *user_data)
{
struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_stream_setup *setup;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
debug("SBC Sink: Close_Cfm");
else
debug("SBC Source: Close_Cfm");
+ setup = find_setup_by_session(session);
if (!setup)
return;
@@ -679,6 +728,8 @@ static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
else
debug("SBC Source: Abort_Ind");
+ a2dp_sep->used_by = NULL;
+
return TRUE;
}
@@ -711,12 +762,14 @@ 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;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
debug("SBC Sink: ReConfigure_Cfm");
else
debug("SBC Source: ReConfigure_Cfm");
+ setup = find_setup_by_session(session);
if (!setup)
return;
@@ -944,8 +997,10 @@ void a2dp_exit()
gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id)
{
struct a2dp_stream_cb *cb_data;
+ struct a2dp_stream_setup *setup;
GSList *l;
+ setup = find_setup_by_dev(dev);
if (!setup)
return FALSE;
@@ -967,6 +1022,7 @@ gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id)
if (!setup->cb) {
setup->canceled = TRUE;
setup->sep->used_by = NULL;
+ setup->sep = NULL;
}
return TRUE;
@@ -983,6 +1039,7 @@ unsigned int a2dp_source_request_stream(struct avdtp *session,
struct a2dp_stream_cb *cb_data;
static unsigned int cb_id = 0;
GSList *l;
+ struct a2dp_stream_setup *setup;
struct a2dp_sep *sep = NULL;
for (l = sources; l != NULL; l = l->next) {
@@ -1002,11 +1059,7 @@ unsigned int a2dp_source_request_stream(struct avdtp *session,
return 0;
}
- if (setup && setup->dev != dev) {
- error("a2dp_source_request_stream: stream setup in progress "
- "already for another device");
- return 0;
- }
+ setup = find_setup_by_session(session);
sep->used_by = dev;
@@ -1019,6 +1072,7 @@ unsigned int a2dp_source_request_stream(struct avdtp *session,
if (setup) {
setup->canceled = FALSE;
+ setup->sep = sep;
setup->cb = g_slist_append(setup->cb, cb_data);
if (start)
setup->start = TRUE;
@@ -1073,14 +1127,8 @@ unsigned int a2dp_source_request_stream(struct avdtp *session,
g_source_remove(sep->suspend_timer);
sep->suspend_timer = 0;
}
- if (sep->session) {
- avdtp_unref(sep->session);
- sep->session = NULL;
- }
- g_idle_add((GSourceFunc) finalize_stream_setup, setup);
- return cb_data->id;
}
- sep->start_requested = TRUE;
+ g_idle_add((GSourceFunc) finalize_stream_setup, setup);
break;
default:
error("SEP in bad state for requesting a new stream");
@@ -1090,6 +1138,8 @@ unsigned int a2dp_source_request_stream(struct avdtp *session,
if (ret)
*ret = sep;
+ setups = g_slist_append(setups, setup);
+
return cb_data->id;
failed: