Linux bluetooth development
 help / color / mirror / Atom feed
* Re: [PATCHv9 18/21] android/socket: Use security level for connect
From: Johan Hedberg @ 2013-11-25 13:40 UTC (permalink / raw)
  To: Andrei Emeltchenko; +Cc: linux-bluetooth
In-Reply-To: <1385384937-29858-19-git-send-email-Andrei.Emeltchenko.news@gmail.com>

Hi Andrei,

On Mon, Nov 25, 2013, Andrei Emeltchenko wrote:
> Use low security level for connections without profile and default
> sec_level for others. rfsock now has pointer to profile info for
> outcoming connections.
> ---
>  android/socket.c | 12 +++++++++++-
>  1 file changed, 11 insertions(+), 1 deletion(-)

Patches 1-17 have been applied, but I can't apply from this one onwards
since the default security level should be MEDIUM and not LOW. OPP is
the only exception that should have LOW and SAP the only exception that
should have HIGH.

Johan

^ permalink raw reply

* Re: [PATCH] Adding paired-devices cmd to the bluetoothctl
From: Johan Hedberg @ 2013-11-25 13:24 UTC (permalink / raw)
  To: Szymon Janc; +Cc: Sebastian, linux-bluetooth, Sebastian Chlad
In-Reply-To: <7144304.2MX7gdvi4Y@uw000953>

Hi Szymon,

On Mon, Nov 25, 2013, Szymon Janc wrote:
> > Paired-devices command lists only paired devices
> > ---
> >  client/main.c | 22 ++++++++++++++++++++++
> >  1 file changed, 22 insertions(+)
> > 
> > diff --git a/client/main.c b/client/main.c
> > index 0dd1510..c39ebf8 100644
> > --- a/client/main.c
> > +++ b/client/main.c
> > @@ -538,6 +538,26 @@ static void cmd_devices(const char *arg)
> >  	}
> >  }
> >  
> > +static void cmd_devices_paired(const char *arg)
> > +{
> > +	GList *list;
> > +
> > +	for (list = g_list_first(dev_list); list; list = g_list_next(list)) {
> > +		DBusMessageIter iter;
> > +		GDBusProxy *proxy = list->data;
> > +		dbus_bool_t paired;
> > +
> > +		if (g_dbus_proxy_get_property(proxy, "Paired", &iter) == FALSE)
> > +			return;
> > +		dbus_message_iter_get_basic(&iter, &paired);
> > +
> > +		if (!paired)
> > +			break;
> 
> Shouldn't this be
>   if (!paired)
>       continue;
> 
> ? Or paired devices are guaranteed to be first on list?

They're not, and the same goes the for return statement in case
g_dbus_proxy_get_property fails. Since this was the last patch pushed I
did a git commit --amend + git push --force to avoid a fixup patch.

Johan

^ permalink raw reply

* [PATCH 3/3] sdptool: Make code consistent for SPP record add
From: Andrei Emeltchenko @ 2013-11-25 13:14 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385285-30592-1-git-send-email-Andrei.Emeltchenko.news@gmail.com>

From: Andrei Emeltchenko <andrei.emeltchenko@intel.com>

Move freeing to the end of the function like it is done for other
records.
---
 tools/sdptool.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tools/sdptool.c b/tools/sdptool.c
index 6682612..b4b65ec 100644
--- a/tools/sdptool.c
+++ b/tools/sdptool.c
@@ -1174,18 +1174,15 @@ static int add_sp(sdp_session_t *session, svc_info_t *si)
 	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
 	root = sdp_list_append(0, &root_uuid);
 	sdp_set_browse_groups(&record, root);
-	sdp_list_free(root, 0);
 
 	sdp_uuid16_create(&sp_uuid, SERIAL_PORT_SVCLASS_ID);
 	svclass_id = sdp_list_append(0, &sp_uuid);
 	sdp_set_service_classes(&record, svclass_id);
-	sdp_list_free(svclass_id, 0);
 
 	sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
 	profile.version = 0x0100;
 	profiles = sdp_list_append(0, &profile);
 	sdp_set_profile_descs(&record, profiles);
-	sdp_list_free(profiles, 0);
 
 	sdp_uuid16_create(&l2cap, L2CAP_UUID);
 	proto[0] = sdp_list_append(0, &l2cap);
@@ -1226,6 +1223,9 @@ end:
 	sdp_list_free(proto[1], 0);
 	sdp_list_free(apseq, 0);
 	sdp_list_free(aproto, 0);
+	sdp_list_free(root, 0);
+	sdp_list_free(svclass_id, 0);
+	sdp_list_free(profiles, 0);
 
 	return ret;
 }
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH 2/3] sdptool: Fix memory leaks creating PBAP record
From: Andrei Emeltchenko @ 2013-11-25 13:14 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385285-30592-1-git-send-email-Andrei.Emeltchenko.news@gmail.com>

From: Andrei Emeltchenko <andrei.emeltchenko@intel.com>

---
 tools/sdptool.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/tools/sdptool.c b/tools/sdptool.c
index 78dc281..6682612 100644
--- a/tools/sdptool.c
+++ b/tools/sdptool.c
@@ -1888,7 +1888,10 @@ end:
 	sdp_list_free(proto[1], 0);
 	sdp_list_free(proto[2], 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 ret;
 }
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH 1/3] sdptool: Fix memory leaks creating OPP record
From: Andrei Emeltchenko @ 2013-11-25 13:14 UTC (permalink / raw)
  To: linux-bluetooth

From: Andrei Emeltchenko <andrei.emeltchenko@intel.com>

---
 tools/sdptool.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/tools/sdptool.c b/tools/sdptool.c
index f985b1e..78dc281 100644
--- a/tools/sdptool.c
+++ b/tools/sdptool.c
@@ -1813,7 +1813,10 @@ end:
 	sdp_list_free(proto[1], 0);
 	sdp_list_free(proto[2], 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, NULL);
 
 	return ret;
 }
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH BlueZ 19/19] unit/AVDTP: Add /TP/SIG/SMG/BV-24-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:11 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (ACP) is able to accept a command to abort a stream.
---
 unit/test-avdtp.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index 949d998..065dc07 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -596,6 +596,17 @@ int main(int argc, char *argv[])
 				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
 			raw_pdu(0x12, 0x03),
 			raw_pdu(0x20, 0x0a, 0x04));
+	define_test("/TP/SIG/SMG/BV-24-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x0a, 0x04),
+			raw_pdu(0x32, 0x0a));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 18/19] unit/AVDTP: Add /TP/SIG/SMG/BV-23-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:11 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (INT) is able to abort a stream by issuing the
AVDTP_ABORT and reporting the replied confirmation.
---
 unit/test-avdtp.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index 88c2b01..949d998 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -293,6 +293,8 @@ static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 
 	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-11-C"))
 		ret = avdtp_get_configuration(session, stream);
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-23-C"))
+		ret = avdtp_abort(session, stream);
 	else
 		ret = avdtp_open(session, stream);
 
@@ -584,6 +586,16 @@ int main(int argc, char *argv[])
 			raw_pdu(0x42, 0x07),
 			raw_pdu(0x50, 0x09, 0x04),
 			raw_pdu(0x52, 0x09));
+	define_test("/TP/SIG/SMG/BV-23-C", test_client,
+			raw_pdu(0xf0, 0x01),
+			raw_pdu(0xf2, 0x01, 0x04, 0x00),
+			raw_pdu(0x00, 0x02, 0x04),
+			raw_pdu(0x02, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x10, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x12, 0x03),
+			raw_pdu(0x20, 0x0a, 0x04));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 17/19] unit/AVDTP: Add /TP/SIG/SMG/BV-22-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:11 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (ACP) is able to accept an AVDTP_SUSPEND_CMD, after
the streaming procedure has been started, by reporting the suspension
and replying the returned confirmation.
---
 unit/test-avdtp.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index 804fdf5..88c2b01 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -569,6 +569,21 @@ int main(int argc, char *argv[])
 			raw_pdu(0xd0, 0x07, 0x04),
 			raw_pdu(0xd2, 0x07),
 			raw_pdu(0xe0, 0x09, 0x04));
+	define_test("/TP/SIG/SMG/BV-22-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x32, 0x06),
+			raw_pdu(0x40, 0x07, 0x04),
+			raw_pdu(0x42, 0x07),
+			raw_pdu(0x50, 0x09, 0x04),
+			raw_pdu(0x52, 0x09));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 16/19] unit/AVDTP: Add /TP/SIG/SMG/BV-21-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:11 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (INT) is able to initiate the suspension of a
streaming procedure, after the streaming procedure has been started, by
issuing the AVDTP_SUSPEND_CMD.
---
 unit/test-avdtp.c | 124 +++++++++++++-----------------------------------------
 1 file changed, 29 insertions(+), 95 deletions(-)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index 4677e22..804fdf5 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -317,11 +317,15 @@ static void sep_start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 			struct avdtp_stream *stream, struct avdtp_error *err,
 			void *user_data)
 {
+	struct context *context = user_data;
 	int ret;
 
 	g_assert(err == NULL);
 
-	ret = avdtp_close(session, stream, FALSE);
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-19-C"))
+		ret = avdtp_close(session, stream, FALSE);
+	else
+		ret = avdtp_suspend(session, stream);
 
 	g_assert_cmpint(ret, ==, 0);
 }
@@ -359,7 +363,8 @@ static void discover_cb(struct avdtp *session, GSList *seps,
 	uint8_t data[4] = { 0x21, 0x02, 2, 32 };
 	int ret;
 
-	if (!context)
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-05-C") ||
+		g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-07-C"))
 		return;
 
 	g_assert(err == NULL);
@@ -391,92 +396,7 @@ static void discover_cb(struct avdtp *session, GSList *seps,
 	g_slist_free_full(caps, g_free);
 }
 
-static void test_discover(gconstpointer data)
-{
-	struct context *context = create_context(0x0100, data);
-
-	avdtp_discover(context->session, discover_cb, NULL);
-
-	execute_context(context);
-}
-
-static void test_get_capabilities(gconstpointer data)
-{
-	struct context *context = create_context(0x0100, data);
-
-	avdtp_discover(context->session, discover_cb, NULL);
-
-	execute_context(context);
-}
-
-static void test_set_configuration(gconstpointer data)
-{
-	struct context *context = create_context(0x0100, data);
-	struct avdtp_local_sep *sep;
-
-	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
-					0x00, FALSE, NULL, NULL, NULL);
-	context->sep = sep;
-
-	avdtp_discover(context->session, discover_cb, context);
-
-	execute_context(context);
-
-	avdtp_unregister_sep(sep);
-}
-
-static void test_get_configuration(gconstpointer data)
-{
-	struct context *context = create_context(0x0100, data);
-	struct avdtp_local_sep *sep;
-
-	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
-					0x00, FALSE, NULL, &sep_cfm,
-					context);
-	context->sep = sep;
-
-	avdtp_discover(context->session, discover_cb, context);
-
-	execute_context(context);
-
-	avdtp_unregister_sep(sep);
-}
-
-static void test_open(gconstpointer data)
-{
-	struct context *context = create_context(0x0100, data);
-	struct avdtp_local_sep *sep;
-
-	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
-					0x00, FALSE, NULL, &sep_cfm,
-					context);
-	context->sep = sep;
-
-	avdtp_discover(context->session, discover_cb, context);
-
-	execute_context(context);
-
-	avdtp_unregister_sep(sep);
-}
-
-static void test_start(gconstpointer data)
-{
-	struct context *context = create_context(0x0100, data);
-	struct avdtp_local_sep *sep;
-
-	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
-					0x00, FALSE, NULL, &sep_cfm,
-					context);
-	context->sep = sep;
-
-	avdtp_discover(context->session, discover_cb, context);
-
-	execute_context(context);
-
-	avdtp_unregister_sep(sep);
-}
-
-static void test_close(gconstpointer data)
+static void test_client(gconstpointer data)
 {
 	struct context *context = create_context(0x0100, data);
 	struct avdtp_local_sep *sep;
@@ -506,12 +426,12 @@ int main(int argc, char *argv[])
 	 * To verify that the following procedures are implemented according to
 	 * their specification in AVDTP.
 	 */
-	define_test("/TP/SIG/SMG/BV-05-C", test_discover,
+	define_test("/TP/SIG/SMG/BV-05-C", test_client,
 			raw_pdu(0x00, 0x01));
 	define_test("/TP/SIG/SMG/BV-06-C", test_server,
 			raw_pdu(0x00, 0x01),
 			raw_pdu(0x02, 0x01, 0x04, 0x00));
-	define_test("/TP/SIG/SMG/BV-07-C", test_get_capabilities,
+	define_test("/TP/SIG/SMG/BV-07-C", test_client,
 			raw_pdu(0x10, 0x01),
 			raw_pdu(0x12, 0x01, 0x04, 0x00),
 			raw_pdu(0x20, 0x02, 0x04));
@@ -521,7 +441,7 @@ int main(int argc, char *argv[])
 			raw_pdu(0x10, 0x02, 0x04),
 			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
 				0xff, 0xff, 0x02, 0x40));
-	define_test("/TP/SIG/SMG/BV-09-C", test_set_configuration,
+	define_test("/TP/SIG/SMG/BV-09-C", test_client,
 			raw_pdu(0x30, 0x01),
 			raw_pdu(0x32, 0x01, 0x04, 0x00),
 			raw_pdu(0x40, 0x02, 0x04),
@@ -538,7 +458,7 @@ int main(int argc, char *argv[])
 			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
 				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
 			raw_pdu(0x22, 0x03));
-	define_test("/TP/SIG/SMG/BV-11-C", test_get_configuration,
+	define_test("/TP/SIG/SMG/BV-11-C", test_client,
 			raw_pdu(0x60, 0x01),
 			raw_pdu(0x62, 0x01, 0x04, 0x00),
 			raw_pdu(0x70, 0x02, 0x04),
@@ -560,7 +480,7 @@ int main(int argc, char *argv[])
 			raw_pdu(0x30, 0x04, 0x04),
 			raw_pdu(0x32, 0x04, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
 				0x21, 0x02, 0x02, 0x20));
-	define_test("/TP/SIG/SMG/BV-15-C", test_open,
+	define_test("/TP/SIG/SMG/BV-15-C", test_client,
 			raw_pdu(0xa0, 0x01),
 			raw_pdu(0xa2, 0x01, 0x04, 0x00),
 			raw_pdu(0xb0, 0x02, 0x04),
@@ -581,7 +501,7 @@ int main(int argc, char *argv[])
 			raw_pdu(0x22, 0x03),
 			raw_pdu(0x30, 0x06, 0x04),
 			raw_pdu(0x32, 0x06));
-	define_test("/TP/SIG/SMG/BV-17-C", test_start,
+	define_test("/TP/SIG/SMG/BV-17-C", test_client,
 			raw_pdu(0xe0, 0x01),
 			raw_pdu(0xe2, 0x01, 0x04, 0x00),
 			raw_pdu(0xf0, 0x02, 0x04),
@@ -606,7 +526,7 @@ int main(int argc, char *argv[])
 			raw_pdu(0x32, 0x06),
 			raw_pdu(0x40, 0x07, 0x04),
 			raw_pdu(0x42, 0x07));
-	define_test("/TP/SIG/SMG/BV-19-C", test_close,
+	define_test("/TP/SIG/SMG/BV-19-C", test_client,
 			raw_pdu(0x30, 0x01),
 			raw_pdu(0x32, 0x01, 0x04, 0x00),
 			raw_pdu(0x40, 0x02, 0x04),
@@ -635,6 +555,20 @@ int main(int argc, char *argv[])
 			raw_pdu(0x42, 0x07),
 			raw_pdu(0x50, 0x08, 0x04),
 			raw_pdu(0x52, 0x08));
+	define_test("/TP/SIG/SMG/BV-21-C", test_client,
+			raw_pdu(0x90, 0x01),
+			raw_pdu(0x92, 0x01, 0x04, 0x00),
+			raw_pdu(0xa0, 0x02, 0x04),
+			raw_pdu(0xa2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0xb0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0xb2, 0x03),
+			raw_pdu(0xc0, 0x06, 0x04),
+			raw_pdu(0xc2, 0x06),
+			raw_pdu(0xd0, 0x07, 0x04),
+			raw_pdu(0xd2, 0x07),
+			raw_pdu(0xe0, 0x09, 0x04));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 15/19] unit/AVDTP: Add /TP/SIG/SMG/BV-20-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:11 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (ACP) is able to accept an AVDTP_CLOSE_CMD after the
streaming procedure has been started, by releasing all the resources
associated to the SEP, reporting of the closing and replying the
returned confirmation.
---
 unit/test-avdtp.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index c01e5fb..4677e22 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -620,6 +620,21 @@ int main(int argc, char *argv[])
 			raw_pdu(0x70, 0x07, 0x04),
 			raw_pdu(0x72, 0x07),
 			raw_pdu(0x80, 0x08, 0x04));
+	define_test("/TP/SIG/SMG/BV-20-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x32, 0x06),
+			raw_pdu(0x40, 0x07, 0x04),
+			raw_pdu(0x42, 0x07),
+			raw_pdu(0x50, 0x08, 0x04),
+			raw_pdu(0x52, 0x08));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 14/19] unit/AVDTP: Add /TP/SIG/SMG/BV-19-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:11 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (INT) is able to initiate the release of a streaming
procedure after the streaming procedure has been started, by issuing the
AVDTP_CLOSE_CMD, releasing all the resources associated to the SEP after
reception of the replied confirmation and reporting the closing.
---
 unit/test-avdtp.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 46 insertions(+), 5 deletions(-)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index 615df00..c01e5fb 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -293,12 +293,8 @@ static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 
 	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-11-C"))
 		ret = avdtp_get_configuration(session, stream);
-	else if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-15-C"))
-		ret = avdtp_open(session, stream);
-	else if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-17-C"))
-		ret = avdtp_open(session, stream);
 	else
-		g_assert_not_reached();
+		ret = avdtp_open(session, stream);
 
 	g_assert_cmpint(ret, ==, 0);
 }
@@ -317,9 +313,23 @@ static void sep_open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 	g_assert_cmpint(ret, ==, 0);
 }
 
+static void sep_start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	int ret;
+
+	g_assert(err == NULL);
+
+	ret = avdtp_close(session, stream, FALSE);
+
+	g_assert_cmpint(ret, ==, 0);
+}
+
 static struct avdtp_sep_cfm sep_cfm = {
 	.set_configuration	= sep_setconf_cfm,
 	.open			= sep_open_cfm,
+	.start			= sep_start_cfm,
 };
 
 static void test_server(gconstpointer data)
