From 666938b54d631956826343ed278e2af4b982fc29 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 24 Aug 2007 12:15:20 +0000 Subject: Support up to two simultaneous streams --- audio/a2dp.c | 435 +++++++++++++++++++++++++++++++++++----------------------- audio/a2dp.h | 5 +- audio/avdtp.c | 92 ++++++++----- audio/avdtp.h | 50 ++++--- audio/sink.c | 6 +- audio/unix.c | 8 +- 6 files changed, 364 insertions(+), 232 deletions(-) diff --git a/audio/a2dp.c b/audio/a2dp.c index febc112c..daeda70c 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -54,12 +54,13 @@ #endif struct a2dp_sep { + uint8_t type; struct avdtp_local_sep *sep; struct avdtp *session; struct avdtp_stream *stream; struct device *used_by; - uint32_t record_id; guint suspend_timer; + gboolean locked; gboolean start_requested; gboolean suspending; gboolean starting; @@ -82,8 +83,11 @@ struct a2dp_stream_setup { static DBusConnection *connection = NULL; -static struct a2dp_sep sink = { NULL }; -static struct a2dp_sep source = { NULL }; +static GSList *sinks = NULL; +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; @@ -138,19 +142,16 @@ static gboolean setconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, GSList *caps, uint8_t *err, - uint8_t *category) + uint8_t *category, void *user_data) { - struct a2dp_sep *a2dp_sep; + struct a2dp_sep *a2dp_sep = user_data; struct device *dev; bdaddr_t addr; - if (sep == sink.sep) { + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Set_Configuration_Ind"); - a2dp_sep = &sink; - } else { + else debug("SBC Source: Set_Configuration_Ind"); - a2dp_sep = &source; - } avdtp_get_peers(session, NULL, &addr); @@ -164,19 +165,20 @@ static gboolean setconf_ind(struct avdtp *session, avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep); a2dp_sep->stream = stream; - if (a2dp_sep == &source) + if (a2dp_sep->type == AVDTP_SEP_TYPE_SOURCE) sink_new_stream(dev, session, stream); return TRUE; } static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, - GSList **caps, uint8_t *err) + GSList **caps, uint8_t *err, void *user_data) { + struct a2dp_sep *a2dp_sep = user_data; struct avdtp_service_capability *media_transport, *media_codec; struct sbc_codec_cap sbc_cap; - if (sep == sink.sep) + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Get_Capability_Ind"); else debug("SBC Source: Get_Capability_Ind"); @@ -226,18 +228,15 @@ static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, - struct avdtp_error *err) + struct avdtp_error *err, void *user_data) { - struct a2dp_sep *a2dp_sep; + struct a2dp_sep *a2dp_sep = user_data; int ret; - if (sep == sink.sep) { + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Set_Configuration_Cfm"); - a2dp_sep = &sink; - } else { + else debug("SBC Source: Set_Configuration_Cfm"); - a2dp_sep = &source; - } if (err) { if (setup) @@ -261,9 +260,11 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, } static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, - uint8_t *err) + uint8_t *err, void *user_data) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep = user_data; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Get_Configuration_Ind"); else debug("SBC Source: Get_Configuration_Ind"); @@ -271,18 +272,24 @@ static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err) + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep = user_data; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Set_Configuration_Cfm"); else debug("SBC Source: Set_Configuration_Cfm"); } static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err) + struct avdtp_stream *stream, uint8_t *err, + void *user_data) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep = user_data; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Open_Ind"); else debug("SBC Source: Open_Ind"); @@ -290,9 +297,12 @@ static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err) + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep = user_data; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Open_Cfm"); else debug("SBC Source: Open_Cfm"); @@ -338,18 +348,15 @@ static gboolean suspend_timeout(struct a2dp_sep *sep) } static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err) + struct avdtp_stream *stream, uint8_t *err, + void *user_data) { - struct a2dp_sep *a2dp_sep; + struct a2dp_sep *a2dp_sep = user_data; - if (sep == sink.sep) { + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Start_Ind"); - a2dp_sep = &sink; - } - else { + else debug("SBC Source: Start_Ind"); - a2dp_sep = &source; - } a2dp_sep->session = avdtp_ref(session); @@ -360,9 +367,12 @@ static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err) + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep = user_data; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Start_Cfm"); else debug("SBC Source: Start_Cfm"); @@ -384,9 +394,12 @@ static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, } static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err) + struct avdtp_stream *stream, uint8_t *err, + void *user_data) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep = user_data; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Suspend_Ind"); else debug("SBC Source: Suspend_Ind"); @@ -394,17 +407,15 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err) + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) { - struct a2dp_sep *a2dp_sep; + struct a2dp_sep *a2dp_sep = user_data; - if (sep == sink.sep) { + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Suspend_Cfm"); - a2dp_sep = &sink; - } else { + else debug("SBC Source: Suspend_Cfm"); - a2dp_sep = &source; - } a2dp_sep->suspending = FALSE; @@ -422,69 +433,63 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, } static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err) + struct avdtp_stream *stream, uint8_t *err, + void *user_data) { - struct a2dp_sep *a2dp_sep; + struct a2dp_sep *a2dp_sep = user_data; - if (sep == sink.sep) { + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Close_Ind"); - a2dp_sep = &sink; - } else { + else debug("SBC Source: Close_Ind"); - a2dp_sep = &source; - } return TRUE; } static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err) + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) { - struct a2dp_sep *a2dp_sep; + struct a2dp_sep *a2dp_sep = user_data; - if (sep == sink.sep) { + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Close_Cfm"); - a2dp_sep = &sink; - } else { + else debug("SBC Source: Close_Cfm"); - a2dp_sep = &source; - } } static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err) + struct avdtp_stream *stream, uint8_t *err, + void *user_data) { - struct a2dp_sep *a2dp_sep; + struct a2dp_sep *a2dp_sep = user_data; - if (sep == sink.sep) { + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Abort_Ind"); - a2dp_sep = &sink; - } else { + else debug("SBC Source: Abort_Ind"); - a2dp_sep = &source; - } return TRUE; } static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err) + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) { - struct a2dp_sep *a2dp_sep; + struct a2dp_sep *a2dp_sep = user_data; - if (sep == sink.sep) { + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Abort_Cfm"); - a2dp_sep = &sink; - } else { + else debug("SBC Source: Abort_Cfm"); - a2dp_sep = &source; - } } static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, - uint8_t *err) + uint8_t *err, void *user_data) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep = user_data; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: ReConfigure_Ind"); else debug("SBC Source: ReConfigure_Ind"); @@ -492,9 +497,12 @@ static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err) + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep = user_data; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: ReConfigure_Cfm"); else debug("SBC Source: ReConfigure_Cfm"); @@ -594,10 +602,62 @@ static int a2dp_sink_record(sdp_buf_t *buf) return 0; } -int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source) +static struct a2dp_sep *a2dp_add_sep(DBusConnection *conn, uint8_t type) { + struct a2dp_sep *sep; + GSList **l; + int (*create_record)(sdp_buf_t *buf); + uint32_t *record_id; sdp_buf_t buf; + sep = g_new0(struct a2dp_sep, 1); + + sep->sep = avdtp_register_sep(type, AVDTP_MEDIA_TYPE_AUDIO, + &ind, &cfm, sep); + if (sep->sep == NULL) { + g_free(sep); + return NULL; + } + + sep->type = type; + + if (type == AVDTP_SEP_TYPE_SOURCE) { + l = &sources; + create_record = a2dp_source_record; + record_id = &source_record_id; + } else { + l = &sinks; + create_record = a2dp_sink_record; + record_id = &sink_record_id; + } + + if (*record_id != 0) + goto add; + + if (create_record(&buf) < 0) { + error("Unable to allocate new service record"); + avdtp_unregister_sep(sep->sep); + g_free(sep); + return NULL; + } + + *record_id = add_service_record(conn, &buf); + free(buf.data); + if (!*record_id) { + error("Unable to register A2DP service record"); + avdtp_unregister_sep(sep->sep); + g_free(sep); + return NULL; + } + +add: + *l = g_slist_append(*l, sep); + + return sep; +} + +int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source) +{ if (!enable_sink && !enable_source) return 0; @@ -606,68 +666,40 @@ int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source avdtp_init(); if (enable_sink) { - source.sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, - AVDTP_MEDIA_TYPE_AUDIO, - &ind, &cfm); - if (source.sep == NULL) - return -1; - - if (a2dp_source_record(&buf) < 0) { - error("Unable to allocate new service record"); - return -1; - } - - source.record_id = add_service_record(conn, &buf); - free(buf.data); - if (!source.record_id) { - error("Unable to register A2DP Source service record"); - return -1; - } + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE); + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE); } - if (enable_source) { - sink.sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, - AVDTP_MEDIA_TYPE_AUDIO, - &ind, &cfm); - if (sink.sep == NULL) - return -1; - - if (a2dp_sink_record(&buf) < 0) { - error("Unable to allocate new service record"); - return -1; - } - - sink.record_id = add_service_record(conn, &buf); - free(buf.data); - if (!sink.record_id) { - error("Unable to register A2DP Sink service record"); - return -1; - } - } + if (enable_source) + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK); return 0; } +static void a2dp_unregister_sep(struct a2dp_sep *sep) +{ + avdtp_unregister_sep(sep->sep); + g_free(sep); +} + void a2dp_exit() { - if (sink.sep) { - avdtp_unregister_sep(sink.sep); - sink.sep = NULL; - } + g_slist_foreach(sinks, (GFunc) a2dp_unregister_sep, NULL); + g_slist_free(sinks); + sinks = NULL; - if (source.sep) { - avdtp_unregister_sep(source.sep); - source.sep = NULL; - } + g_slist_foreach(sources, (GFunc) a2dp_unregister_sep, NULL); + g_slist_free(sources); + sources = NULL; - if (source.record_id) { - remove_service_record(connection, source.record_id); - source.record_id = 0; + if (source_record_id) { + remove_service_record(connection, source_record_id); + source_record_id = 0; } - if (sink.record_id) { - remove_service_record(connection, sink.record_id); - sink.record_id = 0; + if (sink_record_id) { + remove_service_record(connection, sink_record_id); + sink_record_id = 0; } dbus_connection_unref(connection); @@ -878,15 +910,31 @@ gboolean a2dp_source_cancel_stream(int id) } unsigned int a2dp_source_request_stream(struct avdtp *session, - struct device *dev, - gboolean start, a2dp_stream_cb_t cb, - void *user_data) + struct device *dev, + gboolean start, + a2dp_stream_cb_t cb, + void *user_data, + struct a2dp_sep **ret) { struct a2dp_stream_cb *cb_data; static unsigned int cb_id = 0; + GSList *l; + struct a2dp_sep *sep = NULL; - if (source.used_by != NULL && source.used_by != dev) { - error("a2dp_source_request_stream: SEP is locked"); + for (l = sources; l != NULL; l = l->next) { + struct a2dp_sep *tmp = l->data; + + if (tmp->locked) + continue; + + if (tmp->used_by == NULL || tmp->used_by == dev) { + sep = tmp; + break; + } + } + + if (!sep) { + error("a2dp_source_request_stream: no available SEP found"); return 0; } @@ -896,6 +944,10 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, return 0; } + sep->used_by = dev; + + debug("a2dp_source_request_stream: selected SEP %p", sep); + cb_data = g_new(struct a2dp_stream_cb, 1); cb_data->cb = cb; cb_data->user_data = user_data; @@ -914,9 +966,9 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, setup->dev = dev; setup->cb = g_slist_append(setup->cb, cb_data); setup->start = start; - setup->stream = source.stream; + setup->stream = sep->stream; - switch (avdtp_sep_get_state(source.sep)) { + switch (avdtp_sep_get_state(sep->sep)) { case AVDTP_STATE_IDLE: if (avdtp_discover(session, discovery_complete, setup) < 0) { error("avdtp_discover failed"); @@ -928,32 +980,35 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, g_idle_add((GSourceFunc) finalize_stream_setup, setup); break; } - if (source.starting) + if (sep->starting) break; - if (avdtp_start(session, source.stream) < 0) { + if (avdtp_start(session, sep->stream) < 0) { error("avdtp_start failed"); goto failed; } break; case AVDTP_STATE_STREAMING: - if (!start || !source.suspending) { - if (source.suspend_timer) { - g_source_remove(source.suspend_timer); - source.suspend_timer = 0; + if (!start || !sep->suspending) { + if (sep->suspend_timer) { + g_source_remove(sep->suspend_timer); + sep->suspend_timer = 0; } - if (source.session) { - avdtp_unref(source.session); - source.session = NULL; + if (sep->session) { + avdtp_unref(sep->session); + sep->session = NULL; } g_idle_add((GSourceFunc) finalize_stream_setup, setup); return cb_data->id; } - source.start_requested = TRUE; + sep->start_requested = TRUE; break; default: error("SEP in bad state for requesting a new stream"); goto failed; } + + if (ret) + *ret = sep; return cb_data->id; @@ -965,33 +1020,51 @@ failed: gboolean a2dp_source_lock(struct device *dev, struct avdtp *session) { - if (source.used_by) - return FALSE; + GSList *l; - debug("SBC Source locked"); + for (l = sources; l != NULL; l = l->next) { + struct a2dp_sep *sep = l->data; - source.used_by = dev; + if (sep->locked) + continue; - return TRUE; + debug("SBC Source SEP %p locked", sep); + sep->locked = TRUE; + return TRUE; + } + + return FALSE; } gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session) { avdtp_state_t state; + GSList *l; + struct a2dp_sep *sep = NULL; - if (!source.sep) - return FALSE; + for (l = sources; l != NULL; l = l->next) { + struct a2dp_sep *tmp = l->data; + + if (!sep->locked) + continue; + + if (tmp->sep && tmp->used_by == dev) { + sep = tmp; + break; + } + } - if (source.used_by != dev) + if (!sep) return FALSE; - state = avdtp_sep_get_state(source.sep); + state = avdtp_sep_get_state(sep->sep); - source.used_by = NULL; + sep->locked = FALSE; + sep->used_by = NULL; - debug("SBC Source unlocked"); + debug("SBC Source SEP %p unlocked", sep); - if (!source.stream || state == AVDTP_STATE_IDLE) + if (!sep->stream || state == AVDTP_STATE_IDLE) return TRUE; switch (state) { @@ -999,8 +1072,8 @@ gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session) /* Set timer here */ break; case AVDTP_STATE_STREAMING: - if (avdtp_suspend(session, source.stream) == 0) - source.suspending = TRUE; + if (avdtp_suspend(session, sep->stream) == 0) + sep->suspending = TRUE; break; default: break; @@ -1012,20 +1085,28 @@ gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session) gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session) { avdtp_state_t state; + GSList *l; + struct a2dp_sep *sep = NULL; - if (!source.sep) - return FALSE; + for (l = sources; l != NULL; l = l->next) { + struct a2dp_sep *tmp = l->data; - if (source.used_by != dev) + if (tmp->sep && tmp->used_by == dev) { + sep = tmp; + break; + } + } + + if (!sep) return FALSE; - state = avdtp_sep_get_state(source.sep); + state = avdtp_sep_get_state(sep->sep); - if (!source.stream || state != AVDTP_STATE_STREAMING) + if (!sep->stream || state != AVDTP_STATE_STREAMING) return TRUE; - if (avdtp_suspend(session, source.stream) == 0) { - source.suspending = TRUE; + if (avdtp_suspend(session, sep->stream) == 0) { + sep->suspending = TRUE; return TRUE; } @@ -1035,14 +1116,22 @@ gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session) gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session) { avdtp_state_t state; + GSList *l; + struct a2dp_sep *sep = NULL; - if (!source.sep) - return FALSE; + for (l = sources; l != NULL; l = l->next) { + struct a2dp_sep *tmp = l->data; + + if (tmp->sep && tmp->used_by == dev) { + sep = tmp; + break; + } + } - if (source.used_by != dev) + if (!sep) return FALSE; - state = avdtp_sep_get_state(source.sep); + state = avdtp_sep_get_state(sep->sep); if (state < AVDTP_STATE_OPEN) { error("a2dp_source_start_stream: no stream open"); @@ -1052,7 +1141,7 @@ gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session) if (state == AVDTP_STATE_STREAMING) return TRUE; - if (avdtp_start(session, source.stream) < 0) + if (avdtp_start(session, sep->stream) < 0) return FALSE; return TRUE; diff --git a/audio/a2dp.h b/audio/a2dp.h index 6d69189c..0a838b62 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -58,6 +58,8 @@ struct sbc_codec_cap { uint8_t max_bitpool; } __attribute__ ((packed)); +struct a2dp_sep; + typedef void (*a2dp_stream_cb_t) (struct avdtp *session, struct device *dev, struct avdtp_stream *stream, void *user_data); @@ -69,7 +71,8 @@ void a2dp_exit(void); unsigned int a2dp_source_request_stream(struct avdtp *session, struct device *dev, gboolean start, a2dp_stream_cb_t cb, - void *user_data); + void *user_data, + struct a2dp_sep **sep); gboolean a2dp_source_cancel_stream(int id); gboolean a2dp_source_lock(struct device *dev, struct avdtp *session); diff --git a/audio/avdtp.c b/audio/avdtp.c index 0b56340f..99a929f4 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -219,7 +219,7 @@ struct avdtp_local_sep { GSList *caps; struct avdtp_sep_ind *ind; struct avdtp_sep_cfm *cfm; - void *data; + void *user_data; }; struct stream_callback { @@ -573,7 +573,7 @@ static void release_stream(struct avdtp_stream *stream, struct avdtp *session) g_source_remove(stream->io); if (sep->cfm && sep->cfm->abort) - sep->cfm->abort(session, sep, stream, NULL); + sep->cfm->abort(session, sep, stream, NULL, sep->user_data); avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); } @@ -815,7 +815,8 @@ static gboolean avdtp_getcap_cmd(struct avdtp *session, goto failed; } - if (!sep->ind->get_capability(session, sep, &caps, &err)) + if (!sep->ind->get_capability(session, sep, &caps, &err, + sep->user_data)) goto failed; init_response(&rsp->header, &req->header, TRUE); @@ -882,7 +883,8 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, if (sep->ind && sep->ind->set_configuration) { if (!sep->ind->set_configuration(session, sep, stream, stream->caps, &err, - &category)) { + &category, + sep->user_data)) { stream_free(stream); goto failed; } @@ -949,7 +951,8 @@ static gboolean avdtp_open_cmd(struct avdtp *session, struct seid_req *req, stream = sep->stream; if (sep->ind && sep->ind->open) { - if (!sep->ind->open(session, sep, stream, &err)) + if (!sep->ind->open(session, sep, stream, &err, + sep->user_data)) goto failed; } @@ -1009,7 +1012,8 @@ static gboolean avdtp_start_cmd(struct avdtp *session, struct start_req *req, } if (sep->ind && sep->ind->start) { - if (!sep->ind->start(session, sep, stream, &err)) + if (!sep->ind->start(session, sep, stream, &err, + sep->user_data)) goto failed; } @@ -1057,7 +1061,8 @@ static gboolean avdtp_close_cmd(struct avdtp *session, struct seid_req *req, stream = sep->stream; if (sep->ind && sep->ind->close) { - if (!sep->ind->close(session, sep, stream, &err)) + if (!sep->ind->close(session, sep, stream, &err, + sep->user_data)) goto failed; } @@ -1116,7 +1121,8 @@ static gboolean avdtp_suspend_cmd(struct avdtp *session, } if (sep->ind && sep->ind->suspend) { - if (!sep->ind->suspend(session, sep, stream, &err)) + if (!sep->ind->suspend(session, sep, stream, &err, + sep->user_data)) goto failed; } @@ -1156,7 +1162,8 @@ static gboolean avdtp_abort_cmd(struct avdtp *session, struct seid_req *req, } if (sep->ind && sep->ind->abort) { - if (!sep->ind->abort(session, sep, sep->stream, &err)) + if (!sep->ind->abort(session, sep, sep->stream, &err, + sep->user_data)) goto failed; } @@ -1230,7 +1237,8 @@ static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, struct avdtp_local_sep *sep = stream->lsep; if (stream->close_int && sep->cfm && sep->cfm->close) - sep->cfm->close(stream->session, sep, stream, NULL); + sep->cfm->close(stream->session, sep, stream, NULL, + sep->user_data); avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE); @@ -1255,7 +1263,7 @@ static void handle_transport_connect(struct avdtp *session, int sock, stream->mtu = mtu; if (!stream->open_acp && sep->cfm && sep->cfm->open) - sep->cfm->open(session, sep, stream, NULL); + sep->cfm->open(session, sep, stream, NULL, sep->user_data); channel = g_io_channel_unix_new(stream->sock); @@ -1556,36 +1564,41 @@ static gboolean request_timeout(gpointer user_data) case AVDTP_RECONFIGURE: error("Reconfigure request timed out"); if (lsep && lsep->cfm && lsep->cfm->reconfigure) - lsep->cfm->reconfigure(session, lsep, stream, &err); + lsep->cfm->reconfigure(session, lsep, stream, &err, + lsep->user_data); break; case AVDTP_OPEN: error("Open request timed out"); if (lsep && lsep->cfm && lsep->cfm->open) - lsep->cfm->open(session, lsep, stream, &err); + lsep->cfm->open(session, lsep, stream, &err, + lsep->user_data); break; case AVDTP_START: error("Start request timed out"); if (lsep && lsep->cfm && lsep->cfm->start) - lsep->cfm->start(session, lsep, stream, &err); + lsep->cfm->start(session, lsep, stream, &err, + lsep->user_data); break; case AVDTP_SUSPEND: error("Suspend request timed out"); if (lsep && lsep->cfm && lsep->cfm->suspend) - lsep->cfm->suspend(session, lsep, stream, &err); + lsep->cfm->suspend(session, lsep, stream, &err, + lsep->user_data); break; case AVDTP_CLOSE: error("Close request timed out"); if (lsep && lsep->cfm && lsep->cfm->close) - lsep->cfm->close(session, lsep, stream, &err); + lsep->cfm->close(session, lsep, stream, &err, + lsep->user_data); break; case AVDTP_SET_CONFIGURATION: error("SetConfiguration request timed out"); if (lsep && lsep->cfm && lsep->cfm->set_configuration) - lsep->cfm->set_configuration(session, lsep, stream, &err); - /* fallthrough on purpose */ + lsep->cfm->set_configuration(session, lsep, stream, + &err, lsep->user_data); + goto failed; case AVDTP_DISCOVER: error("Discover request timed out"); - if (lsep && lsep->cfm && lsep->cfm->set_configuration) goto failed; case AVDTP_GET_CAPABILITIES: error("GetCapabilities request timed out"); @@ -1756,7 +1769,8 @@ static gboolean avdtp_set_configuration_resp(struct avdtp *session, struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->set_configuration) - sep->cfm->set_configuration(session, sep, stream, NULL); + sep->cfm->set_configuration(session, sep, stream, NULL, + sep->user_data); avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); @@ -1794,7 +1808,7 @@ static gboolean avdtp_start_resp(struct avdtp *session, struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->start) - sep->cfm->start(session, sep, stream, NULL); + sep->cfm->start(session, sep, stream, NULL, sep->user_data); avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); @@ -1825,7 +1839,7 @@ static gboolean avdtp_suspend_resp(struct avdtp *session, avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); if (sep->cfm && sep->cfm->suspend) - sep->cfm->suspend(session, sep, stream, NULL); + sep->cfm->suspend(session, sep, stream, NULL, sep->user_data); return TRUE; } @@ -1837,7 +1851,7 @@ static gboolean avdtp_abort_resp(struct avdtp *session, struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->suspend) - sep->cfm->suspend(session, sep, stream, NULL); + sep->cfm->suspend(session, sep, stream, NULL, sep->user_data); avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); @@ -1973,7 +1987,8 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre error("OPEN request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->open) - sep->cfm->open(session, sep, stream, &err); + sep->cfm->open(session, sep, stream, &err, + sep->user_data); return TRUE; case AVDTP_SET_CONFIGURATION: if (!conf_rej_to_err((void *) header, size, &err, &category)) @@ -1981,7 +1996,8 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre error("SET_CONFIGURATION request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->set_configuration) - sep->cfm->set_configuration(session, sep, stream, &err); + sep->cfm->set_configuration(session, sep, stream, + &err, sep->user_data); return TRUE; case AVDTP_RECONFIGURE: if (!conf_rej_to_err((void *) header, size, &err, &category)) @@ -1989,7 +2005,8 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre error("RECONFIGURE request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->reconfigure) - sep->cfm->reconfigure(session, sep, stream, &err); + sep->cfm->reconfigure(session, sep, stream, &err, + sep->user_data); return TRUE; case AVDTP_START: if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) @@ -1997,7 +2014,8 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre error("START request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->start) - sep->cfm->start(session, sep, stream, &err); + sep->cfm->start(session, sep, stream, &err, + sep->user_data); return TRUE; case AVDTP_SUSPEND: if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) @@ -2005,7 +2023,8 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre error("SUSPEND request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->suspend) - sep->cfm->suspend(session, sep, stream, &err); + sep->cfm->suspend(session, sep, stream, &err, + sep->user_data); return TRUE; case AVDTP_CLOSE: if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) @@ -2013,7 +2032,8 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre error("CLOSE request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->close) - sep->cfm->close(session, sep, stream, &err); + sep->cfm->close(session, sep, stream, &err, + sep->user_data); return TRUE; case AVDTP_ABORT: if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) @@ -2021,7 +2041,8 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre error("ABORT request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->abort) - sep->cfm->abort(session, sep, stream, &err); + sep->cfm->abort(session, sep, stream, &err, + sep->user_data); return TRUE; default: error("Unknown reject response signal id: %u", @@ -2298,6 +2319,9 @@ int avdtp_set_configuration(struct avdtp *session, if (!(lsep && rsep)) return -EINVAL; + debug("avdtp_set_configuration(%p): int_seid=%u, acp_seid=%u", + session, lsep->info.seid, rsep->seid); + new_stream = g_new0(struct avdtp_stream, 1); new_stream->session = session; @@ -2314,8 +2338,8 @@ int avdtp_set_configuration(struct avdtp *session, req = g_malloc0(sizeof(struct setconf_req) + caps_len); init_request(&req->header, AVDTP_SET_CONFIGURATION); - req->acp_seid = lsep->info.seid; - req->int_seid = rsep->seid; + req->int_seid = lsep->info.seid; + req->acp_seid = rsep->seid; /* Copy the capabilities into the request */ for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) { @@ -2457,7 +2481,8 @@ int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream) struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, struct avdtp_sep_ind *ind, - struct avdtp_sep_cfm *cfm) + struct avdtp_sep_cfm *cfm, + void *user_data) { struct avdtp_local_sep *sep; @@ -2472,6 +2497,7 @@ struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, sep->info.media_type = media_type; sep->ind = ind; sep->cfm = cfm; + sep->user_data = user_data; local_seps = g_slist_append(local_seps, sep); diff --git a/audio/avdtp.h b/audio/avdtp.h index 49907cff..d759be48 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -97,28 +97,32 @@ struct avdtp_sep_cfm { void (*set_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, - struct avdtp_error *err); + struct avdtp_error *err, + void *user_data); void (*get_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, - struct avdtp_error *err); + struct avdtp_error *err, + void *user_data); void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, struct avdtp_error *err); + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data); void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, struct avdtp_error *err); + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data); void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, - struct avdtp_error *err); + struct avdtp_error *err, void *user_data); void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, - struct avdtp_error *err); + struct avdtp_error *err, void *user_data); void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, - struct avdtp_error *err); + struct avdtp_error *err, void *user_data); void (*reconfigure) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, - struct avdtp_error *err); + struct avdtp_error *err, void *user_data); }; /* Callbacks for indicating when we received a new command. The return value @@ -126,32 +130,35 @@ struct avdtp_sep_cfm { struct avdtp_sep_ind { gboolean (*get_capability) (struct avdtp *session, struct avdtp_local_sep *sep, - GSList **caps, uint8_t *err); + GSList **caps, uint8_t *err, + void *user_data); gboolean (*set_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, GSList *caps, uint8_t *err, - uint8_t *category); + uint8_t *category, void *user_data); gboolean (*get_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, - uint8_t *err); + uint8_t *err, void *user_data); gboolean (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, uint8_t *err); + struct avdtp_stream *stream, uint8_t *err, + void *user_data); gboolean (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, - uint8_t *err); + struct avdtp_stream *stream, uint8_t *err, + void *user_data); gboolean (*suspend) (struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, - uint8_t *err); + struct avdtp_stream *stream, uint8_t *err, + void *user_data); gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, - uint8_t *err); + struct avdtp_stream *stream, uint8_t *err, + void *user_data); gboolean (*abort) (struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err); + struct avdtp_stream *stream, uint8_t *err, + void *user_data); gboolean (*reconfigure) (struct avdtp *session, struct avdtp_local_sep *lsep, - uint8_t *err); + uint8_t *err, void *user_data); }; typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps, @@ -200,7 +207,8 @@ int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream); struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, struct avdtp_sep_ind *ind, - struct avdtp_sep_cfm *cfm); + struct avdtp_sep_cfm *cfm, + void *user_data); /* Find a matching pair of local and remote SEP ID's */ int avdtp_get_seps(struct avdtp *session, uint8_t type, uint8_t media, diff --git a/audio/sink.c b/audio/sink.c index 8141eff5..0dc12eb3 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -149,11 +149,13 @@ static void stream_setup_complete(struct avdtp *session, struct device *dev, DBusMessage *reply; reply = dbus_message_new_method_return(pending->msg); send_message_and_unref(pending->conn, reply); + debug("Stream successfully created"); } else { err_failed(pending->conn, pending->msg, "Stream setup failed"); avdtp_unref(sink->session); sink->session = NULL; + debug("Stream setup failed"); } pending_request_free(pending); @@ -186,7 +188,7 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, sink->connect = pending; id = a2dp_source_request_stream(sink->session, dev, FALSE, - stream_setup_complete, pending); + stream_setup_complete, pending, NULL); if (id == 0) { pending_request_free(pending); sink->connect = NULL; @@ -196,6 +198,8 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, "Failed to request a stream"); } + debug("stream creation in progress"); + pending->id = id; return DBUS_HANDLER_RESULT_HANDLED; diff --git a/audio/unix.c b/audio/unix.c index 2b9b74f0..a04c5bfb 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -61,10 +61,12 @@ typedef void (*notify_cb_t) (struct device *dev, void *data); struct a2dp_data { struct avdtp *session; struct avdtp_stream *stream; + struct a2dp_sep *sep; }; struct unix_client { struct device *dev; + struct avdtp_local_sep *sep; service_type_t type; union { struct a2dp_data a2dp; @@ -335,7 +337,7 @@ proceed: id = a2dp_source_request_stream(a2dp->session, dev, TRUE, a2dp_setup_complete, - client); + client, &a2dp->sep); if (id == 0) { error("request_stream failed"); goto failed; @@ -414,7 +416,7 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) } if (cond & (G_IO_HUP | G_IO_ERR)) { - debug("Unix client disconnected"); + debug("Unix client disconnected (fd=%d)", client->sock); if (!client->dev) goto failed; if (client->disconnect) @@ -488,7 +490,7 @@ static gboolean server_cb(GIOChannel *chan, GIOCondition cond, gpointer data) return TRUE; } - debug("Accepted new client connection on unix socket"); + debug("Accepted new client connection on unix socket (fd=%d)", cli_sk); client = g_new0(struct unix_client, 1); client->sock = cli_sk; -- cgit