2014-01-20 11:08:32

by Szymon Janc

[permalink] [raw]
Subject: [PATCH v2 1/5] lib: Add flag to force large MTU size used for SDP connection

From: Szymon Janc <[email protected]>

This will allow to workaround Dualshock4 not respecting L2CAP MTU
size while sending SDP response. Use same L2CAP MTU value base on
RFCOMM.
---
lib/sdp.c | 27 +++++++++++++++++++++++++++
lib/sdp_lib.h | 1 +
2 files changed, 28 insertions(+)

diff --git a/lib/sdp.c b/lib/sdp.c
index 886e7cf..3b26ec3 100644
--- a/lib/sdp.c
+++ b/lib/sdp.c
@@ -67,6 +67,9 @@ static uint128_t bluetooth_base_uuid = {

#define SDP_MAX_ATTR_LEN 65535

+/* match MTU used by RFCOMM */
+#define SDP_LARGE_L2CAP_MTU 1013
+
static sdp_data_t *sdp_copy_seq(sdp_data_t *data);
static int sdp_attr_add_new_with_length(sdp_record_t *rec,
uint16_t attr, uint8_t dtd, const void *value, uint32_t len);
@@ -4644,6 +4647,26 @@ static int sdp_connect_local(sdp_session_t *session)
return connect(session->sock, (struct sockaddr *) &sa, sizeof(sa));
}