@@ -466,6 +476,23 @@ static void test_start(gconstpointer data)
 	avdtp_unregister_sep(sep);
 }
 
+static void test_close(gconstpointer data)
+{
+	struct context *context = create_context(0x0100, data);
+	struct avdtp_local_sep *sep;
+
+	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, FALSE, NULL, &sep_cfm,
+					context);
+	context->sep = sep;
+
+	avdtp_discover(context->session, discover_cb, context);
+
+	execute_context(context);
+
+	avdtp_unregister_sep(sep);
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -579,6 +606,20 @@ int main(int argc, char *argv[])
 			raw_pdu(0x32, 0x06),
 			raw_pdu(0x40, 0x07, 0x04),
 			raw_pdu(0x42, 0x07));
+	define_test("/TP/SIG/SMG/BV-19-C", test_close,
+			raw_pdu(0x30, 0x01),
+			raw_pdu(0x32, 0x01, 0x04, 0x00),
+			raw_pdu(0x40, 0x02, 0x04),
+			raw_pdu(0x42, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x50, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x52, 0x03),
+			raw_pdu(0x60, 0x06, 0x04),
+			raw_pdu(0x62, 0x06),
+			raw_pdu(0x70, 0x07, 0x04),
+			raw_pdu(0x72, 0x07),
+			raw_pdu(0x80, 0x08, 0x04));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 13/19] unit/AVDTP: Add /TP/SIG/SMG/BV-18-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:11 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

To verify that the IUT (ACP) reports the reception of valid
AVDTP_START_CMD and replies the returned confirmation.
---
 unit/test-avdtp.c | 138 ++++++++++++++++++++++++++++++------------------------
 1 file changed, 76 insertions(+), 62 deletions(-)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index 7d2c7bc..615df00 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -46,6 +46,7 @@ struct test_pdu {
 };
 
 struct test_data {
+	char *test_name;
 	struct test_pdu *pdu_list;
 };
 
@@ -64,20 +65,23 @@ struct test_data {
 			args, { }, { }					\
 		};							\
 		static struct test_data data;				\
+		data.test_name = g_strdup(name);			\
 		data.pdu_list = g_malloc(sizeof(pdus));			\
 		memcpy(data.pdu_list, pdus, sizeof(pdus));		\
-		g_test_add_data_func(name, &data, function);		\
+		g_test_add_data_func_full(name, &data, function, test_free); \
 	} while (0)
 
 struct context {
 	GMainLoop *main_loop;
 	struct avdtp *session;
 	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
 	guint source;
 	int fd;
 	int mtu;
+	gboolean pending_open;
 	unsigned int pdu_offset;
-	const struct test_pdu *pdu_list;
+	const struct test_data *data;
 };
 
 static void test_debug(const char *str, void *user_data)
@@ -87,6 +91,14 @@ static void test_debug(const char *str, void *user_data)
 	g_print("%s%s\n", prefix, str);
 }
 
+static void test_free(void *user_data)
+{
+	struct test_data *data = user_data;
+
+	g_free(data->test_name);
+	g_free(data->pdu_list);
+}
+
 static void context_quit(struct context *context)
 {
 	g_main_loop_quit(context->main_loop);
@@ -98,7 +110,7 @@ static gboolean send_pdu(gpointer user_data)
 	const struct test_pdu *pdu;
 	ssize_t len;
 
-	pdu = &context->pdu_list[context->pdu_offset++];
+	pdu = &context->data->pdu_list[context->pdu_offset++];
 
 	len = write(context->fd, pdu->data, pdu->size);
 
@@ -112,7 +124,7 @@ static gboolean send_pdu(gpointer user_data)
 
 static void context_process(struct context *context)
 {
-	if (!context->pdu_list[context->pdu_offset].valid) {
+	if (!context->data->pdu_list[context->pdu_offset].valid) {
 		context_quit(context);
 		return;
 	}
@@ -120,6 +132,17 @@ static void context_process(struct context *context)
 	g_idle_add(send_pdu, context);
 }
 
+static gboolean transport_open(struct avdtp_stream *stream)
+{
+	int fd;
+
+	fd = open("/dev/null", O_RDWR, 0);
+	if (fd < 0)
+		g_assert_not_reached();
+
+	return avdtp_stream_set_transport(stream, fd, 672, 672);
+}
+
 static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
 							gpointer user_data)
 {
@@ -129,7 +152,7 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
 	ssize_t len;
 	int fd;
 
-	pdu = &context->pdu_list[context->pdu_offset++];
+	pdu = &context->data->pdu_list[context->pdu_offset++];
 
 	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
 		return FALSE;
@@ -147,12 +170,17 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
 
 	g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
 
+	if (context->pending_open) {
+		context->pending_open = FALSE;
+		g_assert(transport_open(context->stream));
+	}
+
 	context_process(context);
 
 	return TRUE;
 }
 
-static struct context *create_context(uint16_t version)
+static struct context *create_context(uint16_t version, gconstpointer data)
 {
 	struct context *context = g_new0(struct context, 1);
 	GIOChannel *channel;
@@ -181,6 +209,7 @@ static struct context *create_context(uint16_t version)
 	g_io_channel_unref(channel);
 
 	context->fd = sv[1];
+	context->data = data;
 
 	return context;
 }
@@ -227,8 +256,21 @@ static gboolean sep_getcap_ind(struct avdtp *session,
 	return TRUE;
 }
 
+static gboolean sep_open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data)
+{
+	struct context *context = user_data;
+
+	context->pending_open = TRUE;
+	context->stream = stream;
+
+	return TRUE;
+}
+
 static struct avdtp_sep_ind sep_ind = {
 	.get_capability		= sep_getcap_ind,
+	.open			= sep_open_ind,
 };
 
 static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
@@ -244,21 +286,19 @@ static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 	if (!context)
 		return;
 
-	pdu = &context->pdu_list[context->pdu_offset];
+	pdu = &context->data->pdu_list[context->pdu_offset];
 
 	if (pdu->size < 2)
 		return;
 
-	switch (pdu->data[1]) {
-	case 0x04:
+	if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-11-C"))
 		ret = avdtp_get_configuration(session, stream);
-		break;
-	case 0x06:
+	else if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-15-C"))
 		ret = avdtp_open(session, stream);
-		break;
-	default:
+	else if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-17-C"))
+		ret = avdtp_open(session, stream);
+	else
 		g_assert_not_reached();
-	}
 
 	g_assert_cmpint(ret, ==, 0);
 }
@@ -271,11 +311,7 @@ static void sep_open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 
 	g_assert(err == NULL);
 
-	ret = open("/dev/null", O_RDWR, 0);
-	if (ret < 0)
-		g_assert_not_reached();
-
-	avdtp_stream_set_transport(stream, ret, 672, 672);
+	g_assert(transport_open(stream));
 
 	ret = avdtp_start(session, stream);
 	g_assert_cmpint(ret, ==, 0);
@@ -288,22 +324,17 @@ static struct avdtp_sep_cfm sep_cfm = {
 
 static void test_server(gconstpointer data)
 {
-	const struct test_data *test = data;
-	struct context *context = create_context(0x0100);
+	struct context *context = create_context(0x0100, data);
 	struct avdtp_local_sep *sep;
 
-	context->pdu_list = test->pdu_list;
-
 	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO,
-					0x00, TRUE, &sep_ind, NULL, NULL);
+					0x00, TRUE, &sep_ind, NULL, context);
 
 	g_idle_add(send_pdu, context);
 
 	execute_context(context);
 
 	avdtp_unregister_sep(sep);
-
-	g_free(test->pdu_list);
 }
 
 static void discover_cb(struct avdtp *session, GSList *seps,
@@ -352,40 +383,27 @@ static void discover_cb(struct avdtp *session, GSList *seps,
 
 static void test_discover(gconstpointer data)
 {
-	const struct test_data *test = data;
-	struct context *context = create_context(0x0100);
-
-	context->pdu_list = test->pdu_list;
+	struct context *context = create_context(0x0100, data);
 
 	avdtp_discover(context->session, discover_cb, NULL);
 
 	execute_context(context);
-
-	g_free(test->pdu_list);
 }
 
 static void test_get_capabilities(gconstpointer data)
 {
-	const struct test_data *test = data;
-	struct context *context = create_context(0x0100);
-
-	context->pdu_list = test->pdu_list;
+	struct context *context = create_context(0x0100, data);
 
 	avdtp_discover(context->session, discover_cb, NULL);
 
 	execute_context(context);
-
-	g_free(test->pdu_list);
 }
 
 static void test_set_configuration(gconstpointer data)
 {
-	const struct test_data *test = data;
-	struct context *context = create_context(0x0100);
+	struct context *context = create_context(0x0100, data);
 	struct avdtp_local_sep *sep;
 
-	context->pdu_list = test->pdu_list;
-
 	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
 					0x00, FALSE, NULL, NULL, NULL);
 	context->sep = sep;
@@ -395,18 +413,13 @@ static void test_set_configuration(gconstpointer data)
 	execute_context(context);
 
 	avdtp_unregister_sep(sep);
-
-	g_free(test->pdu_list);
 }
 
 static void test_get_configuration(gconstpointer data)
 {
-	const struct test_data *test = data;
-	struct context *context = create_context(0x0100);
+	struct context *context = create_context(0x0100, data);
 	struct avdtp_local_sep *sep;
 
-	context->pdu_list = test->pdu_list;
-
 	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
 					0x00, FALSE, NULL, &sep_cfm,
 					context);
@@ -417,18 +430,13 @@ static void test_get_configuration(gconstpointer data)
 	execute_context(context);
 
 	avdtp_unregister_sep(sep);
-
-	g_free(test->pdu_list);
 }
 
 static void test_open(gconstpointer data)
 {
-	const struct test_data *test = data;
-	struct context *context = create_context(0x0100);
+	struct context *context = create_context(0x0100, data);
 	struct avdtp_local_sep *sep;
 
-	context->pdu_list = test->pdu_list;
-
 	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
 					0x00, FALSE, NULL, &sep_cfm,
 					context);
@@ -439,18 +447,13 @@ static void test_open(gconstpointer data)
 	execute_context(context);
 
 	avdtp_unregister_sep(sep);
-
-	g_free(test->pdu_list);
 }
 
 static void test_start(gconstpointer data)
 {
-	const struct test_data *test = data;
-	struct context *context = create_context(0x0100);
+	struct context *context = create_context(0x0100, data);
 	struct avdtp_local_sep *sep;
 
-	context->pdu_list = test->pdu_list;
-
 	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
 					0x00, FALSE, NULL, &sep_cfm,
 					context);
@@ -461,8 +464,6 @@ static void test_start(gconstpointer data)
 	execute_context(context);
 
 	avdtp_unregister_sep(sep);
-
-	g_free(test->pdu_list);
 }
 
 int main(int argc, char *argv[])
@@ -565,6 +566,19 @@ int main(int argc, char *argv[])
 			raw_pdu(0x10, 0x06, 0x04),
 			raw_pdu(0x12, 0x06),
 			raw_pdu(0x20, 0x07, 0x04));
+	define_test("/TP/SIG/SMG/BV-18-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x32, 0x06),
+			raw_pdu(0x40, 0x07, 0x04),
+			raw_pdu(0x42, 0x07));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 12/19] unit/AVDTP: Add /TP/SIG/SMG/BV-17-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:11 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (INT) is able to start a streaming procedure after
the stream establishment is complete, by issuing the AVDTP_START_CMD,
reporting the replied confirmation and streaming packets.
---
 unit/test-avdtp.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index 2bf782c..7d2c7bc 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -30,6 +30,7 @@
 #include <stdbool.h>
 #include <inttypes.h>
 #include <string.h>
+#include <fcntl.h>
 #include <sys/socket.h>
 
 #include <glib.h>
@@ -262,8 +263,27 @@ static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 	g_assert_cmpint(ret, ==, 0);
 }
 
+static void sep_open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data)
+{
+	int ret;
+
+	g_assert(err == NULL);
+
+	ret = open("/dev/null", O_RDWR, 0);
+	if (ret < 0)
+		g_assert_not_reached();
+
+	avdtp_stream_set_transport(stream, ret, 672, 672);
+
+	ret = avdtp_start(session, stream);
+	g_assert_cmpint(ret, ==, 0);
+}
+
 static struct avdtp_sep_cfm sep_cfm = {
 	.set_configuration	= sep_setconf_cfm,
+	.open			= sep_open_cfm,
 };
 
 static void test_server(gconstpointer data)
@@ -423,6 +443,28 @@ static void test_open(gconstpointer data)
 	g_free(test->pdu_list);
 }
 
+static void test_start(gconstpointer data)
+{
+	const struct test_data *test = data;
+	struct context *context = create_context(0x0100);
+	struct avdtp_local_sep *sep;
+
+	context->pdu_list = test->pdu_list;
+
+	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, FALSE, NULL, &sep_cfm,
+					context);
+	context->sep = sep;
+
+	avdtp_discover(context->session, discover_cb, context);
+
+	execute_context(context);
+
+	avdtp_unregister_sep(sep);
+
+	g_free(test->pdu_list);
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -511,6 +553,18 @@ int main(int argc, char *argv[])
 			raw_pdu(0x22, 0x03),
 			raw_pdu(0x30, 0x06, 0x04),
 			raw_pdu(0x32, 0x06));
+	define_test("/TP/SIG/SMG/BV-17-C", test_start,
+			raw_pdu(0xe0, 0x01),
+			raw_pdu(0xe2, 0x01, 0x04, 0x00),
+			raw_pdu(0xf0, 0x02, 0x04),
+			raw_pdu(0xf2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x00, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x02, 0x03),
+			raw_pdu(0x10, 0x06, 0x04),
+			raw_pdu(0x12, 0x06),
+			raw_pdu(0x20, 0x07, 0x04));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 11/19] unit/AVDTP: Add /TP/SIG/SMG/BV-16-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:11 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (ACP) reports the reception of valid AVDTP_OPEN_CMD
for transport sessions associated to a SEP and replies the returned
confirmation.
---
 unit/test-avdtp.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index 4e46987..2bf782c 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -500,6 +500,17 @@ int main(int argc, char *argv[])
 				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
 			raw_pdu(0xc2, 0x03),
 			raw_pdu(0xd0, 0x06, 0x04));
+	define_test("/TP/SIG/SMG/BV-16-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x06, 0x04),
+			raw_pdu(0x32, 0x06));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 10/19] unit/AVDTP: Add /TP/SIG/SMG/BV-15-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:11 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (INT) is able to establish a stream connection to a
previously configured remote SEP by establishing one L2CAP channel
(AVDTP_OPEN_CMD), and reports the related confirmation.
---
 unit/test-avdtp.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 54 insertions(+), 2 deletions(-)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index a266819..4e46987 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -40,7 +40,7 @@
 
 struct test_pdu {
 	bool valid;
-	const void *data;
+	const uint8_t *data;
 	size_t size;
 };
 
@@ -234,11 +234,31 @@ static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 				struct avdtp_stream *stream,
 				struct avdtp_error *err, void *user_data)
 {
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
 	int ret;
 
 	g_assert(err == NULL);
 
-	ret = avdtp_get_configuration(session, stream);
+	if (!context)
+		return;
+
+	pdu = &context->pdu_list[context->pdu_offset];
+
+	if (pdu->size < 2)
+		return;
+
+	switch (pdu->data[1]) {
+	case 0x04:
+		ret = avdtp_get_configuration(session, stream);
+		break;
+	case 0x06:
+		ret = avdtp_open(session, stream);
+		break;
+	default:
+		g_assert_not_reached();
+	}
+
 	g_assert_cmpint(ret, ==, 0);
 }
 
@@ -381,6 +401,28 @@ static void test_get_configuration(gconstpointer data)
 	g_free(test->pdu_list);
 }
 
+static void test_open(gconstpointer data)
+{
+	const struct test_data *test = data;
+	struct context *context = create_context(0x0100);
+	struct avdtp_local_sep *sep;
+
+	context->pdu_list = test->pdu_list;
+
+	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, FALSE, NULL, &sep_cfm,
+					context);
+	context->sep = sep;
+
+	avdtp_discover(context->session, discover_cb, context);
+
+	execute_context(context);
+
+	avdtp_unregister_sep(sep);
+
+	g_free(test->pdu_list);
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -448,6 +490,16 @@ int main(int argc, char *argv[])
 			raw_pdu(0x30, 0x04, 0x04),
 			raw_pdu(0x32, 0x04, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
 				0x21, 0x02, 0x02, 0x20));
+	define_test("/TP/SIG/SMG/BV-15-C", test_open,
+			raw_pdu(0xa0, 0x01),
+			raw_pdu(0xa2, 0x01, 0x04, 0x00),
+			raw_pdu(0xb0, 0x02, 0x04),
+			raw_pdu(0xb2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0xc0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0xc2, 0x03),
+			raw_pdu(0xd0, 0x06, 0x04));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 09/19] unit/AVDTP: Add /TP/SIG/SMG/BV-12-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:11 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (ACP) reports the reception of a valid get
configuration command for remote SEP and replies the returned
configuration.
---
 unit/test-avdtp.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index 1f43c08..a266819 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -368,7 +368,8 @@ static void test_get_configuration(gconstpointer data)
 	context->pdu_list = test->pdu_list;
 
 	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
-					0x00, FALSE, &sep_ind, &sep_cfm, NULL);
+					0x00, FALSE, NULL, &sep_cfm,
+					context);
 	context->sep = sep;
 
 	avdtp_discover(context->session, discover_cb, context);
@@ -435,6 +436,18 @@ int main(int argc, char *argv[])
 				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
 			raw_pdu(0x82, 0x03),
 			raw_pdu(0x90, 0x04, 0x04));
+	define_test("/TP/SIG/SMG/BV-12-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03),
+			raw_pdu(0x30, 0x04, 0x04),
+			raw_pdu(0x32, 0x04, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0x21, 0x02, 0x02, 0x20));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 08/19] unit/AVDTP: Add /TP/SIG/SMG/BV-11-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:10 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (INT) is able to issue a valid get configuration
command for remote SEP and report the replied configuration.
---
 unit/test-avdtp.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index e39ed24..1f43c08 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -230,6 +230,22 @@ static struct avdtp_sep_ind sep_ind = {
 	.get_capability		= sep_getcap_ind,
 };
 
