diff options
Diffstat (limited to 'audio')
-rw-r--r-- | audio/a2dp.c | 102 |
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: |