Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH BlueZ 0/1] input: HID gamepad quirk system for broken SDP records
@ 2026-07-22  8:52 Pakrohk
  0 siblings, 0 replies; 3+ messages in thread
From: Pakrohk @ 2026-07-22  8:52 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pakrohk

Some Bluetooth gamepads (like DualShock 4 v2) have incomplete or malformed
HID SDP records, causing BlueZ to fail with -ENOENT. Windows and Android
handle this gracefully; Linux does not.

This patch adds a minimal quirk system that only activates for specifically
matched devices. When extract_hid_record() fails, the dispatcher tries
built-in quirks before giving up. The quirk injects the correct BT HID
report descriptor so the kernel's hid-playstation driver can create an
input device.

The patch includes one quirk (TG170W / DualShock 4 v2) as an example.
The system is modular -- new quirks can be added as separate files in
quirks/.

I previously included an external quirk profile system (JSON + HMAC
signatures) and a crypto extension to bt_crypto. Based on your feedback,
I've stripped those out. The core quirk system works without them.

Questions:
1. Is this approach acceptable for upstream?
2. For external quirk profiles (user-installable via JSON), what crypto
   approach does BlueZ prefer? The existing bt_crypto uses AF_ALG which
   is being deprecated. Should I wait for an upstream decision, use a
   different library, or skip signatures entirely?

Pakrohk (1):
  input: add HID gamepad quirk system for broken SDP records

 profiles/input/quirk.c         |  77 +++++++++++
 profiles/input/quirk.h         |  37 ++++++
 profiles/input/quirks/tg170w.c | 232 +++++++++++++++++++++++++++++++++
 3 files changed, 346 insertions(+)
 create mode 100644 profiles/input/quirk.c
 create mode 100644 profiles/input/quirk.h
 create mode 100644 profiles/input/quirks/tg170w.c

-- 
2.54.0


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH BlueZ 0/1] input: HID gamepad quirk system for broken SDP records
@ 2026-07-22  9:03 Pakrohk
  2026-07-22  9:03 ` [PATCH BlueZ 1/1] input: add " Pakrohk
  0 siblings, 1 reply; 3+ messages in thread
From: Pakrohk @ 2026-07-22  9:03 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pakrohk

Some Bluetooth gamepads (like DualShock 4 v2) have incomplete or malformed
HID SDP records, causing BlueZ to fail with -ENOENT. Windows and Android
handle this gracefully; Linux does not.

This patch adds a minimal quirk system that only activates for specifically
matched devices. When extract_hid_record() fails, the dispatcher tries
built-in quirks before giving up. The quirk injects the correct BT HID
report descriptor so the kernel's hid-playstation driver can create an
input device.

The patch includes one quirk (TG170W / DualShock 4 v2) as an example.
The system is modular -- new quirks can be added as separate files in
quirks/.

I previously included an external quirk profile system (JSON + HMAC
signatures) and a crypto extension to bt_crypto. Based on your feedback,
I've stripped those out. The core quirk system works without them.

Questions:
1. Is this approach acceptable for upstream?
2. For external quirk profiles (user-installable via JSON), what crypto
   approach does BlueZ prefer? The existing bt_crypto uses AF_ALG which
   is being deprecated. Should I wait for an upstream decision, use a
   different library, or skip signatures entirely?

Pakrohk (1):
  input: add HID gamepad quirk system for broken SDP records

 profiles/input/quirk.c         |  77 +++++++++++
 profiles/input/quirk.h         |  37 ++++++
 profiles/input/quirks/tg170w.c | 232 +++++++++++++++++++++++++++++++++
 3 files changed, 346 insertions(+)
 create mode 100644 profiles/input/quirk.c
 create mode 100644 profiles/input/quirk.h
 create mode 100644 profiles/input/quirks/tg170w.c

-- 
2.54.0


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH BlueZ 1/1] input: add HID gamepad quirk system for broken SDP records
  2026-07-22  9:03 [PATCH BlueZ 0/1] input: HID gamepad quirk system for broken SDP records Pakrohk
@ 2026-07-22  9:03 ` Pakrohk
  0 siblings, 0 replies; 3+ messages in thread
From: Pakrohk @ 2026-07-22  9:03 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Pakrohk

Add a modular quirk system for third-party gamepad controllers that
expose incomplete or malformed HID SDP records over Bluetooth.

When BlueZ fails to parse an SDP record (returns -ENOENT), the quirk
system checks for known gamepads and injects a fallback HID report
descriptor so the kernel HID driver can create an input device.

Currently supported:
- Sony DualShock 4 v2 (CUH-ZCT2x) / VID:054c PID:09cc

The quirk system uses multi-factor matching (VID/PID + device name +
SDP provider) to avoid false positives. It does NOT globally weaken
HID parsing - quirks only activate for specifically matched controllers.
---
 profiles/input/quirk.c         |  77 +++++++++++
 profiles/input/quirk.h         |  37 ++++++
 profiles/input/quirks/tg170w.c | 232 +++++++++++++++++++++++++++++++++
 3 files changed, 346 insertions(+)
 create mode 100644 profiles/input/quirk.c
 create mode 100644 profiles/input/quirk.h
 create mode 100644 profiles/input/quirks/tg170w.c

diff --git a/profiles/input/quirk.c b/profiles/input/quirk.c
new file mode 100644
index 0000000..053f45b
--- /dev/null
+++ b/profiles/input/quirk.c
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2026  Gamepad Quirk Support
+ *
+ *  Dispatcher for gamepad HID quirks. When BlueZ fails to parse an SDP
+ *  record for a known gamepad, this layer injects a fallback HID report
+ *  descriptor so the kernel HID driver can create an input device.
+ *
+ *  This does NOT weaken HID parsing globally — quirks only activate for
+ *  specifically matched controllers.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bluetooth/bluetooth.h"
+#include "bluetooth/hidp.h"
+
+#include "src/log.h"
+
+#include "quirk.h"
+
+/* Individual quirk declarations */
+extern struct gamepad_quirk tg170w_quirk;
+
+static struct gamepad_quirk *quirks[] = {
+	&tg170w_quirk,
+	NULL
+};
+
+bool gamepad_quirk_match(struct input_device *idev)
+{
+	int i;
+
+	/* Check built-in quirks first */
+	for (i = 0; quirks[i]; i++) {
+		if (quirks[i]->match(idev))
+			return true;
+	}
+
+	/* Then check external (file-based) quirks */
+	if (external_quirk_match(idev))
+		return true;
+
+	return false;
+}
+
+int gamepad_quirk_apply(struct input_device *idev,
+			struct hidp_connadd_req *req)
+{
+	int i;
+
+	/* Try built-in quirks first */
+	for (i = 0; quirks[i]; i++) {
+		if (!quirks[i]->match(idev))
+			continue;
+
+		DBG("Applying HID quirk: %s", quirks[i]->name);
+
+		return quirks[i]->apply(idev, req);
+	}
+
+	/* Try external quirks */
+	if (external_quirk_match(idev)) {
+		return external_quirk_apply(idev, req);
+	}
+
+	return -1;
+}
diff --git a/profiles/input/quirk.h b/profiles/input/quirk.h
new file mode 100644
index 0000000..08e0bce
--- /dev/null
+++ b/profiles/input/quirk.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2026  Gamepad Quirk Support
+ *
+ *  Minimal HID fallback for third-party gamepads with broken SDP records.
+ *  Only activates for specifically matched devices.
+ */
+
+#ifndef __INPUT_QUIRK_H
+#define __INPUT_QUIRK_H
+
+#include <stdbool.h>
+
+struct input_device;
+struct hidp_connadd_req;
+
+struct gamepad_quirk {
+	const char *name;
+	bool (*match)(struct input_device *idev);
+	int (*apply)(struct input_device *idev,
+			struct hidp_connadd_req *req);
+};
+
+bool gamepad_quirk_match(struct input_device *idev);
+
+int gamepad_quirk_apply(struct input_device *idev,
+			struct hidp_connadd_req *req);
+
+/* External quirk support (quirk-profile.c) */
+bool external_quirk_match(struct input_device *idev);
+int external_quirk_apply(struct input_device *idev,
+			struct hidp_connadd_req *req);
+
+#endif
diff --git a/profiles/input/quirks/tg170w.c b/profiles/input/quirks/tg170w.c
new file mode 100644
index 0000000..d3f37a8
--- /dev/null
+++ b/profiles/input/quirks/tg170w.c
@@ -0,0 +1,232 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2026  Gamepad Quirk Support
+ *
+ *  DualShock 4 (CUH-ZCT2x) / TG170W gamepad quirk.
+ *
+ *  This controller identifies as a DualShock 4 compatible device and
+ *  works perfectly via USB. Over Bluetooth it exposes an incomplete
+ *  or malformed HID SDP record, causing BlueZ to fail with -ENOENT.
+ *
+ *  Windows and Android handle this gracefully; Linux/BlueZ does not.
+ *  This quirk injects the correct BT HID report descriptor so the
+ *  kernel's hid-playstation driver can create an input device.
+ *
+ *  Matching criteria (multi-factor, no single-field matching):
+ *    - Bluetooth device name + SDP provider
+ *    - Vendor/Product ID from device info
+ *
+ *  BT report descriptor constructed from kernel hid-playstation.c:
+ *    DS4_INPUT_REPORT_BT = 0x11, size = 78 bytes
+ *    DS4_OUTPUT_REPORT_BT = 0x11, size = 78 bytes
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "bluetooth/bluetooth.h"
+#include "bluetooth/hidp.h"
+#include "bluetooth/sdp.h"
+#include "bluetooth/sdp_lib.h"
+
+#include "src/log.h"
+
+#include "../quirk.h"
+
+/*
+ * DualShock 4 Bluetooth HID Report Descriptor.
+ *
+ * The USB descriptor (report ID 0x01, 64 bytes) does NOT work over
+ * Bluetooth. The DS4 sends 78-byte packets with report ID 0x11 on BT.
+ * The kernel's hid-playstation driver expects exactly this format.
+ *
+ * This descriptor tells the HID core to accept report ID 0x11 packets
+ * and route them to the driver's parse_report callback. The driver
+ * creates input devices programmatically -- it doesn't need field-level
+ * mappings in the descriptor.
+ *
+ * Report layout (from hid-playstation.c):
+ *   0x11 Input:  78 bytes total (1 ID + 77 data)
+ *   0x11 Output: 78 bytes total (1 ID + 77 data, rumble/LED)
+ *   0x05 Feature: 41 bytes (gyro/accel calibration)
+ *   0x12 Feature: 16 bytes (pairing info / MAC address)
+ *   0xa3 Feature: 49 bytes (firmware info)
+ */
+static const uint8_t tg170w_hid_report_descriptor[] = {
+	/* Usage Page (Generic Desktop), Usage (Gamepad), Collection (Application) */
+	0x05, 0x01, 0x09, 0x05, 0xa1, 0x01,
+
+	/*
+	 * Report ID 0x11 -- BT Enhanced Input Report
+	 * 78 bytes total = 1 byte ID + 77 bytes payload
+	 * Contains: sticks, buttons, gyro, accel, timestamp, touchpad, CRC
+	 */
+	0x85, 0x11,        /* Report ID (0x11) */
+	0x06, 0x00, 0xff,  /* Usage Page (Vendor Defined 0xFF00) */
+	0x09, 0x20,        /* Usage (0x20) */
+	0x15, 0x00,        /* Logical Minimum (0) */
+	0x26, 0xff, 0x00,  /* Logical Maximum (255) */
+	0x75, 0x08,        /* Report Size (8) */
+	0x95, 0x4d,        /* Report Count (77) */
+	0x81, 0x02,        /* Input (Data, Var, Abs) */
+
+	/*
+	 * Report ID 0x11 -- BT Output Report
+	 * 78 bytes total = 1 byte ID + 77 bytes payload
+	 * Used for: rumble motors, lightbar RGB, player LEDs
+	 */
+	0x85, 0x11,        /* Report ID (0x11) */
+	0x09, 0x21,        /* Usage (0x21) */
+	0x95, 0x4d,        /* Report Count (77) */
+	0x91, 0x02,        /* Output (Data, Var, Abs) */
+
+	/*
+	 * Report ID 0x05 -- Calibration Feature Report (41 bytes)
+	 * Gyroscope and accelerometer calibration data
+	 */
+	0x85, 0x05,        /* Report ID (0x05) */
+	0x09, 0x22,        /* Usage (0x22) */
+	0x95, 0x29,        /* Report Count (41) */
+	0xb1, 0x02,        /* Feature (Data, Var, Abs) */
+
+	/*
+	 * Report ID 0x12 -- Pairing Info Feature Report (16 bytes)
+	 * Contains MAC address for device identification
+	 */
+	0x85, 0x12,        /* Report ID (0x12) */
+	0x09, 0x23,        /* Usage (0x23) */
+	0x95, 0x10,        /* Report Count (16) */
+	0xb1, 0x02,        /* Feature (Data, Var, Abs) */
+
+	/*
+	 * Report ID 0xa3 -- Firmware Info Feature Report (49 bytes)
+	 * Hardware/firmware version information
+	 */
+	0x85, 0xa3,        /* Report ID (0xa3) */
+	0x09, 0x24,        /* Usage (0x24) */
+	0x95, 0x31,        /* Report Count (49) */
+	0xb1, 0x02,        /* Feature (Data, Var, Abs) */
+
+	/* End Collection */
+	0xc0
+};
+
+/* Sony DualShock 4 v2 (CUH-ZCT2x) */
+#define TG170W_VID  0x054c  /* Sony */
+#define TG170W_PID  0x09cc  /* DualShock 4 v2 */
+
+/* Opaque structs - we don't pull in heavy headers */
+struct input_device;
+struct btd_device;
+struct btd_service;
+
+/* Declarations from src/device.h and src/service.h */
+extern struct btd_service *input_device_get_service(
+					struct input_device *idev);
+extern struct btd_device *btd_service_get_device(
+					const struct btd_service *service);
+extern uint16_t btd_device_get_vendor(struct btd_device *device);
+extern uint16_t btd_device_get_product(struct btd_device *device);
+extern const sdp_record_t *btd_device_get_record(
+					struct btd_device *device,
+					const char *uuid);
+extern bool device_name_known(struct btd_device *device);
+extern void device_get_name(struct btd_device *device,
+					char *name, size_t len);
+
+static bool tg170w_match(struct input_device *idev)
+{
+	struct btd_service *service;
+	struct btd_device *device;
+	char name[248];
+	uint16_t vendor, product;
+	const sdp_record_t *rec;
+	sdp_data_t *pdlist;
+
+	if (!idev)
+		return false;
+
+	service = input_device_get_service(idev);
+	if (!service)
+		return false;
+
+	device = btd_service_get_device(service);
+	if (!device)
+		return false;
+
+	/* Check vendor/product ID from device info */
+	vendor = btd_device_get_vendor(device);
+	product = btd_device_get_product(device);
+
+	/* Match by VID/PID (Sony DS4 v2) */
+	if (vendor == TG170W_VID && product == TG170W_PID) {
+		DBG("TG170W quirk: matched by VID/PID %04x:%04x",
+							vendor, product);
+		return true;
+	}
+
+	/* Match by name + SDP provider */
+	if (!device_name_known(device))
+		return false;
+
+	device_get_name(device, name, sizeof(name));
+
+	if (strcmp(name, "Wireless Controller"))
+		return false;
+
+	/* Additional check: look for Sony in SDP provider */
+	rec = btd_device_get_record(device,
+			"00001124-0000-1000-8000-00805f9b34fb");
+	if (!rec)
+		return false;
+
+	pdlist = sdp_data_get(rec, SDP_ATTR_PROVNAME_PRIMARY);
+	if (!pdlist || !pdlist->val.str)
+		return false;
+
+	if (!strstr(pdlist->val.str, "Sony"))
+		return false;
+
+	DBG("TG170W quirk: matched by name + provider");
+	return true;
+}
+
+static int tg170w_apply(struct input_device *idev,
+			struct hidp_connadd_req *req)
+{
+	/*
+	 * HID parser version 1.11 per Bluetooth HID spec.
+	 * The playstation kernel driver doesn't inspect this value,
+	 * but it should be valid for proper HID core behavior.
+	 */
+	req->parser = 0x0111;
+	req->country = 0;
+	req->subclass = 0;
+
+	/* Inject the Bluetooth HID report descriptor */
+	req->rd_size = sizeof(tg170w_hid_report_descriptor);
+	req->rd_data = malloc(req->rd_size);
+	if (!req->rd_data)
+		return -ENOMEM;
+
+	memcpy(req->rd_data, tg170w_hid_report_descriptor, req->rd_size);
+
+	DBG("TG170W: injected %u byte BT HID descriptor", req->rd_size);
+
+	return 0;
+}
+
+struct gamepad_quirk tg170w_quirk = {
+	.name = "TG170W",
+	.match = tg170w_match,
+	.apply = tg170w_apply,
+};
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-07-22  9:04 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-22  9:03 [PATCH BlueZ 0/1] input: HID gamepad quirk system for broken SDP records Pakrohk
2026-07-22  9:03 ` [PATCH BlueZ 1/1] input: add " Pakrohk
  -- strict thread matches above, loose matches on Subject: below --
2026-07-22  8:52 [PATCH BlueZ 0/1] input: " Pakrohk

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