2014-01-20 00:31:50

by Szymon Janc

[permalink] [raw]
Subject: [PATCH 1/5] lib: Add flag to double L2CAP IMTU size used for SDP connection

This will allow to workaround Dualshock4 not respecting L2CAP MTU
size while sending SDP response.
---
lib/sdp.c | 22 ++++++++++++++++++++++
lib/sdp_lib.h | 1 +
2 files changed, 23 insertions(+)

diff --git a/lib/sdp.c b/lib/sdp.c
index 886e7cf..4d9a4bb 100644
--- a/lib/sdp.c
+++ b/lib/sdp.c
@@ -4644,6 +4644,25 @@ static int sdp_connect_local(sdp_session_t *session)
return connect(session->sock, (struct sockaddr *) &sa, sizeof(sa));
}

+static int double_l2cap_imtu(int sk)
+{
+ 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 = 2 * l2o.imtu;
+
+ 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 +4697,9 @@ static int sdp_connect_l2cap(const bdaddr_t *src,
setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
}

+ if ((flags & SDP_DOUBLE_IMTU) && double_l2cap_imtu(sk) < 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..56ba2ea 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_DOUBLE_IMTU 0x08

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



2014-01-20 20:00:17

by Szymon Janc

[permalink] [raw]
Subject: Re: [PATCH 1/5] lib: Add flag to double L2CAP IMTU size used for SDP connection

Hi Simon,

On Monday 20 January 2014 12:31:14 [email protected] wrote:

<snip>

> I also noticed a weird log at one point, although only saw this once and
> don't know what triggered it.
> --
> [bluetooth]#
> [CHG] Device 1C:66:6D:07:C3:E0 Class: 0x200404
> [CHG] Device 1C:66:6D:07:C3:E0 Icon: audio-card <--------!!
> [CHG] Device 1C:66:6D:07:C3:E0 Class: 0x002508
> [CHG] Device 1C:66:6D:07:C3:E0 Icon: input-gaming
> --

Yes, I've seen this as well. I suspect that DS4 has some extra features
related to audio (it has jack for headphones) and it enables that for PS4
only, possibly switching to 'gamepad mode' if not connected to PS4. Maybe
related to connecting USB first or sth...

But this is just my suspicion...

--
Szymon K. Janc
[email protected]

2014-01-20 17:31:14

by Simon Wood

[permalink] [raw]
Subject: Re: [PATCH 1/5] lib: Add flag to double L2CAP IMTU size used for SDP connection

> This will allow to workaround Dualshock4 not respecting L2CAP MTU
> size while sending SDP response.

Maybe a little late (I see that there's a V2 of this patch series), but I
can confirm that I was able to get my 'crappy' laptop to connect to the
DS4 and stream the basic joystick data.

The connection was sensitive to technique, ie. it wouldn't 'pair then
connect' or allow pairing initiated by DS4.
--
[liveuser@localhost ~]$ bluetoothctl
[NEW] Controller 00:0F:B3:99:6B:CC localhost [default]
[bluetooth]# power on
[CHG] Controller 00:0F:B3:99:6B:CC Class: 0x00010c
Changing power on succeeded
[CHG] Controller 00:0F:B3:99:6B:CC Powered: yes
[bluetooth]# agent on
Agent registered
[bluetooth]# default-agent
Default agent request successful
[bluetooth]# scan on
Discovery started
[CHG] Controller 00:0F:B3:99:6B:CC Discovering: yes
[NEW] Device 1C:66:6D:07:C3:E0 1C-66-6D-07-C3-E0
[CHG] Device 1C:66:6D:07:C3:E0 LegacyPairing: no
[CHG] Device 1C:66:6D:07:C3:E0 Name: Wireless Controller
[CHG] Device 1C:66:6D:07:C3:E0 Alias: Wireless Controller
[CHG] Device 1C:66:6D:07:C3:E0 LegacyPairing: yes <--------- wait for this!!
[bluetooth]# connect 1C:66:6D:07:C3:E0
Attempting to connect to 1C:66:6D:07:C3:E0
[CHG] Device 1C:66:6D:07:C3:E0 Connected: yes
[CHG] Device 1C:66:6D:07:C3:E0 Modalias: usb:v054Cp05C4d0100
[CHG] Device 1C:66:6D:07:C3:E0 Modalias: usb:v054Cp05C4d0100
[CHG] Device 1C:66:6D:07:C3:E0 UUIDs has unsupported type
Request PIN code
[agent] Enter PIN code: 0000
[CHG] Device 1C:66:6D:07:C3:E0 Paired: yes
Connection successful
[bluetooth]#
--

I also noticed a weird log at one point, although only saw this once and
don't know what triggered it.
--
[bluetooth]#
[CHG] Device 1C:66:6D:07:C3:E0 Class: 0x200404
[CHG] Device 1C:66:6D:07:C3:E0 Icon: audio-card <--------!!
[CHG] Device 1C:66:6D:07:C3:E0 Class: 0x002508
[CHG] Device 1C:66:6D:07:C3:E0 Icon: input-gaming
--

I will try with the V2 patch series.
Simon


Attachments:
crappy_hciconfig.txt (638.00 B)

2014-01-20 00:31:52

by Szymon Janc

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

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 17a8def..d320c54 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.5.3


2014-01-20 00:31:53

by Szymon Janc

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

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

diff --git a/src/device.c b/src/device.c
index 1bd27a1..6ae14b2 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,22 @@ 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 IMTU. This might
+ * results in SDP response being dropped by kernel. Workaround this by
+ * forcing SDP code to use bigger IMTU while connecting. */
+ if (vid == 0x054c && pid == 0x05c4)
+ return SDP_DOUBLE_IMTU;
+
+ return 0;
+}
+
static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
{
struct btd_adapter *adapter = device->adapter;
@@ -3512,9 +3530,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.5.3


2014-01-20 00:31:54

by Szymon Janc

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

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 6ae14b2..43d5876 100644
--- a/src/device.c
+++ b/src/device.c
@@ -3513,6 +3513,14 @@ static uint16_t get_sdp_flags(struct btd_device *device)
if (vid == 0x054c && pid == 0x05c4)
return SDP_DOUBLE_IMTU;

+ 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_DOUBLE_IMTU;
+
return 0;
}

--
1.8.5.3


2014-01-20 00:31:51

by Szymon Janc

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

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.5.3