+static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data)
+{
+	int ret;
+
+	g_assert(err == NULL);
+
+	ret = avdtp_get_configuration(session, stream);
+	g_assert_cmpint(ret, ==, 0);
+}
+
+static struct avdtp_sep_cfm sep_cfm = {
+	.set_configuration	= sep_setconf_cfm,
+};
+
 static void test_server(gconstpointer data)
 {
 	const struct test_data *test = data;
@@ -343,6 +359,27 @@ static void test_set_configuration(gconstpointer data)
 	g_free(test->pdu_list);
 }
 
+static void test_get_configuration(gconstpointer data)
+{
+	const struct test_data *test = data;
+	struct context *context = create_context(0x0100);
+	struct avdtp_local_sep *sep;
+
+	context->pdu_list = test->pdu_list;
+
+	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, FALSE, &sep_ind, &sep_cfm, NULL);
+	context->sep = sep;
+
+	avdtp_discover(context->session, discover_cb, context);
+
+	execute_context(context);
+
+	avdtp_unregister_sep(sep);
+
+	g_free(test->pdu_list);
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -388,6 +425,16 @@ int main(int argc, char *argv[])
 			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
 				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
 			raw_pdu(0x22, 0x03));
+	define_test("/TP/SIG/SMG/BV-11-C", test_get_configuration,
+			raw_pdu(0x60, 0x01),
+			raw_pdu(0x62, 0x01, 0x04, 0x00),
+			raw_pdu(0x70, 0x02, 0x04),
+			raw_pdu(0x72, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x80, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x82, 0x03),
+			raw_pdu(0x90, 0x04, 0x04));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 07/19] unit/AVDTP: Add /TP/SIG/SMG/BV-10-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:10 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (ACP) reports the reception of a valid set
configuration command for remote SEP, and configures the SEP as
requested and replies the returned confirmation.
---
 unit/test-avdtp.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index b0991d9..e39ed24 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -379,6 +379,15 @@ int main(int argc, char *argv[])
 				0xff, 0xff, 0x02, 0x40),
 			raw_pdu(0x50, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
 				0x00, 0x00, 0x21, 0x02, 0x02, 0x20));
+	define_test("/TP/SIG/SMG/BV-10-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+			raw_pdu(0x22, 0x03));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 06/19] unit/AVDTP: Add /TP/SIG/SMG/BV-09-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:10 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (INT) is able to issue a valid set configuration
command for remote SEP and reports the replied confirmation.
---
 unit/test-avdtp.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 68 insertions(+)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index 0b92489..b0991d9 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -71,6 +71,7 @@ struct test_data {
 struct context {
 	GMainLoop *main_loop;
 	struct avdtp *session;
+	struct avdtp_local_sep *sep;
 	guint source;
 	int fd;
 	int mtu;
@@ -252,7 +253,45 @@ static void test_server(gconstpointer data)
 static void discover_cb(struct avdtp *session, GSList *seps,
 				struct avdtp_error *err, void *user_data)
 {
+	struct context *context = user_data;
+	struct avdtp_stream *stream;
+	struct avdtp_remote_sep *rsep;
+	struct avdtp_service_capability *media_transport, *media_codec;
+	struct avdtp_media_codec_capability *cap;
+	GSList *caps;
+	uint8_t data[4] = { 0x21, 0x02, 2, 32 };
+	int ret;
+
+	if (!context)
+		return;
+
+	g_assert(err == NULL);
+	g_assert_cmpint(g_slist_length(seps), !=, 0);
+
+	rsep = avdtp_find_remote_sep(session, context->sep);
+	g_assert(rsep != NULL);
+
+	media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+						NULL, 0);
+
+	caps = g_slist_append(NULL, media_transport);
+
+	cap = g_malloc0(sizeof(*cap) + sizeof(data));
+	cap->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+	cap->media_codec_type = 0x00;
+	memcpy(cap->data, data, sizeof(data));
 
+	media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, cap,
+						sizeof(*cap) + sizeof(data));
+
+	caps = g_slist_append(caps, media_codec);
+	g_free(cap);
+
+	ret = avdtp_set_configuration(session, rsep, context->sep, caps,
+								&stream);
+	g_assert_cmpint(ret, ==, 0);
+
+	g_slist_free_full(caps, g_free);
 }
 
 static void test_discover(gconstpointer data)
@@ -283,6 +322,27 @@ static void test_get_capabilities(gconstpointer data)
 	g_free(test->pdu_list);
 }
 
+static void test_set_configuration(gconstpointer data)
+{
+	const struct test_data *test = data;
+	struct context *context = create_context(0x0100);
+	struct avdtp_local_sep *sep;
+
+	context->pdu_list = test->pdu_list;
+
+	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, FALSE, NULL, NULL, NULL);
+	context->sep = sep;
+
+	avdtp_discover(context->session, discover_cb, context);
+
+	execute_context(context);
+
+	avdtp_unregister_sep(sep);
+
+	g_free(test->pdu_list);
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -311,6 +371,14 @@ int main(int argc, char *argv[])
 			raw_pdu(0x10, 0x02, 0x04),
 			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
 				0xff, 0xff, 0x02, 0x40));
+	define_test("/TP/SIG/SMG/BV-09-C", test_set_configuration,
+			raw_pdu(0x30, 0x01),
+			raw_pdu(0x32, 0x01, 0x04, 0x00),
+			raw_pdu(0x40, 0x02, 0x04),
+			raw_pdu(0x42, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40),
+			raw_pdu(0x50, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+				0x00, 0x00, 0x21, 0x02, 0x02, 0x20));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 05/19] unit/AVDTP: Add /TP/SIG/SMG/BV-08-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:10 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (ACP) reports the reception of a valid query for
remote SEP capabilities and replies the returned capabilities.
---
 unit/test-avdtp.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 46 insertions(+), 2 deletions(-)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index 81155a4..0b92489 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -195,26 +195,64 @@ static void execute_context(struct context *context)
 	g_free(context);
 }
 
+static gboolean sep_getcap_ind(struct avdtp *session,
+					struct avdtp_local_sep *sep,
+					gboolean get_all, GSList **caps,
+					uint8_t *err, void *user_data)
+{
+	struct avdtp_service_capability *media_transport, *media_codec;
+	struct avdtp_media_codec_capability *codec_caps;
+	uint8_t cap[4] = { 0xff, 0xff, 2, 64 };
+
+	*caps = NULL;
+
+	media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+						NULL, 0);
+
+	*caps = g_slist_append(*caps, media_transport);
+
+	codec_caps = g_malloc0(sizeof(*codec_caps) + sizeof(cap));
+	codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+	codec_caps->media_codec_type = 0x00;
+	memcpy(codec_caps->data, cap, sizeof(cap));
+
+	media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps,
+					sizeof(*codec_caps) + sizeof(cap));
+
+	*caps = g_slist_append(*caps, media_codec);
+	g_free(codec_caps);
+
+	return TRUE;
+}
+
+static struct avdtp_sep_ind sep_ind = {
+	.get_capability		= sep_getcap_ind,
+};
+
 static void test_server(gconstpointer data)
 {
 	const struct test_data *test = data;
 	struct context *context = create_context(0x0100);
+	struct avdtp_local_sep *sep;
 
 	context->pdu_list = test->pdu_list;
 
-	avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO, 0x00,
-							TRUE, NULL, NULL, NULL);
+	sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO,
+					0x00, TRUE, &sep_ind, NULL, NULL);
 
 	g_idle_add(send_pdu, context);
 
 	execute_context(context);
 
+	avdtp_unregister_sep(sep);
+
 	g_free(test->pdu_list);
 }
 
 static void discover_cb(struct avdtp *session, GSList *seps,
 				struct avdtp_error *err, void *user_data)
 {
+
 }
 
 static void test_discover(gconstpointer data)
@@ -267,6 +305,12 @@ int main(int argc, char *argv[])
 			raw_pdu(0x10, 0x01),
 			raw_pdu(0x12, 0x01, 0x04, 0x00),
 			raw_pdu(0x20, 0x02, 0x04));
+	define_test("/TP/SIG/SMG/BV-08-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00),
+			raw_pdu(0x10, 0x02, 0x04),
+			raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+				0xff, 0xff, 0x02, 0x40));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 04/19] unit/AVDTP: Add /TP/SIG/SMG/BV-07-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:10 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (INT) is able to issue a valid query for remote SEP
capabilities and reports the replied ones.
---
 unit/test-avdtp.c | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index 5623153..81155a4 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -198,11 +198,12 @@ static void execute_context(struct context *context)
 static void test_server(gconstpointer data)
 {
 	const struct test_data *test = data;
-	struct context *context = create_context();
+	struct context *context = create_context(0x0100);
 
 	context->pdu_list = test->pdu_list;
+
 	avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO, 0x00,
-				TRUE, NULL, NULL, NULL);
+							TRUE, NULL, NULL, NULL);
 
 	g_idle_add(send_pdu, context);
 
@@ -230,6 +231,20 @@ static void test_discover(gconstpointer data)
 	g_free(test->pdu_list);
 }
 
+static void test_get_capabilities(gconstpointer data)
+{
+	const struct test_data *test = data;
+	struct context *context = create_context(0x0100);
+
+	context->pdu_list = test->pdu_list;
+
+	avdtp_discover(context->session, discover_cb, NULL);
+
+	execute_context(context);
+
+	g_free(test->pdu_list);
+}
+
 int main(int argc, char *argv[])
 {
 	g_test_init(&argc, &argv, NULL);
@@ -248,6 +263,10 @@ int main(int argc, char *argv[])
 	define_test("/TP/SIG/SMG/BV-06-C", test_server,
 			raw_pdu(0x00, 0x01),
 			raw_pdu(0x02, 0x01, 0x04, 0x00));
+	define_test("/TP/SIG/SMG/BV-07-C", test_get_capabilities,
+			raw_pdu(0x10, 0x01),
+			raw_pdu(0x12, 0x01, 0x04, 0x00),
+			raw_pdu(0x20, 0x02, 0x04));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 03/19] unit/AVDTP: Add /TP/SIG/SMG/BV-06-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:10 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (ACP) reports the reception of a valid stream
discover command and replies the returned list of SEPs and media types.
---
 unit/test-avdtp.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index 2dc9424..5623153 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -195,6 +195,22 @@ static void execute_context(struct context *context)
 	g_free(context);
 }
 
+static void test_server(gconstpointer data)
+{
+	const struct test_data *test = data;
+	struct context *context = create_context();
+
+	context->pdu_list = test->pdu_list;
+	avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO, 0x00,
+				TRUE, NULL, NULL, NULL);
+
+	g_idle_add(send_pdu, context);
+
+	execute_context(context);
+
+	g_free(test->pdu_list);
+}
+
 static void discover_cb(struct avdtp *session, GSList *seps,
 				struct avdtp_error *err, void *user_data)
 {
@@ -229,6 +245,9 @@ int main(int argc, char *argv[])
 	 */
 	define_test("/TP/SIG/SMG/BV-05-C", test_discover,
 			raw_pdu(0x00, 0x01));
+	define_test("/TP/SIG/SMG/BV-06-C", test_server,
+			raw_pdu(0x00, 0x01),
+			raw_pdu(0x02, 0x01, 0x04, 0x00));
 
 	return g_test_run();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 02/19] unit/AVDTP: Add /TP/SIG/SMG/BV-05-C test
From: Luiz Augusto von Dentz @ 2013-11-25 13:10 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Verify that the IUT (INT) is able to issue a valid stream discover command
and report the replied SEPs and media types.
---
 Makefile.am       |   8 ++
 unit/test-avdtp.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 242 insertions(+)
 create mode 100644 unit/test-avdtp.c

diff --git a/Makefile.am b/Makefile.am
index 328ccdb..2bb2eb5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -249,6 +249,14 @@ unit_test_sdp_SOURCES = unit/test-sdp.c \
 				src/sdpd-service.c src/sdpd-request.c
 unit_test_sdp_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
 
+unit_tests += unit/test-avdtp
+
+unit_test_avdtp_SOURCES = unit/test-avdtp.c \
+				src/shared/util.h src/shared/util.c \
+				src/log.h src/log.c \
+				android/avdtp.c android/avdtp.h
+unit_test_avdtp_LDADD = @GLIB_LIBS@
+
 unit_tests += unit/test-gdbus-client
 
 unit_test_gdbus_client_SOURCES = unit/test-gdbus-client.c
diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
new file mode 100644
index 0000000..2dc9424
--- /dev/null
+++ b/unit/test-avdtp.c
@@ -0,0 +1,234 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/log.h"
+#include "android/avdtp.h"
+
+struct test_pdu {
+	bool valid;
+	const void *data;
+	size_t size;
+};
+
+struct test_data {
+	struct test_pdu *pdu_list;
+};
+
+#define data(args...) ((const unsigned char[]) { args })
+
+#define raw_pdu(args...) \
+	{							\
+		.valid = true,					\
+		.data = data(args),				\
+		.size = sizeof(data(args)),			\
+	}
+
+#define define_test(name, function, args...) \
+	do {								\
+		const struct test_pdu pdus[] = {			\
+			args, { }, { }					\
+		};							\
+		static struct test_data data;				\
+		data.pdu_list = g_malloc(sizeof(pdus));			\
+		memcpy(data.pdu_list, pdus, sizeof(pdus));		\
+		g_test_add_data_func(name, &data, function);		\
+	} while (0)
+
+struct context {
+	GMainLoop *main_loop;
+	struct avdtp *session;
+	guint source;
+	int fd;
+	int mtu;
+	unsigned int pdu_offset;
+	const struct test_pdu *pdu_list;
+};
+
+static void test_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	g_print("%s%s\n", prefix, str);
+}
+
+static void context_quit(struct context *context)
+{
+	g_main_loop_quit(context->main_loop);
+}
+
+static gboolean send_pdu(gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	ssize_t len;
+
+	pdu = &context->pdu_list[context->pdu_offset++];
+
+	len = write(context->fd, pdu->data, pdu->size);
+
+	if (g_test_verbose())
+		util_hexdump('<', pdu->data, len, test_debug, "AVDTP: ");
+
+	g_assert(len == (ssize_t) pdu->size);
+
+	return FALSE;
+}
+
+static void context_process(struct context *context)
+{
+	if (!context->pdu_list[context->pdu_offset].valid) {
+		context_quit(context);
+		return;
+	}
+
+	g_idle_add(send_pdu, context);
+}
+
+static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct context *context = user_data;
+	const struct test_pdu *pdu;
+	unsigned char buf[512];
+	ssize_t len;
+	int fd;
+
+	pdu = &context->pdu_list[context->pdu_offset++];
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+		return FALSE;
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	len = read(fd, buf, sizeof(buf));
+
+	g_assert(len > 0);
+
+	if (g_test_verbose())
+		util_hexdump('>', buf, len, test_debug, "AVDTP: ");
+
+	g_assert((size_t) len == pdu->size);
+
+	g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+
+	context_process(context);
+
+	return TRUE;
+}
+
+static struct context *create_context(uint16_t version)
+{
+	struct context *context = g_new0(struct context, 1);
+	GIOChannel *channel;
+	int err, sv[2];
+
+	context->main_loop = g_main_loop_new(NULL, FALSE);
+	g_assert(context->main_loop);
+
+	err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+	g_assert(err == 0);
+
+	context->session = avdtp_new(sv[0], 672, 672, version);
+	g_assert(context->session != NULL);
+
+	channel = g_io_channel_unix_new(sv[1]);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	context->source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				test_handler, context);
+	g_assert(context->source > 0);
+
+	g_io_channel_unref(channel);
+
+	context->fd = sv[1];
+
+	return context;
+}
+
+static void execute_context(struct context *context)
+{
+	g_main_loop_run(context->main_loop);
+
+	g_source_remove(context->source);
+	avdtp_unref(context->session);
+
+	g_main_loop_unref(context->main_loop);
+
+	g_free(context);
+}
+
+static void discover_cb(struct avdtp *session, GSList *seps,
+				struct avdtp_error *err, void *user_data)
+{
+}
+
+static void test_discover(gconstpointer data)
+{
+	const struct test_data *test = data;
+	struct context *context = create_context(0x0100);
+
+	context->pdu_list = test->pdu_list;
+
+	avdtp_discover(context->session, discover_cb, NULL);
+
+	execute_context(context);
+
+	g_free(test->pdu_list);
+}
+
+int main(int argc, char *argv[])
+{
+	g_test_init(&argc, &argv, NULL);
+
+	if (g_test_verbose())
+		__btd_log_init("*", 0);
+
+	/*
+	 * Stream Management Service
+	 *
+	 * To verify that the following procedures are implemented according to
+	 * their specification in AVDTP.
+	 */
+	define_test("/TP/SIG/SMG/BV-05-C", test_discover,
+			raw_pdu(0x00, 0x01));
+
+	return g_test_run();
+}
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 01/19] android: Add initial AVDTP code
From: Luiz Augusto von Dentz @ 2013-11-25 13:10 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1385385070-21010-1-git-send-email-luiz.dentz@gmail.com>

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds initial AVDTP code which has no dependency on core or btio, in
fact it is transport agnostic.
---
 android/Makefile.am |    1 +
 android/a2dp.c      |   23 +
 android/avdtp.c     | 3256 +++++++++++++++++++++++++++++++++++++++++++++++++++
 android/avdtp.h     |  275 +++++
 4 files changed, 3555 insertions(+)
 create mode 100644 android/avdtp.c
 create mode 100644 android/avdtp.h

diff --git a/android/Makefile.am b/android/Makefile.am
index c4544ea..c02cc86 100644
--- a/android/Makefile.am
+++ b/android/Makefile.am
@@ -19,6 +19,7 @@ android_bluetoothd_SOURCES = android/main.c \
 				android/bluetooth.h android/bluetooth.c \
 				android/hidhost.h android/hidhost.c \
 				android/ipc.h android/ipc.c \
+				android/avdtp.h android/avdtp.c \
 				android/a2dp.h android/a2dp.c \
 				android/socket.h android/socket.c \
 				android/pan.h android/pan.c \
diff --git a/android/a2dp.c b/android/a2dp.c
index 55a433e..2251001 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -43,6 +43,7 @@
 #include "ipc.h"
 #include "utils.h"
 #include "bluetooth.h"
+#include "avdtp.h"
 
 #define L2CAP_PSM_AVDTP 0x19
 #define SVC_HINT_CAPTURING 0x08
