diff options
author | Johan Hedberg <johan.hedberg@nokia.com> | 2007-08-16 21:48:33 +0000 |
---|---|---|
committer | Johan Hedberg <johan.hedberg@nokia.com> | 2007-08-16 21:48:33 +0000 |
commit | 0f458da19471c933a4105cc450c48548b72edc8e (patch) | |
tree | 3fe4c38c29c7adb1eb13cf273d8335efa97d1337 /audio | |
parent | 871c0518ec53309a38debbb86a36c035c3470fba (diff) |
Handle error situations better
Diffstat (limited to 'audio')
-rw-r--r-- | audio/a2dp.c | 64 | ||||
-rw-r--r-- | audio/a2dp.h | 3 | ||||
-rw-r--r-- | audio/avdtp.c | 166 | ||||
-rw-r--r-- | audio/avdtp.h | 31 | ||||
-rw-r--r-- | audio/sink.c | 16 | ||||
-rw-r--r-- | audio/unix.c | 82 |
6 files changed, 280 insertions, 82 deletions
diff --git a/audio/a2dp.c b/audio/a2dp.c index bc9c4013..2ad660b6 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -190,9 +190,10 @@ 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_stream *stream, + struct avdtp_error *err) { - int err; + int ret; if (sep == sink.sep) { debug("SBC Sink: Set_Configuration_Cfm"); @@ -201,15 +202,22 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, debug("SBC Source: Set_Configuration_Cfm"); + if (err) { + source.stream = NULL; + if (setup) + finalize_stream_setup(setup); + return; + } + source.stream = stream; if (!setup) return; - err = avdtp_open(session, stream); - if (err < 0) { - error("Error on avdtp_open %s (%d)", strerror(-err), - -err); + ret = avdtp_open(session, stream); + if (ret < 0) { + error("Error on avdtp_open %s (%d)", strerror(-ret), + -ret); setup->stream = FALSE; finalize_stream_setup(setup); } @@ -226,7 +234,7 @@ 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_stream *stream, struct avdtp_error *err) { if (sep == sink.sep) debug("SBC Sink: Set_Configuration_Cfm"); @@ -245,7 +253,7 @@ 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_stream *stream, struct avdtp_error *err) { if (sep == sink.sep) debug("SBC Sink: Open_Cfm"); @@ -256,11 +264,17 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, return; if (setup->canceled) { - avdtp_close(session, stream); + if (!err) + avdtp_close(session, stream); stream_setup_free(setup); return; } + if (err) { + setup->stream = NULL; + goto finalize; + } + if (setup->start) { if (avdtp_start(session, stream) == 0) return; @@ -269,6 +283,7 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, setup->stream = NULL; } +finalize: finalize_stream_setup(setup); } @@ -287,7 +302,7 @@ 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_stream *stream, struct avdtp_error *err) { if (sep == sink.sep) debug("SBC Sink: Start_Cfm"); @@ -298,11 +313,15 @@ static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, return; if (setup->canceled) { - avdtp_close(session, stream); + if (!err) + avdtp_close(session, stream); stream_setup_free(setup); return; } + if (err) + setup->stream = NULL; + finalize_stream_setup(setup); } @@ -317,7 +336,7 @@ 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_stream *stream, struct avdtp_error *err) { if (sep == sink.sep) { debug("SBC Sink: Suspend_Cfm"); @@ -328,6 +347,13 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, source.suspending = FALSE; + if (err) { + source.start_requested = FALSE; + if (setup) + finalize_stream_setup(setup); + return; + } + if (source.start_requested) { avdtp_start(session, stream); source.start_requested = FALSE; @@ -350,7 +376,7 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream) + struct avdtp_stream *stream, struct avdtp_error *err) { if (sep == sink.sep) { debug("SBC Sink: Close_Cfm"); @@ -378,7 +404,7 @@ static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream) + struct avdtp_stream *stream, struct avdtp_error *err) { if (sep == sink.sep) debug("SBC Sink: Abort_Cfm"); @@ -396,7 +422,8 @@ static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, return TRUE; } -static void reconf_cfm(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) { if (sep == sink.sep) debug("SBC Sink: ReConfigure_Cfm"); @@ -781,7 +808,8 @@ gboolean a2dp_source_cancel_stream(int id) return TRUE; } -int a2dp_source_request_stream(struct avdtp *session, struct device *dev, +unsigned int a2dp_source_request_stream(struct avdtp *session, + struct device *dev, gboolean start, a2dp_stream_cb_t cb, void *user_data) { @@ -791,7 +819,7 @@ int a2dp_source_request_stream(struct avdtp *session, struct device *dev, cb_data = g_new(struct a2dp_stream_cb, 1); cb_data->cb = cb; cb_data->user_data = user_data; - cb_data->id = cb_id++; + cb_data->id = ++cb_id; if (setup) { setup->canceled = FALSE; @@ -839,7 +867,7 @@ int a2dp_source_request_stream(struct avdtp *session, struct device *dev, failed: stream_setup_free(setup); cb_id--; - return -1; + return 0; } gboolean a2dp_source_lock(struct device *dev, struct avdtp *session) diff --git a/audio/a2dp.h b/audio/a2dp.h index cf13de8d..6d69189c 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -66,7 +66,8 @@ int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source); void a2dp_exit(void); -int a2dp_source_request_stream(struct avdtp *session, struct device *dev, +unsigned int a2dp_source_request_stream(struct avdtp *session, + struct device *dev, gboolean start, a2dp_stream_cb_t cb, void *user_data); gboolean a2dp_source_cancel_stream(int id); diff --git a/audio/avdtp.c b/audio/avdtp.c index 7194177f..1687714b 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -218,6 +218,12 @@ struct avdtp_local_sep { void *data; }; +struct stream_callback { + avdtp_stream_state_cb cb; + void *user_data; + unsigned int id; +}; + struct avdtp_stream { int sock; uint16_t mtu; @@ -225,9 +231,8 @@ struct avdtp_stream { struct avdtp_local_sep *lsep; uint8_t rseid; GSList *caps; + GSList *callbacks; struct avdtp_service_capability *codec; - avdtp_stream_state_cb cb; - void *user_data; guint io; /* Transport GSource ID */ guint timer; /* Waiting for other side to close or open the transport channel */ @@ -456,10 +461,13 @@ static void stream_free(struct avdtp_stream *stream) if (stream->timer) g_source_remove(stream->timer); - if (stream->caps) { - g_slist_foreach(stream->caps, (GFunc) g_free, NULL); - g_slist_free(stream->caps); - } + + g_slist_foreach(stream->callbacks, (GFunc) g_free, NULL); + g_slist_free(stream->callbacks); + + g_slist_foreach(stream->caps, (GFunc) g_free, NULL); + g_slist_free(stream->caps); + g_free(stream); } @@ -485,9 +493,14 @@ static void avdtp_sep_set_state(struct avdtp *session, old_state = sep->state; sep->state = state; - if (stream && stream->cb) - stream->cb(stream, old_state, state, err_ptr, - stream->user_data); + if (stream) { + GSList *l; + for (l = stream->callbacks; l != NULL; l = g_slist_next(l)) { + struct stream_callback *cb = l->data; + cb->cb(stream, old_state, state, err_ptr, + cb->user_data); + } + } if (state == AVDTP_STATE_IDLE) { session->streams = g_slist_remove(session->streams, stream); @@ -1170,7 +1183,7 @@ 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); + sep->cfm->close(stream->session, sep, stream, NULL); avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE); @@ -1195,7 +1208,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); + sep->cfm->open(session, sep, stream, NULL); channel = g_io_channel_unix_new(stream->sock); @@ -1465,33 +1478,75 @@ static gboolean request_timeout(gpointer user_data) struct avdtp *session = user_data; struct pending_req *req; struct seid_req sreq; - struct avdtp_remote_sep *sep; + struct avdtp_remote_sep *rsep; + struct avdtp_local_sep *lsep; struct avdtp_stream *stream; uint8_t seid; - - error("Request timed out"); + struct avdtp_error err; req = session->req; session->req = NULL; - switch (req->msg->signal_id) { - case AVDTP_DISCOVER: - case AVDTP_GET_CAPABILITIES: - case AVDTP_SET_CONFIGURATION: - case AVDTP_ABORT: - goto failed; - } + avdtp_error_init(&err, AVDTP_ERROR_ERRNO, ETIMEDOUT); seid = ((struct seid_req *) (req->msg))->acp_seid; - sep = find_remote_sep(session->seps, seid); - if (!sep) { + rsep = find_remote_sep(session->seps, seid); + if (!rsep) { error("Unable to find matching remote SEID %u", seid); goto failed; } stream = find_stream_by_rseid(session, seid); + if (stream) + lsep = stream->lsep; + else + lsep = NULL; + + switch (req->msg->signal_id) { + case AVDTP_RECONFIGURE: + error("Reconfigure request timed out"); + if (lsep && lsep->cfm && lsep->cfm->reconfigure) + lsep->cfm->reconfigure(session, lsep, stream, &err); + break; + case AVDTP_OPEN: + error("Open request timed out"); + if (lsep && lsep->cfm && lsep->cfm->open) + lsep->cfm->open(session, lsep, stream, &err); + break; + case AVDTP_START: + error("Start request timed out"); + if (lsep && lsep->cfm && lsep->cfm->start) + lsep->cfm->start(session, lsep, stream, &err); + break; + case AVDTP_SUSPEND: + error("Suspend request timed out"); + if (lsep && lsep->cfm && lsep->cfm->suspend) + lsep->cfm->suspend(session, lsep, stream, &err); + break; + case AVDTP_CLOSE: + error("Close request timed out"); + if (lsep && lsep->cfm && lsep->cfm->close) + lsep->cfm->close(session, lsep, stream, &err); + 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 */ + 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"); + goto failed; + case AVDTP_ABORT: + error("Abort request timed out"); + goto failed; + } + memset(&sreq, 0, sizeof(sreq)); init_request(&sreq.header, AVDTP_ABORT); sreq.acp_seid = seid; @@ -1646,7 +1701,7 @@ 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); + sep->cfm->set_configuration(session, sep, stream, NULL); avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); @@ -1684,7 +1739,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); + sep->cfm->start(session, sep, stream, NULL); avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); @@ -1715,7 +1770,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); + sep->cfm->suspend(session, sep, stream, NULL); return TRUE; } @@ -1727,7 +1782,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); + sep->cfm->suspend(session, sep, stream, NULL); avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); @@ -1845,6 +1900,7 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre { struct avdtp_error err; uint8_t acp_seid, category; + struct avdtp_local_sep *sep = stream ? stream->lsep : NULL; switch (header->signal_id) { case AVDTP_DISCOVER: @@ -1864,42 +1920,56 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre return FALSE; 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); return TRUE; case AVDTP_SET_CONFIGURATION: if (!conf_rej_to_err((void *) header, size, &err, &category)) return FALSE; 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); return TRUE; case AVDTP_RECONFIGURE: if (!conf_rej_to_err((void *) header, size, &err, &category)) return FALSE; 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); return TRUE; case AVDTP_START: if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) return FALSE; 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); return TRUE; case AVDTP_SUSPEND: if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) return FALSE; 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); return TRUE; case AVDTP_CLOSE: if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) return FALSE; 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); return TRUE; case AVDTP_ABORT: if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) return FALSE; 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); return TRUE; default: error("Unknown reject response signal id: %u", @@ -2085,11 +2155,45 @@ int avdtp_get_seps(struct avdtp *session, uint8_t acp_type, uint8_t media_type, return -EINVAL; } -void avdtp_stream_set_cb(struct avdtp *session, struct avdtp_stream *stream, - avdtp_stream_state_cb cb, void *data) +gboolean avdtp_stream_remove_cb(struct avdtp *session, + struct avdtp_stream *stream, + unsigned int id) { - stream->cb = cb; - stream->user_data = data; + GSList *l; + struct stream_callback *cb; + + for (cb = NULL, l = stream->callbacks; l != NULL; l = l->next) { + struct stream_callback *tmp = l->data; + if (tmp->id == id) { + cb = tmp; + break; + } + } + + if (!cb) + return FALSE; + + stream->callbacks = g_slist_remove(stream->callbacks, cb); + g_free(cb); + + return TRUE; +} + +unsigned int avdtp_stream_add_cb(struct avdtp *session, + struct avdtp_stream *stream, + avdtp_stream_state_cb cb, void *data) +{ + struct stream_callback *stream_cb; + static unsigned int id = 0; + + stream_cb = g_new(struct stream_callback, 1); + stream_cb->cb = cb; + stream_cb->user_data = data; + stream_cb->id = ++id; + + stream->callbacks = g_slist_append(stream->callbacks, stream_cb);; + + return stream_cb->id; } int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream) diff --git a/audio/avdtp.h b/audio/avdtp.h index 0043c480..49907cff 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -96,22 +96,29 @@ typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream, struct avdtp_sep_cfm { void (*set_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream); + struct avdtp_stream *stream, + struct avdtp_error *err); void (*get_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream); + struct avdtp_stream *stream, + struct avdtp_error *err); void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream); + struct avdtp_stream *stream, struct avdtp_error *err); void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream); + struct avdtp_stream *stream, struct avdtp_error *err); void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream); + struct avdtp_stream *stream, + struct avdtp_error *err); void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream); + struct avdtp_stream *stream, + struct avdtp_error *err); void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream); + struct avdtp_stream *stream, + struct avdtp_error *err); void (*reconfigure) (struct avdtp *session, - struct avdtp_local_sep *lsep); + struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err); }; /* Callbacks for indicating when we received a new command. The return value @@ -165,8 +172,12 @@ struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep); int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, void *user_data); -void avdtp_stream_set_cb(struct avdtp *session, struct avdtp_stream *stream, - avdtp_stream_state_cb cb, void *data); +unsigned int avdtp_stream_add_cb(struct avdtp *session, + struct avdtp_stream *stream, + avdtp_stream_state_cb cb, void *data); +gboolean avdtp_stream_remove_cb(struct avdtp *session, + struct avdtp_stream *stream, + unsigned int id); gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, uint16_t *mtu, GSList **caps); diff --git a/audio/sink.c b/audio/sink.c index 0f69bbf6..ab9f401b 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -48,7 +48,7 @@ struct pending_request { DBusConnection *conn; DBusMessage *msg; - int id; + unsigned int id; }; struct sink { @@ -71,9 +71,11 @@ static void pending_request_free(struct pending_request *pending) g_free(pending); } -void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, - avdtp_state_t new_state, - struct avdtp_error *err, void *user_data) +static void stream_state_changed(struct avdtp_stream *stream, + avdtp_state_t old_state, + avdtp_state_t new_state, + struct avdtp_error *err, + void *user_data) { struct device *dev = user_data; struct sink *sink = dev->sink; @@ -153,7 +155,7 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, struct device *dev = data; struct sink *sink = dev->sink; struct pending_request *pending; - int id; + unsigned int id; if (!sink->session) sink->session = avdtp_get(&dev->src, &dev->dst); @@ -171,7 +173,7 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, id = a2dp_source_request_stream(sink->session, dev, FALSE, stream_setup_complete, pending); - if (id < 0) { + if (id == 0) { pending_request_free(pending); sink->connect = NULL; avdtp_unref(sink->session); @@ -315,7 +317,7 @@ gboolean sink_new_stream(struct device *dev, struct avdtp *session, sink->stream = stream; sink->initiator = FALSE; - avdtp_stream_set_cb(session, stream, stream_state_changed, dev); + avdtp_stream_add_cb(session, stream, stream_state_changed, dev); return TRUE; } diff --git a/audio/unix.c b/audio/unix.c index 10856b93..94c84500 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -58,15 +58,21 @@ typedef enum { typedef void (*notify_cb_t) (struct device *dev, void *data); +struct a2dp_data { + struct avdtp *session; + struct avdtp_stream *stream; +}; + struct unix_client { struct device *dev; service_type_t type; union { - struct avdtp *session; + struct a2dp_data a2dp; void *data; - } data; + } d; int sock; - int req_id; + unsigned int req_id; + unsigned int cb_id; notify_cb_t disconnect; notify_cb_t suspend; notify_cb_t play; @@ -78,11 +84,17 @@ static int unix_sock = -1; static void client_free(struct unix_client *client) { + struct a2dp_data *a2dp; + switch (client->type) { case TYPE_SINK: case TYPE_SOURCE: - if (client->data.session) - avdtp_unref(client->data.session); + a2dp = &client->d.a2dp; + if (client->cb_id > 0) + avdtp_stream_remove_cb(a2dp->session, a2dp->stream, + client->cb_id); + if (a2dp->session); + avdtp_unref(a2dp->session); break; default: break; @@ -140,6 +152,30 @@ static service_type_t select_service(struct device *dev) return TYPE_NONE; } + +static void stream_state_changed(struct avdtp_stream *stream, + avdtp_state_t old_state, + avdtp_state_t new_state, + struct avdtp_error *err, + void *user_data) +{ + struct unix_client *client = user_data; + struct a2dp_data *a2dp = &client->d.a2dp; + + switch (new_state) { + case AVDTP_STATE_IDLE: + if (a2dp->session) { + avdtp_unref(a2dp->session); + a2dp->session = NULL; + } + a2dp->stream = NULL; + client->cb_id = 0; + break; + default: + break; + } +} + static void a2dp_setup_complete(struct avdtp *session, struct device *dev, struct avdtp_stream *stream, void *user_data) @@ -151,12 +187,17 @@ static void a2dp_setup_complete(struct avdtp *session, struct device *dev, struct avdtp_media_codec_capability *codec_cap; struct sbc_codec_cap *sbc_cap; struct ipc_codec_sbc *sbc = (void *) cfg->data; + struct a2dp_data *a2dp = &client->d.a2dp; int fd; GSList *caps; + client->req_id = 0; + if (!stream) goto failed; + a2dp->stream = stream; + if (!avdtp_stream_get_transport(stream, &fd, &cfg->pkt_len, &caps)) { error("Unable to get stream transport"); goto failed; @@ -223,11 +264,18 @@ static void a2dp_setup_complete(struct avdtp *session, struct device *dev, unix_send_cfg(client->sock, cfg, fd); + client->cb_id = avdtp_stream_add_cb(session, stream, + stream_state_changed, client); + return; failed: + error("stream setup failed"); unix_send_cfg(client->sock, NULL, -1); a2dp_source_unlock(dev, session); + avdtp_unref(a2dp->session); + a2dp->session = NULL; + a2dp->stream = NULL; } static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, @@ -235,7 +283,9 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, { struct ipc_data_cfg *rsp; struct device *dev; - int ret, fd, id; + int ret, fd; + unsigned int id; + struct a2dp_data *a2dp; dev = manager_get_connected_device(); if (dev) @@ -250,18 +300,20 @@ proceed: switch (client->type) { case TYPE_SINK: - if (!client->data.session) - client->data.session = avdtp_get(&dev->src, &dev->dst); + a2dp = &client->d.a2dp; + + if (!a2dp->session) + a2dp->session = avdtp_get(&dev->src, &dev->dst); - if (!a2dp_source_lock(dev, client->data.session)) { + if (!a2dp_source_lock(dev, a2dp->session)) { error("Unable to lock A2DP source SEP"); goto failed; } - id = a2dp_source_request_stream(client->data.session, dev, + id = a2dp_source_request_stream(a2dp->session, dev, TRUE, a2dp_setup_complete, client); - if (id < 0) { + if (id == 0) { error("request_stream failed"); goto failed; } @@ -273,7 +325,7 @@ proceed: break; case TYPE_HEADSET: - if (!headset_lock(dev, client->data.data)) { + if (!headset_lock(dev, client->d.data)) { error("Unable to lock headset"); goto failed; } @@ -330,11 +382,11 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) switch (client->type) { case TYPE_HEADSET: - cb_data = client->data.data; + cb_data = client->d.data; break; case TYPE_SINK: case TYPE_SOURCE: - cb_data = client->data.session; + cb_data = client->d.a2dp.session; break; default: cb_data = NULL; @@ -345,7 +397,7 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) debug("Unix client disconnected"); if (client->disconnect) client->disconnect(client->dev, cb_data); - if (client->type == TYPE_SINK && client->req_id >= 0) + if (client->type == TYPE_SINK && client->req_id > 0) a2dp_source_cancel_stream(client->req_id); goto failed; } |