+static int set_l2cap_mtu(int sk, uint16_t mtu)
+{
+ struct l2cap_options l2o;
+ socklen_t len;
+
+ memset(&l2o, 0, sizeof(l2o));
+ len = sizeof(l2o);
+
+ if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0)
+ return -1;
+
+ l2o.imtu = mtu;
+ l2o.omtu = mtu;
+
+ if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0)
+ return -1;
+
+ return 0;
+}
+
static int sdp_connect_l2cap(const bdaddr_t *src,
const bdaddr_t *dst, sdp_session_t *session)
{
@@ -4678,6 +4701,10 @@ static int sdp_connect_l2cap(const bdaddr_t *src,
setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
}

+ if ((flags & SDP_LARGE_MTU) &&
+ set_l2cap_mtu(sk, SDP_LARGE_L2CAP_MTU) < 0)
+ return -1;
+
sa.l2_psm = htobs(SDP_PSM);
sa.l2_bdaddr = *dst;

diff --git a/lib/sdp_lib.h b/lib/sdp_lib.h
index 6e1eb91..3ded393 100644
--- a/lib/sdp_lib.h
+++ b/lib/sdp_lib.h
@@ -81,6 +81,7 @@ static inline void sdp_list_foreach(sdp_list_t *list, sdp_list_func_t f, void *u
#define SDP_RETRY_IF_BUSY 0x01
#define SDP_WAIT_ON_CLOSE 0x02
#define SDP_NON_BLOCKING 0x04
+#define SDP_LARGE_MTU 0x08

/*
* a session with an SDP server
--
1.8.3.2



2014-01-20 18:48:31

by Szymon Janc

[permalink] [raw]
Subject: Re: [PATCH v2 1/5] lib: Add flag to force large MTU size used for SDP connection

Hi Simon,

On Monday 20 January 2014 13:28:57 [email protected] wrote:
> > From: Szymon Janc <[email protected]>
> >
> > This will allow to workaround Dualshock4 not respecting L2CAP MTU
> > size while sending SDP response. Use same L2CAP MTU value base on
> > RFCOMM.
>
> I can confirm that the V2 series of patches work with my DS4 and crappy
> laptop. Still require particular 'connect' sequence...

Thanks for testing, and yes this sequence is about dedicated vs general
bonding mentioned in commit message.

>
> I note that I do not need the original '[PATCH 3/3] input: Add DualShock 4
> detection' patch. I am using just this sequence of 5 patches on top of
> 5.13.

This detection is used only when using SSP controller. And only in case when
dedicated bonding is used - when DS4 tries to connect immediately after
pairing is completed, but before bluetoothd was able to retrieve SDP.

--
Szymon K. Janc
[email protected]

2014-01-20 18:28:57

by Simon Wood

[permalink] [raw]
Subject: Re: [PATCH v2 1/5] lib: Add flag to force large MTU size used for SDP connection

> From: Szymon Janc <[email protected]>
>
> This will allow to workaround Dualshock4 not respecting L2CAP MTU
> size while sending SDP response. Use same L2CAP MTU value base on
> RFCOMM.

I can confirm that the V2 series of patches work with my DS4 and crappy
laptop. Still require particular 'connect' sequence...

I note that I do not need the original '[PATCH 3/3] input: Add DualShock 4
detection' patch. I am using just this sequence of 5 patches on top of
5.13.
Simon



2014-01-20 13:20:47

by Johan Hedberg

[permalink] [raw]
Subject: Re: [PATCH v2 1/5] lib: Add flag to force large MTU size used for SDP connection

Hi Szymon,

On Mon, Jan 20, 2014, Szymon Janc wrote:
> This will allow to workaround Dualshock4 not respecting L2CAP MTU
> size while sending SDP response. Use same L2CAP MTU value base on
> RFCOMM.
> ---
> lib/sdp.c | 27 +++++++++++++++++++++++++++
> lib/sdp_lib.h | 1 +
> 2 files changed, 28 insertions(+)

All patches in this set have been applied. Thanks.

Johan

2014-01-20 11:08:35

by Szymon Janc

[permalink] [raw]
Subject: [PATCH v2 4/5] device: Add workaround for Sony Dualshock 4 broken SDP

From: Szymon Janc <[email protected]>

Sony DualShock 4 is not respecting negotiated L2CAP MTU. This might
results in SDP response being dropped by kernel. Workaround this by
forcing SDP code to use bigger MTU while connecting.
---
src/device.c | 25 +++++++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/src/device.c b/src/device.c
index 1bd27a1..76b876e 100644
--- a/src/device.c
+++ b/src/device.c
@@ -118,6 +118,7 @@ struct browse_req {
int search_uuid;
int reconnect_attempt;
guint listener_id;
+ uint16_t sdp_flags;
};

struct included_search {
@@ -2971,7 +2972,8 @@ static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
bt_search_service(btd_adapter_get_address(adapter),
&device->bdaddr, &uuid,
- browse_cb, user_data, NULL, 0);
+ browse_cb, user_data, NULL,
+ req->sdp_flags);
return;
}

@@ -3498,6 +3500,23 @@ done:
return 0;
}

+static uint16_t get_sdp_flags(struct btd_device *device)
+{
+ uint16_t vid, pid;
+
+ vid = btd_device_get_vendor(device);
+ pid = btd_device_get_product(device);
+
+ /* Sony DualShock 4 is not respecting negotiated L2CAP MTU. This might
+ * results in SDP response being dropped by kernel. Workaround this by
+ * forcing SDP code to use bigger MTU while connecting.
+ */
+ if (vid == 0x054c && pid == 0x05c4)
+ return SDP_LARGE_MTU;
+
+ return 0;
+}
+
static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
{
struct btd_adapter *adapter = device->adapter;
@@ -3512,9 +3531,11 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
req->device = device;
sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);

+ req->sdp_flags = get_sdp_flags(device);
+
err = bt_search_service(btd_adapter_get_address(adapter),
&device->bdaddr, &uuid, browse_cb, req, NULL,
- 0);
+ req->sdp_flags);
if (err < 0) {
browse_request_free(req);
return err;
--
1.8.3.2


2014-01-20 11:08:34

by Szymon Janc

[permalink] [raw]
Subject: [PATCH v2 3/5] core: Add flags parameter to bt_search_service

From: Szymon Janc <[email protected]>

This allows to pass custom SDP flags to sdp_connect.
---
android/bluetooth.c | 4 ++--
android/hidhost.c | 4 ++--
android/socket.c | 2 +-
profiles/health/hdp_util.c | 6 +++---
src/device.c | 5 +++--
src/profile.c | 2 +-
src/sdp-client.c | 8 ++++----
src/sdp-client.h | 2 +-
8 files changed, 17 insertions(+), 16 deletions(-)

diff --git a/android/bluetooth.c b/android/bluetooth.c
index 505d5a7..83612b4 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -693,7 +693,7 @@ static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
if (uuid_list[req->search_uuid]) {
sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
bt_search_service(&adapter.bdaddr, &req->bdaddr, &uuid,
- browse_cb, user_data, NULL);
+ browse_cb, user_data, NULL, 0);
return;
}

@@ -729,7 +729,7 @@ static uint8_t browse_remote_sdp(const bdaddr_t *addr)
sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);

