2015-02-27 15:02:48

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 00/20] Audio deduplication part 3

From: Andrei Emeltchenko <[email protected]>

3rd series of patches for avdtp & a2dp deduplication.

Andrei Emeltchenko (20):
audio/avdtp: Copy SEP list to avdtp session
audio/avdtp: Refactor avdtp_new
audio/avdtp: Add stream related debugs
audio/avdtp: Remove avdtp_server from avdtp code
audio/avdtp: Move connection logic from avdtp to a2dp
audio/avdtp: Refactor transport and control channels
audio/avdtp: Remove start timer on stream_free()
audio/avdtp: Add check for callback before run it
audio/avdtp: Add AVDTP_STATE_OPEN to allowed state
audio/avdtp: Fix style issues
android/avdtp: Move bluetooth service code to callback
audio/avdtp: Make use of disconnect callback
audio/avdtp: Add finalize_discovery() on unref
android/avdtp: Add set vendor codec function
audio/avdtp: Add error checks and style fixes
audio/avdtp: Add handling missing cases in avdtp_parse_rej
audio/avdtp: Add delay reporting in capabilities
audio/avdtp: Remove disconnect timer code
android/avdtp: Refactor avdtp_new()
audio/avdtp: Refactor freeing avdtp session

android/a2dp.c | 4 +-
android/avdtp.c | 27 +-
android/avdtp.h | 4 +-
android/avdtptest.c | 3 +-
profiles/audio/a2dp.c | 183 +++++++++++++-
profiles/audio/a2dp.h | 4 +
profiles/audio/avdtp.c | 610 ++++++++++++++++++++++++++++-----------------
profiles/audio/avdtp.h | 29 ++-
profiles/audio/media.c | 2 +
profiles/audio/sink.c | 2 +
profiles/audio/source.c | 2 +
profiles/audio/transport.c | 2 +
unit/test-avdtp.c | 5 +-
13 files changed, 621 insertions(+), 256 deletions(-)

--
2.1.0



2015-02-27 15:02:53

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 05/20] audio/avdtp: Move connection logic from avdtp to a2dp

From: Andrei Emeltchenko <[email protected]>

l2cap connection logic moved from avdtp.c to a2dp.c
---
profiles/audio/a2dp.c | 24 ++++++++++++++++++++++++
profiles/audio/a2dp.h | 1 +
profiles/audio/avdtp.c | 29 ++---------------------------
profiles/audio/media.c | 2 ++
profiles/audio/sink.c | 2 ++
profiles/audio/source.c | 2 ++
profiles/audio/transport.c | 2 ++
7 files changed, 35 insertions(+), 27 deletions(-)

diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index c96971f..e23455a 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -1265,6 +1265,30 @@ static void avdtp_server_destroy(struct avdtp_server *server)
g_free(server);
}

