summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2007-08-16 21:48:33 +0000
committerJohan Hedberg <johan.hedberg@nokia.com>2007-08-16 21:48:33 +0000
commit0f458da19471c933a4105cc450c48548b72edc8e (patch)
tree3fe4c38c29c7adb1eb13cf273d8335efa97d1337 /audio
parent871c0518ec53309a38debbb86a36c035c3470fba (diff)
Handle error situations better
Diffstat (limited to 'audio')
-rw-r--r--audio/a2dp.c64
-rw-r--r--audio/a2dp.h3
-rw-r--r--audio/avdtp.c166
-rw-r--r--audio/avdtp.h31
-rw-r--r--audio/sink.c16
-rw-r--r--audio/unix.c82
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;
}