if (bt_search_service(&adapter.bdaddr,
- &req->bdaddr, &uuid, browse_cb, req, NULL) < 0) {
+ &req->bdaddr, &uuid, browse_cb, req, NULL , 0) < 0) {
browse_req_free(req);
return HAL_STATUS_FAILED;
}
diff --git a/android/hidhost.c b/android/hidhost.c
index aed9899..d66e863 100644
--- a/android/hidhost.c
+++ b/android/hidhost.c
@@ -751,7 +751,7 @@ static void bt_hid_connect(const void *buf, uint16_t len)

bt_string2uuid(&uuid, HID_UUID);
if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
- hid_sdp_search_cb, dev, NULL) < 0) {
+ hid_sdp_search_cb, dev, NULL, 0) < 0) {
error("Failed to search sdp details");
hid_device_free(dev);
status = HAL_STATUS_FAILED;
@@ -1254,7 +1254,7 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)

bt_string2uuid(&uuid, HID_UUID);
if (bt_search_service(&src, &dev->dst, &uuid,
- hid_sdp_search_cb, dev, NULL) < 0) {
+ hid_sdp_search_cb, dev, NULL, 0) < 0) {
error("failed to search sdp details");
hid_device_free(dev);
return;
diff --git a/android/socket.c b/android/socket.c
index 69b39ee..f662e79 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -1091,7 +1091,7 @@ static uint8_t connect_rfcomm(const bdaddr_t *addr, int chan,
rfsock->profile = get_profile_by_uuid(uuid);

if (bt_search_service(&adapter_addr, &rfsock->dst, &uu,
- sdp_search_cb, rfsock, NULL) < 0) {
+ sdp_search_cb, rfsock, NULL, 0) < 0) {
error("Failed to search SDP records");
goto failed;
}
diff --git a/profiles/health/hdp_util.c b/profiles/health/hdp_util.c
index 7de87a8..b9a09e9 100644
--- a/profiles/health/hdp_util.c
+++ b/profiles/health/hdp_util.c
@@ -864,7 +864,7 @@ gboolean hdp_get_mdep(struct hdp_device *device, struct hdp_application *app,

bt_string2uuid(&uuid, HDP_UUID);
if (bt_search_service(src, dst, &uuid, get_mdep_cb, mdep_data,
- free_mdep_data) < 0) {
+ free_mdep_data, 0) < 0) {
g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
"Can't get remote SDP record");
g_free(mdep_data);
@@ -1092,7 +1092,7 @@ gboolean hdp_establish_mcl(struct hdp_device *device,

bt_string2uuid(&uuid, HDP_UUID);
if (bt_search_service(src, dst, &uuid, search_cb, conn_data,
- destroy_con_mcl_data) < 0) {
+ destroy_con_mcl_data, 0) < 0) {
g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
"Can't get remote SDP record");
g_free(conn_data);
@@ -1161,7 +1161,7 @@ gboolean hdp_get_dcpsm(struct hdp_device *device, hdp_continue_dcpsm_f func,

bt_string2uuid(&uuid, HDP_UUID);
if (bt_search_service(src, dst, &uuid, get_dcpsm_cb, dcpsm_data,
- free_dcpsm_data) < 0) {
+ free_dcpsm_data, 0) < 0) {
g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
"Can't get remote SDP record");
g_free(dcpsm_data);
diff --git a/src/device.c b/src/device.c
index bcc5561..1bd27a1 100644
--- a/src/device.c
+++ b/src/device.c
@@ -2971,7 +2971,7 @@ static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
bt_search_service(btd_adapter_get_address(adapter),
&device->bdaddr, &uuid,
- browse_cb, user_data, NULL);
+ browse_cb, user_data, NULL, 0);
return;
}

@@ -3513,7 +3513,8 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);