+GIOChannel *l2cap_connect(struct avdtp *session, BtIOConnect cb)
+{
+ GError *err = NULL;
+ GIOChannel *io;
+ const bdaddr_t *src;
+
+ src = btd_adapter_get_address(avdtp_get_adapter(session));
+
+ io = bt_io_connect(cb, session, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, src,
+ BT_IO_OPT_DEST_BDADDR,
+ device_get_address(avdtp_get_device(session)),
+ BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("%s", err->message);
+ g_error_free(err);
+ return NULL;
+ }
+
+ return io;
+}
+
static void a2dp_clean_lsep(struct a2dp_sep *sep)
{
struct avdtp_local_sep *lsep = sep->lsep;
diff --git a/profiles/audio/a2dp.h b/profiles/audio/a2dp.h
index fdcc528..a4d6947 100644
--- a/profiles/audio/a2dp.h
+++ b/profiles/audio/a2dp.h
@@ -91,3 +91,4 @@ struct avdtp *a2dp_avdtp_get(struct btd_device *device);
void avdtp_server_remove_session(struct avdtp_server *server,
struct avdtp *session);
struct btd_adapter *avdtp_server_get_adapter(struct avdtp_server *server);
+GIOChannel *l2cap_connect(struct avdtp *session, BtIOConnect cb);
diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 05b3d2c..1ea9fe4 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -2454,31 +2454,6 @@ bool avdtp_request_authorization(struct avdtp *session, const bdaddr_t *src,
return true;
}

-static GIOChannel *l2cap_connect(struct avdtp *session)
-{
- GError *err = NULL;
- GIOChannel *io;
- const bdaddr_t *src;
-
- src = btd_adapter_get_address(avdtp_get_adapter(session));
-
- io = bt_io_connect(avdtp_connect_cb, session,
- NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR, src,
- BT_IO_OPT_DEST_BDADDR,
- device_get_address(session->device),
- BT_IO_OPT_PSM, AVDTP_PSM,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
- BT_IO_OPT_INVALID);
- if (!io) {
- error("%s", err->message);
- g_error_free(err);
- return NULL;
- }
-
- return io;
-}
-
static void queue_request(struct avdtp *session, struct pending_req *req,
gboolean priority)
{
@@ -2614,7 +2589,7 @@ static int send_req(struct avdtp *session, gboolean priority,
int err;

if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {
- session->io = l2cap_connect(session);
+ session->io = l2cap_connect(session, avdtp_connect_cb);
if (!session->io) {
err = -EIO;
goto failed;
@@ -2796,7 +2771,7 @@ static gboolean avdtp_open_resp(struct avdtp *session, struct avdtp_stream *stre
{
struct avdtp_local_sep *sep = stream->lsep;

- stream->io = l2cap_connect(session);
+ stream->io = l2cap_connect(session, avdtp_connect_cb);
if (!stream->io) {
avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
return FALSE;
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index 633695c..18fe2c1 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -46,6 +46,8 @@
#include "src/error.h"
#include "src/shared/queue.h"

+#include "btio/btio.h"
+
#include "avdtp.h"
#include "media.h"
#include "transport.h"
diff --git a/profiles/audio/sink.c b/profiles/audio/sink.c
index 7cf22d9..ad4bbaa 100644
--- a/profiles/audio/sink.c
+++ b/profiles/audio/sink.c
@@ -46,6 +46,8 @@
#include "src/dbus-common.h"
#include "src/shared/queue.h"

+#include "btio/btio.h"
+
#include "avdtp.h"
#include "media.h"
#include "a2dp.h"
diff --git a/profiles/audio/source.c b/profiles/audio/source.c
index fd68917..039157c 100644
--- a/profiles/audio/source.c
+++ b/profiles/audio/source.c
@@ -47,6 +47,8 @@
#include "src/dbus-common.h"
#include "src/shared/queue.h"

+#include "btio/btio.h"
+
#include "avdtp.h"
#include "media.h"
#include "a2dp.h"
diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index a267bfd..ca11a86 100644
--- a/profiles/audio/transport.c
+++ b/profiles/audio/transport.c
@@ -41,6 +41,8 @@
#include "src/error.h"
#include "src/shared/queue.h"

+#include "btio/btio.h"
+
#include "avdtp.h"
#include "media.h"
#include "transport.h"
--
2.1.0


2015-02-27 15:03:07

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 19/20] android/avdtp: Refactor avdtp_new()

From: Andrei Emeltchenko <[email protected]>

avdtp_new() is refactored to two functions avdtp_new() and
avdtp_set_control() similar to set avdtp_set_transport(). This is needed
for code which creates avdtp session without fd like inside sink, source
and transport code.
---
android/a2dp.c | 4 +++-
android/avdtp.c | 27 ++++++++++++++++-----------
android/avdtp.h | 4 ++--
android/avdtptest.c | 3 ++-
unit/test-avdtp.c | 5 +++--
5 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/android/a2dp.c b/android/a2dp.c
index f219042..1a6e062 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -647,10 +647,12 @@ static void signaling_connect_cb(GIOChannel *chan, GError *err,
fd = g_io_channel_unix_get_fd(chan);

/* FIXME: Add proper version */
- session = avdtp_new(fd, imtu, omtu, 0x0100, lseps);
+ session = avdtp_new(0x0100, lseps);
if (!session)
goto failed;

+ avdtp_set_control(session, fd, imtu, omtu);
+
dev->session = session;

avdtp_add_disconnect_cb(dev->session, disconnect_cb, dev);
diff --git a/android/avdtp.c b/android/avdtp.c
index b8a2147..dd31a0b 100644
--- a/android/avdtp.c
+++ b/android/avdtp.c
@@ -2113,28 +2113,35 @@ static int set_priority(int fd, int priority)
return err;
}

-struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version,
- struct queue *lseps)
+struct avdtp *avdtp_new(uint16_t version, struct queue *lseps)
{
struct avdtp *session;
- GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
- int new_fd;

if (!lseps)
return NULL;

+ session = g_new0(struct avdtp, 1);
+ session->version = version;
+ session->lseps = lseps;
+
+ return avdtp_ref(session);
+}
+
+bool avdtp_set_control(struct avdtp *session, int fd, size_t imtu, size_t omtu)
+{
+ GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+ int new_fd;
+
new_fd = dup(fd);
if (new_fd < 0) {
error("dup(): %s (%d)", strerror(errno), errno);
- return NULL;
+ return false;
}

if (set_priority(new_fd, 6) < 0)
- return NULL;
+ return false;

- session = g_new0(struct avdtp, 1);
session->io = g_io_channel_unix_new(new_fd);
- session->version = version;
session->imtu = imtu;
session->omtu = omtu;
session->buf = g_malloc0(MAX(session->imtu, session->omtu));
@@ -2150,9 +2157,7 @@ struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version,
(GIOFunc) session_cb, session,
NULL);

- session->lseps = lseps;
-
- return avdtp_ref(session);
+ return true;
}

unsigned int avdtp_add_disconnect_cb(struct avdtp *session,
diff --git a/android/avdtp.h b/android/avdtp.h
index 07516a8..ce84029 100644
--- a/android/avdtp.h
+++ b/android/avdtp.h
@@ -205,8 +205,8 @@ typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps,
struct avdtp_error *err, void *user_data);
typedef void (*avdtp_disconnect_cb_t) (void *user_data);

-struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version,
- struct queue *lseps);
+struct avdtp *avdtp_new(uint16_t version, struct queue *lseps);
+bool avdtp_set_control(struct avdtp *session, int fd, size_t imtu, size_t omtu);

unsigned int avdtp_add_disconnect_cb(struct avdtp *session,
avdtp_disconnect_cb_t cb,
diff --git a/android/avdtptest.c b/android/avdtptest.c
index 6529423..763a6f3 100644
--- a/android/avdtptest.c
+++ b/android/avdtptest.c
@@ -413,13 +413,14 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
return;
}

- avdtp = avdtp_new(fd, imtu, omtu, version, lseps);
+ avdtp = avdtp_new(version, lseps);
if (!avdtp) {
printf("Failed to create avdtp instance\n");
g_main_loop_quit(mainloop);
return;
}

+ avdtp_set_control(avdtp, fd, imtu, omtu);
avdtp_add_disconnect_cb(avdtp, disconnect_cb, NULL);

if (preconf) {
diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index 805f08d..b91cf97 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -236,10 +236,11 @@ static struct context *context_new(uint16_t version, uint16_t imtu,
context->lseps = queue_new();
g_assert(context->lseps);

- context->session = avdtp_new(sv[0], imtu, omtu, version,
- context->lseps);
+ context->session = avdtp_new(version, context->lseps);
g_assert(context->session != NULL);

+ g_assert(avdtp_set_control(context->session, sv[0], imtu, omtu));
+
channel = g_io_channel_unix_new(sv[1]);

g_io_channel_set_close_on_unref(channel, TRUE);
--
2.1.0


2015-02-27 15:03:00

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 12/20] audio/avdtp: Make use of disconnect callback

From: Andrei Emeltchenko <[email protected]>

disconnect callback will be used for cancelling authorization request.
---
profiles/audio/a2dp.c | 8 ++++++
profiles/audio/avdtp.c | 74 ++++++++++++++++++++++++++++++++++++++++++--------
profiles/audio/avdtp.h | 7 +++++
3 files changed, 77 insertions(+), 12 deletions(-)

diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index df2b2b8..ac7e985 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -1448,6 +1448,13 @@ static void auth_cb(DBusError *derr, void *user_data)
avdtp_accept(session, signaling_connect_cb);
}

+static void disconnect_cb(void *user_data)
+{
+ struct avdtp *session = user_data;
+
+ avdtp_cancel_authorization(session);
+}
+
static void avdtp_confirm_cb(GIOChannel *chan, gpointer data)
{
struct avdtp *session;
@@ -1490,6 +1497,7 @@ static void avdtp_confirm_cb(GIOChannel *chan, gpointer data)
return;

btd_device_add_uuid(device, ADVANCED_AUDIO_UUID);
+ avdtp_add_disconnect_cb(session, disconnect_cb, session);
avdtp_request_authorization(session, &src, &dst, auth_cb);

return;
diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index fb860ef..4361e23 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -343,16 +343,22 @@ struct stream_callback {
unsigned int id;
};

-struct avdtp_state_callback {
- avdtp_session_state_cb cb;
- struct btd_device *dev;
+struct discover_callback {
unsigned int id;
+ avdtp_discover_cb_t cb;
void *user_data;
};

-struct discover_callback {
+struct disconnect_callback {
+ unsigned int id;
+ avdtp_disconnect_cb_t cb;
+ void *user_data;
+};
+
+struct avdtp_state_callback {
+ avdtp_session_state_cb cb;
+ struct btd_device *dev;
unsigned int id;
- avdtp_discover_cb_t cb;
void *user_data;
};

@@ -415,6 +421,8 @@ struct avdtp {
struct discover_callback *discover;
struct pending_req *req;

+ GSList *disconnect;
+
guint dc_timer;

/* Attempt stream setup instead of disconnecting */
@@ -1060,7 +1068,7 @@ static void release_stream(struct avdtp_stream *stream, struct avdtp *session)
avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
}

-static int avdtp_cancel_authorization(struct avdtp *session)
+int avdtp_cancel_authorization(struct avdtp *session)
{
int err;

@@ -1121,6 +1129,7 @@ void avdtp_free(void *data)
g_slist_free_full(session->req_queue, pending_req_free);
g_slist_free_full(session->prio_queue, pending_req_free);
g_slist_free_full(session->seps, sep_free);
+ g_slist_free_full(session->disconnect, g_free);

g_free(session->buf);

@@ -1128,21 +1137,27 @@ void avdtp_free(void *data)
g_free(session);
}

-void connection_lost(struct avdtp *session, int err)
+static void process_disconnect(void *data)
{
- char address[18];
+ struct disconnect_callback *callback = data;

- ba2str(device_get_address(session->device), address);
- DBG("Disconnected from %s", address);
+ callback->cb(callback->user_data);

- if (err != EACCES)
- avdtp_cancel_authorization(session);
+ g_free(callback);
+}
+
+void connection_lost(struct avdtp *session, int err)
+{
+ DBG("Disconnected: %s (%d)", strerror(err), err);

g_slist_foreach(session->streams, (GFunc) release_stream, session);
session->streams = NULL;

finalize_discovery(session, err);

+ g_slist_free_full(session->disconnect, process_disconnect);
+ session->disconnect = NULL;
+
avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);

if (session->ref > 0)
@@ -2507,6 +2522,41 @@ bool avdtp_request_authorization(struct avdtp *session, const bdaddr_t *src,
return true;
}

+unsigned int avdtp_add_disconnect_cb(struct avdtp *session,
+ avdtp_disconnect_cb_t cb,
+ void *user_data)
+{
+ struct disconnect_callback *callback;
+ static unsigned int id = 0;
+
+ callback = g_new0(struct disconnect_callback, 1);
+ callback->id = ++id;
+ callback->cb = cb;
+ callback->user_data = user_data;
+ session->disconnect = g_slist_append(session->disconnect, callback);
+
+ return id;
+}
+
+gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id)
+{
+ GSList *l;
+
+ for (l = session->disconnect; l; l = g_slist_next(l)) {
+ struct disconnect_callback *callback = l->data;
+
+ if (callback->id != id)
+ continue;
+
+ session->disconnect = g_slist_remove(session->disconnect,
+ callback);
+ g_free(callback);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static void queue_request(struct avdtp *session, struct pending_req *req,
gboolean priority)
{
diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h
index 88e8cb4..af55f76 100644
--- a/profiles/audio/avdtp.h
+++ b/profiles/audio/avdtp.h
@@ -213,6 +213,12 @@ struct avdtp_sep_ind {

typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps,
struct avdtp_error *err, void *user_data);
+typedef void (*avdtp_disconnect_cb_t) (void *user_data);
+
+unsigned int avdtp_add_disconnect_cb(struct avdtp *session,
+ avdtp_disconnect_cb_t cb,
+ void *user_data);
+gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id);

void avdtp_unref(struct avdtp *session);
struct avdtp *avdtp_ref(struct avdtp *session);
@@ -305,6 +311,7 @@ void avdtp_accept(struct avdtp *session, BtIOConnect cb);
bool avdtp_request_authorization(struct avdtp *session, const bdaddr_t *src,
const bdaddr_t *dst,
service_auth_cb cb);
+int avdtp_cancel_authorization(struct avdtp *session);
bool avdtp_set_control(struct avdtp *session, int fd, size_t imtu, size_t omtu);
bool avdtp_stream_set_transport(struct avdtp *session, int fd, size_t imtu,
size_t omtu);
--
2.1.0


2015-02-27 15:02:56

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 08/20] audio/avdtp: Add check for callback before run it

From: Andrei Emeltchenko <[email protected]>

---
profiles/audio/avdtp.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 83338b0..e409d38 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -1035,15 +1035,17 @@ static void finalize_discovery(struct avdtp *session, int err)
if (!discover)
return;

+ session->discover = NULL;
+
avdtp_error_init(&avdtp_err, AVDTP_ERRNO, err);

if (discover->id > 0)
g_source_remove(discover->id);

- discover->cb(session, session->seps, err ? &avdtp_err : NULL,
+ if (discover->cb)
+ discover->cb(session, session->seps, err ? &avdtp_err : NULL,
discover->user_data);
g_free(discover);
- session->discover = NULL;
}

static void release_stream(struct avdtp_stream *stream, struct avdtp *session)
--
2.1.0


2015-02-27 15:03:06

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 18/20] audio/avdtp: Remove disconnect timer code

From: Andrei Emeltchenko <[email protected]>

disconnect timer code setup streams instead of disconnecting as it
mentioned in the related comment. That does not look very right thing to
do in this situation and this is not used in android code. Removing it
simplifies avdtp code a lot and makes it easily to merge with android
code base.
---
profiles/audio/avdtp.c | 67 +-------------------------------------------------
1 file changed, 1 insertion(+), 66 deletions(-)

diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index c0ded8d..25de5b3 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -425,11 +425,6 @@ struct avdtp {
struct pending_req *req;

GSList *disconnect;
-
- guint dc_timer;
-
- /* Attempt stream setup instead of disconnecting */
- gboolean stream_setup;
};

static GSList *state_callbacks = NULL;
@@ -1098,13 +1093,6 @@ static void sep_free(gpointer data)
g_free(sep);
}

-static void remove_disconnect_timer(struct avdtp *session)
-{
- g_source_remove(session->dc_timer);
- session->dc_timer = 0;
- session->stream_setup = FALSE;
-}
-
void avdtp_free(void *data)
{
struct avdtp *session = data;
@@ -1123,9 +1111,6 @@ void avdtp_free(void *data)
session->io_id = 0;
}

- if (session->dc_timer)
- remove_disconnect_timer(session);
-
if (session->req)
pending_req_free(session->req);

@@ -1173,44 +1158,6 @@ void connection_lost(struct avdtp *session, int err)
avdtp_free(session);
}

-static gboolean disconnect_timeout(gpointer user_data)
-{
- struct avdtp *session = user_data;
- struct btd_service *service;
- gboolean stream_setup;
-
- session->dc_timer = 0;
-
- stream_setup = session->stream_setup;
- session->stream_setup = FALSE;
-
- service = btd_device_get_service(session->device, A2DP_SINK_UUID);
- if (service && stream_setup) {
- sink_setup_stream(service, session);
- return FALSE;
- }
-
- service = btd_device_get_service(session->device, A2DP_SOURCE_UUID);
- if (service && stream_setup) {
- source_setup_stream(service, session);
- return FALSE;
- }
-
- connection_lost(session, ETIMEDOUT);
-
- return FALSE;
-}
-
-static void set_disconnect_timer(struct avdtp *session)
-{
- if (session->dc_timer)
- remove_disconnect_timer(session);
-
- session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT,
- disconnect_timeout,
- session);
-}
-
void avdtp_unref(struct avdtp *session)
{
if (!session)
@@ -1224,8 +1171,6 @@ void avdtp_unref(struct avdtp *session)
return;

finalize_discovery(session, ECONNABORTED);
-
- set_disconnect_timer(session);
}

struct avdtp *avdtp_ref(struct avdtp *session)
@@ -1233,8 +1178,7 @@ struct avdtp *avdtp_ref(struct avdtp *session)
session->ref++;

DBG("%p: ref=%d", session, session->ref);
- if (session->dc_timer)
- remove_disconnect_timer(session);
+
return session;
}

@@ -2375,9 +2319,6 @@ bool avdtp_set_control(struct avdtp *session, int fd, size_t imtu, size_t omtu)
(GIOFunc) session_cb, session,
NULL);

- if (session->stream_setup)
- set_disconnect_timer(session);
-
process_queue(session);

return true;
@@ -2448,8 +2389,6 @@ static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
(GIOFunc) session_cb, session,
NULL);

- if (session->stream_setup)
- set_disconnect_timer(session);
} else if (session->pending_open)
handle_transport_connect(session, chan, session->imtu,
session->omtu);
@@ -3773,10 +3712,6 @@ void avdtp_accept(struct avdtp *session, BtIOConnect cb)
g_error_free(err);
return;
}
-
- /* This is so that connect_cb will know to do the right thing
- * with respect to the disconnect timer */
- session->stream_setup = TRUE;
}

struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
--
2.1.0


2015-02-27 15:02:59

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 11/20] android/avdtp: Move bluetooth service code to callback

From: Andrei Emeltchenko <[email protected]>

Move bluetooth device and service code out of avdtp. The code would be
run from endpoint_setconf_ind() callback.
---
profiles/audio/a2dp.c | 29 +++++++++++++++++++++++++++++
profiles/audio/avdtp.c | 32 --------------------------------
2 files changed, 29 insertions(+), 32 deletions(-)

diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index ef8ed5e..df2b2b8 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -450,7 +450,9 @@ static gboolean endpoint_setconf_ind(struct avdtp *session,
avdtp_set_configuration_cb cb,
void *user_data)
{
+ struct btd_device *device = avdtp_get_device(session);
struct a2dp_sep *a2dp_sep = user_data;
+ struct btd_service *service;
struct a2dp_setup *setup;

if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
@@ -458,6 +460,33 @@ static gboolean endpoint_setconf_ind(struct avdtp *session,
else
DBG("Source %p: Set_Configuration_Ind", sep);

+ switch (a2dp_sep->type) {
+ case AVDTP_SEP_TYPE_SOURCE:
+ service = btd_device_get_service(device, A2DP_SINK_UUID);
+ if (service == NULL) {
+ btd_device_add_uuid(device, A2DP_SINK_UUID);
+ service = btd_device_get_service(device,
+ A2DP_SINK_UUID);
+ if (service == NULL) {
+ error("Unable to get a audio sink object");
+ return FALSE;
+ }
+ }
+ break;
+ case AVDTP_SEP_TYPE_SINK:
+ service = btd_device_get_service(device, A2DP_SOURCE_UUID);
+ if (service == NULL) {
+ btd_device_add_uuid(device, A2DP_SOURCE_UUID);
+ service = btd_device_get_service(device,
+ A2DP_SOURCE_UUID);
+ if (service == NULL) {
+ error("Unable to get a audio source object");
+ return FALSE;
+ }
+ }
+ break;
+ }
+
setup = a2dp_setup_get(session);
if (!session)
return FALSE;
diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 9c8185a..fb860ef 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -1439,7 +1439,6 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction,
struct avdtp_local_sep *sep;
struct avdtp_stream *stream;
uint8_t err, category = 0x00;
- struct btd_service *service;
GSList *l;

if (size < sizeof(struct setconf_req)) {
@@ -1458,37 +1457,6 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction,
goto failed;
}

- switch (sep->info.type) {
- case AVDTP_SEP_TYPE_SOURCE:
- service = btd_device_get_service(session->device,
- A2DP_SINK_UUID);
- if (service == NULL) {
- btd_device_add_uuid(session->device, A2DP_SINK_UUID);
- service = btd_device_get_service(session->device,
- A2DP_SINK_UUID);
- if (service == NULL) {
- error("Unable to get a audio sink object");
- err = AVDTP_BAD_STATE;
- goto failed;
- }
- }
- break;
- case AVDTP_SEP_TYPE_SINK:
- service = btd_device_get_service(session->device,
- A2DP_SOURCE_UUID);
- if (service == NULL) {
- btd_device_add_uuid(session->device, A2DP_SOURCE_UUID);
- service = btd_device_get_service(session->device,
- A2DP_SOURCE_UUID);
- if (service == NULL) {
- error("Unable to get a audio source object");
- err = AVDTP_BAD_STATE;
- goto failed;
- }
- }
- break;
- }
-
stream = g_new0(struct avdtp_stream, 1);
stream->session = session;
stream->lsep = sep;
--
2.1.0


2015-02-27 15:03:08

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 20/20] audio/avdtp: Refactor freeing avdtp session

From: Andrei Emeltchenko <[email protected]>

---
profiles/audio/avdtp.c | 43 +++++++++++++++++++++++++++++++++++++++----
profiles/audio/avdtp.h | 2 ++
2 files changed, 41 insertions(+), 4 deletions(-)

diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 25de5b3..1e3e817 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -425,6 +425,8 @@ struct avdtp {
struct pending_req *req;

GSList *disconnect;
+
+ bool shutdown;
};

static GSList *state_callbacks = NULL;
@@ -1031,6 +1033,11 @@ static void avdtp_sep_set_state(struct avdtp *session,
DBG("stream %p removed from session %p", stream, session);
stream_free(stream);
}
+
+ if (session->io && session->shutdown && session->streams == NULL) {
+ int sock = g_io_channel_unix_get_fd(session->io);
+ shutdown(sock, SHUT_RDWR);
+ }
}

static void finalize_discovery(struct avdtp *session, int err)
@@ -1144,6 +1151,8 @@ void connection_lost(struct avdtp *session, int err)
g_slist_foreach(session->streams, (GFunc) release_stream, session);
session->streams = NULL;

+ avdtp_ref(session);
+
finalize_discovery(session, err);

g_slist_free_full(session->disconnect, process_disconnect);
@@ -1151,11 +1160,9 @@ void connection_lost(struct avdtp *session, int err)

avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);

- if (session->ref > 0)
- return;
-
avdtp_server_remove_session(session->server, session);
- avdtp_free(session);
+
+ avdtp_unref(session);
}

void avdtp_unref(struct avdtp *session)
@@ -1171,6 +1178,8 @@ void avdtp_unref(struct avdtp *session)
return;

finalize_discovery(session, ECONNABORTED);
+
+ avdtp_free(session);
}

struct avdtp *avdtp_ref(struct avdtp *session)
@@ -2527,6 +2536,32 @@ gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id)
return FALSE;
}

+void avdtp_shutdown(struct avdtp *session)
+{
+ GSList *l;
+ bool aborting = false;
+
+ if (!session->io)
+ return;
+
+ for (l = session->streams; l; l = g_slist_next(l)) {
+ struct avdtp_stream *stream = l->data;
+
+ if (stream->abort_int ||
+ avdtp_close(session, stream, TRUE) == 0)
+ aborting = true;
+ }
+
+ if (aborting) {
+ /* defer shutdown until all streams are aborted properly */
+ session->shutdown = true;
+ } else {
+ int sock = g_io_channel_unix_get_fd(session->io);
+
+ shutdown(sock, SHUT_RDWR);
+ }
+}
+
static void queue_request(struct avdtp *session, struct pending_req *req,
gboolean priority)
{
diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h
index 0fc8fe2..2fc471e 100644
--- a/profiles/audio/avdtp.h
+++ b/profiles/audio/avdtp.h
@@ -220,6 +220,8 @@ unsigned int avdtp_add_disconnect_cb(struct avdtp *session,
void *user_data);
gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id);

+void avdtp_shutdown(struct avdtp *session);
+
void avdtp_unref(struct avdtp *session);
struct avdtp *avdtp_ref(struct avdtp *session);

--
2.1.0


2015-02-27 15:02:55

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 07/20] audio/avdtp: Remove start timer on stream_free()

From: Andrei Emeltchenko <[email protected]>

Makes code identical to android/avdtp
---
profiles/audio/avdtp.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 1bfbff7..83338b0 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -717,6 +717,9 @@ static void stream_free(void *data)
if (stream->timer)
g_source_remove(stream->timer);

+ if (stream->start_timer > 0)
+ g_source_remove(stream->start_timer);
+
if (stream->io)
close_stream(stream);

--
2.1.0


2015-02-27 15:03:05

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 17/20] audio/avdtp: Add delay reporting in capabilities

From: Andrei Emeltchenko <[email protected]>

---
profiles/audio/avdtp.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 64e09fd..c0ded8d 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -1427,6 +1427,12 @@ static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction,
g_free(cap);
}

+ if (get_all && sep->delay_reporting) {
+ ptr[0] = AVDTP_DELAY_REPORTING;
+ ptr[1] = 0x00;
+ rsp_size += 2;
+ }
+
g_slist_free(caps);

return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, cmd,
--
2.1.0


2015-02-27 15:02:50

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 02/20] audio/avdtp: Refactor avdtp_new

From: Andrei Emeltchenko <[email protected]>

avdtp_new() is split to avdtp_new() and avdtp_transport_connect() which
takes care about opening transport channel.
---
profiles/audio/a2dp.c | 10 +++++++---
profiles/audio/avdtp.c | 20 ++++++++++++--------
profiles/audio/avdtp.h | 2 +-
3 files changed, 20 insertions(+), 12 deletions(-)

diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 6a86c19..e669f43 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -1222,7 +1222,7 @@ struct avdtp *a2dp_avdtp_get(struct btd_device *device)
if (server == NULL)
return NULL;

- session = avdtp_new(server, server->sessions, NULL, device);
+ session = avdtp_new(server, server->sessions, device);
if (!session)
return NULL;

@@ -1328,11 +1328,15 @@ static void avdtp_confirm_cb(GIOChannel *chan, gpointer data)
if (!avdtp_server)
goto drop;

- session = avdtp_new(avdtp_server, avdtp_server->sessions, chan, device);
+ session = avdtp_new(avdtp_server, avdtp_server->sessions, device);
if (!session)
goto drop;

- avdtp_request_authorization(session, &src, &dst, auth_cb);
+
+ if (avdtp_transport_connect(session, chan)) {
+ btd_device_add_uuid(device, ADVANCED_AUDIO_UUID);
+ avdtp_request_authorization(session, &src, &dst, auth_cb);
+ }

return;

diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 1a16170..aaaf3b2 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -2374,7 +2374,6 @@ failed:
}

struct avdtp *avdtp_new(struct avdtp_server *server, GSList *sessions,
- GIOChannel *chan,
struct btd_device *device)
{
struct avdtp *session;
@@ -2396,9 +2395,14 @@ struct avdtp *avdtp_new(struct avdtp_server *server, GSList *sessions,
server->sessions = g_slist_append(server->sessions, session);

session->lseps = server->seps;
- if (!chan)
- return session;

+ DBG("Created session %p added to server %p", session, server);
+
+ return session;
+}
+
+bool avdtp_transport_connect(struct avdtp *session, GIOChannel *chan)
+{
/* This state (ie, session is already *connecting*) happens when the
* device initiates a connect (really a config'd L2CAP channel) even
* though there is a connect we initiated in progress. In sink.c &
@@ -2414,7 +2418,7 @@ struct avdtp *avdtp_new(struct avdtp_server *server, GSList *sessions,
if (!bt_io_accept(chan, avdtp_connect_cb, session, NULL, NULL))
goto drop;

- return NULL;
+ return false;
}

if (session->io) {
@@ -2422,17 +2426,17 @@ struct avdtp *avdtp_new(struct avdtp_server *server, GSList *sessions,
goto drop;
}

- btd_device_add_uuid(device, ADVANCED_AUDIO_UUID);
-
session->io = g_io_channel_ref(chan);
avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);

session->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
(GIOFunc) session_cb, session);

- return session;
+ return true;
+
drop:
- return NULL;
+ g_io_channel_shutdown(chan, TRUE, NULL);
+ return false;
}

bool avdtp_request_authorization(struct avdtp *session, const bdaddr_t *src,
diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h
index c2c223a..4aa3936 100644
--- a/profiles/audio/avdtp.h
+++ b/profiles/audio/avdtp.h
@@ -294,8 +294,8 @@ struct btd_device *avdtp_get_device(struct avdtp *session);
struct avdtp_server *avdtp_get_server(struct avdtp_local_sep *lsep);

struct avdtp *avdtp_new(struct avdtp_server *server, GSList *sessions,
- GIOChannel *chan,
struct btd_device *device);
+bool avdtp_transport_connect(struct avdtp *session, GIOChannel *chan);
void avdtp_free(void *data);
void connection_lost(struct avdtp *session, int err);
void avdtp_accept(struct avdtp *session);
--
2.1.0


2015-02-27 15:02:52

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 04/20] audio/avdtp: Remove avdtp_server from avdtp code

From: Andrei Emeltchenko <[email protected]>

Finishing removing avdtp_server code from avdtp.c. At the moment code is
moved to a2dp.c
---
profiles/audio/a2dp.c | 22 ++++++++++++++++++----
profiles/audio/a2dp.h | 3 +++
profiles/audio/avdtp.c | 41 ++++++++++++++++++-----------------------
profiles/audio/avdtp.h | 5 +++--
4 files changed, 42 insertions(+), 29 deletions(-)

diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index e669f43..c96971f 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -120,7 +120,7 @@ struct avdtp_server {
struct btd_adapter *adapter;
GIOChannel *io;
struct queue *seps;
- GSList *sessions;
+ struct queue *sessions;
};

static GSList *servers = NULL;
@@ -1222,7 +1222,7 @@ struct avdtp *a2dp_avdtp_get(struct btd_device *device)
if (server == NULL)
return NULL;

- session = avdtp_new(server, server->sessions, device);
+ session = avdtp_new(server, server->sessions, device, server->seps);
if (!session)
return NULL;

@@ -1240,9 +1240,20 @@ static struct a2dp_server *a2dp_server_register(struct btd_adapter *adapter)
return server;
}

+void avdtp_server_remove_session(struct avdtp_server *server,
+ struct avdtp *session)
+{
+ queue_remove(server->sessions, session);
+}
+
+struct btd_adapter *avdtp_server_get_adapter(struct avdtp_server *server)
+{
+ return server->adapter;
+}
+
static void avdtp_server_destroy(struct avdtp_server *server)
{
- g_slist_free_full(server->sessions, avdtp_free);
+ queue_destroy(server->sessions, avdtp_free);

avdtp_servers = g_slist_remove(avdtp_servers, server);

@@ -1250,6 +1261,7 @@ static void avdtp_server_destroy(struct avdtp_server *server)
g_io_channel_unref(server->io);
btd_adapter_unref(server->adapter);
queue_destroy(server->seps, NULL);
+ queue_destroy(server->sessions, NULL);
g_free(server);
}

@@ -1328,7 +1340,8 @@ static void avdtp_confirm_cb(GIOChannel *chan, gpointer data)
if (!avdtp_server)
goto drop;

- session = avdtp_new(avdtp_server, avdtp_server->sessions, device);
+ session = avdtp_new(avdtp_server, avdtp_server->sessions, device,
+ avdtp_server->seps);
if (!session)
goto drop;

@@ -1379,6 +1392,7 @@ static struct avdtp_server *avdtp_server_init(struct btd_adapter *adapter)

server->adapter = btd_adapter_ref(adapter);
server->seps = queue_new();
+ server->sessions = queue_new();

avdtp_servers = g_slist_append(avdtp_servers, server);

diff --git a/profiles/audio/a2dp.h b/profiles/audio/a2dp.h
index 544eea1..fdcc528 100644
--- a/profiles/audio/a2dp.h
+++ b/profiles/audio/a2dp.h
@@ -88,3 +88,6 @@ gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session);
struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep);
struct btd_device *a2dp_setup_get_device(struct a2dp_setup *setup);
struct avdtp *a2dp_avdtp_get(struct btd_device *device);
+void avdtp_server_remove_session(struct avdtp_server *server,
+ struct avdtp *session);
+struct btd_adapter *avdtp_server_get_adapter(struct avdtp_server *server);
diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 0296700..05b3d2c 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -50,6 +50,7 @@
#include "src/device.h"

#include "avdtp.h"
+#include "a2dp.h"
#include "sink.h"
#include "source.h"

@@ -324,13 +325,6 @@ struct avdtp_remote_sep {
struct avdtp_stream *stream;
};

-struct avdtp_server {
- struct btd_adapter *adapter;
- GIOChannel *io;
- struct queue *seps;
- GSList *sessions;
-};
-
struct avdtp_local_sep {
avdtp_state_t state;
struct avdtp_stream *stream;
@@ -1132,7 +1126,6 @@ void avdtp_free(void *data)

void connection_lost(struct avdtp *session, int err)
{
- struct avdtp_server *server = session->server;
char address[18];

ba2str(device_get_address(session->device), address);
@@ -1151,7 +1144,7 @@ void connection_lost(struct avdtp *session, int err)
if (session->ref > 0)
return;

- server->sessions = g_slist_remove(server->sessions, session);
+ avdtp_server_remove_session(session->server, session);
avdtp_free(session);
}

@@ -2262,16 +2255,18 @@ failed:
return FALSE;
}

-static struct avdtp *find_session(GSList *list, struct btd_device *device)
+static bool match_by_device(const void *data, const void *user_data)
{
- for (; list != NULL; list = g_slist_next(list)) {
- struct avdtp *s = list->data;
+ const struct avdtp *session = data;
+ const struct btd_device *device = user_data;

- if (s->device == device)
- return s;
- }
+ return session->device == device;
+}

- return NULL;
+static struct avdtp *find_session(struct queue *sessions,
+ struct btd_device *device)
+{
+ return queue_find(sessions, match_by_device, device);
}

static uint16_t get_version(struct avdtp *session)
@@ -2379,8 +2374,9 @@ failed:
connection_lost(session, err_no);
}

-struct avdtp *avdtp_new(struct avdtp_server *server, GSList *sessions,
- struct btd_device *device)
+struct avdtp *avdtp_new(struct avdtp_server *server, struct queue *sessions,
+ struct btd_device *device,
+ struct queue *lseps)
{
struct avdtp *session;

@@ -2397,10 +2393,9 @@ struct avdtp *avdtp_new(struct avdtp_server *server, GSList *sessions,
session->state = AVDTP_SESSION_STATE_DISCONNECTED;

session->version = get_version(session);
+ session->lseps = lseps;

- server->sessions = g_slist_append(server->sessions, session);
-
- session->lseps = server->seps;
+ queue_push_tail(sessions, session);

DBG("Created session %p added to server %p", session, server);

@@ -2465,7 +2460,7 @@ static GIOChannel *l2cap_connect(struct avdtp *session)
GIOChannel *io;
const bdaddr_t *src;

- src = btd_adapter_get_address(session->server->adapter);
+ src = btd_adapter_get_address(avdtp_get_adapter(session));

io = bt_io_connect(avdtp_connect_cb, session,
NULL, &err,
@@ -3724,7 +3719,7 @@ avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep)

struct btd_adapter *avdtp_get_adapter(struct avdtp *session)
{
- return session->server->adapter;
+ return avdtp_server_get_adapter(session->server);
}

struct btd_device *avdtp_get_device(struct avdtp *session)
diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h
index 4aa3936..28277cc 100644
--- a/profiles/audio/avdtp.h
+++ b/profiles/audio/avdtp.h
@@ -293,8 +293,9 @@ struct btd_adapter *avdtp_get_adapter(struct avdtp *session);
struct btd_device *avdtp_get_device(struct avdtp *session);
struct avdtp_server *avdtp_get_server(struct avdtp_local_sep *lsep);

-struct avdtp *avdtp_new(struct avdtp_server *server, GSList *sessions,
- struct btd_device *device);
+struct avdtp *avdtp_new(struct avdtp_server *server, struct queue *sessions,
+ struct btd_device *device,
+ struct queue *lseps);
bool avdtp_transport_connect(struct avdtp *session, GIOChannel *chan);
void avdtp_free(void *data);
void connection_lost(struct avdtp *session, int err);
--
2.1.0


2015-02-27 15:02:51

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 03/20] audio/avdtp: Add stream related debugs

From: Andrei Emeltchenko <[email protected]>

---
profiles/audio/avdtp.c | 23 +++++++++++++++++++----
1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index aaaf3b2..0296700 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -969,7 +969,7 @@ static void avdtp_sep_set_state(struct avdtp *session,
err_ptr = &err;
} else {
err_ptr = NULL;
- DBG("stream state changed: %s -> %s",
+ DBG("stream %p state changed: %s -> %s", stream,
avdtp_statestr(sep->state),
avdtp_statestr(state));
}
@@ -1025,6 +1025,8 @@ static void avdtp_sep_set_state(struct avdtp *session,
if (state == AVDTP_STATE_IDLE &&
g_slist_find(session->streams, stream)) {
session->streams = g_slist_remove(session->streams, stream);
+
+ DBG("stream %p removed from session %p", stream, session);
stream_free(stream);
}
}
@@ -1428,6 +1430,8 @@ static void setconf_cb(struct avdtp *session, struct avdtp_stream *stream,
sep->info.inuse = 1;
session->streams = g_slist_append(session->streams, stream);

+ DBG("stream %p added to session %p", stream, session);
+
avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
}

@@ -1530,6 +1534,8 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction,
sep->info.inuse = 1;
session->streams = g_slist_append(session->streams, stream);

+ DBG("stream %p added to session %p", stream, session);
+
avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
}

@@ -3352,7 +3358,7 @@ int avdtp_set_configuration(struct avdtp *session,
if (!(lsep && rsep))
return -EINVAL;

- DBG("%p: int_seid=%u, acp_seid=%u", session,
+ DBG("session %p: int_seid=%u, acp_seid=%u", session,
lsep->info.seid, rsep->seid);

new_stream = g_new0(struct avdtp_stream, 1);
@@ -3360,6 +3366,8 @@ int avdtp_set_configuration(struct avdtp *session,
new_stream->lsep = lsep;
new_stream->rseid = rsep->seid;

+ DBG("new_stream %p session %p", new_stream, session);
+
if (rsep->delay_reporting && lsep->delay_reporting) {
struct avdtp_service_capability *delay_reporting;

@@ -3399,6 +3407,9 @@ int avdtp_set_configuration(struct avdtp *session,
lsep->stream = new_stream;
rsep->stream = new_stream;
session->streams = g_slist_append(session->streams, new_stream);
+
+ DBG("stream %p added to session %p", new_stream, session);
+
if (stream)
*stream = new_stream;
}
@@ -3445,11 +3456,15 @@ int avdtp_start(struct avdtp *session, struct avdtp_stream *stream)
struct start_req req;
int ret;

- if (!g_slist_find(session->streams, stream))
+ if (!g_slist_find(session->streams, stream)) {
+ DBG("Cannot find stream %p in session %p", stream, session);
return -EINVAL;
+ }

- if (stream->lsep->state != AVDTP_STATE_OPEN)
+ if (stream->lsep->state != AVDTP_STATE_OPEN) {
+ DBG("Wrong stream state %d", stream->lsep->state);
return -EINVAL;
+ }

/* Recommendation 12:
* If the RD has configured and opened a stream it is also responsible
--
2.1.0


2015-02-27 15:03:01

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 13/20] audio/avdtp: Add finalize_discovery() on unref

From: Andrei Emeltchenko <[email protected]>

---
profiles/audio/avdtp.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 4361e23..6132c13 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -1217,6 +1217,8 @@ void avdtp_unref(struct avdtp *session)
if (session->ref > 0)
return;

+ finalize_discovery(session, ECONNABORTED);
+
set_disconnect_timer(session);
}

--
2.1.0


2015-02-27 15:02:49

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 01/20] audio/avdtp: Copy SEP list to avdtp session

From: Andrei Emeltchenko <[email protected]>

Copying SEP list allows to remove server usage from
find_local_sep_by_seid() and other SEP list code.
---
profiles/audio/avdtp.c | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 926f01c..1a16170 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -402,6 +402,7 @@ struct avdtp {
guint io_id;

GSList *seps; /* Elements of type struct avdtp_remote_sep * */
+ struct queue *lseps; /* Elements of type struct avdtp_local_sep * */

GSList *streams; /* Elements of type struct avdtp_stream * */

@@ -1222,10 +1223,10 @@ static bool match_by_seid(const void *data, const void *user_data)
return sep->info.seid == seid;
}

-static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp_server *server,
+static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp *session,
uint8_t seid)
{
- return queue_find(server->seps, match_by_seid, INT_TO_PTR(seid));
+ return queue_find(session->lseps, match_by_seid, INT_TO_PTR(seid));
}

struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
@@ -1329,7 +1330,7 @@ static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction,
struct seid_info *seps, *p;
gboolean ret;

- sep_count = queue_length(session->server->seps);
+ sep_count = queue_length(session->lseps);

if (sep_count == 0) {
uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND;
@@ -1342,7 +1343,7 @@ static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction,
seps = g_new0(struct seid_info, sep_count);
p = seps;

- queue_foreach(session->server->seps, copy_seps, &p);
+ queue_foreach(session->lseps, copy_seps, &p);

ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
AVDTP_DISCOVER, seps, rsp_size);
@@ -1368,7 +1369,7 @@ static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction,
goto failed;
}

- sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
@@ -1445,7 +1446,7 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction,
return FALSE;
}

- sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
@@ -1560,7 +1561,7 @@ static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction,

memset(buf, 0, sizeof(buf));

- sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
@@ -1676,7 +1677,7 @@ static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction,
return FALSE;
}

- sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
@@ -1736,8 +1737,7 @@ static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction,
for (i = 0; i < seid_count; i++, seid++) {
failed_seid = seid->seid;

- sep = find_local_sep_by_seid(session->server,
- req->first_seid.seid);
+ sep = find_local_sep_by_seid(session, req->first_seid.seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
@@ -1787,7 +1787,7 @@ static gboolean avdtp_close_cmd(struct avdtp *session, uint8_t transaction,
return FALSE;
}

- sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
@@ -1848,8 +1848,7 @@ static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction,
for (i = 0; i < seid_count; i++, seid++) {
failed_seid = seid->seid;

- sep = find_local_sep_by_seid(session->server,
- req->first_seid.seid);
+ sep = find_local_sep_by_seid(session, req->first_seid.seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
@@ -1896,7 +1895,7 @@ static gboolean avdtp_abort_cmd(struct avdtp *session, uint8_t transaction,
return FALSE;
}

- sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep || !sep->stream)
return TRUE;

@@ -1934,7 +1933,7 @@ static gboolean avdtp_delayreport_cmd(struct avdtp *session,
return FALSE;
}

- sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
@@ -2396,6 +2395,7 @@ struct avdtp *avdtp_new(struct avdtp_server *server, GSList *sessions,

server->sessions = g_slist_append(server->sessions, session);

+ session->lseps = server->seps;
if (!chan)
return session;

--
2.1.0


2015-02-27 15:02:57

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 09/20] audio/avdtp: Add AVDTP_STATE_OPEN to allowed state

From: Andrei Emeltchenko <[email protected]>

In delay report handling add AVDTP_STATE_OPEN to good states.
---
profiles/audio/avdtp.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index e409d38..434ebc7 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -1944,10 +1944,17 @@ static gboolean avdtp_delayreport_cmd(struct avdtp *session,

stream = sep->stream;

- if (sep->state != AVDTP_STATE_CONFIGURED &&
- sep->state != AVDTP_STATE_STREAMING) {
+ switch (sep->state) {
+ case AVDTP_STATE_IDLE:
+ case AVDTP_STATE_ABORTING:
+ case AVDTP_STATE_CLOSING:
err = AVDTP_BAD_STATE;
goto failed;
+ case AVDTP_STATE_CONFIGURED:
+ case AVDTP_STATE_OPEN:
+ case AVDTP_STATE_STREAMING:
+ default:
+ break;
}

stream->delay = ntohs(req->delay);
--
2.1.0


2015-02-27 15:03:03

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 15/20] audio/avdtp: Add error checks and style fixes

From: Andrei Emeltchenko <[email protected]>

The patch adds error checks and fixes several style issues fixed in
android code base.
---
profiles/audio/avdtp.c | 48 ++++++++++++++++++++++++++++++++++++------------
1 file changed, 36 insertions(+), 12 deletions(-)

diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index c396e8f..aa1fec5 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -1134,6 +1134,9 @@ void avdtp_free(void *data)
g_slist_free_full(session->seps, sep_free);
g_slist_free_full(session->disconnect, g_free);

+ /* Free copy of the SEP list */
+ session->lseps = NULL;
+
g_free(session->buf);

btd_device_unref(session->device);
@@ -1228,6 +1231,7 @@ void avdtp_unref(struct avdtp *session)
struct avdtp *avdtp_ref(struct avdtp *session)
{
session->ref++;
+
DBG("%p: ref=%d", session, session->ref);
if (session->dc_timer)
remove_disconnect_timer(session);
@@ -2166,8 +2170,11 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,

DBG("");

- if (cond & G_IO_NVAL)
+ if (cond & G_IO_NVAL) {
+ session->io_id = 0;
+
return FALSE;
+ }

header = (void *) session->buf;

@@ -2268,6 +2275,7 @@ next:
return TRUE;

failed:
+ session->io_id = 0;
connection_lost(session, EIO);

return FALSE;
@@ -2747,6 +2755,11 @@ static int send_request(struct avdtp *session, gboolean priority,
{
struct pending_req *req;

+ if (size > 0 && !buffer) {
+ DBG("Invalid buffer %p", buffer);
+ return -EINVAL;
+ }
+
if (stream && stream->abort_int && signal_id != AVDTP_ABORT) {
DBG("Unable to send requests while aborting");
return -EINVAL;
@@ -2754,11 +2767,14 @@ static int send_request(struct avdtp *session, gboolean priority,

req = g_new0(struct pending_req, 1);
req->signal_id = signal_id;
- req->data = g_malloc(size);
- memcpy(req->data, buffer, size);
req->data_size = size;
req->stream = stream;

+ if (size > 0) {
+ req->data = g_malloc(size);
+ memcpy(req->data, buffer, size);
+ }
+
return send_req(session, priority, req);
}

@@ -2900,14 +2916,14 @@ 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->user_data);
-
/* We might be in STREAMING already if both sides send START_CMD at the
* same time and the one in SNK role doesn't reject it as it should */
if (sep->state != AVDTP_STATE_STREAMING)
avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);

+ if (sep->cfm && sep->cfm->start)
+ sep->cfm->start(session, sep, stream, NULL, sep->user_data);
+
return TRUE;
}

@@ -3323,13 +3339,19 @@ struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
{
struct avdtp_service_capability *cap;

- if (category < AVDTP_MEDIA_TRANSPORT || category > AVDTP_DELAY_REPORTING)
+ if (category < AVDTP_MEDIA_TRANSPORT ||
+ category > AVDTP_DELAY_REPORTING)
+ return NULL;
+
+ if (length > 0 && !data)
return NULL;

cap = g_malloc(sizeof(struct avdtp_service_capability) + length);
cap->category = category;
cap->length = length;
- memcpy(cap->data, data, length);
+
+ if (length > 0)
+ memcpy(cap->data, data, length);

return cap;
}
@@ -3613,14 +3635,15 @@ int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
if (!g_slist_find(session->streams, stream))
return -EINVAL;

- if (stream->lsep->state < AVDTP_STATE_OPEN)
- return -EINVAL;
-
if (stream->close_int == TRUE) {
error("avdtp_close: rejecting since close is already initiated");
return -EINVAL;
}

+ /* If stream is not yet in the OPEN state, let's use ABORT_CMD */
+ if (stream->lsep->state < AVDTP_STATE_OPEN)
+ return avdtp_abort(session, stream);
+
if (immediate && session->req && stream == session->req->stream)
return avdtp_abort(session, stream);

@@ -3663,7 +3686,8 @@ int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream)
if (stream->lsep->state == AVDTP_STATE_ABORTING)
return -EINVAL;

- if (session->req && stream == session->req->stream)
+ if (session->req && session->req->timeout > 0 &&
+ stream == session->req->stream)
return cancel_request(session, ECANCELED);

memset(&req, 0, sizeof(req));
--
2.1.0


2015-02-27 15:02:58

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 10/20] audio/avdtp: Fix style issues

From: Andrei Emeltchenko <[email protected]>

Makes code in profiles/ and android/ identical in style.
---
profiles/audio/avdtp.c | 38 ++++++++++++++++++++++++--------------
profiles/audio/avdtp.h | 3 ++-
2 files changed, 26 insertions(+), 15 deletions(-)

diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 434ebc7..9c8185a 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -1498,11 +1498,15 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction,
&stream->codec,
&stream->delay_reporting);

- /* Verify that the Media Transport capability's length = 0. Reject otherwise */
+ /*
+ * Verify that the Media Transport capability's length = 0.
+ * Reject otherwise
+ */
for (l = stream->caps; l != NULL; l = g_slist_next(l)) {
struct avdtp_service_capability *cap = l->data;

- if (cap->category == AVDTP_MEDIA_TRANSPORT && cap->length != 0) {
+ if (cap->category == AVDTP_MEDIA_TRANSPORT &&
+ cap->length != 0) {
err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT;
goto failed_stream;
}
@@ -1574,7 +1578,7 @@ static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction,
goto failed;
}

- for (l = sep->stream->caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+ for (l = sep->stream->caps, rsp_size = 0; l; l = g_slist_next(l)) {
struct avdtp_service_capability *cap = l->data;

if (rsp_size + cap->length + 2 > (int) sizeof(buf))
@@ -2038,7 +2042,8 @@ static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session,
switch (header->packet_type) {
case AVDTP_PKT_TYPE_SINGLE:
if (size < sizeof(*single)) {
- error("Received too small single packet (%zu bytes)", size);
+ error("Received too small single packet (%zu bytes)",
+ size);
return PARSE_ERROR;
}
if (session->in.active) {
@@ -2059,7 +2064,8 @@ static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session,
break;
case AVDTP_PKT_TYPE_START:
if (size < sizeof(*start)) {
- error("Received too small start packet (%zu bytes)", size);
+ error("Received too small start packet (%zu bytes)",
+ size);
return PARSE_ERROR;
}
if (session->in.active) {
@@ -2103,7 +2109,8 @@ static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session,
break;
case AVDTP_PKT_TYPE_END:
if (size < sizeof(struct avdtp_continue_header)) {
- error("Received too small end packet (%zu bytes)", size);
+ error("Received too small end packet (%zu bytes)",
+ size);
return PARSE_ERROR;
}
if (!session->in.active) {
@@ -2616,7 +2623,7 @@ static int cancel_request(struct avdtp *session, int err)
error("SetConfiguration: %s (%d)", strerror(err), err);
if (lsep && lsep->cfm && lsep->cfm->set_configuration)
lsep->cfm->set_configuration(session, lsep, stream,
- &averr, lsep->user_data);
+ &averr, lsep->user_data);
goto failed;
case AVDTP_DISCOVER:
error("Discover: %s (%d)", strerror(err), err);
@@ -2815,9 +2822,9 @@ static gboolean avdtp_get_capabilities_resp(struct avdtp *session,
}

static gboolean avdtp_set_configuration_resp(struct avdtp *session,
- struct avdtp_stream *stream,
- struct avdtp_single_header *resp,
- int size)
+ struct avdtp_stream *stream,
+ struct avdtp_single_header *resp,
+ int size)
{
struct avdtp_local_sep *sep = stream->lsep;

@@ -2832,7 +2839,8 @@ static gboolean avdtp_set_configuration_resp(struct avdtp *session,

static gboolean avdtp_reconfigure_resp(struct avdtp *session,
struct avdtp_stream *stream,
- struct avdtp_single_header *resp, int size)
+ struct avdtp_single_header *resp,
+ int size)
{
return TRUE;
}
@@ -2918,7 +2926,8 @@ static gboolean avdtp_delay_report_resp(struct avdtp *session,
struct avdtp_local_sep *sep = stream->lsep;

if (sep->cfm && sep->cfm->delay_report)
- sep->cfm->delay_report(session, sep, stream, NULL, sep->user_data);
+ sep->cfm->delay_report(session, sep, stream, NULL,
+ sep->user_data);

return TRUE;
}
@@ -3274,7 +3283,8 @@ struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep)
}

struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
- void *data, int length)
+ const void *data,
+ int length)
{
struct avdtp_service_capability *cap;

@@ -3746,7 +3756,7 @@ const char *avdtp_strerror(struct avdtp_error *err)
if (err->category == AVDTP_ERRNO)
return strerror(err->err.posix_errno);

- switch(err->err.error_code) {
+ switch (err->err.error_code) {
case AVDTP_BAD_HEADER_FORMAT:
return "Bad Header Format";
case AVDTP_BAD_LENGTH:
diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h
index a68dc4d..88e8cb4 100644
--- a/profiles/audio/avdtp.h
+++ b/profiles/audio/avdtp.h
@@ -218,7 +218,8 @@ void avdtp_unref(struct avdtp *session);
struct avdtp *avdtp_ref(struct avdtp *session);

struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
- void *data, int size);
+ const void *data,
+ int size);

struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep);

--
2.1.0


2015-02-27 15:03:04

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 16/20] audio/avdtp: Add handling missing cases in avdtp_parse_rej

From: Andrei Emeltchenko <[email protected]>

The patch adds handling some missing cases in avdtp_parse_rej().
---
profiles/audio/avdtp.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index aa1fec5..64e09fd 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -3116,8 +3116,17 @@ static gboolean avdtp_parse_rej(struct avdtp *session,
case AVDTP_GET_ALL_CAPABILITIES:
if (!seid_rej_to_err(buf, size, &err))
return FALSE;
- error("GET_CAPABILITIES request rejected: %s (%d)",
- avdtp_strerror(&err), err.err.error_code);
+ error("%s request rejected: %s (%d)",
+ signal_id == AVDTP_DISCOVER ? "DISCOVER" :
+ signal_id == AVDTP_GET_CAPABILITIES ?
+ "GET_CAPABILITIES" : "GET_ALL_CAPABILITIES",
+ avdtp_strerror(&err), err.err.error_code);
+ if (session->discover) {
+ session->discover->cb(session, session->seps, &err,
+ session->discover->user_data);
+ g_free(session->discover);
+ session->discover = NULL;
+ }
return TRUE;
case AVDTP_OPEN:
if (!seid_rej_to_err(buf, size, &err))
@@ -3137,6 +3146,15 @@ static gboolean avdtp_parse_rej(struct avdtp *session,
sep->cfm->set_configuration(session, sep, stream,
&err, sep->user_data);
return TRUE;
+ case AVDTP_GET_CONFIGURATION:
+ if (!seid_rej_to_err(buf, size, &err))
+ return FALSE;
+ error("GET_CONFIGURATION request rejected: %s (%d)",
+ avdtp_strerror(&err), err.err.error_code);
+ if (sep && sep->cfm && sep->cfm->get_configuration)
+ sep->cfm->get_configuration(session, sep, stream, &err,
+ sep->user_data);
+ return TRUE;
case AVDTP_RECONFIGURE:
if (!conf_rej_to_err(buf, size, &err))
return FALSE;
--
2.1.0


2015-02-27 15:02:54

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 06/20] audio/avdtp: Refactor transport and control channels

From: Andrei Emeltchenko <[email protected]>

Refactor transport and signaling channels using
avdtp_stream_set_transport() and avdtp_set_control().
---
profiles/audio/a2dp.c | 104 ++++++++++++++++++++++++++++++++++++---
profiles/audio/avdtp.c | 131 ++++++++++++++++++++++++++++++++++++++++---------
profiles/audio/avdtp.h | 8 ++-
3 files changed, 212 insertions(+), 31 deletions(-)

diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index e23455a..ef8ed5e 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -702,12 +702,95 @@ static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
return TRUE;
}

+static void transport_connect_cb(GIOChannel *chan, GError *err,
+ gpointer user_data)
+{
+ struct avdtp *session = user_data;
+ uint16_t imtu, omtu;
+ GError *gerr = NULL;
+ char address[18];
+ int fd;
+
+ DBG("");
+
+ if (err) {
+ error("%s", err->message);
+ goto failed;
+ }
+
+ bt_io_get(chan, &gerr,
+ BT_IO_OPT_IMTU, &imtu,
+ BT_IO_OPT_OMTU, &omtu,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ goto failed;
+ }
+
+ ba2str(device_get_address(avdtp_get_device(session)), address);
+ DBG("AVDTP: connected transport channel to %s", address);
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ if (!avdtp_stream_set_transport(session, fd, imtu, omtu)) {
+ error("avdtp_stream_set_transport: failed");
+ goto failed;
+ }
+
+ return;
+
+failed:
+ avdtp_send_abort(session);
+}
+
+static void signaling_connect_cb(GIOChannel *chan, GError *err,
+ gpointer user_data)
+{
+ struct avdtp *session = user_data;
+ uint16_t imtu, omtu;
+ int err_no = -EIO;
+ GError *gerr = NULL;
+ char address[18];
+ int fd;
+
+ DBG("");
+
+ if (err) {
+ err_no = err->code;
+ error("%s", err->message);
+ goto failed;
+ }
+
+ bt_io_get(chan, &gerr,
+ BT_IO_OPT_IMTU, &imtu,
+ BT_IO_OPT_OMTU, &omtu,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ goto failed;
+ }
+
+ ba2str(device_get_address(avdtp_get_device(session)), address);
+ DBG("AVDTP: connected signaling channel to %s", address);
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ if (avdtp_set_control(session, fd, imtu, omtu))
+ return;
+
+failed:
+ connection_lost(session, err_no);
+}
+
static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream, struct avdtp_error *err,
void *user_data)
{
struct a2dp_sep *a2dp_sep = user_data;
struct a2dp_setup *setup;
+ GIOChannel *io;

if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
DBG("Sink %p: Open_Cfm", sep);
@@ -728,9 +811,9 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
finalize_resume(setup);
}

- finalize_config(setup);
-
- return;
+ io = l2cap_connect(session, transport_connect_cb);
+ if (avdtp_stream_set_io(session, stream, sep, io))
+ finalize_config(setup);
}

static gboolean suspend_timeout(struct a2dp_sep *sep)
@@ -1271,6 +1354,8 @@ GIOChannel *l2cap_connect(struct avdtp *session, BtIOConnect cb)
GIOChannel *io;
const bdaddr_t *src;

+ DBG("session %p", session);
+
src = btd_adapter_get_address(avdtp_get_adapter(session));

io = bt_io_connect(cb, session, NULL, &err,
@@ -1323,13 +1408,15 @@ static void auth_cb(DBusError *derr, void *user_data)
{
struct avdtp *session = user_data;

+ DBG("");
+
if (derr && dbus_error_is_set(derr)) {
error("Access denied: %s", derr->message);
connection_lost(session, EACCES);
return;
}

- avdtp_accept(session);
+ avdtp_accept(session, signaling_connect_cb);
}

static void avdtp_confirm_cb(GIOChannel *chan, gpointer data)
@@ -1370,10 +1457,11 @@ static void avdtp_confirm_cb(GIOChannel *chan, gpointer data)
goto drop;


- if (avdtp_transport_connect(session, chan)) {
- btd_device_add_uuid(device, ADVANCED_AUDIO_UUID);
- avdtp_request_authorization(session, &src, &dst, auth_cb);
- }
+ if (!avdtp_transport_connect(session, chan))
+ return;
+
+ btd_device_add_uuid(device, ADVANCED_AUDIO_UUID);
+ avdtp_request_authorization(session, &src, &dst, auth_cb);

return;

diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 1ea9fe4..1bfbff7 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -799,6 +799,8 @@ static void handle_transport_connect(struct avdtp *session, GIOChannel *io,
stream->timer = 0;
}

+ DBG("session %p io %p", session, io);
+
if (io == NULL) {
if (!stream->open_acp && sep->cfm && sep->cfm->open) {
struct avdtp_error err;
@@ -841,9 +843,6 @@ static void handle_transport_connect(struct avdtp *session, GIOChannel *io,
}

proceed:
- if (!stream->open_acp && sep->cfm && sep->cfm->open)
- sep->cfm->open(session, sep, stream, NULL, sep->user_data);
-
avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);

stream->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
@@ -2255,6 +2254,21 @@ failed:
return FALSE;
}

+static int set_priority(int fd, int priority)
+{
+ int err;
+
+ err = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority,
+ sizeof(priority));
+ if (err == 0 || errno == ENOTSOCK)
+ return 0;
+
+ err = -errno;
+ error("setsockopt(SO_PRIORITY): %s (%d)", strerror(-err), -err);
+
+ return err;
+}
+
static bool match_by_device(const void *data, const void *user_data)
{
const struct avdtp *session = data;
@@ -2296,6 +2310,58 @@ static uint16_t get_version(struct avdtp *session)
return ver;
}

+bool avdtp_set_control(struct avdtp *session, int fd, size_t imtu, size_t omtu)
+{
+ GIOChannel *chan = g_io_channel_unix_new(fd);
+
+ session->imtu = imtu;
+ session->omtu = omtu;
+
+ DBG("AVDTP imtu=%u, omtu=%u", session->imtu, session->omtu);
+
+ session->buf = g_malloc0(MAX(imtu, omtu));
+ avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTED);
+
+ if (!session->io)
+ session->io = g_io_channel_ref(chan);
+
+ if (session->io_id)
+ g_source_remove(session->io_id);
+
+ /* This watch should be low priority since otherwise the
+ * connect callback might be dispatched before the session
+ * callback if the kernel wakes us up at the same time for
+ * them. This could happen if a headset is very quick in
+ * sending the Start command after connecting the stream
+ * transport channel.
+ */
+ session->io_id = g_io_add_watch_full(chan,
+ G_PRIORITY_LOW,
+ G_IO_IN | G_IO_ERR | G_IO_HUP
+ | G_IO_NVAL,
+ (GIOFunc) session_cb, session,
+ NULL);
+
+ if (session->stream_setup)
+ set_disconnect_timer(session);
+
+ process_queue(session);
+
+ return true;
+}
+
+bool avdtp_stream_set_io(struct avdtp *session, struct avdtp_stream *stream,
+ struct avdtp_local_sep *sep, GIOChannel *io)
+{
+ if (!io) {
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+ return false;
+ }
+
+ stream->io = io;
+ return true;
+}
+
static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
{
struct avdtp *session = user_data;
@@ -2588,14 +2654,7 @@ static int send_req(struct avdtp *session, gboolean priority,
static int transaction = 0;
int err;

- if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {
- session->io = l2cap_connect(session, avdtp_connect_cb);
- if (!session->io) {
- err = -EIO;
- goto failed;
- }
- avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);
- }
+ DBG("session %p", session);

if (session->state < AVDTP_SESSION_STATE_CONNECTED ||
session->req != NULL) {
@@ -2766,19 +2825,17 @@ static gboolean avdtp_reconfigure_resp(struct avdtp *session,
return TRUE;
}

-static gboolean avdtp_open_resp(struct avdtp *session, struct avdtp_stream *stream,
+static gboolean avdtp_open_resp(struct avdtp *session,
+ struct avdtp_stream *stream,
struct seid_rej *resp, int size)
{
struct avdtp_local_sep *sep = stream->lsep;

- stream->io = l2cap_connect(session, avdtp_connect_cb);
- if (!stream->io) {
- avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
- return FALSE;
- }
-
session->pending_open = stream;

+ if (!stream->open_acp && sep->cfm && sep->cfm->open)
+ sep->cfm->open(session, sep, stream, NULL, sep->user_data);
+
return TRUE;
}

@@ -3135,6 +3192,24 @@ struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
return NULL;
}

+bool avdtp_stream_set_transport(struct avdtp *session, int fd, size_t imtu,
+ size_t omtu)
+{
+ GIOChannel *io;
+
+ if (set_priority(fd, 5) < 0)
+ return FALSE;
+
+ io = g_io_channel_unix_new(fd);
+
+ handle_transport_connect(session, io, imtu, omtu);
+
+ g_io_channel_unref(io);
+
+ return TRUE;
+
+}
+
gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
uint16_t *imtu, uint16_t *omtu,
GSList **caps)
@@ -3545,6 +3620,17 @@ int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream)
return ret;
}

+void avdtp_send_abort(struct avdtp *session)
+{
+ struct avdtp_stream *stream = session->pending_open;
+
+ handle_transport_connect(session, NULL, 0, 0);
+
+ if (avdtp_abort(session, stream) < 0)
+ avdtp_sep_set_state(session, stream->lsep,
+ AVDTP_STATE_IDLE);
+}
+
int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
uint16_t delay)
{
@@ -3570,19 +3656,20 @@ int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
&req, sizeof(req));
}

-void avdtp_accept(struct avdtp *session)
+void avdtp_accept(struct avdtp *session, BtIOConnect cb)
{
GError *err = NULL;

- if (!bt_io_accept(session->io, avdtp_connect_cb, session, NULL,
- &err)) {
+ DBG("session %p", session);
+
+ if (!bt_io_accept(session->io, cb, session, NULL, &err)) {
error("bt_io_accept: %s", err->message);
connection_lost(session, EACCES);
g_error_free(err);
return;
}

- /* This is so that avdtp_connect_cb will know to do the right thing
+ /* This is so that connect_cb will know to do the right thing
* with respect to the disconnect timer */
session->stream_setup = TRUE;
}
diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h
index 28277cc..a68dc4d 100644
--- a/profiles/audio/avdtp.h
+++ b/profiles/audio/avdtp.h
@@ -264,6 +264,7 @@ int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream);
int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
gboolean immediate);
int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream);
+void avdtp_send_abort(struct avdtp *session);
int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
uint16_t delay);

@@ -299,7 +300,12 @@ struct avdtp *avdtp_new(struct avdtp_server *server, struct queue *sessions,
bool avdtp_transport_connect(struct avdtp *session, GIOChannel *chan);
void avdtp_free(void *data);
void connection_lost(struct avdtp *session, int err);
-void avdtp_accept(struct avdtp *session);
+void avdtp_accept(struct avdtp *session, BtIOConnect cb);
bool avdtp_request_authorization(struct avdtp *session, const bdaddr_t *src,
const bdaddr_t *dst,
service_auth_cb cb);
+bool avdtp_set_control(struct avdtp *session, int fd, size_t imtu, size_t omtu);
+bool avdtp_stream_set_transport(struct avdtp *session, int fd, size_t imtu,
+ size_t omtu);
+bool avdtp_stream_set_io(struct avdtp *session, struct avdtp_stream *stream,
+ struct avdtp_local_sep *sep, GIOChannel *io);
--
2.1.0


2015-02-27 15:03:02

by Andrei Emeltchenko

[permalink] [raw]
Subject: [RFCv1 14/20] android/avdtp: Add set vendor codec function

From: Andrei Emeltchenko <[email protected]>

---
profiles/audio/avdtp.c | 22 ++++++++++++++++++++++
profiles/audio/avdtp.h | 2 ++
2 files changed, 24 insertions(+)

diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 6132c13..c396e8f 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -49,6 +49,7 @@
#include "src/adapter.h"
#include "src/device.h"

+#include "../profiles/audio/a2dp-codecs.h"
#include "avdtp.h"
#include "a2dp.h"
#include "sink.h"
@@ -330,6 +331,8 @@ struct avdtp_local_sep {
struct avdtp_stream *stream;
struct seid_info info;
uint8_t codec;
+ uint32_t vndcodec_vendor;
+ uint16_t vndcodec_codec;
gboolean delay_reporting;
GSList *caps;
struct avdtp_sep_ind *ind;
@@ -1274,6 +1277,18 @@ struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
if (codec_data->media_codec_type != lsep->codec)
continue;

+ /* FIXME: Add Vendor Specific Codec match to SEP callback */
+ if (lsep->codec == A2DP_CODEC_VENDOR) {
+ a2dp_vendor_codec_t *vndcodec =
+ (void *) codec_data->data;
+
+ if (btohl(vndcodec->vendor_id) != lsep->vndcodec_vendor)
+ continue;
+
+ if (btohs(vndcodec->codec_id) != lsep->vndcodec_codec)
+ continue;
+ }
+
if (sep->stream == NULL)
return sep;
}
@@ -3753,6 +3768,13 @@ struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
return sep;
}

+void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
+ uint16_t codec_id)
+{
+ sep->vndcodec_vendor = vendor_id;
+ sep->vndcodec_codec = codec_id;
+}
+
int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep)
{
if (!sep)
diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h
index af55f76..0fc8fe2 100644
--- a/profiles/audio/avdtp.h
+++ b/profiles/audio/avdtp.h
@@ -282,6 +282,8 @@ struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
struct avdtp_sep_ind *ind,
struct avdtp_sep_cfm *cfm,
void *user_data);
+void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
+ uint16_t codec_id);

/* Find a matching pair of local and remote SEP ID's */
struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
--
2.1.0


2015-04-29 15:46:10

by chanyeol

[permalink] [raw]
Subject: Re: [RFCv1 14/20] android/avdtp: Add set vendor codec function

Hi,
On Fri, 2015-02-27 at 17:03 +0200, Andrei Emeltchenko wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> ---
> profiles/audio/avdtp.c | 22 ++++++++++++++++++++++
> profiles/audio/avdtp.h | 2 ++
> 2 files changed, 24 insertions(+)
>
> diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
> index 6132c13..c396e8f 100644
> --- a/profiles/audio/avdtp.c
> +++ b/profiles/audio/avdtp.c
> @@ -49,6 +49,7 @@
> #include "src/adapter.h"
> #include "src/device.h"
>
> +#include "../profiles/audio/a2dp-codecs.h"
> #include "avdtp.h"
> #include "a2dp.h"
> #include "sink.h"
> @@ -330,6 +331,8 @@ struct avdtp_local_sep {
> struct avdtp_stream *stream;
> struct seid_info info;
> uint8_t codec;
> + uint32_t vndcodec_vendor;
> + uint16_t vndcodec_codec;
> gboolean delay_reporting;
> GSList *caps;
> struct avdtp_sep_ind *ind;
> @@ -1274,6 +1277,18 @@ struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
> if (codec_data->media_codec_type != lsep->codec)
> continue;
>
> + /* FIXME: Add Vendor Specific Codec match to SEP callback */
> + if (lsep->codec == A2DP_CODEC_VENDOR) {
> + a2dp_vendor_codec_t *vndcodec =
> + (void *) codec_data->data;
> +
> + if (btohl(vndcodec->vendor_id) != lsep->vndcodec_vendor)
> + continue;
> +
> + if (btohs(vndcodec->codec_id) != lsep->vndcodec_codec)
> + continue;
> + }
> +
> if (sep->stream == NULL)
> return sep;
> }
> @@ -3753,6 +3768,13 @@ struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
> return sep;
> }
>
> +void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
> + uint16_t codec_id)
> +{
> + sep->vndcodec_vendor = vendor_id;
> + sep->vndcodec_codec = codec_id;
> +}
> +
> int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep)
> {
> if (!sep)
> diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h
> index af55f76..0fc8fe2 100644
> --- a/profiles/audio/avdtp.h
> +++ b/profiles/audio/avdtp.h
> @@ -282,6 +282,8 @@ struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
> struct avdtp_sep_ind *ind,
> struct avdtp_sep_cfm *cfm,
> void *user_data);
> +void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
> + uint16_t codec_id);
>
> /* Find a matching pair of local and remote SEP ID's */
> struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,

I find out that bluez a2dp also do not work fine in case of multiple
vendor codecs in remote. So I think bluez also needs this android bluez
patch.

Originally I consider to use check_vendor(profile/audio/a2dp.c)function
from avdtp_find_remote_sep. but it does not seem good to me.

Could you give us the guidance?

Thanks
Chanyeol


2015-04-30 14:18:23

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [RFCv1 14/20] android/avdtp: Add set vendor codec function

Hi Chanyeol,

On Wed, Apr 29, 2015 at 6:46 PM, chanten <[email protected]> wrote:
> Hi,
> On Fri, 2015-02-27 at 17:03 +0200, Andrei Emeltchenko wrote:
>> From: Andrei Emeltchenko <[email protected]>
>>
>> ---
>> profiles/audio/avdtp.c | 22 ++++++++++++++++++++++
>> profiles/audio/avdtp.h | 2 ++
>> 2 files changed, 24 insertions(+)
>>
>> diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
>> index 6132c13..c396e8f 100644
>> --- a/profiles/audio/avdtp.c
>> +++ b/profiles/audio/avdtp.c
>> @@ -49,6 +49,7 @@
>> #include "src/adapter.h"
>> #include "src/device.h"
>>
>> +#include "../profiles/audio/a2dp-codecs.h"
>> #include "avdtp.h"
>> #include "a2dp.h"
>> #include "sink.h"
>> @@ -330,6 +331,8 @@ struct avdtp_local_sep {
>> struct avdtp_stream *stream;
>> struct seid_info info;
>> uint8_t codec;
>> + uint32_t vndcodec_vendor;
>> + uint16_t vndcodec_codec;
>> gboolean delay_reporting;
>> GSList *caps;
>> struct avdtp_sep_ind *ind;
>> @@ -1274,6 +1277,18 @@ struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
>> if (codec_data->media_codec_type != lsep->codec)
>> continue;
>>
>> + /* FIXME: Add Vendor Specific Codec match to SEP callback */
>> + if (lsep->codec == A2DP_CODEC_VENDOR) {
>> + a2dp_vendor_codec_t *vndcodec =
>> + (void *) codec_data->data;
>> +
>> + if (btohl(vndcodec->vendor_id) != lsep->vndcodec_vendor)
>> + continue;
>> +
>> + if (btohs(vndcodec->codec_id) != lsep->vndcodec_codec)
>> + continue;
>> + }
>> +
>> if (sep->stream == NULL)
>> return sep;
>> }
>> @@ -3753,6 +3768,13 @@ struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
>> return sep;
>> }
>>
>> +void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
>> + uint16_t codec_id)
>> +{
>> + sep->vndcodec_vendor = vendor_id;
>> + sep->vndcodec_codec = codec_id;
>> +}
>> +
>> int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep)
>> {
>> if (!sep)
>> diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h
>> index af55f76..0fc8fe2 100644
>> --- a/profiles/audio/avdtp.h
>> +++ b/profiles/audio/avdtp.h
>> @@ -282,6 +282,8 @@ struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
>> struct avdtp_sep_ind *ind,
>> struct avdtp_sep_cfm *cfm,
>> void *user_data);
>> +void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
>> + uint16_t codec_id);
>>
>> /* Find a matching pair of local and remote SEP ID's */
>> struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
>
> I find out that bluez a2dp also do not work fine in case of multiple
> vendor codecs in remote. So I think bluez also needs this android bluez
> patch.
>
> Originally I consider to use check_vendor(profile/audio/a2dp.c)function
> from avdtp_find_remote_sep. but it does not seem good to me.
>
> Could you give us the guidance?

We will be migrating to use android implementation, which btw has unit
tests, but yes without this patch vendor codecs might not work if the
codecs don't match.


--
Luiz Augusto von Dentz

2015-04-29 15:46:43

by chanyeol

[permalink] [raw]
Subject: Re: [RFCv1 14/20] android/avdtp: Add set vendor codec function

Hi,
On Fri, 2015-02-27 at 17:03 +0200, Andrei Emeltchenko wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> ---
> profiles/audio/avdtp.c | 22 ++++++++++++++++++++++
> profiles/audio/avdtp.h | 2 ++
> 2 files changed, 24 insertions(+)
>
> diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
> index 6132c13..c396e8f 100644
> --- a/profiles/audio/avdtp.c
> +++ b/profiles/audio/avdtp.c
> @@ -49,6 +49,7 @@
> #include "src/adapter.h"
> #include "src/device.h"
>
> +#include "../profiles/audio/a2dp-codecs.h"
> #include "avdtp.h"
> #include "a2dp.h"
> #include "sink.h"
> @@ -330,6 +331,8 @@ struct avdtp_local_sep {
> struct avdtp_stream *stream;
> struct seid_info info;
> uint8_t codec;
> + uint32_t vndcodec_vendor;
> + uint16_t vndcodec_codec;
> gboolean delay_reporting;
> GSList *caps;
> struct avdtp_sep_ind *ind;
> @@ -1274,6 +1277,18 @@ struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
> if (codec_data->media_codec_type != lsep->codec)
> continue;
>
> + /* FIXME: Add Vendor Specific Codec match to SEP callback */
> + if (lsep->codec == A2DP_CODEC_VENDOR) {
> + a2dp_vendor_codec_t *vndcodec =
> + (void *) codec_data->data;
> +
> + if (btohl(vndcodec->vendor_id) != lsep->vndcodec_vendor)
> + continue;
> +
> + if (btohs(vndcodec->codec_id) != lsep->vndcodec_codec)
> + continue;
> + }
> +
> if (sep->stream == NULL)
> return sep;
> }
> @@ -3753,6 +3768,13 @@ struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
> return sep;
> }
>
> +void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
> + uint16_t codec_id)
> +{
> + sep->vndcodec_vendor = vendor_id;
> + sep->vndcodec_codec = codec_id;
> +}
> +
> int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep)
> {
> if (!sep)
> diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h
> index af55f76..0fc8fe2 100644
> --- a/profiles/audio/avdtp.h
> +++ b/profiles/audio/avdtp.h
> @@ -282,6 +282,8 @@ struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
> struct avdtp_sep_ind *ind,
> struct avdtp_sep_cfm *cfm,
> void *user_data);
> +void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id,
> + uint16_t codec_id);
>
> /* Find a matching pair of local and remote SEP ID's */
> struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,

I find out that bluez a2dp also do not work fine in case of multiple
vendor codecs in remote. So I think bluez also needs this android bluez
patch.

Originally I consider to use check_vendor(profile/audio/a2dp.c)function
from avdtp_find_remote_sep. but it does not seem good to me.

Could you give us the guidance?

Thanks
Chanyeol