2009-06-04 19:09:51

by João Paulo Rechi Vita

[permalink] [raw]
Subject: [PATCH 1/6] Create A2DP Sink SDP record.

---
audio/a2dp.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 56 insertions(+), 1 deletions(-)

diff --git a/audio/a2dp.c b/audio/a2dp.c
index 2c4d047..1ef23c4 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -995,7 +995,62 @@ static sdp_record_t *a2dp_source_record()

static sdp_record_t *a2dp_sink_record()
{
- return NULL;
+ sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+ uuid_t root_uuid, l2cap, avdtp, a2snk;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *aproto, *proto[2];
+ sdp_record_t *record;
+ sdp_data_t *psm, *version, *features;
+ uint16_t lp = AVDTP_UUID, ver = 0x0100, feat = 0x000F;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(0, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&a2snk, AUDIO_SINK_SVCLASS_ID);
+ svclass_id = sdp_list_append(0, &a2snk);
+ sdp_set_service_classes(record, svclass_id);
+
+ sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(0, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(0, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(0, proto[0]);
+
+ sdp_uuid16_create(&avdtp, AVDTP_UUID);
+ proto[1] = sdp_list_append(0, &avdtp);
+ version = sdp_data_alloc(SDP_UINT16, &ver);
+ proto[1] = sdp_list_append(proto[1], version);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(0, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ features = sdp_data_alloc(SDP_UINT16, &feat);
+ sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+ sdp_set_info_attr(record, "Audio Sink", 0, 0);
+
+ free(psm);
+ free(version);
+ sdp_list_free(proto[0], 0);
+ sdp_list_free(proto[1], 0);
+ sdp_list_free(apseq, 0);
+ sdp_list_free(pfseq, 0);
+ sdp_list_free(aproto, 0);
+ sdp_list_free(root, 0);
+ sdp_list_free(svclass_id, 0);
+
+ return record;
}

static struct a2dp_sep *a2dp_add_sep(struct a2dp_server *server, uint8_t type,
--
1.6.0.4



2009-06-04 19:09:56

by João Paulo Rechi Vita

[permalink] [raw]
Subject: [PATCH 6/6] Create a2dp_sink_suspend().

---
audio/a2dp.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
audio/a2dp.h | 2 ++
2 files changed, 53 insertions(+), 0 deletions(-)

diff --git a/audio/a2dp.c b/audio/a2dp.c
index 1c05f21..721ad16 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -1811,6 +1811,57 @@ failed:
return 0;
}

+unsigned int a2dp_sink_suspend(struct avdtp *session, struct a2dp_sep *sep,
+ a2dp_stream_cb_t cb, void *user_data)
+{
+ struct a2dp_setup_cb *cb_data;
+ struct a2dp_setup *setup;
+
+ cb_data = g_new0(struct a2dp_setup_cb, 1);
+ cb_data->suspend_cb = cb;
+ cb_data->user_data = user_data;
+ cb_data->id = ++cb_id;
+
+ setup = find_setup_by_session(session);
+ if (!setup) {
+ setup = g_new0(struct a2dp_setup, 1);
+ setup->session = avdtp_ref(session);
+ setup->dev = a2dp_get_dev(session);
+ setups = g_slist_append(setups, setup);
+ }
+
+ setup_ref(setup);
+ setup->cb = g_slist_append(setup->cb, cb_data);
+ setup->sep = sep;
+ setup->stream = sep->stream;
+
+ switch (avdtp_sep_get_state(sep->sep)) {
+ case AVDTP_STATE_IDLE:
+ error("a2dp_sink_suspend: no stream to suspend");
+ goto failed;
+ break;
+ case AVDTP_STATE_OPEN:
+ g_idle_add((GSourceFunc) finalize_suspend, setup);
+ break;
+ case AVDTP_STATE_STREAMING:
+ if (avdtp_suspend(session, sep->stream) < 0) {
+ error("avdtp_suspend failed");
+ goto failed;
+ }
+ break;
+ default:
+ error("SEP in bad state for suspend");
+ goto failed;
+ }
+
+ return cb_data->id;
+
+failed:
+ setup_unref(setup);
+ cb_id--;
+ return 0;
+}
+
gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session)
{
if (sep->locked)
diff --git a/audio/a2dp.h b/audio/a2dp.h
index db378da..cb1bcbd 100644
--- a/audio/a2dp.h
+++ b/audio/a2dp.h
@@ -150,6 +150,8 @@ unsigned int a2dp_sink_config(struct avdtp *session, struct a2dp_sep *sep,
void *user_data);
unsigned int a2dp_sink_resume(struct avdtp *session, struct a2dp_sep *sep,
a2dp_stream_cb_t cb, void *user_data);
+unsigned int a2dp_sink_suspend(struct avdtp *session, struct a2dp_sep *sep,
+ a2dp_stream_cb_t cb, void *user_data);
gboolean a2dp_sink_cancel(struct audio_device *dev, unsigned int id);

gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session);
--
1.6.0.4


2009-06-04 19:09:55

by João Paulo Rechi Vita

[permalink] [raw]
Subject: [PATCH 5/6] Create a2dp_sink_resume().

---
audio/a2dp.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
audio/a2dp.h | 2 +
2 files changed, 61 insertions(+), 0 deletions(-)

diff --git a/audio/a2dp.c b/audio/a2dp.c
index dc4f699..1c05f21 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -1752,6 +1752,65 @@ failed:
return 0;
}

+unsigned int a2dp_sink_resume(struct avdtp *session, struct a2dp_sep *sep,
+ a2dp_stream_cb_t cb, void *user_data)
+{
+ struct a2dp_setup_cb *cb_data;
+ struct a2dp_setup *setup;
+
+ cb_data = g_new0(struct a2dp_setup_cb, 1);
+ cb_data->resume_cb = cb;
+ cb_data->user_data = user_data;
+ cb_data->id = ++cb_id;
+
+ setup = find_setup_by_session(session);
+ if (!setup) {
+ setup = g_new0(struct a2dp_setup, 1);
+ setup->session = avdtp_ref(session);
+ setup->dev = a2dp_get_dev(session);
+ setups = g_slist_append(setups, setup);
+ }
+
+ setup_ref(setup);
+ setup->cb = g_slist_append(setup->cb, cb_data);
+ setup->sep = sep;
+ setup->stream = sep->stream;
+
+ switch (avdtp_sep_get_state(sep->sep)) {
+ case AVDTP_STATE_IDLE:
+ goto failed;
+ break;
+ case AVDTP_STATE_OPEN:
+ if (avdtp_start(session, sep->stream) < 0) {
+ error("avdtp_start failed");
+ goto failed;
+ }
+ break;
+ case AVDTP_STATE_STREAMING:
+ if (!sep->suspending && sep->suspend_timer) {
+ g_source_remove(sep->suspend_timer);
+ sep->suspend_timer = 0;
+ avdtp_unref(sep->session);
+ sep->session = NULL;
+ }
+ if (sep->suspending)
+ setup->start = TRUE;
+ else
+ g_idle_add((GSourceFunc) finalize_resume, setup);
+ break;
+ default:
+ error("SEP in bad state for resume");
+ goto failed;
+ }
+
+ return cb_data->id;
+
+failed:
+ setup_unref(setup);
+ cb_id--;
+ return 0;
+}
+
gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session)
{
if (sep->locked)
diff --git a/audio/a2dp.h b/audio/a2dp.h
index 3be38a7..db378da 100644
--- a/audio/a2dp.h
+++ b/audio/a2dp.h
@@ -148,6 +148,8 @@ struct a2dp_sep *a2dp_sink_get(struct avdtp *session,
unsigned int a2dp_sink_config(struct avdtp *session, struct a2dp_sep *sep,
a2dp_config_cb_t cb, GSList *caps,
void *user_data);
+unsigned int a2dp_sink_resume(struct avdtp *session, struct a2dp_sep *sep,
+ a2dp_stream_cb_t cb, void *user_data);
gboolean a2dp_sink_cancel(struct audio_device *dev, unsigned int id);

gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session);
--
1.6.0.4


2009-06-04 19:09:54

by João Paulo Rechi Vita

[permalink] [raw]
Subject: [PATCH 4/6] Create a2dp_sink_config().

---
audio/a2dp.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
audio/a2dp.h | 3 +
2 files changed, 121 insertions(+), 0 deletions(-)

diff --git a/audio/a2dp.c b/audio/a2dp.c
index 605dfed..dc4f699 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -1634,6 +1634,124 @@ struct a2dp_sep *a2dp_sink_get(struct avdtp *session,
return NULL;
}

+unsigned int a2dp_sink_config(struct avdtp *session, struct a2dp_sep *sep,
+ a2dp_config_cb_t cb, GSList *caps,
+ void *user_data)
+{
+ struct a2dp_setup_cb *cb_data;
+ GSList *l;
+ struct a2dp_server *server;
+ struct a2dp_setup *setup;
+ struct a2dp_sep *tmp;
+ struct avdtp_local_sep *lsep;
+ struct avdtp_remote_sep *rsep;
+ struct avdtp_service_capability *cap;
+ struct avdtp_media_codec_capability *codec_cap = NULL;
+ int posix_err;
+ bdaddr_t src;
+
+ avdtp_get_peers(session, &src, NULL);
+ server = find_server(servers, &src);
+ if (!server)
+ return 0;
+
+ for (l = caps; l != NULL; l = l->next) {
+ cap = l->data;
+
+ if (cap->category != AVDTP_MEDIA_CODEC)
+ continue;
+
+ codec_cap = (void *) cap->data;
+ break;
+ }
+
+ if (!codec_cap)
+ return 0;
+
+ if (sep->codec != codec_cap->media_codec_type)
+ return 0;
+
+ debug("a2dp_sink_config: selected SEP %p", sep->sep);
+
+ cb_data = g_new0(struct a2dp_setup_cb, 1);
+ cb_data->config_cb = cb;
+ cb_data->user_data = user_data;
+ cb_data->id = ++cb_id;
+
+ setup = find_setup_by_session(session);
+ if (!setup) {
+ setup = g_new0(struct a2dp_setup, 1);
+ setup->session = avdtp_ref(session);
+ setup->dev = a2dp_get_dev(session);
+ setups = g_slist_append(setups, setup);
+ }
+
+ setup_ref(setup);
+ setup->cb = g_slist_append(setup->cb, cb_data);
+ setup->sep = sep;
+ setup->stream = sep->stream;
+ setup->client_caps = caps;
+
+ switch (avdtp_sep_get_state(sep->sep)) {
+ case AVDTP_STATE_IDLE:
+ for (l = server->sinks; l != NULL; l = l->next) {
+ tmp = l->data;
+
+ if (avdtp_has_stream(session, tmp->stream))
+ break;
+ }
+
+ if (l != NULL) {
+ setup->reconfigure = TRUE;
+ if (avdtp_close(session, tmp->stream) < 0) {
+ error("avdtp_close failed");
+ goto failed;
+ }
+ break;
+ }
+
+ if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SOURCE,
+ codec_cap->media_type,
+ codec_cap->media_codec_type,
+ &lsep, &rsep) < 0) {
+ error("No matching ACP and INT SEPs found");
+ goto failed;
+ }
+
+ posix_err = avdtp_set_configuration(session, rsep, lsep,
+ caps, &setup->stream);
+ if (posix_err < 0) {
+ error("avdtp_set_configuration: %s",
+ strerror(-posix_err));
+ goto failed;
+ }
+ break;
+ case AVDTP_STATE_OPEN:
+ case AVDTP_STATE_STREAMING:
+ if (avdtp_stream_has_capabilities(setup->stream, caps)) {
+ debug("Configuration match: resuming");
+ g_idle_add((GSourceFunc) finalize_config, setup);
+ } else if (!setup->reconfigure) {
+ setup->reconfigure = TRUE;
+ if (avdtp_close(session, sep->stream) < 0) {
+ error("avdtp_close failed");
+ goto failed;
+ }
+ }
+ break;
+ default:
+ error("SEP in bad state for requesting a new stream");
+ goto failed;
+ }
+
+ return cb_data->id;
+
+failed:
+ setup_unref(setup);
+ cb_id--;
+ return 0;
+}
+
gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session)
{
if (sep->locked)
diff --git a/audio/a2dp.h b/audio/a2dp.h
index f0a61e3..3be38a7 100644
--- a/audio/a2dp.h
+++ b/audio/a2dp.h
@@ -145,6 +145,9 @@ gboolean a2dp_source_cancel(struct audio_device *dev, unsigned int id);

struct a2dp_sep *a2dp_sink_get(struct avdtp *session,
struct avdtp_remote_sep *sep);
+unsigned int a2dp_sink_config(struct avdtp *session, struct a2dp_sep *sep,
+ a2dp_config_cb_t cb, GSList *caps,
+ void *user_data);
gboolean a2dp_sink_cancel(struct audio_device *dev, unsigned int id);

gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session);
--
1.6.0.4


2009-06-04 19:09:52

by João Paulo Rechi Vita

[permalink] [raw]
Subject: [PATCH 2/6] Create a2dp_sink_cancel().

---
audio/a2dp.c | 35 +++++++++++++++++++++++++++++++++++
audio/a2dp.h | 2 ++
2 files changed, 37 insertions(+), 0 deletions(-)

diff --git a/audio/a2dp.c b/audio/a2dp.c
index 1ef23c4..67e2576 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -1566,6 +1566,41 @@ failed:
return 0;
}

+gboolean a2dp_sink_cancel(struct audio_device *dev, unsigned int id)
+{
+ struct a2dp_setup_cb *cb_data;
+ struct a2dp_setup *setup;
+ GSList *l;
+
+ debug("a2dp_sink_cancel()");
+
+ setup = find_setup_by_dev(dev);
+ if (!setup)
+ return FALSE;
+
+ for (cb_data = NULL, l = setup->cb; l != NULL; l = g_slist_next(l)) {
+ struct a2dp_setup_cb *cb = l->data;
+
+ if (cb->id == id) {
+ cb_data = cb;
+ break;
+ }
+ }
+
+ if (!cb_data)
+ error("a2dp_sink_cancel: no matching callback with id %u", id);
+
+ setup->cb = g_slist_remove(setup->cb, cb_data);
+ g_free(cb_data);
+
+ if (!setup->cb) {
+ setup->canceled = TRUE;
+ setup->sep = NULL;
+ }
+
+ return TRUE;
+}
+
gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session)
{
if (sep->locked)
diff --git a/audio/a2dp.h b/audio/a2dp.h
index 0e5f796..f72480c 100644
--- a/audio/a2dp.h
+++ b/audio/a2dp.h
@@ -143,6 +143,8 @@ unsigned int a2dp_source_suspend(struct avdtp *session, struct a2dp_sep *sep,
a2dp_stream_cb_t cb, void *user_data);
gboolean a2dp_source_cancel(struct audio_device *dev, unsigned int id);

+gboolean a2dp_sink_cancel(struct audio_device *dev, unsigned int id);
+
gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session);
gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session);
gboolean a2dp_sep_get_lock(struct a2dp_sep *sep);
--
1.6.0.4


2009-06-04 19:09:53

by João Paulo Rechi Vita

[permalink] [raw]
Subject: [PATCH 3/6] Create a2dp_sink_get().

---
audio/a2dp.c | 33 +++++++++++++++++++++++++++++++++
audio/a2dp.h | 2 ++
2 files changed, 35 insertions(+), 0 deletions(-)

diff --git a/audio/a2dp.c b/audio/a2dp.c
index 67e2576..605dfed 100644
--- a/audio/a2dp.c
+++ b/audio/a2dp.c
@@ -1601,6 +1601,39 @@ gboolean a2dp_sink_cancel(struct audio_device *dev, unsigned int id)
return TRUE;
}

+struct a2dp_sep *a2dp_sink_get(struct avdtp *session,
+ struct avdtp_remote_sep *rsep)
+{
+ GSList *l;
+ struct a2dp_server *server;
+ struct avdtp_service_capability *cap;
+ struct avdtp_media_codec_capability *codec_cap = NULL;
+ bdaddr_t src;
+
+ avdtp_get_peers(session, &src, NULL);
+ server = find_server(servers, &src);
+ if (!server)
+ return NULL;
+
+ cap = avdtp_get_codec(rsep);
+ codec_cap = (void *) cap->data;
+
+ for (l = server->sinks; l != NULL; l = l->next) {
+ struct a2dp_sep *sep = l->data;
+
+ if (sep->locked)
+ continue;
+
+ if (sep->codec != codec_cap->media_codec_type)
+ continue;
+
+ if (!sep->stream || avdtp_has_stream(session, sep->stream))
+ return sep;
+ }
+
+ return NULL;
+}
+
gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session)
{
if (sep->locked)
diff --git a/audio/a2dp.h b/audio/a2dp.h
index f72480c..f0a61e3 100644
--- a/audio/a2dp.h
+++ b/audio/a2dp.h
@@ -143,6 +143,8 @@ unsigned int a2dp_source_suspend(struct avdtp *session, struct a2dp_sep *sep,
a2dp_stream_cb_t cb, void *user_data);
gboolean a2dp_source_cancel(struct audio_device *dev, unsigned int id);

+struct a2dp_sep *a2dp_sink_get(struct avdtp *session,
+ struct avdtp_remote_sep *sep);
gboolean a2dp_sink_cancel(struct audio_device *dev, unsigned int id);

gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session);
--
1.6.0.4