err = bt_search_service(btd_adapter_get_address(adapter),
- &device->bdaddr, &uuid, browse_cb, req, NULL);
+ &device->bdaddr, &uuid, browse_cb, req, NULL,
+ 0);
if (err < 0) {
browse_request_free(req);
return err;
diff --git a/src/profile.c b/src/profile.c
index 3c0d27c..e833181 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -1605,7 +1605,7 @@ static int resolve_service(struct ext_io *conn, const bdaddr_t *src,
bt_string2uuid(&uuid, ext->remote_uuid);
sdp_uuid128_to_uuid(&uuid);

- err = bt_search_service(src, dst, &uuid, record_cb, conn, NULL);
+ err = bt_search_service(src, dst, &uuid, record_cb, conn, NULL, 0);
if (err == 0)
conn->resolving = true;

diff --git a/src/sdp-client.c b/src/sdp-client.c
index 0599626..ff06b4d 100644
--- a/src/sdp-client.c
+++ b/src/sdp-client.c
@@ -264,7 +264,7 @@ failed:
static int create_search_context(struct search_context **ctxt,
const bdaddr_t *src,
const bdaddr_t *dst,
- uuid_t *uuid)
+ uuid_t *uuid, uint16_t flags)
{
sdp_session_t *s;
GIOChannel *chan;
@@ -276,7 +276,7 @@ static int create_search_context(struct search_context **ctxt,

s = get_cached_sdp_session(src, dst);
if (!s)
- s = sdp_connect(src, dst, SDP_NON_BLOCKING);
+ s = sdp_connect(src, dst, SDP_NON_BLOCKING | flags);

if (!s)
return -errno;
@@ -311,7 +311,7 @@ static int create_search_context(struct search_context **ctxt,

int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
uuid_t *uuid, bt_callback_t cb, void *user_data,
- bt_destroy_t destroy)
+ bt_destroy_t destroy, uint16_t flags)
{
struct search_context *ctxt = NULL;
int err;
@@ -319,7 +319,7 @@ int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
if (!cb)
return -EINVAL;

- err = create_search_context(&ctxt, src, dst, uuid);
+ err = create_search_context(&ctxt, src, dst, uuid, flags);
if (err < 0)
return err;

diff --git a/src/sdp-client.h b/src/sdp-client.h
index 9191594..9aa5a4d 100644
--- a/src/sdp-client.h
+++ b/src/sdp-client.h
@@ -26,6 +26,6 @@ typedef void (*bt_destroy_t) (gpointer user_data);

int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
uuid_t *uuid, bt_callback_t cb, void *user_data,
- bt_destroy_t destroy);
+ bt_destroy_t destroy, uint16_t flags);
int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst);
void bt_clear_cached_session(const bdaddr_t *src, const bdaddr_t *dst);
--
1.8.3.2


2014-01-20 11:08:36

by Szymon Janc

[permalink] [raw]
Subject: [PATCH v2 5/5] device: Match Dualshock4 with name and class

From: Szymon Janc <[email protected]>

This allows to use DS4 with legacy adapters. This seems to require
general bonding to work correctly i.e. after dedicated bonding
connection is rejected with "Authentication Failure" error.
---
src/device.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/src/device.c b/src/device.c
index 76b876e..8e2a515 100644
--- a/src/device.c
+++ b/src/device.c
@@ -3514,6 +3514,14 @@ static uint16_t get_sdp_flags(struct btd_device *device)
if (vid == 0x054c && pid == 0x05c4)
return SDP_LARGE_MTU;

+ if (btd_adapter_ssp_enabled(device->adapter))
+ return 0;
+
+ /* if no EIR try matching Sony DualShock 4 with name and class */
+ if (!strncmp(device->name, "Wireless Controller", MAX_NAME_LENGTH) &&
+ device->class == 0x2508)
+ return SDP_LARGE_MTU;
+
return 0;
}

--
1.8.3.2


2014-01-20 11:08:33

by Szymon Janc

[permalink] [raw]
Subject: [PATCH v2 2/5] core: Opencode get_sdp_session in sdp-client

From: Szymon Janc <[email protected]>

This is only used once and provides no benefit compared to opencoding.
---
src/sdp-client.c | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)

diff --git a/src/sdp-client.c b/src/sdp-client.c
index 51f3048..0599626 100644
--- a/src/sdp-client.c
+++ b/src/sdp-client.c
@@ -86,17 +86,6 @@ static sdp_session_t *get_cached_sdp_session(const bdaddr_t *src, const bdaddr_t
return NULL;
}

-static sdp_session_t *get_sdp_session(const bdaddr_t *src, const bdaddr_t *dst)
-{
- sdp_session_t *session;
-
- session = get_cached_sdp_session(src, dst);
- if (session)
- return session;
-
- return sdp_connect(src, dst, SDP_NON_BLOCKING);
-}
-
static void cache_sdp_session(bdaddr_t *src, bdaddr_t *dst,
sdp_session_t *session)
{
@@ -285,7 +274,10 @@ static int create_search_context(struct search_context **ctxt,
if (!ctxt)
return -EINVAL;

- s = get_sdp_session(src, dst);
+ s = get_cached_sdp_session(src, dst);
+ if (!s)
+ s = sdp_connect(src, dst, SDP_NON_BLOCKING);
+
if (!s)
return -errno;

--
1.8.3.2