@@ -58,6 +59,7 @@ struct a2dp_device {
 	uint8_t		state;
 	GIOChannel	*io;
 	guint		watch;
+	struct avdtp	*session;
 };
 
 static int device_cmp(gconstpointer s, gconstpointer user_data)
@@ -70,6 +72,9 @@ static int device_cmp(gconstpointer s, gconstpointer user_data)
 
 static void a2dp_device_free(struct a2dp_device *dev)
 {
+	if (dev->session)
+		avdtp_unref(dev->session);
+
 	if (dev->watch > 0)
 		g_source_remove(dev->watch);
 
@@ -129,6 +134,9 @@ static void signaling_connect_cb(GIOChannel *chan, GError *err,
 							gpointer user_data)
 {
 	struct a2dp_device *dev = user_data;
+	uint16_t imtu, omtu;
+	GError *gerr = NULL;
+	int fd;
 
 	if (err) {
 		bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
@@ -136,6 +144,21 @@ static void signaling_connect_cb(GIOChannel *chan, GError *err,
 		return;
 	}
 
+	bt_io_get(chan, &gerr,
+			BT_IO_OPT_IMTU, &imtu,
+			BT_IO_OPT_OMTU, &omtu,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+		error("%s", gerr->message);
+		g_error_free(gerr);
+		return;
+	}
+
+	/* FIXME: Add proper version */
+	fd = g_io_channel_unix_get_fd(chan);
+	dev->session = avdtp_new(fd, imtu, omtu, 0x0100);
+
 	dev->watch = g_io_add_watch(dev->io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
 								watch_cb, dev);
 
diff --git a/android/avdtp.c b/android/avdtp.c
new file mode 100644
index 0000000..ac08acd
--- /dev/null
+++ b/android/avdtp.c
@@ -0,0 +1,3256 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <glib.h>
+
+#include "log.h"
+#include "avdtp.h"
+
+#define AVDTP_PSM 25
+
+#define MAX_SEID 0x3E
+
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#define AVDTP_DISCOVER				0x01
+#define AVDTP_GET_CAPABILITIES			0x02
+#define AVDTP_SET_CONFIGURATION			0x03
+#define AVDTP_GET_CONFIGURATION			0x04
+#define AVDTP_RECONFIGURE			0x05
+#define AVDTP_OPEN				0x06
+#define AVDTP_START				0x07
+#define AVDTP_CLOSE				0x08
+#define AVDTP_SUSPEND				0x09
+#define AVDTP_ABORT				0x0A
+#define AVDTP_SECURITY_CONTROL			0x0B
+#define AVDTP_GET_ALL_CAPABILITIES		0x0C
+#define AVDTP_DELAY_REPORT			0x0D
+
+#define AVDTP_PKT_TYPE_SINGLE			0x00
+#define AVDTP_PKT_TYPE_START			0x01
+#define AVDTP_PKT_TYPE_CONTINUE			0x02
+#define AVDTP_PKT_TYPE_END			0x03
+
+#define AVDTP_MSG_TYPE_COMMAND			0x00
+#define AVDTP_MSG_TYPE_GEN_REJECT		0x01
+#define AVDTP_MSG_TYPE_ACCEPT			0x02
+#define AVDTP_MSG_TYPE_REJECT			0x03
+
+#define REQ_TIMEOUT 6
+#define ABORT_TIMEOUT 2
+#define DISCONNECT_TIMEOUT 1
+#define START_TIMEOUT 1
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_common_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct avdtp_single_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+	uint8_t signal_id:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+	uint8_t no_of_packets;
+	uint8_t signal_id:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+	uint8_t message_type:2;
+	uint8_t packet_type:2;
+	uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct seid_info {
+	uint8_t rfa0:1;
+	uint8_t inuse:1;
+	uint8_t seid:6;
+	uint8_t rfa2:3;
+	uint8_t type:1;
+	uint8_t media_type:4;
+} __attribute__ ((packed));
+
+struct seid {
+	uint8_t rfa0:2;
+	uint8_t seid:6;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_common_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct avdtp_single_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+	uint8_t rfa0:2;
+	uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+	uint8_t no_of_packets;
+	uint8_t rfa0:2;
+	uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+	uint8_t transaction:4;
+	uint8_t packet_type:2;
+	uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct seid_info {
+	uint8_t seid:6;
+	uint8_t inuse:1;
+	uint8_t rfa0:1;
+	uint8_t media_type:4;
+	uint8_t type:1;
+	uint8_t rfa2:3;
+} __attribute__ ((packed));
+
+struct seid {
+	uint8_t seid:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+/* packets */
+
+struct discover_resp {
+	struct seid_info seps[0];
+} __attribute__ ((packed));
+
+struct getcap_resp {
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct start_req {
+	struct seid first_seid;
+	struct seid other_seids[0];
+} __attribute__ ((packed));
+
+struct suspend_req {
+	struct seid first_seid;
+	struct seid other_seids[0];
+} __attribute__ ((packed));
+
+struct seid_rej {
+	uint8_t error;
+} __attribute__ ((packed));
+
+struct conf_rej {
+	uint8_t category;
+	uint8_t error;
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct seid_req {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+} __attribute__ ((packed));
+
+struct setconf_req {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+	uint8_t rfa1:2;
+	uint8_t int_seid:6;
+
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+	uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+
+	uint8_t serv_cap;
+	uint8_t serv_cap_len;
+
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct delay_req {
+	uint8_t rfa0:2;
+	uint8_t acp_seid:6;
+	uint16_t delay;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct seid_req {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct setconf_req {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+	uint8_t int_seid:6;
+	uint8_t rfa1:2;
+
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+	uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+
+	uint8_t serv_cap;
+	uint8_t serv_cap_len;
+
+	uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct delay_req {
+	uint8_t acp_seid:6;
+	uint8_t rfa0:2;
+	uint16_t delay;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct in_buf {
+	gboolean active;
+	int no_of_packets;
+	uint8_t transaction;
+	uint8_t message_type;
+	uint8_t signal_id;
+	uint8_t buf[1024];
+	uint8_t data_size;
+};
+
+struct pending_req {
+	uint8_t transaction;
+	uint8_t signal_id;
+	void *data;
+	size_t data_size;
+	struct avdtp_stream *stream; /* Set if the request targeted a stream */
+	guint timeout;
+	gboolean collided;
+};
+
+struct avdtp_remote_sep {
+	uint8_t seid;
+	uint8_t type;
+	uint8_t media_type;
+	struct avdtp_service_capability *codec;
+	gboolean delay_reporting;
+	GSList *caps; /* of type struct avdtp_service_capability */
+	struct avdtp_stream *stream;
+};
+
+struct avdtp_local_sep {
+	avdtp_state_t state;
+	struct avdtp_stream *stream;
+	struct seid_info info;
+	uint8_t codec;
+	gboolean delay_reporting;
+	GSList *caps;
+	struct avdtp_sep_ind *ind;
+	struct avdtp_sep_cfm *cfm;
+	void *user_data;
+};
+
+struct stream_callback {
+	avdtp_stream_state_cb cb;
+	void *user_data;
+	unsigned int id;
+};
+
+struct discover_callback {
+	unsigned int id;
+	avdtp_discover_cb_t cb;
+	void *user_data;
+};
+
+struct avdtp_stream {
+	GIOChannel *io;
+	uint16_t imtu;
+	uint16_t omtu;
+	struct avdtp *session;
+	struct avdtp_local_sep *lsep;
+	uint8_t rseid;
+	GSList *caps;
+	GSList *callbacks;
+	struct avdtp_service_capability *codec;
+	guint io_id;		/* Transport GSource ID */
+	guint timer;		/* Waiting for other side to close or open
+				 * the transport channel */
+	gboolean open_acp;	/* If we are in ACT role for Open */
+	gboolean close_int;	/* If we are in INT role for Close */
+	gboolean abort_int;	/* If we are in INT role for Abort */
+	guint start_timer;	/* Wait START command timer */
+	gboolean delay_reporting;
+	uint16_t delay;		/* AVDTP 1.3 Delay Reporting feature */
+	gboolean starting;	/* only valid while sep state == OPEN */
+};
+
+/* Structure describing an AVDTP connection between two devices */
+
+struct avdtp {
+	unsigned int ref;
+
+	uint16_t version;
+
+	struct avdtp_server *server;
+
+	guint auth_id;
+
+	GIOChannel *io;
+	guint io_id;
+
+	GSList *seps; /* Elements of type struct avdtp_remote_sep * */
+
+	GSList *streams; /* Elements of type struct avdtp_stream * */
+
+	GSList *req_queue; /* Elements of type struct pending_req * */
+	GSList *prio_queue; /* Same as req_queue but is processed before it */
+
+	struct avdtp_stream *pending_open;
+
+	uint16_t imtu;
+	uint16_t omtu;
+
+	struct in_buf in;
+
+	char *buf;
+
+	struct discover_callback *discover;
+	struct pending_req *req;
+};
+
+static GSList *lseps = NULL;
+
+static int send_request(struct avdtp *session, gboolean priority,
+			struct avdtp_stream *stream, uint8_t signal_id,
+			void *buffer, size_t size);
+static gboolean avdtp_parse_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					uint8_t transaction, uint8_t signal_id,
+					void *buf, int size);
+static gboolean avdtp_parse_rej(struct avdtp *session,
+					struct avdtp_stream *stream,
+					uint8_t transaction, uint8_t signal_id,
+					void *buf, int size);
+static int process_queue(struct avdtp *session);
+static void avdtp_sep_set_state(struct avdtp *session,
+				struct avdtp_local_sep *sep,
+				avdtp_state_t state);
+
+static const char *avdtp_statestr(avdtp_state_t state)
+{
+	switch (state) {
+	case AVDTP_STATE_IDLE:
+		return "IDLE";
+	case AVDTP_STATE_CONFIGURED:
+		return "CONFIGURED";
+	case AVDTP_STATE_OPEN:
+		return "OPEN";
+	case AVDTP_STATE_STREAMING:
+		return "STREAMING";
+	case AVDTP_STATE_CLOSING:
+		return "CLOSING";
+	case AVDTP_STATE_ABORTING:
+		return "ABORTING";
+	default:
+		return "<unknown state>";
+	}
+}
+
+static gboolean try_send(int sk, void *data, size_t len)
+{
+	int err;
+
+	do {
+		err = send(sk, data, len, 0);
+	} while (err < 0 && errno == EINTR);
+
+	if (err < 0) {
+		error("send: %s (%d)", strerror(errno), errno);
+		return FALSE;
+	} else if ((size_t) err != len) {
+		error("try_send: complete buffer not sent (%d/%zu bytes)",
+								err, len);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean avdtp_send(struct avdtp *session, uint8_t transaction,
+				uint8_t message_type, uint8_t signal_id,
+				void *data, size_t len)
+{
+	unsigned int cont_fragments, sent;
+	struct avdtp_start_header start;
+	struct avdtp_continue_header cont;
+	int sock;
+
+	if (session->io == NULL) {
+		error("avdtp_send: session is closed");
+		return FALSE;
+	}
+
+	sock = g_io_channel_unix_get_fd(session->io);
+
+	/* Single packet - no fragmentation */
+	if (sizeof(struct avdtp_single_header) + len <= session->omtu) {
+		struct avdtp_single_header single;
+
+		memset(&single, 0, sizeof(single));
+
+		single.transaction = transaction;
+		single.packet_type = AVDTP_PKT_TYPE_SINGLE;
+		single.message_type = message_type;
+		single.signal_id = signal_id;
+
+		memcpy(session->buf, &single, sizeof(single));
+		memcpy(session->buf + sizeof(single), data, len);
+
+		return try_send(sock, session->buf, sizeof(single) + len);
+	}
+
+	/* Check if there is enough space to start packet */
+	if (session->omtu < sizeof(start)) {
+		error("No enough space to fragment packet");
+		return FALSE;
+	}
+
+	/* Count the number of needed fragments */
+	cont_fragments = (len - (session->omtu - sizeof(start))) /
+					(session->omtu - sizeof(cont)) + 1;
+
+	DBG("%zu bytes split into %d fragments", len, cont_fragments + 1);
+
+	/* Send the start packet */
+	memset(&start, 0, sizeof(start));
+	start.transaction = transaction;
+	start.packet_type = AVDTP_PKT_TYPE_START;
+	start.message_type = message_type;
+	start.no_of_packets = cont_fragments + 1;
+	start.signal_id = signal_id;
+
+	memcpy(session->buf, &start, sizeof(start));
+	memcpy(session->buf + sizeof(start), data,
+					session->omtu - sizeof(start));
+
+	if (!try_send(sock, session->buf, session->omtu))
+		return FALSE;
+
+	DBG("first packet with %zu bytes sent", session->omtu - sizeof(start));
+
+	sent = session->omtu - sizeof(start);
+
+	/* Send the continue fragments and the end packet */
+	while (sent < len) {
+		int left, to_copy;
+
+		left = len - sent;
+		if (left + sizeof(cont) > session->omtu) {
+			cont.packet_type = AVDTP_PKT_TYPE_CONTINUE;
+			to_copy = session->omtu - sizeof(cont);
+			DBG("sending continue with %d bytes", to_copy);
+		} else {
+			cont.packet_type = AVDTP_PKT_TYPE_END;
+			to_copy = left;
+			DBG("sending end with %d bytes", to_copy);
+		}
+
+		cont.transaction = transaction;
+		cont.message_type = message_type;
+
+		memcpy(session->buf, &cont, sizeof(cont));
+		memcpy(session->buf + sizeof(cont), data + sent, to_copy);
+
+		if (!try_send(sock, session->buf, to_copy + sizeof(cont)))
+			return FALSE;
+
+		sent += to_copy;
+	}
+
+	return TRUE;
+}
+
+static void pending_req_free(void *data)
+{
+	struct pending_req *req = data;
+
+	if (req->timeout)
+		g_source_remove(req->timeout);
+	g_free(req->data);
+	g_free(req);
+}
+
+static void close_stream(struct avdtp_stream *stream)
+{
+	int sock;
+
+	if (stream->io == NULL)
+		return;
+
+	sock = g_io_channel_unix_get_fd(stream->io);
+
+	shutdown(sock, SHUT_RDWR);
+
+	g_io_channel_shutdown(stream->io, FALSE, NULL);
+
+	g_io_channel_unref(stream->io);
+	stream->io = NULL;
+}
+
+static gboolean stream_close_timeout(gpointer user_data)
+{
+	struct avdtp_stream *stream = user_data;
+
+	DBG("Timed out waiting for peer to close the transport channel");
+
+	stream->timer = 0;
+
+	close_stream(stream);
+
+	return FALSE;
+}
+
+static gboolean stream_open_timeout(gpointer user_data)
+{
+	struct avdtp_stream *stream = user_data;
+
+	DBG("Timed out waiting for peer to open the transport channel");
+
+	stream->timer = 0;
+
+	stream->session->pending_open = NULL;
+
+	avdtp_abort(stream->session, stream);
+
+	return FALSE;
+}
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t category, int id)
+{
+	err->category = category;
+
+	if (category == AVDTP_ERRNO)
+		err->err.posix_errno = id;
+	else
+		err->err.error_code = id;
+}
+
+uint8_t avdtp_error_category(struct avdtp_error *err)
+{
+	return err->category;
+}
+
+int avdtp_error_error_code(struct avdtp_error *err)
+{
+	assert(err->category != AVDTP_ERRNO);
+	return err->err.error_code;
+}
+
+int avdtp_error_posix_errno(struct avdtp_error *err)
+{
+	assert(err->category == AVDTP_ERRNO);
+	return err->err.posix_errno;
+}
+
+static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session,
+							uint8_t rseid)
+{
+	GSList *l;
+
+	for (l = session->streams; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_stream *stream = l->data;
+
+		if (stream->rseid == rseid)
+			return stream;
+	}
+
+	return NULL;
+}
+
+static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid)
+{
+	GSList *l;
+
+	for (l = seps; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_remote_sep *sep = l->data;
+
+		if (sep->seid == seid)
+			return sep;
+	}
+
+	return NULL;
+}
+
+static void stream_free(void *data)
+{
+	struct avdtp_stream *stream = data;
+	struct avdtp_remote_sep *rsep;
+
+	stream->lsep->info.inuse = 0;
+	stream->lsep->stream = NULL;
+
+	rsep = find_remote_sep(stream->session->seps, stream->rseid);
+	if (rsep)
+		rsep->stream = NULL;
+
+	if (stream->timer)
+		g_source_remove(stream->timer);
+
+	if (stream->io)
+		close_stream(stream);
+
+	if (stream->io_id)
+		g_source_remove(stream->io_id);
+
+	g_slist_free_full(stream->callbacks, g_free);
+	g_slist_free_full(stream->caps, g_free);
+
+	g_free(stream);
+}
+
+static gboolean transport_cb(GIOChannel *chan, GIOCondition cond,
+				gpointer data)
+{
+	struct avdtp_stream *stream = data;
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	if (stream->close_int && sep->cfm && sep->cfm->close)
+		sep->cfm->close(stream->session, sep, stream, NULL,
+				sep->user_data);
+
+	if (!(cond & G_IO_NVAL))
+		close_stream(stream);
+
+	stream->io_id = 0;
+
+	if (!stream->abort_int)
+		avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE);
+
+	return FALSE;
+}
+
+static void handle_transport_connect(struct avdtp *session, GIOChannel *io,
+					uint16_t imtu, uint16_t omtu)
+{
+	struct avdtp_stream *stream = session->pending_open;
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	session->pending_open = NULL;
+
+	if (stream->timer) {
+		g_source_remove(stream->timer);
+		stream->timer = 0;
+	}
+
+	if (io == NULL)
+		return;
+
+	if (stream->io == NULL)
+		stream->io = g_io_channel_ref(io);
+
+	stream->omtu = omtu;
+	stream->imtu = imtu;
+
+	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,
+					(GIOFunc) transport_cb, stream);
+}
+
+static int pending_req_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct pending_req *req = a;
+	const struct avdtp_stream *stream = b;
+
+	if (req->stream == stream)
+		return 0;
+
+	return -1;
+}
+
+static void cleanup_queue(struct avdtp *session, struct avdtp_stream *stream)
+{
+	GSList *l;
+	struct pending_req *req;
+
+	while ((l = g_slist_find_custom(session->prio_queue, stream,
+							pending_req_cmp))) {
+		req = l->data;
+		pending_req_free(req);
+		session->prio_queue = g_slist_remove(session->prio_queue, req);
+	}
+
+	while ((l = g_slist_find_custom(session->req_queue, stream,
+							pending_req_cmp))) {
+		req = l->data;
+		pending_req_free(req);
+		session->req_queue = g_slist_remove(session->req_queue, req);
+	}
+}
+
+static void handle_unanswered_req(struct avdtp *session,
+						struct avdtp_stream *stream)
+{
+	struct pending_req *req;
+	struct avdtp_local_sep *lsep;
+	struct avdtp_error err;
+
+	if (session->req->signal_id == AVDTP_ABORT) {
+		/* Avoid freeing the Abort request here */
+		DBG("handle_unanswered_req: Abort req, returning");
+		session->req->stream = NULL;
+		return;
+	}
+
+	req = session->req;
+	session->req = NULL;
+
+	avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+
+	lsep = stream->lsep;
+
+	switch (req->signal_id) {
+	case AVDTP_RECONFIGURE:
+		error("No reply to Reconfigure request");
+		if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+			lsep->cfm->reconfigure(session, lsep, stream, &err,
+						lsep->user_data);
+		break;
+	case AVDTP_OPEN:
+		error("No reply to Open request");
+		if (lsep && lsep->cfm && lsep->cfm->open)
+			lsep->cfm->open(session, lsep, stream, &err,
+					lsep->user_data);
+		break;
+	case AVDTP_START:
+		error("No reply to Start request");
+		if (lsep && lsep->cfm && lsep->cfm->start)
+			lsep->cfm->start(session, lsep, stream, &err,
+						lsep->user_data);
+		break;
+	case AVDTP_SUSPEND:
+		error("No reply to Suspend request");
+		if (lsep && lsep->cfm && lsep->cfm->suspend)
+			lsep->cfm->suspend(session, lsep, stream, &err,
+						lsep->user_data);
+		break;
+	case AVDTP_CLOSE:
+		error("No reply to Close request");
+		if (lsep && lsep->cfm && lsep->cfm->close)
+			lsep->cfm->close(session, lsep, stream, &err,
+						lsep->user_data);
+		break;
+	case AVDTP_SET_CONFIGURATION:
+		error("No reply to SetConfiguration request");
+		if (lsep && lsep->cfm && lsep->cfm->set_configuration)
+			lsep->cfm->set_configuration(session, lsep, stream,
+							&err, lsep->user_data);
+	}
+
+	pending_req_free(req);
+}
+
+static void avdtp_sep_set_state(struct avdtp *session,
+				struct avdtp_local_sep *sep,
+				avdtp_state_t state)
+{
+	struct avdtp_stream *stream = sep->stream;
+	avdtp_state_t old_state;
+	struct avdtp_error err, *err_ptr = NULL;
+	GSList *l;
+
+	if (!stream) {
+		error("Error changing sep state: stream not available");
+		return;
+	}
+
+	if (sep->state == state) {
+		avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+		DBG("stream state change failed: %s", avdtp_strerror(&err));
+		err_ptr = &err;
+	} else {
+		err_ptr = NULL;
+		DBG("stream state changed: %s -> %s",
+				avdtp_statestr(sep->state),
+				avdtp_statestr(state));
+	}
+
+	old_state = sep->state;
+	sep->state = state;
+
+	switch (state) {
+	case AVDTP_STATE_CONFIGURED:
+		if (sep->info.type == AVDTP_SEP_TYPE_SINK)
+			avdtp_delay_report(session, stream, stream->delay);
+		break;
+	case AVDTP_STATE_OPEN:
+		stream->starting = FALSE;
+		break;
+	case AVDTP_STATE_STREAMING:
+		if (stream->start_timer) {
+			g_source_remove(stream->start_timer);
+			stream->start_timer = 0;
+		}
+		stream->open_acp = FALSE;
+		break;
+	case AVDTP_STATE_CLOSING:
+	case AVDTP_STATE_ABORTING:
+		if (stream->start_timer) {
+			g_source_remove(stream->start_timer);
+			stream->start_timer = 0;
+		}
+		break;
+	case AVDTP_STATE_IDLE:
+		if (stream->start_timer) {
+			g_source_remove(stream->start_timer);
+			stream->start_timer = 0;
+		}
+		if (session->pending_open == stream)
+			handle_transport_connect(session, NULL, 0, 0);
+		if (session->req && session->req->stream == stream)
+			handle_unanswered_req(session, stream);
+		/* Remove pending commands for this stream from the queue */
+		cleanup_queue(session, stream);
+		break;
+	default:
+		break;
+	}
+
+	l = stream->callbacks;
+	while (l != NULL) {
+		struct stream_callback *cb = l->data;
+		l = g_slist_next(l);
+		cb->cb(stream, old_state, state, err_ptr, cb->user_data);
+	}
+
+	if (state == AVDTP_STATE_IDLE &&
+				g_slist_find(session->streams, stream)) {
+		session->streams = g_slist_remove(session->streams, stream);
+		stream_free(stream);
+	}
+}
+
+static void finalize_discovery(struct avdtp *session, int err)
+{
+	struct discover_callback *discover = session->discover;
+	struct avdtp_error avdtp_err;
+
+	if (!discover)
+		return;
+
+	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,
+							discover->user_data);
+	g_free(discover);
+	session->discover = NULL;
+}
+
+static void release_stream(struct avdtp_stream *stream, struct avdtp *session)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	if (sep->cfm && sep->cfm->abort &&
+				(sep->state != AVDTP_STATE_ABORTING ||
+							stream->abort_int))
+		sep->cfm->abort(session, sep, stream, NULL, sep->user_data);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+}
+
+static void sep_free(gpointer data)
+{
+	struct avdtp_remote_sep *sep = data;
+
+	g_slist_free_full(sep->caps, g_free);
+	g_free(sep);
+}
+
+static void avdtp_free(void *data)
+{
+	struct avdtp *session = data;
+
+	DBG("%p", session);
+
+	g_slist_free_full(session->streams, stream_free);
+
+	if (session->io) {
+		g_io_channel_shutdown(session->io, FALSE, NULL);
+		g_io_channel_unref(session->io);
+	}
+
+	if (session->io_id) {
+		g_source_remove(session->io_id);
+		session->io_id = 0;
+	}
+
+	if (session->req)
+		pending_req_free(session->req);
+
+	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_free(session->buf);
+
+	g_free(session);
+}
+
+static 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);
+
+	if (session->ref > 0)
+		return;
+
+	avdtp_free(session);
+}
+
+void avdtp_unref(struct avdtp *session)
+{
+	if (!session)
+		return;
+
+	session->ref--;
+
+	DBG("%p: ref=%d", session, session->ref);
+
+	if (session->ref > 0)
+		return;
+
+	finalize_discovery(session, ECONNABORTED);
+
+	avdtp_free(session);
+}
+
+struct avdtp *avdtp_ref(struct avdtp *session)
+{
+	session->ref++;
+
+	DBG("%p: ref=%d", session, session->ref);
+
+	return session;
+}
+
+static struct avdtp_local_sep *find_local_sep_by_seid(uint8_t seid)
+{
+	GSList *l;
+
+	for (l = lseps; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_local_sep *sep = l->data;
+
+		if (sep->info.seid == seid)
+			return sep;
+	}
+
+	return NULL;
+}
+
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+						struct avdtp_local_sep *lsep)
+{
+	GSList *l;
+
+	if (lsep->info.inuse)
+		return NULL;
+
+	for (l = session->seps; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_remote_sep *sep = l->data;
+		struct avdtp_service_capability *cap;
+		struct avdtp_media_codec_capability *codec_data;
+
+		/* Type must be different: source <-> sink */
+		if (sep->type == lsep->info.type)
+			continue;
+
+		if (sep->media_type != lsep->info.media_type)
+			continue;
+
+		if (!sep->codec)
+			continue;
+
+		cap = sep->codec;
+		codec_data = (void *) cap->data;
+
+		if (codec_data->media_codec_type != lsep->codec)
+			continue;
+
+		if (sep->stream == NULL)
+			return sep;
+	}
+
+	return NULL;
+}
+
+static GSList *caps_to_list(uint8_t *data, int size,
+				struct avdtp_service_capability **codec,
+				gboolean *delay_reporting)
+{
+	GSList *caps;
+	int processed;
+
+	if (delay_reporting)
+		*delay_reporting = FALSE;
+
+	for (processed = 0, caps = NULL; processed + 2 <= size;) {
+		struct avdtp_service_capability *cap;
+		uint8_t length, category;
+
+		category = data[0];
+		length = data[1];
+
+		if (processed + 2 + length > size) {
+			error("Invalid capability data in getcap resp");
+			break;
+		}
+
+		cap = g_malloc(sizeof(struct avdtp_service_capability) +
+					length);
+		memcpy(cap, data, 2 + length);
+
+		processed += 2 + length;
+		data += 2 + length;
+
+		caps = g_slist_append(caps, cap);
+
+		if (category == AVDTP_MEDIA_CODEC &&
+				length >=
+				sizeof(struct avdtp_media_codec_capability))
+			*codec = cap;
+		else if (category == AVDTP_DELAY_REPORTING && delay_reporting)
+			*delay_reporting = TRUE;
+	}
+
+	return caps;
+}
+
+static gboolean avdtp_unknown_cmd(struct avdtp *session, uint8_t transaction,
+							uint8_t signal_id)
+{
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_GEN_REJECT,
+							signal_id, NULL, 0);
+}
+
+static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction,
+							void *buf, int size)
+{
+	GSList *l;
+	unsigned int rsp_size, sep_count, i;
+	struct seid_info *seps;
+	gboolean ret;
+
+	sep_count = g_slist_length(lseps);
+
+	if (sep_count == 0) {
+		uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND;
+		return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+					AVDTP_DISCOVER, &err, sizeof(err));
+	}
+
+	rsp_size = sep_count * sizeof(struct seid_info);
+
+	seps = g_new0(struct seid_info, sep_count);
+
+	for (l = lseps, i = 0; l != NULL; l = l->next, i++) {
+		struct avdtp_local_sep *sep = l->data;
+
+		memcpy(&seps[i], &sep->info, sizeof(struct seid_info));
+	}
+
+	ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+				AVDTP_DISCOVER, seps, rsp_size);
+	g_free(seps);
+
+	return ret;
+}
+
+static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction,
+					struct seid_req *req, unsigned int size,
+					gboolean get_all)
+{
+	GSList *l, *caps;
+	struct avdtp_local_sep *sep = NULL;
+	unsigned int rsp_size;
+	uint8_t err, buf[1024], *ptr = buf;
+	uint8_t cmd;
+
+	cmd = get_all ? AVDTP_GET_ALL_CAPABILITIES : AVDTP_GET_CAPABILITIES;
+
+	if (size < sizeof(struct seid_req)) {
+		err = AVDTP_BAD_LENGTH;
+		goto failed;
+	}
+
+	sep = find_local_sep_by_seid(req->acp_seid);
+	if (!sep) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	if (!sep->ind->get_capability(session, sep, get_all, &caps,
+							&err, sep->user_data))
+		goto failed;
+
+	for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_service_capability *cap = l->data;
+
+		if (rsp_size + cap->length + 2 > sizeof(buf))
+			break;
+
+		memcpy(ptr, cap, cap->length + 2);
+		rsp_size += cap->length + 2;
+		ptr += cap->length + 2;
+
+		g_free(cap);
+	}
+
+	g_slist_free(caps);
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, cmd,
+								buf, rsp_size);
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, cmd,
+							&err, sizeof(err));
+}
+
+static void setconf_cb(struct avdtp *session, struct avdtp_stream *stream,
+						struct avdtp_error *err)
+{
+	struct conf_rej rej;
+	struct avdtp_local_sep *sep;
+
+	if (err != NULL) {
+		rej.error = AVDTP_UNSUPPORTED_CONFIGURATION;
+		rej.category = err->err.error_code;
+		avdtp_send(session, session->in.transaction,
+				AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION,
+				&rej, sizeof(rej));
+		return;
+	}
+
+	if (!avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_ACCEPT,
+					AVDTP_SET_CONFIGURATION, NULL, 0)) {
+		stream_free(stream);
+		return;
+	}
+
+	sep = stream->lsep;
+	sep->stream = stream;
+	sep->info.inuse = 1;
+	session->streams = g_slist_append(session->streams, stream);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+}
+
+static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction,
+				struct setconf_req *req, unsigned int size)
+{
+	struct conf_rej rej;
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	uint8_t err, category = 0x00;
+	GSList *l;
+
+	if (size < sizeof(struct setconf_req)) {
+		error("Too short getcap request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(req->acp_seid);
+	if (!sep) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	if (sep->stream) {
+		err = AVDTP_SEP_IN_USE;
+		goto failed;
+	}
+
+	stream = g_new0(struct avdtp_stream, 1);
+	stream->session = session;
+	stream->lsep = sep;
+	stream->rseid = req->int_seid;
+	stream->caps = caps_to_list(req->caps,
+					size - sizeof(struct setconf_req),
+					&stream->codec,
+					&stream->delay_reporting);
+
+	/* 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) {
+			err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT;
+			goto failed_stream;
+		}
+	}
+
+	if (stream->delay_reporting && session->version < 0x0103)
+		session->version = 0x0103;
+
+	if (sep->ind && sep->ind->set_configuration) {
+		if (!sep->ind->set_configuration(session, sep, stream,
+							stream->caps,
+							setconf_cb,
+							sep->user_data)) {
+			err = AVDTP_UNSUPPORTED_CONFIGURATION;
+			category = 0x00;
+			goto failed_stream;
+		}
+	} else {
+		if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+					AVDTP_SET_CONFIGURATION, NULL, 0)) {
+			stream_free(stream);
+			return FALSE;
+		}
+
+		sep->stream = stream;
+		sep->info.inuse = 1;
+		session->streams = g_slist_append(session->streams, stream);
+
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+	}
+
+	return TRUE;
+
+failed_stream:
+	stream_free(stream);
+failed:
+	rej.error = err;
+	rej.category = category;
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_SET_CONFIGURATION, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction,
+					struct seid_req *req, int size)
+{
+	GSList *l;
+	struct avdtp_local_sep *sep = NULL;
+	int rsp_size;
+	uint8_t err;
+	uint8_t buf[1024];
+	uint8_t *ptr = buf;
+
+	if (size < (int) sizeof(struct seid_req)) {
+		error("Too short getconf request");
+		return FALSE;
+	}
+
+	memset(buf, 0, sizeof(buf));
+
+	sep = find_local_sep_by_seid(req->acp_seid);
+	if (!sep) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+	if (!sep->stream || !sep->stream->caps) {
+		err = AVDTP_UNSUPPORTED_CONFIGURATION;
+		goto failed;
+	}
+
+	for (l = sep->stream->caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+		struct avdtp_service_capability *cap = l->data;
+
+		if (rsp_size + cap->length + 2 > (int) sizeof(buf))
+			break;
+
+		memcpy(ptr, cap, cap->length + 2);
+		rsp_size += cap->length + 2;
+		ptr += cap->length + 2;
+	}
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+				AVDTP_GET_CONFIGURATION, buf, rsp_size);
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_GET_CONFIGURATION, &err, sizeof(err));
+}
+
+static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction,
+					struct seid_req *req, int size)
+{
+	struct conf_rej rej;
+
+	rej.error = AVDTP_NOT_SUPPORTED_COMMAND;
+	rej.category = 0x00;
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+					AVDTP_RECONFIGURE, &rej, sizeof(rej));
+}
+
+static void check_seid_collision(struct pending_req *req, uint8_t id)
+{
+	struct seid_req *seid = req->data;
+
+	if (seid->acp_seid == id)
+		req->collided = TRUE;
+}
+
+static void check_start_collision(struct pending_req *req, uint8_t id)
+{
+	struct start_req *start = req->data;
+	struct seid *seid = &start->first_seid;
+	int count = 1 + req->data_size - sizeof(struct start_req);
+	int i;
+
+	for (i = 0; i < count; i++, seid++) {
+		if (seid->seid == id) {
+			req->collided = TRUE;
+			return;
+		}
+	}
+}
+
+static void check_suspend_collision(struct pending_req *req, uint8_t id)
+{
+	struct suspend_req *suspend = req->data;
+	struct seid *seid = &suspend->first_seid;
+	int count = 1 + req->data_size - sizeof(struct suspend_req);
+	int i;
+
+	for (i = 0; i < count; i++, seid++) {
+		if (seid->seid == id) {
+			req->collided = TRUE;
+			return;
+		}
+	}
+}
+
+static void avdtp_check_collision(struct avdtp *session, uint8_t cmd,
+					struct avdtp_stream *stream)
+{
+	struct pending_req *req = session->req;
+
+	if (req == NULL || (req->signal_id != cmd && cmd != AVDTP_ABORT))
+		return;
+
+	if (cmd == AVDTP_ABORT)
+		cmd = req->signal_id;
+
+	switch (cmd) {
+	case AVDTP_OPEN:
+	case AVDTP_CLOSE:
+		check_seid_collision(req, stream->rseid);
+		break;
+	case AVDTP_START:
+		check_start_collision(req, stream->rseid);
+		break;
+	case AVDTP_SUSPEND:
+		check_suspend_collision(req, stream->rseid);
+		break;
+	}
+}
+
+static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction,
+				struct seid_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	uint8_t err;
+
+	if (size < sizeof(struct seid_req)) {
+		error("Too short abort request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(req->acp_seid);
+	if (!sep) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	if (sep->state != AVDTP_STATE_CONFIGURED) {
+		err = AVDTP_BAD_STATE;
+		goto failed;
+	}
+
+	stream = sep->stream;
+
+	if (sep->ind && sep->ind->open) {
+		if (!sep->ind->open(session, sep, stream, &err,
+					sep->user_data))
+			goto failed;
+	}
+
+	avdtp_check_collision(session, AVDTP_OPEN, stream);
+
+	if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_OPEN, NULL, 0))
+		return FALSE;
+
+	stream->open_acp = TRUE;
+	session->pending_open = stream;
+	stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+						stream_open_timeout,
+						stream);
+
+	return TRUE;
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_OPEN, &err, sizeof(err));
+}
+
+static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction,
+				struct start_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	struct stream_rej rej;
+	struct seid *seid;
+	uint8_t err, failed_seid;
+	int seid_count, i;
+
+	if (size < sizeof(struct start_req)) {
+		error("Too short start request");
+		return FALSE;
+	}
+
+	seid_count = 1 + size - sizeof(struct start_req);
+
+	seid = &req->first_seid;
+
+	for (i = 0; i < seid_count; i++, seid++) {
+		failed_seid = seid->seid;
+
+		sep = find_local_sep_by_seid(req->first_seid.seid);
+		if (!sep || !sep->stream) {
+			err = AVDTP_BAD_ACP_SEID;
+			goto failed;
+		}
+
+		stream = sep->stream;
+
+		/* Also reject start cmd if state is not open */
+		if (sep->state != AVDTP_STATE_OPEN) {
+			err = AVDTP_BAD_STATE;
+			goto failed;
+		}
+		stream->starting = TRUE;
+
+		if (sep->ind && sep->ind->start) {
+			if (!sep->ind->start(session, sep, stream, &err,
+						sep->user_data))
+				goto failed;
+		}
+
+		avdtp_check_collision(session, AVDTP_START, stream);
+
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
+	}
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_START, NULL, 0);
+
+failed:
+	DBG("Rejecting (%d)", err);
+	memset(&rej, 0, sizeof(rej));
+	rej.acp_seid = failed_seid;
+	rej.error = err;
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_START, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_close_cmd(struct avdtp *session, uint8_t transaction,
+				struct seid_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	uint8_t err;
+
+	if (size < sizeof(struct seid_req)) {
+		error("Too short close request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(req->acp_seid);
+	if (!sep || !sep->stream) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	if (sep->state != AVDTP_STATE_OPEN &&
+			sep->state != AVDTP_STATE_STREAMING) {
+		err = AVDTP_BAD_STATE;
+		goto failed;
+	}
+
+	stream = sep->stream;
+
+	if (sep->ind && sep->ind->close) {
+		if (!sep->ind->close(session, sep, stream, &err,
+					sep->user_data))
+			goto failed;
+	}
+
+	avdtp_check_collision(session, AVDTP_CLOSE, stream);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+	if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_CLOSE, NULL, 0))
+		return FALSE;
+
+	stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+					stream_close_timeout,
+					stream);
+
+	return TRUE;
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+					AVDTP_CLOSE, &err, sizeof(err));
+}
+
+static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction,
+				struct suspend_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	struct stream_rej rej;
+	struct seid *seid;
+	uint8_t err, failed_seid;
+	int seid_count, i;
+
+	if (size < sizeof(struct suspend_req)) {
+		error("Too short suspend request");
+		return FALSE;
+	}
+
+	seid_count = 1 + size - sizeof(struct suspend_req);
+
+	seid = &req->first_seid;
+
+	for (i = 0; i < seid_count; i++, seid++) {
+		failed_seid = seid->seid;
+
+		sep = find_local_sep_by_seid(req->first_seid.seid);
+		if (!sep || !sep->stream) {
+			err = AVDTP_BAD_ACP_SEID;
+			goto failed;
+		}
+
+		stream = sep->stream;
+
+		if (sep->state != AVDTP_STATE_STREAMING) {
+			err = AVDTP_BAD_STATE;
+			goto failed;
+		}
+
+		if (sep->ind && sep->ind->suspend) {
+			if (!sep->ind->suspend(session, sep, stream, &err,
+						sep->user_data))
+				goto failed;
+		}
+
+		avdtp_check_collision(session, AVDTP_SUSPEND, stream);
+
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+	}
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_SUSPEND, NULL, 0);
+
+failed:
+	memset(&rej, 0, sizeof(rej));
+	rej.acp_seid = failed_seid;
+	rej.error = err;
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+				AVDTP_SUSPEND, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_abort_cmd(struct avdtp *session, uint8_t transaction,
+				struct seid_req *req, unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	uint8_t err;
+	gboolean ret;
+
+	if (size < sizeof(struct seid_req)) {
+		error("Too short abort request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(req->acp_seid);
+	if (!sep || !sep->stream)
+		return TRUE;
+
+	if (sep->ind && sep->ind->abort)
+		sep->ind->abort(session, sep, sep->stream, &err,
+							sep->user_data);
+
+	avdtp_check_collision(session, AVDTP_ABORT, sep->stream);
+
+	ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_ABORT, NULL, 0);
+	if (ret)
+		avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+	return ret;
+}
+
+static gboolean avdtp_secctl_cmd(struct avdtp *session, uint8_t transaction,
+					struct seid_req *req, int size)
+{
+	return avdtp_unknown_cmd(session, transaction, AVDTP_SECURITY_CONTROL);
+}
+
+static gboolean avdtp_delayreport_cmd(struct avdtp *session,
+					uint8_t transaction,
+					struct delay_req *req,
+					unsigned int size)
+{
+	struct avdtp_local_sep *sep;
+	struct avdtp_stream *stream;
+	uint8_t err;
+
+	if (size < sizeof(struct delay_req)) {
+		error("Too short delay report request");
+		return FALSE;
+	}
+
+	sep = find_local_sep_by_seid(req->acp_seid);
+	if (!sep || !sep->stream) {
+		err = AVDTP_BAD_ACP_SEID;
+		goto failed;
+	}
+
+	stream = sep->stream;
+
+	if (sep->state != AVDTP_STATE_CONFIGURED &&
+					sep->state != AVDTP_STATE_STREAMING) {
+		err = AVDTP_BAD_STATE;
+		goto failed;
+	}
+
+	stream->delay = ntohs(req->delay);
+
+	if (sep->ind && sep->ind->delayreport) {
+		if (!sep->ind->delayreport(session, sep, stream->rseid,
+						stream->delay, &err,
+						sep->user_data))
+			goto failed;
+	}
+
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+						AVDTP_DELAY_REPORT, NULL, 0);
+
+failed:
+	return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+					AVDTP_DELAY_REPORT, &err, sizeof(err));
+}
+
+static gboolean avdtp_parse_cmd(struct avdtp *session, uint8_t transaction,
+				uint8_t signal_id, void *buf, int size)
+{
+	switch (signal_id) {
+	case AVDTP_DISCOVER:
+		DBG("Received DISCOVER_CMD");
+		return avdtp_discover_cmd(session, transaction, buf, size);
+	case AVDTP_GET_CAPABILITIES:
+		DBG("Received  GET_CAPABILITIES_CMD");
+		return avdtp_getcap_cmd(session, transaction, buf, size,
+									FALSE);
+	case AVDTP_GET_ALL_CAPABILITIES:
+		DBG("Received  GET_ALL_CAPABILITIES_CMD");
+		return avdtp_getcap_cmd(session, transaction, buf, size, TRUE);
+	case AVDTP_SET_CONFIGURATION:
+		DBG("Received SET_CONFIGURATION_CMD");
+		return avdtp_setconf_cmd(session, transaction, buf, size);
+	case AVDTP_GET_CONFIGURATION:
+		DBG("Received GET_CONFIGURATION_CMD");
+		return avdtp_getconf_cmd(session, transaction, buf, size);
+	case AVDTP_RECONFIGURE:
+		DBG("Received RECONFIGURE_CMD");
+		return avdtp_reconf_cmd(session, transaction, buf, size);
+	case AVDTP_OPEN:
+		DBG("Received OPEN_CMD");
+		return avdtp_open_cmd(session, transaction, buf, size);
+	case AVDTP_START:
+		DBG("Received START_CMD");
+		return avdtp_start_cmd(session, transaction, buf, size);
+	case AVDTP_CLOSE:
+		DBG("Received CLOSE_CMD");
+		return avdtp_close_cmd(session, transaction, buf, size);
+	case AVDTP_SUSPEND:
+		DBG("Received SUSPEND_CMD");
+		return avdtp_suspend_cmd(session, transaction, buf, size);
+	case AVDTP_ABORT:
+		DBG("Received ABORT_CMD");
+		return avdtp_abort_cmd(session, transaction, buf, size);
+	case AVDTP_SECURITY_CONTROL:
+		DBG("Received SECURITY_CONTROL_CMD");
+		return avdtp_secctl_cmd(session, transaction, buf, size);
+	case AVDTP_DELAY_REPORT:
+		DBG("Received DELAY_REPORT_CMD");
+		return avdtp_delayreport_cmd(session, transaction, buf, size);
+	default:
+		DBG("Received unknown request id %u", signal_id);
+		return avdtp_unknown_cmd(session, transaction, signal_id);
+	}
+}
+
+enum avdtp_parse_result { PARSE_ERROR, PARSE_FRAGMENT, PARSE_SUCCESS };
+
+static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session,
+							void *buf, size_t size)
+{
+	struct avdtp_common_header *header = buf;
+	struct avdtp_single_header *single = (void *) session->buf;
+	struct avdtp_start_header *start = (void *) session->buf;
+	void *payload;
+	gsize payload_size;
+
+	switch (header->packet_type) {
+	case AVDTP_PKT_TYPE_SINGLE:
+		if (size < sizeof(*single)) {
+			error("Received too small single packet (%zu bytes)", size);
+			return PARSE_ERROR;
+		}
+		if (session->in.active) {
+			error("SINGLE: Invalid AVDTP packet fragmentation");
+			return PARSE_ERROR;
+		}
+
+		payload = session->buf + sizeof(*single);
+		payload_size = size - sizeof(*single);
+
+		session->in.active = TRUE;
+		session->in.data_size = 0;
+		session->in.no_of_packets = 1;
+		session->in.transaction = header->transaction;
+		session->in.message_type = header->message_type;
+		session->in.signal_id = single->signal_id;
+
+		break;
+	case AVDTP_PKT_TYPE_START:
+		if (size < sizeof(*start)) {
+			error("Received too small start packet (%zu bytes)", size);
+			return PARSE_ERROR;
+		}
+		if (session->in.active) {
+			error("START: Invalid AVDTP packet fragmentation");
+			return PARSE_ERROR;
+		}
+
+		session->in.active = TRUE;
+		session->in.data_size = 0;
+		session->in.transaction = header->transaction;
+		session->in.message_type = header->message_type;
+		session->in.no_of_packets = start->no_of_packets;
+		session->in.signal_id = start->signal_id;
+
+		payload = session->buf + sizeof(*start);
+		payload_size = size - sizeof(*start);
+
+		break;
+	case AVDTP_PKT_TYPE_CONTINUE:
+		if (size < sizeof(struct avdtp_continue_header)) {
+			error("Received too small continue packet (%zu bytes)",
+									size);
+			return PARSE_ERROR;
+		}
+		if (!session->in.active) {
+			error("CONTINUE: Invalid AVDTP packet fragmentation");
+			return PARSE_ERROR;
+		}
+		if (session->in.transaction != header->transaction) {
+			error("Continue transaction id doesn't match");
+			return PARSE_ERROR;
+		}
+		if (session->in.no_of_packets <= 1) {
+			error("Too few continue packets");
+			return PARSE_ERROR;
+		}
+
+		payload = session->buf + sizeof(struct avdtp_continue_header);
+		payload_size = size - sizeof(struct avdtp_continue_header);
+
+		break;
+	case AVDTP_PKT_TYPE_END:
+		if (size < sizeof(struct avdtp_continue_header)) {
+			error("Received too small end packet (%zu bytes)", size);
+			return PARSE_ERROR;
+		}
+		if (!session->in.active) {
+			error("END: Invalid AVDTP packet fragmentation");
+			return PARSE_ERROR;
+		}
+		if (session->in.transaction != header->transaction) {
+			error("End transaction id doesn't match");
+			return PARSE_ERROR;
+		}
+		if (session->in.no_of_packets > 1) {
+			error("Got an end packet too early");
+			return PARSE_ERROR;
+		}
+
+		payload = session->buf + sizeof(struct avdtp_continue_header);
+		payload_size = size - sizeof(struct avdtp_continue_header);
+
+		break;
+	default:
+		error("Invalid AVDTP packet type 0x%02X", header->packet_type);
+		return PARSE_ERROR;
+	}
+
+	if (session->in.data_size + payload_size >
+					sizeof(session->in.buf)) {
+		error("Not enough incoming buffer space!");
+		return PARSE_ERROR;
+	}
+
+	memcpy(session->in.buf + session->in.data_size, payload, payload_size);
+	session->in.data_size += payload_size;
+
+	if (session->in.no_of_packets > 1) {
+		session->in.no_of_packets--;
+		DBG("Received AVDTP fragment. %d to go",
+						session->in.no_of_packets);
+		return PARSE_FRAGMENT;
+	}
+
+	session->in.active = FALSE;
+
+	return PARSE_SUCCESS;
+}
+
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
+				gpointer data)
+{
+	struct avdtp *session = data;
+	struct avdtp_common_header *header;
+	ssize_t size;
+	int fd;
+
+	DBG("");
+
+	if (cond & G_IO_NVAL)
+		return FALSE;
+
+	header = (void *) session->buf;
+
+	if (cond & (G_IO_HUP | G_IO_ERR))
+		goto failed;
+
+	fd = g_io_channel_unix_get_fd(chan);
+	size = read(fd, session->buf, session->imtu);
+	if (size < 0) {
+		error("IO Channel read error");
+		goto failed;
+	}
+
+	if ((size_t) size < sizeof(struct avdtp_common_header)) {
+		error("Received too small packet (%zu bytes)", size);
+		goto failed;
+	}
+
+	switch (avdtp_parse_data(session, session->buf, size)) {
+	case PARSE_ERROR:
+		goto failed;
+	case PARSE_FRAGMENT:
+		return TRUE;
+	case PARSE_SUCCESS:
+		break;
+	}
+
+	if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) {
+		if (!avdtp_parse_cmd(session, session->in.transaction,
+					session->in.signal_id,
+					session->in.buf,
+					session->in.data_size)) {
+			error("Unable to handle command. Disconnecting");
+			goto failed;
+		}
+
+		if (session->req && session->req->collided) {
+			DBG("Collision detected");
+			goto next;
+		}
+
+		return TRUE;
+	}
+
+	if (session->req == NULL) {
+		error("No pending request, ignoring message");
+		return TRUE;
+	}
+
+	if (header->transaction != session->req->transaction) {
+		error("Transaction label doesn't match");
+		return TRUE;
+	}
+
+	if (session->in.signal_id != session->req->signal_id) {
+		error("Response signal doesn't match");
+		return TRUE;
+	}
+
+	g_source_remove(session->req->timeout);
+	session->req->timeout = 0;
+
+	switch (header->message_type) {
+	case AVDTP_MSG_TYPE_ACCEPT:
+		if (!avdtp_parse_resp(session, session->req->stream,
+						session->in.transaction,
+						session->in.signal_id,
+						session->in.buf,
+						session->in.data_size)) {
+			error("Unable to parse accept response");
+			goto failed;
+		}
+		break;
+	case AVDTP_MSG_TYPE_REJECT:
+		if (!avdtp_parse_rej(session, session->req->stream,
+						session->in.transaction,
+						session->in.signal_id,
+						session->in.buf,
+						session->in.data_size)) {
+			error("Unable to parse reject response");
+			goto failed;
+		}
+		break;
+	case AVDTP_MSG_TYPE_GEN_REJECT:
+		error("Received a General Reject message");
+		break;
+	default:
+		error("Unknown message type 0x%02X", header->message_type);
+		break;
+	}
+
+next:
+	pending_req_free(session->req);
+	session->req = NULL;
+
+	process_queue(session);
+
+	return TRUE;
+
+failed:
+	connection_lost(session, EIO);
+
+	return FALSE;
+}
+
+struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version)
+{
+	struct avdtp *session;
+	GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+	session = g_new0(struct avdtp, 1);
+	session->io = g_io_channel_unix_new(fd);
+	session->version = version;
+	session->imtu = imtu;
+	session->omtu = omtu;
+	session->buf = g_malloc0(MAX(session->imtu, session->omtu));
+
+	/* 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(session->io, G_PRIORITY_LOW, cond,
+						(GIOFunc) session_cb, session,
+						NULL);
+
+	return avdtp_ref(session);
+}
+
+static void queue_request(struct avdtp *session, struct pending_req *req,
+			gboolean priority)
+{
+	if (priority)
+		session->prio_queue = g_slist_append(session->prio_queue, req);
+	else
+		session->req_queue = g_slist_append(session->req_queue, req);
+}
+
+static uint8_t req_get_seid(struct pending_req *req)
+{
+	if (req->signal_id == AVDTP_DISCOVER)
+		return 0;
+
+	return ((struct seid_req *) (req->data))->acp_seid;
+}
+
+static int cancel_request(struct avdtp *session, int err)
+{
+	struct pending_req *req;
+	struct seid_req sreq;
+	struct avdtp_local_sep *lsep;
+	struct avdtp_stream *stream;
+	uint8_t seid;
+	struct avdtp_error averr;
+
+	req = session->req;
+	session->req = NULL;
+
+	avdtp_error_init(&averr, AVDTP_ERRNO, err);
+
+	seid = req_get_seid(req);
+	if (seid)
+		stream = find_stream_by_rseid(session, seid);
+	else
+		stream = NULL;
+
+	if (stream) {
+		stream->abort_int = TRUE;
+		lsep = stream->lsep;
+	} else
+		lsep = NULL;
+
+	switch (req->signal_id) {
+	case AVDTP_RECONFIGURE:
+		error("Reconfigure: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+			lsep->cfm->reconfigure(session, lsep, stream, &averr,
+						lsep->user_data);
+		break;
+	case AVDTP_OPEN:
+		error("Open: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->open)
+			lsep->cfm->open(session, lsep, stream, &averr,
+					lsep->user_data);
+		break;
+	case AVDTP_START:
+		error("Start: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->start) {
+			lsep->cfm->start(session, lsep, stream, &averr,
+						lsep->user_data);
+			if (stream)
+				stream->starting = FALSE;
+		}
+		break;
+	case AVDTP_SUSPEND:
+		error("Suspend: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->suspend)
+			lsep->cfm->suspend(session, lsep, stream, &averr,
+						lsep->user_data);
+		break;
+	case AVDTP_CLOSE:
+		error("Close: %s (%d)", strerror(err), err);
+		if (lsep && lsep->cfm && lsep->cfm->close) {
+			lsep->cfm->close(session, lsep, stream, &averr,
+						lsep->user_data);
+			if (stream)
+				stream->close_int = FALSE;
+		}
+		break;
+	case AVDTP_SET_CONFIGURATION:
+		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);
+		goto failed;
+	case AVDTP_DISCOVER:
+		error("Discover: %s (%d)", strerror(err), err);
+		goto failed;
+	case AVDTP_GET_CAPABILITIES:
+		error("GetCapabilities: %s (%d)", strerror(err), err);
+		goto failed;
+	case AVDTP_ABORT:
+		error("Abort: %s (%d)", strerror(err), err);
+		goto failed;
+	}
+
+	if (!stream)
+		goto failed;
+
+	memset(&sreq, 0, sizeof(sreq));
+	sreq.acp_seid = seid;
+
+	err = send_request(session, TRUE, stream, AVDTP_ABORT, &sreq,
+				sizeof(sreq));
+	if (err < 0) {
+		error("Unable to send abort request");
+		goto failed;
+	}
+
+	goto done;
+
+failed:
+	connection_lost(session, err);
+done:
+	pending_req_free(req);
+	return err;
+}
+
+static gboolean request_timeout(gpointer user_data)
+{
+	struct avdtp *session = user_data;
+
+	cancel_request(session, ETIMEDOUT);
+
+	return FALSE;
+}
+
+static int send_req(struct avdtp *session, gboolean priority,
+			struct pending_req *req)
+{
+	static int transaction = 0;
+	int err;
+
+	if (session->req != NULL) {
+		queue_request(session, req, priority);
+		return 0;
+	}
+
+	req->transaction = transaction++;
+	transaction %= 16;
+
+	/* FIXME: Should we retry to send if the buffer
+	was not totally sent or in case of EINTR? */
+	if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,
+				req->signal_id, req->data, req->data_size)) {
+		err = -EIO;
+		goto failed;
+	}
+
+	session->req = req;
+
+	req->timeout = g_timeout_add_seconds(req->signal_id == AVDTP_ABORT ?
+					ABORT_TIMEOUT : REQ_TIMEOUT,
+					request_timeout,
+					session);
+	return 0;
+
+failed:
+	g_free(req->data);
+	g_free(req);
+	return err;
+}
+
+static int send_request(struct avdtp *session, gboolean priority,
+			struct avdtp_stream *stream, uint8_t signal_id,
+			void *buffer, size_t size)
+{
+	struct pending_req *req;
+
+	if (stream && stream->abort_int && signal_id != AVDTP_ABORT) {
+		DBG("Unable to send requests while aborting");
+		return -EINVAL;
+	}
+
+	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;
+
+	return send_req(session, priority, req);
+}
+
+static gboolean avdtp_discover_resp(struct avdtp *session,
+					struct discover_resp *resp, int size)
+{
+	int sep_count, i;
+	uint8_t getcap_cmd;
+	int ret = 0;
+	gboolean getcap_pending = FALSE;
+
+	if (session->version >= 0x0103)
+		getcap_cmd = AVDTP_GET_ALL_CAPABILITIES;
+	else
+		getcap_cmd = AVDTP_GET_CAPABILITIES;
+
+	sep_count = size / sizeof(struct seid_info);
+
+	for (i = 0; i < sep_count; i++) {
+		struct avdtp_remote_sep *sep;
+		struct avdtp_stream *stream;
+		struct seid_req req;
+
+		DBG("seid %d type %d media %d in use %d",
+				resp->seps[i].seid, resp->seps[i].type,
+				resp->seps[i].media_type, resp->seps[i].inuse);
+
+		stream = find_stream_by_rseid(session, resp->seps[i].seid);
+
+		sep = find_remote_sep(session->seps, resp->seps[i].seid);
+		if (!sep) {
+			if (resp->seps[i].inuse && !stream)
+				continue;
+			sep = g_new0(struct avdtp_remote_sep, 1);
+			session->seps = g_slist_append(session->seps, sep);
+		}
+
+		sep->stream = stream;
+		sep->seid = resp->seps[i].seid;
+		sep->type = resp->seps[i].type;
+		sep->media_type = resp->seps[i].media_type;
+
+		memset(&req, 0, sizeof(req));
+		req.acp_seid = sep->seid;
+
+		ret = send_request(session, TRUE, NULL, getcap_cmd,
+							&req, sizeof(req));
+		if (ret < 0)
+			break;
+		getcap_pending = TRUE;
+	}
+
+	if (!getcap_pending)
+		finalize_discovery(session, -ret);
+
+	return TRUE;
+}
+
+static gboolean avdtp_get_capabilities_resp(struct avdtp *session,
+						struct getcap_resp *resp,
+						unsigned int size)
+{
+	struct avdtp_remote_sep *sep;
+	uint8_t seid;
+
+	/* Check for minimum required packet size includes:
+	 *   1. getcap resp header
+	 *   2. media transport capability (2 bytes)
+	 *   3. media codec capability type + length (2 bytes)
+	 *   4. the actual media codec elements
+	 * */
+	if (size < (sizeof(struct getcap_resp) + 4 +
+				sizeof(struct avdtp_media_codec_capability))) {
+		error("Too short getcap resp packet");
+		return FALSE;
+	}
+
+	seid = ((struct seid_req *) session->req->data)->acp_seid;
+
+	sep = find_remote_sep(session->seps, seid);
+
+	DBG("seid %d type %d media %d", sep->seid,
+					sep->type, sep->media_type);
+
+	if (sep->caps) {
+		g_slist_free_full(sep->caps, g_free);
+		sep->caps = NULL;
+		sep->codec = NULL;
+		sep->delay_reporting = FALSE;
+	}
+
+	sep->caps = caps_to_list(resp->caps, size - sizeof(struct getcap_resp),
+					&sep->codec, &sep->delay_reporting);
+
+	return TRUE;
+}
+
+static gboolean avdtp_set_configuration_resp(struct avdtp *session,
+						struct avdtp_stream *stream,
+						struct avdtp_single_header *resp,
+						int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	if (sep->cfm && sep->cfm->set_configuration)
+		sep->cfm->set_configuration(session, sep, stream, NULL,
+						sep->user_data);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+
+	return TRUE;
+}
+
+static gboolean avdtp_reconfigure_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct avdtp_single_header *resp, int size)
+{
+	return TRUE;
+}
+
+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;
+
+	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;
+}
+
+static gboolean avdtp_start_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct seid_rej *resp, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	/* 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;
+}
+
+static gboolean avdtp_close_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct seid_rej *resp, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+	close_stream(stream);
+
+	return TRUE;
+}
+
+static gboolean avdtp_suspend_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					void *data, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+
+	if (sep->cfm && sep->cfm->suspend)
+		sep->cfm->suspend(session, sep, stream, NULL, sep->user_data);
+
+	return TRUE;
+}
+
+static gboolean avdtp_abort_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					struct seid_rej *resp, int size)
+{
+	struct avdtp_local_sep *sep = stream->lsep;
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+	if (sep->cfm && sep->cfm->abort)
+		sep->cfm->abort(session, sep, stream, NULL, sep->user_data);
+
+	avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+
+	return TRUE;
+}
+
+static gboolean avdtp_delay_report_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					void *data, int size)
+{
+	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);
+
+	return TRUE;
+}
+
+static gboolean avdtp_parse_resp(struct avdtp *session,
+					struct avdtp_stream *stream,
+					uint8_t transaction, uint8_t signal_id,
+					void *buf, int size)
+{
+	struct pending_req *next;
+	const char *get_all = "";
+
+	if (session->prio_queue)
+		next = session->prio_queue->data;
+	else if (session->req_queue)
+		next = session->req_queue->data;
+	else
+		next = NULL;
+
+	switch (signal_id) {
+	case AVDTP_DISCOVER:
+		DBG("DISCOVER request succeeded");
+		return avdtp_discover_resp(session, buf, size);
+	case AVDTP_GET_ALL_CAPABILITIES:
+		get_all = "ALL_";
+	case AVDTP_GET_CAPABILITIES:
+		DBG("GET_%sCAPABILITIES request succeeded", get_all);
+		if (!avdtp_get_capabilities_resp(session, buf, size))
+			return FALSE;
+		if (!(next && (next->signal_id == AVDTP_GET_CAPABILITIES ||
+				next->signal_id == AVDTP_GET_ALL_CAPABILITIES)))
+			finalize_discovery(session, 0);
+		return TRUE;
+	}
+
+	/* The remaining commands require an existing stream so bail out
+	 * here if the stream got unexpectedly disconnected */
+	if (!stream) {
+		DBG("AVDTP: stream was closed while waiting for reply");
+		return TRUE;
+	}
+
+	switch (signal_id) {
+	case AVDTP_SET_CONFIGURATION:
+		DBG("SET_CONFIGURATION request succeeded");
+		return avdtp_set_configuration_resp(session, stream,
+								buf, size);
+	case AVDTP_RECONFIGURE:
+		DBG("RECONFIGURE request succeeded");
+		return avdtp_reconfigure_resp(session, stream, buf, size);
+	case AVDTP_OPEN:
+		DBG("OPEN request succeeded");
+		return avdtp_open_resp(session, stream, buf, size);
+	case AVDTP_SUSPEND:
+		DBG("SUSPEND request succeeded");
+		return avdtp_suspend_resp(session, stream, buf, size);
+	case AVDTP_START:
+		DBG("START request succeeded");
+		return avdtp_start_resp(session, stream, buf, size);
+	case AVDTP_CLOSE:
+		DBG("CLOSE request succeeded");
+		return avdtp_close_resp(session, stream, buf, size);
+	case AVDTP_ABORT:
+		DBG("ABORT request succeeded");
+		return avdtp_abort_resp(session, stream, buf, size);
+	case AVDTP_DELAY_REPORT:
+		DBG("DELAY_REPORT request succeeded");
+		return avdtp_delay_report_resp(session, stream, buf, size);
+	}
+
+	error("Unknown signal id in accept response: %u", signal_id);
+	return TRUE;
+}
+
+static gboolean seid_rej_to_err(struct seid_rej *rej, unsigned int size,
+					struct avdtp_error *err)
+{
+	if (size < sizeof(struct seid_rej)) {
+		error("Too small packet for seid_rej");
+		return FALSE;
+	}
+
+	avdtp_error_init(err, 0x00, rej->error);
+
+	return TRUE;
+}
+
+static gboolean conf_rej_to_err(struct conf_rej *rej, unsigned int size,
+				struct avdtp_error *err)
+{
+	if (size < sizeof(struct conf_rej)) {
+		error("Too small packet for conf_rej");
+		return FALSE;
+	}
+
+	avdtp_error_init(err, rej->category, rej->error);
+
+	return TRUE;
+}
+
+static gboolean stream_rej_to_err(struct stream_rej *rej, unsigned int size,
+					struct avdtp_error *err,
+					uint8_t *acp_seid)
+{
+	if (size < sizeof(struct stream_rej)) {
+		error("Too small packet for stream_rej");
+		return FALSE;
+	}
+
+	avdtp_error_init(err, 0x00, rej->error);
+
+	if (acp_seid)
+		*acp_seid = rej->acp_seid;
+
+	return TRUE;
+}
+
+static gboolean avdtp_parse_rej(struct avdtp *session,
+					struct avdtp_stream *stream,
+					uint8_t transaction, uint8_t signal_id,
+					void *buf, int size)
+{
+	struct avdtp_error err;
+	uint8_t acp_seid;
+	struct avdtp_local_sep *sep = stream ? stream->lsep : NULL;
+
+	switch (signal_id) {
+	case AVDTP_DISCOVER:
+		if (!seid_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("DISCOVER request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		return TRUE;
+	case AVDTP_GET_CAPABILITIES:
+	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);
+		return TRUE;
+	case AVDTP_OPEN:
+		if (!seid_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("OPEN request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->open)
+			sep->cfm->open(session, sep, stream, &err,
+					sep->user_data);
+		return TRUE;
+	case AVDTP_SET_CONFIGURATION:
+		if (!conf_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("SET_CONFIGURATION request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->set_configuration)
+			sep->cfm->set_configuration(session, sep, stream,
+							&err, sep->user_data);
+		return TRUE;
+	case AVDTP_RECONFIGURE:
+		if (!conf_rej_to_err(buf, size, &err))
+			return FALSE;
+		error("RECONFIGURE request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->reconfigure)
+			sep->cfm->reconfigure(session, sep, stream, &err,
+						sep->user_data);
+		return TRUE;
+	case AVDTP_START:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("START request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->start) {
+			sep->cfm->start(session, sep, stream, &err,
+					sep->user_data);
+			stream->starting = FALSE;
+		}
+		return TRUE;
+	case AVDTP_SUSPEND:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("SUSPEND request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->suspend)
+			sep->cfm->suspend(session, sep, stream, &err,
+						sep->user_data);
+		return TRUE;
+	case AVDTP_CLOSE:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("CLOSE request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->close) {
+			sep->cfm->close(session, sep, stream, &err,
+					sep->user_data);
+			stream->close_int = FALSE;
+		}
+		return TRUE;
+	case AVDTP_ABORT:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("ABORT request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->abort)
+			sep->cfm->abort(session, sep, stream, &err,
+					sep->user_data);
+		return FALSE;
+	case AVDTP_DELAY_REPORT:
+		if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+			return FALSE;
+		error("DELAY_REPORT request rejected: %s (%d)",
+				avdtp_strerror(&err), err.err.error_code);
+		if (sep && sep->cfm && sep->cfm->delay_report)
+			sep->cfm->delay_report(session, sep, stream, &err,
+							sep->user_data);
+		return TRUE;
+	default:
+		error("Unknown reject response signal id: %u", signal_id);
+		return TRUE;
+	}
+}
+
+struct avdtp_service_capability *avdtp_stream_get_codec(
+						struct avdtp_stream *stream)
+{
+	GSList *l;
+
+	for (l = stream->caps; l; l = l->next) {
+		struct avdtp_service_capability *cap = l->data;
+
+		if (cap->category == AVDTP_MEDIA_CODEC)
+			return cap;
+	}
+
+	return NULL;
+}
+
+static gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,
+					struct avdtp_service_capability *cap)
+{
+	GSList *l;
+	struct avdtp_service_capability *stream_cap;
+
+	for (l = stream->caps; l; l = g_slist_next(l)) {
+		stream_cap = l->data;
+
+		if (stream_cap->category != cap->category ||
+			stream_cap->length != cap->length)
+			continue;
+
+		if (memcmp(stream_cap->data, cap->data, cap->length) == 0)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+					GSList *caps)
+{
+	for (; caps; caps = g_slist_next(caps)) {
+		struct avdtp_service_capability *cap = caps->data;
+
+		if (!avdtp_stream_has_capability(stream, cap))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
+						struct avdtp_stream *stream)
+{
+	GSList *l;
+
+	for (l = stream->session->seps; l; l = l->next) {
+		struct avdtp_remote_sep *sep = l->data;
+
+		if (sep->seid == stream->rseid)
+			return sep;
+	}
+
+	return NULL;
+}
+
+gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd,
+						size_t imtu, size_t omtu)
+{
+	GIOChannel *io;
+
+	if (stream != stream->session->pending_open)
+		return FALSE;
+
+	io = g_io_channel_unix_new(fd);
+
+	handle_transport_connect(stream->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)
+{
+	if (stream->io == NULL)
+		return FALSE;
+
+	if (sock)
+		*sock = g_io_channel_unix_get_fd(stream->io);
+
+	if (omtu)
+		*omtu = stream->omtu;
+
+	if (imtu)
+		*imtu = stream->imtu;
+
+	if (caps)
+		*caps = stream->caps;
+
+	return TRUE;
+}
+
+static int process_queue(struct avdtp *session)
+{
+	GSList **queue, *l;
+	struct pending_req *req;
+
+	if (session->req)
+		return 0;
+
+	if (session->prio_queue)
+		queue = &session->prio_queue;
+	else
+		queue = &session->req_queue;
+
+	if (!*queue)
+		return 0;
+
+	l = *queue;
+	req = l->data;
+
+	*queue = g_slist_remove(*queue, req);
+
+	return send_req(session, FALSE, req);
+}
+
+struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep)
+{
+	return sep->codec;
+}
+
+struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
+							void *data, int length)
+{
+	struct avdtp_service_capability *cap;
+
+	if (category < AVDTP_MEDIA_TRANSPORT || category > AVDTP_DELAY_REPORTING)
+		return NULL;
+
+	cap = g_malloc(sizeof(struct avdtp_service_capability) + length);
+	cap->category = category;
+	cap->length = length;
+	memcpy(cap->data, data, length);
+
+	return cap;
+}
+
+static gboolean process_discover(gpointer data)
+{
+	struct avdtp *session = data;
+
+	session->discover->id = 0;
+
+	finalize_discovery(session, 0);
+
+	return FALSE;
+}
+
+int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
+			void *user_data)
+{
+	int err;
+
+	if (session->discover)
+		return -EBUSY;
+
+	session->discover = g_new0(struct discover_callback, 1);
+
+	if (session->seps) {
+		session->discover->cb = cb;
+		session->discover->user_data = user_data;
+		session->discover->id = g_idle_add(process_discover, session);
+		return 0;
+	}
+
+	err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0);
+	if (err == 0) {
+		session->discover->cb = cb;
+		session->discover->user_data = user_data;
+	}
+
+	return err;
+}
+
+gboolean avdtp_stream_remove_cb(struct avdtp *session,
+				struct avdtp_stream *stream,
+				unsigned int id)
+{
+	GSList *l;
+	struct stream_callback *cb;
+
+	if (!stream)
+		return FALSE;
+
+	for (cb = NULL, l = stream->callbacks; l != NULL; l = l->next) {
+		struct stream_callback *tmp = l->data;
+		if (tmp && tmp->id == id) {
+			cb = tmp;
+			break;
+		}
+	}
+
+	if (!cb)
+		return FALSE;
+
+	stream->callbacks = g_slist_remove(stream->callbacks, cb);
+	g_free(cb);
+
+	return TRUE;
+}
+
+unsigned int avdtp_stream_add_cb(struct avdtp *session,
+					struct avdtp_stream *stream,
+					avdtp_stream_state_cb cb, void *data)
+{
+	struct stream_callback *stream_cb;
+	static unsigned int id = 0;
+
+	stream_cb = g_new(struct stream_callback, 1);
+	stream_cb->cb = cb;
+	stream_cb->user_data = data;
+	stream_cb->id = ++id;
+
+	stream->callbacks = g_slist_append(stream->callbacks, stream_cb);
+
+	return stream_cb->id;
+}
+
+int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct seid_req req;
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	return send_request(session, FALSE, stream, AVDTP_GET_CONFIGURATION,
+							&req, sizeof(req));
+}
+
+static void copy_capabilities(gpointer data, gpointer user_data)
+{
+	struct avdtp_service_capability *src_cap = data;
+	struct avdtp_service_capability *dst_cap;
+	GSList **l = user_data;
+
+	dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data,
+					src_cap->length);
+
+	*l = g_slist_append(*l, dst_cap);
+}
+
+int avdtp_set_configuration(struct avdtp *session,
+				struct avdtp_remote_sep *rsep,
+				struct avdtp_local_sep *lsep,
+				GSList *caps,
+				struct avdtp_stream **stream)
+{
+	struct setconf_req *req;
+	struct avdtp_stream *new_stream;
+	unsigned char *ptr;
+	int err, caps_len;
+	struct avdtp_service_capability *cap;
+	GSList *l;
+
+	if (!(lsep && rsep))
+		return -EINVAL;
+
+	DBG("%p: int_seid=%u, acp_seid=%u", session,
+			lsep->info.seid, rsep->seid);
+
+	new_stream = g_new0(struct avdtp_stream, 1);
+	new_stream->session = session;
+	new_stream->lsep = lsep;
+	new_stream->rseid = rsep->seid;
+
+	if (rsep->delay_reporting && lsep->delay_reporting) {
+		struct avdtp_service_capability *delay_reporting;
+
+		delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+								NULL, 0);
+		caps = g_slist_append(caps, delay_reporting);
+		new_stream->delay_reporting = TRUE;
+	}
+
+	g_slist_foreach(caps, copy_capabilities, &new_stream->caps);
+
+	/* Calculate total size of request */
+	for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) {
+		cap = l->data;
+		caps_len += cap->length + 2;
+	}
+
+	req = g_malloc0(sizeof(struct setconf_req) + caps_len);
+
+	req->int_seid = lsep->info.seid;
+	req->acp_seid = rsep->seid;
+
+	/* Copy the capabilities into the request */
+	for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) {
+		cap = l->data;
+		memcpy(ptr, cap, cap->length + 2);
+		ptr += cap->length + 2;
+	}
+
+	err = send_request(session, FALSE, new_stream,
+				AVDTP_SET_CONFIGURATION, req,
+				sizeof(struct setconf_req) + caps_len);
+	if (err < 0)
+		stream_free(new_stream);
+	else {
+		lsep->info.inuse = 1;
+		lsep->stream = new_stream;
+		rsep->stream = new_stream;
+		session->streams = g_slist_append(session->streams, new_stream);
+		if (stream)
+			*stream = new_stream;
+	}
+
+	g_free(req);
+
+	return err;
+}
+
+int avdtp_open(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct seid_req req;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state > AVDTP_STATE_CONFIGURED)
+		return -EINVAL;
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	return send_request(session, FALSE, stream, AVDTP_OPEN,
+							&req, sizeof(req));
+}
+
+static gboolean start_timeout(gpointer user_data)
+{
+	struct avdtp_stream *stream = user_data;
+	struct avdtp *session = stream->session;
+
+	stream->open_acp = FALSE;
+
+	if (avdtp_start(session, stream) < 0)
+		error("wait_timeout: avdtp_start failed");
+
+	stream->start_timer = 0;
+
+	return FALSE;
+}
+
+int avdtp_start(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct start_req req;
+	int ret;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state != AVDTP_STATE_OPEN)
+		return -EINVAL;
+
+	/* Recommendation 12:
+	 *  If the RD has configured and opened a stream it is also responsible
+	 *  to start the streaming via GAVDP_START.
+	 */
+	if (stream->open_acp) {
+		/* If timer already active wait it */
+		if (stream->start_timer)
+			return 0;
+
+		stream->start_timer = g_timeout_add_seconds(START_TIMEOUT,
+								start_timeout,
+								stream);
+		return 0;
+	}
+
+	if (stream->close_int == TRUE) {
+		error("avdtp_start: rejecting start since close is initiated");
+		return -EINVAL;
+	}
+
+	if (stream->starting == TRUE) {
+		DBG("stream already started");
+		return -EINPROGRESS;
+	}
+
+	memset(&req, 0, sizeof(req));
+	req.first_seid.seid = stream->rseid;
+
+	ret = send_request(session, FALSE, stream, AVDTP_START,
+							&req, sizeof(req));
+	if (ret == 0)
+		stream->starting = TRUE;
+
+	return ret;
+}
+
+int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
+		gboolean immediate)
+{
+	struct seid_req req;
+	int ret;
+
+	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 (immediate && session->req && stream == session->req->stream)
+		return avdtp_abort(session, stream);
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	ret = send_request(session, FALSE, stream, AVDTP_CLOSE,
+							&req, sizeof(req));
+	if (ret == 0)
+		stream->close_int = TRUE;
+
+	return ret;
+}
+
+int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct seid_req req;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state <= AVDTP_STATE_OPEN || stream->close_int)
+		return -EINVAL;
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	return send_request(session, FALSE, stream, AVDTP_SUSPEND,
+							&req, sizeof(req));
+}
+
+int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream)
+{
+	struct seid_req req;
+	int ret;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state == AVDTP_STATE_ABORTING)
+		return -EINVAL;
+
+	if (session->req && session->req->timeout > 0 &&
+						stream == session->req->stream)
+		return cancel_request(session, ECANCELED);
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+
+	ret = send_request(session, TRUE, stream, AVDTP_ABORT,
+							&req, sizeof(req));
+	if (ret == 0)
+		stream->abort_int = TRUE;
+
+	return ret;
+}
+
+int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
+							uint16_t delay)
+{
+	struct delay_req req;
+
+	if (!g_slist_find(session->streams, stream))
+		return -EINVAL;
+
+	if (stream->lsep->state != AVDTP_STATE_CONFIGURED &&
+				stream->lsep->state != AVDTP_STATE_STREAMING)
+		return -EINVAL;
+
+	if (!stream->delay_reporting || session->version < 0x0103)
+		return -EINVAL;
+
+	stream->delay = delay;
+
+	memset(&req, 0, sizeof(req));
+	req.acp_seid = stream->rseid;
+	req.delay = htons(delay);
+
+	return send_request(session, TRUE, stream, AVDTP_DELAY_REPORT,
+							&req, sizeof(req));
+}
+
+struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type,
+						uint8_t codec_type,
+						gboolean delay_reporting,
+						struct avdtp_sep_ind *ind,
+						struct avdtp_sep_cfm *cfm,
+						void *user_data)
+{
+	struct avdtp_local_sep *sep;
+
+	if (g_slist_length(lseps) > MAX_SEID)
+		return NULL;
+
+	sep = g_new0(struct avdtp_local_sep, 1);
+
+	sep->state = AVDTP_STATE_IDLE;
+	sep->info.seid = g_slist_length(lseps) + 1;
+	sep->info.type = type;
+	sep->info.media_type = media_type;
+	sep->codec = codec_type;
+	sep->ind = ind;
+	sep->cfm = cfm;
+	sep->user_data = user_data;
+	sep->delay_reporting = TRUE;
+
+	DBG("SEP %p registered: type:%d codec:%d seid:%d", sep,
+			sep->info.type, sep->codec, sep->info.seid);
+	lseps = g_slist_append(lseps, sep);
+
+	return sep;
+}
+
+int avdtp_unregister_sep(struct avdtp_local_sep *sep)
+{
+	if (!sep)
+		return -EINVAL;
+
+	lseps = g_slist_remove(lseps, sep);
+
+	if (sep->stream)
+		release_stream(sep->stream, sep->stream->session);
+
+	DBG("SEP %p unregistered: type:%d codec:%d seid:%d", sep,
+			sep->info.type, sep->codec, sep->info.seid);
+
+	g_free(sep);
+
+	return 0;
+}
+
+const char *avdtp_strerror(struct avdtp_error *err)
+{
+	if (err->category == AVDTP_ERRNO)
+		return strerror(err->err.posix_errno);
+
+	switch(err->err.error_code) {
+	case AVDTP_BAD_HEADER_FORMAT:
+		return "Bad Header Format";
+	case AVDTP_BAD_LENGTH:
+		return "Bad Packet Length";
+	case AVDTP_BAD_ACP_SEID:
+		return "Bad Acceptor SEID";
+	case AVDTP_SEP_IN_USE:
+		return "Stream End Point in Use";
+	case AVDTP_SEP_NOT_IN_USE:
+		return "Stream End Point Not in Use";
+	case AVDTP_BAD_SERV_CATEGORY:
+		return "Bad Service Category";
+	case AVDTP_BAD_PAYLOAD_FORMAT:
+		return "Bad Payload format";
+	case AVDTP_NOT_SUPPORTED_COMMAND:
+		return "Command Not Supported";
+	case AVDTP_INVALID_CAPABILITIES:
+		return "Invalid Capabilities";
+	case AVDTP_BAD_RECOVERY_TYPE:
+		return "Bad Recovery Type";
+	case AVDTP_BAD_MEDIA_TRANSPORT_FORMAT:
+		return "Bad Media Transport Format";
+	case AVDTP_BAD_RECOVERY_FORMAT:
+		return "Bad Recovery Format";
+	case AVDTP_BAD_ROHC_FORMAT:
+		return "Bad Header Compression Format";
+	case AVDTP_BAD_CP_FORMAT:
+		return "Bad Content Protection Format";
+	case AVDTP_BAD_MULTIPLEXING_FORMAT:
+		return "Bad Multiplexing Format";
+	case AVDTP_UNSUPPORTED_CONFIGURATION:
+		return "Configuration not supported";
+	case AVDTP_BAD_STATE:
+		return "Bad State";
+	default:
+		return "Unknown error";
+	}
+}
+
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep)
+{
+	return sep->state;
+}
+
+gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream)
+{
+	return g_slist_find(session->streams, stream) ? TRUE : FALSE;
+}
diff --git a/android/avdtp.h b/android/avdtp.h
new file mode 100644
index 0000000..9760875
--- /dev/null
+++ b/android/avdtp.h
@@ -0,0 +1,275 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct avdtp;
+struct avdtp_stream;
+struct avdtp_local_sep;
+struct avdtp_remote_sep;
+struct avdtp_error {
+	uint8_t category;
+	union {
+		uint8_t error_code;
+		int posix_errno;
+	} err;
+};
+
+/* SEP capability categories */
+#define AVDTP_MEDIA_TRANSPORT			0x01
+#define AVDTP_REPORTING				0x02
+#define AVDTP_RECOVERY				0x03
+#define AVDTP_CONTENT_PROTECTION		0x04
+#define AVDTP_HEADER_COMPRESSION		0x05
+#define AVDTP_MULTIPLEXING			0x06
+#define AVDTP_MEDIA_CODEC			0x07
+#define AVDTP_DELAY_REPORTING			0x08
+#define AVDTP_ERRNO				0xff
+
+/* AVDTP error definitions */
+#define AVDTP_BAD_HEADER_FORMAT			0x01
+#define AVDTP_BAD_LENGTH			0x11
+#define AVDTP_BAD_ACP_SEID			0x12
+#define AVDTP_SEP_IN_USE			0x13
+#define AVDTP_SEP_NOT_IN_USE			0x14
+#define AVDTP_BAD_SERV_CATEGORY			0x17
+#define AVDTP_BAD_PAYLOAD_FORMAT		0x18
+#define AVDTP_NOT_SUPPORTED_COMMAND		0x19
+#define AVDTP_INVALID_CAPABILITIES		0x1A
+#define AVDTP_BAD_RECOVERY_TYPE			0x22
+#define AVDTP_BAD_MEDIA_TRANSPORT_FORMAT	0x23
+#define AVDTP_BAD_RECOVERY_FORMAT		0x25
+#define AVDTP_BAD_ROHC_FORMAT			0x26
+#define AVDTP_BAD_CP_FORMAT			0x27
+#define AVDTP_BAD_MULTIPLEXING_FORMAT		0x28
+#define AVDTP_UNSUPPORTED_CONFIGURATION		0x29
+#define AVDTP_BAD_STATE				0x31
+
+/* SEP types definitions */
+#define AVDTP_SEP_TYPE_SOURCE			0x00
+#define AVDTP_SEP_TYPE_SINK			0x01
+
+/* Media types definitions */
+#define AVDTP_MEDIA_TYPE_AUDIO			0x00
+#define AVDTP_MEDIA_TYPE_VIDEO			0x01
+#define AVDTP_MEDIA_TYPE_MULTIMEDIA		0x02
+
+typedef enum {
+	AVDTP_STATE_IDLE,
+	AVDTP_STATE_CONFIGURED,
+	AVDTP_STATE_OPEN,
+	AVDTP_STATE_STREAMING,
+	AVDTP_STATE_CLOSING,
+	AVDTP_STATE_ABORTING,
+} avdtp_state_t;
+
+struct avdtp_service_capability {
+	uint8_t category;
+	uint8_t length;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_media_codec_capability {
+	uint8_t rfa0:4;
+	uint8_t media_type:4;
+	uint8_t media_codec_type;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_media_codec_capability {
+	uint8_t media_type:4;
+	uint8_t rfa0:4;
+	uint8_t media_codec_type;
+	uint8_t data[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream,
+					avdtp_state_t old_state,
+					avdtp_state_t new_state,
+					struct avdtp_error *err,
+					void *user_data);
+
+typedef void (*avdtp_set_configuration_cb) (struct avdtp *session,
+						struct avdtp_stream *stream,
+						struct avdtp_error *err);
+
+/* Callbacks for when a reply is received to a command that we sent */
+struct avdtp_sep_cfm {
+	void (*set_configuration) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					struct avdtp_error *err,
+					void *user_data);
+	void (*get_configuration) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					struct avdtp_error *err,
+					void *user_data);
+	void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data);
+	void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep,
+			struct avdtp_stream *stream, struct avdtp_error *err,
+			void *user_data);
+	void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+	void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+	void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+	void (*reconfigure) (struct avdtp *session,
+				struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+	void (*delay_report) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream,
+				struct avdtp_error *err, void *user_data);
+};
+
+/* Callbacks for indicating when we received a new command. The return value
+ * indicates whether the command should be rejected or accepted */
+struct avdtp_sep_ind {
+	gboolean (*get_capability) (struct avdtp *session,
+					struct avdtp_local_sep *sep,
+					gboolean get_all,
+					GSList **caps, uint8_t *err,
+					void *user_data);
+	gboolean (*set_configuration) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					struct avdtp_stream *stream,
+					GSList *caps,
+					avdtp_set_configuration_cb cb,
+					void *user_data);
+	gboolean (*get_configuration) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					uint8_t *err, void *user_data);
+	gboolean (*open) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	gboolean (*start) (struct avdtp *session, struct avdtp_local_sep *lsep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	gboolean (*suspend) (struct avdtp *session,
+				struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	void (*abort) (struct avdtp *session, struct avdtp_local_sep *sep,
+				struct avdtp_stream *stream, uint8_t *err,
+				void *user_data);
+	gboolean (*reconfigure) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					uint8_t *err, void *user_data);
+	gboolean (*delayreport) (struct avdtp *session,
+					struct avdtp_local_sep *lsep,
+					uint8_t rseid, uint16_t delay,
+					uint8_t *err, void *user_data);
+};
+
+typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps,
+					struct avdtp_error *err, void *user_data);
+
+struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version);
+
+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);
+
+struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep);
+
+int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
+			void *user_data);
+
+gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream);
+
+unsigned int avdtp_stream_add_cb(struct avdtp *session,
+					struct avdtp_stream *stream,
+					avdtp_stream_state_cb cb, void *data);
+gboolean avdtp_stream_remove_cb(struct avdtp *session,
+				struct avdtp_stream *stream,
+				unsigned int id);
+
+gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd,
+						size_t imtu, size_t omtu);
+gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
+					uint16_t *imtu, uint16_t *omtu,
+					GSList **caps);
+struct avdtp_service_capability *avdtp_stream_get_codec(
+						struct avdtp_stream *stream);
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+					GSList *caps);
+struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
+						struct avdtp_stream *stream);
+
+int avdtp_set_configuration(struct avdtp *session,
+				struct avdtp_remote_sep *rsep,
+				struct avdtp_local_sep *lsep,
+				GSList *caps,
+				struct avdtp_stream **stream);
+
+int avdtp_get_configuration(struct avdtp *session,
+				struct avdtp_stream *stream);
+
+int avdtp_open(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_start(struct avdtp *session, struct avdtp_stream *stream);
+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);
+int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
+							uint16_t delay);
+
+struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type,
+						uint8_t codec_type,
+						gboolean delay_reporting,
+						struct avdtp_sep_ind *ind,
+						struct avdtp_sep_cfm *cfm,
+						void *user_data);
+
+/* Find a matching pair of local and remote SEP ID's */
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+						struct avdtp_local_sep *lsep);
+
+int avdtp_unregister_sep(struct avdtp_local_sep *sep);
+
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep);
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id);
+const char *avdtp_strerror(struct avdtp_error *err);
+uint8_t avdtp_error_category(struct avdtp_error *err);
+int avdtp_error_error_code(struct avdtp_error *err);
+int avdtp_error_posix_errno(struct avdtp_error *err);
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH BlueZ 00/19] Initial AVDTP for Android
From: Luiz Augusto von Dentz @ 2013-11-25 13:10 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

The code is based on the current implementation available in
profiles/audio/avdtp.{c,h} but it is transport agnostic and does not
contain any external dependency except glib for IO handling.

Both the signaling and the transport connection has to be handled by
the upper layer, that is why it is not modified in place to avoid
breaking current implemention until this work is considered stable.

Two new function are added:

  - avdtp_new: Creates AVDTP session and attach signaling transport
  - avdtp_stream_set_transport: Set stream transport

Both receives a fd as parameter so in future it should be possible to
replace the IO handling.

This set also includes a initial test set based on the current AVDTP
test specification, once merged more tests will be added as well,
then the intent is to make this used by audio plugin.

Luiz Augusto von Dentz (19):
  android: Add initial AVDTP code
  unit/AVDTP: Add /TP/SIG/SMG/BV-05-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-06-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-07-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-08-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-09-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-10-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-11-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-12-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-15-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-16-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-17-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-18-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-19-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-20-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-21-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-22-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-23-C test
  unit/AVDTP: Add /TP/SIG/SMG/BV-24-C test

 Makefile.am         |    8 +
 android/Makefile.am |    1 +
 android/a2dp.c      |   21 +
 android/avdtp.c     | 3257 +++++++++++++++++++++++++++++++++++++++++++++++++++
 android/avdtp.h     |  276 +++++
 unit/test-avdtp.c   |  616 ++++++++++
 6 files changed, 4179 insertions(+)
 create mode 100644 android/avdtp.c
 create mode 100644 android/avdtp.h
 create mode 100644 unit/test-avdtp.c

-- 
1.8.3.1


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox