All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH BlueZ v2 0/3] Add HID gamepad quirk fallback for broken SDP records
@ 2026-07-21 14:17 Pakrohk
  2026-07-21 14:17 ` [PATCH BlueZ v2 1/3] src: add HMAC-SHA256 to bt_crypto using kernel AF_ALG Pakrohk
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Pakrohk @ 2026-07-21 14:17 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, marcel, Pakrohk

From: Pakrohk <Pakrohk@users.noreply.github.com>

This patch series adds a modular quirk system that provides HID report
descriptor fallbacks for third-party Bluetooth gamepads with incomplete
or malformed SDP records.

Problem:
Third-party Bluetooth HID gamepads (notably TG170W / DualShock 4 v2
clones, VID:PID 054c:09cc) expose incomplete HID SDP records. BlueZ's
extract_hid_record() fails with -ENOENT, preventing HIDP registration.
The Bluetooth link is active but no /dev/input/event* device is created.
Windows and Android handle this gracefully by falling back to known HID
descriptors. BlueZ does not.

Solution:
A two-tier quirk system that activates only when SDP parsing fails for a
specifically matched device. Does not globally weaken HID parsing.

  hidp_add_connection()
    -> extract_hid_record()           // normal SDP path
      -> FAILS (-ENOENT)
    -> gamepad_quirk_apply()          // check built-in + external quirks
      -> inject fallback HID descriptor
    -> continue HIDP setup normally

Patch [1] adds HMAC-SHA256 to the internal bt_crypto library using the
kernel AF_ALG interface, following the existing cmac(aes) pattern. This
replaces the previous openssl CLI dependency.

Patch [2] adds the core quirk system: built-in quirk for TG170W/DS4 v2,
external quirk profile loader with HMAC signature verification, and the
device.c integration point.

Patch [3] adds bluez-quirkctl, a CLI tool for managing external quirk
profiles (validate, install, list, remove).

External quirk profiles are JSON files installed to
/var/lib/bluez/quirks/ with HMAC-SHA256 signatures, loaded at bluetoothd
startup. This enables community-maintained gamepad support without BlueZ
source modifications.

Testing:
  - TG170W / DualShock 4 v2 (054c:09cc): buttons, sticks, touchpad
    verified via evtest
  - Kernel creates three input devices: Wireless Controller,
    Wireless Controller Motion Sensors, Wireless Controller Touchpad
  - External profile validation, installation, loading tested end-to-end
  - Tampered profiles rejected by signature verification

---
Patch 1: src: add HMAC-SHA256 to bt_crypto using kernel AF_ALG
Patch 2: input: add HID gamepad quirk fallback for broken SDP records
Patch 3: tools: add bluez-quirkctl for managing quirk profiles

-- 
2.49.0



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

* [PATCH BlueZ v2 1/3] src: add HMAC-SHA256 to bt_crypto using kernel AF_ALG
  2026-07-21 14:17 [PATCH BlueZ v2 0/3] Add HID gamepad quirk fallback for broken SDP records Pakrohk
@ 2026-07-21 14:17 ` Pakrohk
  2026-07-21 14:28   ` Luiz Augusto von Dentz
  2026-07-21 14:17 ` [PATCH BlueZ v2 2/3] input: add HID gamepad quirk fallback for broken SDP records Pakrohk
  2026-07-21 14:17 ` [PATCH BlueZ v2 3/3] tools: add bluez-quirkctl for managing quirk profiles Pakrohk
  2 siblings, 1 reply; 6+ messages in thread
From: Pakrohk @ 2026-07-21 14:17 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, marcel, Pakrohk

From: Pakrohk <Pakrohk@users.noreply.github.com>

Add HMAC-SHA256 computation to the bt_crypto library using the Linux
kernel AF_ALG interface. This follows the exact same pattern as the
existing cmac(aes) implementation.

The HMAC-SHA256 function is needed for verifying external quirk profile
signatures. The previous implementation used popen("openssl dgst ...")
which had command injection risks and an openssl CLI dependency.

Using kernel AF_ALG:
  - No external library dependencies
  - Matches existing BlueZ crypto patterns
  - No shell invocation or temp files
  - Works in bluetoothd's process context

The hmac(sha256) algorithm is available in all modern Linux kernels
via the crypto API (/proc/crypto).

Signed-off-by: Pakrohk <Pakrohk@users.noreply.github.com>
diff --git a/src/shared/crypto.c b/src/shared/crypto.c
index cb99116..f5883bd 100644
--- a/src/shared/crypto.c
+++ b/src/shared/crypto.c
@@ -69,6 +69,7 @@ struct bt_crypto {
 	int ecb_aes;
 	int urandom;
 	int cmac_aes;
+	int hmac_sha256;
 };
 
 static int urandom_setup(void)
@@ -126,6 +127,28 @@ static int cmac_aes_setup(void)
 	return fd;
 }
 
+static int hmac_sha256_setup(void)
+{
+	struct sockaddr_alg salg;
+	int fd;
+
+	fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
+	if (fd < 0)
+		return -1;
+
+	memset(&salg, 0, sizeof(salg));
+	salg.salg_family = AF_ALG;
+	strcpy((char *) salg.salg_type, "hash");
+	strcpy((char *) salg.salg_name, "hmac(sha256)");
+
+	if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
 static struct bt_crypto *singleton;
 
 struct bt_crypto *bt_crypto_new(void)
@@ -159,6 +182,16 @@ struct bt_crypto *bt_crypto_new(void)
 		return NULL;
 	}
 
+	singleton->hmac_sha256 = hmac_sha256_setup();
+	if (singleton->hmac_sha256 < 0) {
+		close(singleton->cmac_aes);
+		close(singleton->urandom);
+		close(singleton->ecb_aes);
+		free(singleton);
+		singleton = NULL;
+		return NULL;
+	}
+
 	return bt_crypto_ref(singleton);
 }
 
@@ -183,6 +216,7 @@ void bt_crypto_unref(struct bt_crypto *crypto)
 	close(crypto->urandom);
 	close(crypto->ecb_aes);
 	close(crypto->cmac_aes);
+	close(crypto->hmac_sha256);
 
 	free(crypto);
 	singleton = NULL;
@@ -1005,3 +1039,35 @@ bool bt_crypto_sirk(struct bt_crypto *crypto, const char *str, uint16_t vendor,
 	/* Encrypt sirk using k as LTK with sef function */
 	return bt_crypto_sef(crypto, k, sirk_plaintext, sirk);
 }
+
+bool bt_crypto_hmac_sha256(struct bt_crypto *crypto,
+				const uint8_t *key, size_t key_len,
+				const uint8_t *msg, size_t msg_len,
+				uint8_t res[32])
+{
+	ssize_t len;
+	int fd;
+
+	if (!crypto)
+		return false;
+
+	fd = alg_new(crypto->hmac_sha256, key, key_len);
+	if (fd < 0)
+		return false;
+
+	len = send(fd, msg, msg_len, 0);
+	if (len < 0) {
+		close(fd);
+		return false;
+	}
+
+	len = read(fd, res, 32);
+	if (len < 0) {
+		close(fd);
+		return false;
+	}
+
+	close(fd);
+
+	return true;
+}
diff --git a/src/shared/crypto.h b/src/shared/crypto.h
index d85f807..6b234bc 100644
--- a/src/shared/crypto.h
+++ b/src/shared/crypto.h
@@ -62,3 +62,8 @@ bool bt_crypto_sirk(struct bt_crypto *crypto, const char *str, uint16_t vendor,
 			uint8_t sirk[16]);
 bool bt_crypto_rsi(struct bt_crypto *crypto, const uint8_t sirk[16],
 					uint8_t rsi[6]);
+
+bool bt_crypto_hmac_sha256(struct bt_crypto *crypto,
+				const uint8_t *key, size_t key_len,
+				const uint8_t *msg, size_t msg_len,
+				uint8_t res[32]);


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

* [PATCH BlueZ v2 2/3] input: add HID gamepad quirk fallback for broken SDP records
  2026-07-21 14:17 [PATCH BlueZ v2 0/3] Add HID gamepad quirk fallback for broken SDP records Pakrohk
  2026-07-21 14:17 ` [PATCH BlueZ v2 1/3] src: add HMAC-SHA256 to bt_crypto using kernel AF_ALG Pakrohk
@ 2026-07-21 14:17 ` Pakrohk
  2026-07-21 14:36   ` Luiz Augusto von Dentz
  2026-07-21 14:17 ` [PATCH BlueZ v2 3/3] tools: add bluez-quirkctl for managing quirk profiles Pakrohk
  2 siblings, 1 reply; 6+ messages in thread
From: Pakrohk @ 2026-07-21 14:17 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, marcel, Pakrohk

From: Pakrohk <Pakrohk@users.noreply.github.com>

Add a modular gamepad quirk system to profiles/input/ that provides HID
report descriptor fallbacks when BlueZ's SDP parser fails.

When extract_hid_record() returns -ENOENT for a known gamepad, the quirk
system checks registered quirks and injects a fallback HID descriptor so
the kernel's HID driver can create an input device.

Two tiers of quirk support:

Built-in quirks:
  Hardcoded C entries for known broken controllers. Currently supports
  DualShock 4 v2 / TG170W (054c:09cc) with a minimal BT HID descriptor
  matching report ID 0x11 (78 bytes, as expected by hid-playstation).

External quirk profiles:
  JSON files in /var/lib/bluez/quirks/ with HMAC-SHA256 signatures.
  Loaded at bluetoothd startup. Enables community-maintained support
  without BlueZ source modifications.

Architecture:
  quirk.h     - quirk struct definition and dispatch API
  quirk.c     - quirk registry and dispatch logic
  quirk-profile.h/c - external JSON profile loader with HMAC verification
  quirks/tg170w.c - built-in quirk for DualShock 4 v2

The quirk only activates when SDP parsing fails AND the device matches
a registered quirk. This does not globally weaken HID parsing.

Multi-factor matching avoids false positives:
  match = (vendor_id == 0x054c && product_id == 0x09cc)
       || (device_name == "Wireless Controller"
           && SDP provider contains "Sony")

Signed-off-by: Pakrohk <Pakrohk@users.noreply.github.com>
diff --git a/profiles/input/device.c b/profiles/input/device.c
index 8017e07..0089c3d 100644
--- a/profiles/input/device.c
+++ b/profiles/input/device.c
@@ -1089,11 +1089,8 @@ static int hidp_add_connection(struct input_device *idev)
 
 	err = extract_hid_record(idev, req);
 	if (err < 0) {
-		/* Try gamepad quirk fallback for known broken devices */
-		if (gamepad_quirk_match(idev)) {
-			DBG("HID SDP failed, trying gamepad quirk");
-			err = gamepad_quirk_apply(idev, req);
-		}
+		DBG("HID SDP failed, trying gamepad quirk");
+		err = gamepad_quirk_apply(idev, req);
 	}
 
 	if (err < 0) {
diff --git a/profiles/input/quirk-profile.c b/profiles/input/quirk-profile.c
index 8ec3835..4240154 100644
--- a/profiles/input/quirk-profile.c
+++ b/profiles/input/quirk-profile.c
@@ -28,22 +28,20 @@
 
 #include "bluetooth/bluetooth.h"
 #include "bluetooth/hidp.h"
+#include "bluetooth/sdp.h"
+#include "bluetooth/sdp_lib.h"
 
 #include "src/log.h"
 
 #include "quirk.h"
 #include "quirk-profile.h"
 
-/* Accessors 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 bool device_name_known(struct btd_device *device);
-extern void device_get_name(struct btd_device *device,
-					char *name, size_t len);
+#include "gdbus/gdbus.h"
+
+#include "src/device.h"
+#include "src/service.h"
+#include "src/shared/crypto.h"
+#include "device.h"
 
 #define MAX_EXTERNAL_QUIRKS 32
 #define HMAC_KEY_PATH QUIRK_PROFILE_DIR "/.hmac_key"
@@ -67,6 +65,7 @@ struct external_quirk {
 
 static struct external_quirk *ext_quirks[MAX_EXTERNAL_QUIRKS + 1];
 static int num_ext_quirks;
+static struct bt_crypto *crypto;
 
 /*
  * Parse a hex string like "0501 0905" into a byte buffer.
@@ -139,77 +138,23 @@ static char *read_file(const char *path, size_t *out_len)
 }
 
 /*
- * Compute HMAC-SHA256 using openssl CLI.
- */
-static char *hmac_sha256(const uint8_t *key, int key_len,
-			 const void *data, size_t data_len)
-{
-	char key_hex[HMAC_KEY_SIZE * 2 + 1];
-	char *cmd;
-	char *result = NULL;
-	FILE *p, *tmpf;
-	int i;
-	const char *tmpfile = "/tmp/.bluez_quirk_hmac_input";
-	char line[256];
-	char *eq, *end;
-
-	for (i = 0; i < key_len; i++)
-		snprintf(key_hex + i * 2, 3, "%02x", key[i]);
-	key_hex[key_len * 2] = '\0';
-
-	tmpf = fopen(tmpfile, "we");
-	if (!tmpf)
-		return NULL;
-
-	fwrite(data, 1, data_len, tmpf);
-	fclose(tmpf);
-
-	cmd = malloc(strlen(key_hex) + 128);
-	if (!cmd) {
-		unlink(tmpfile);
-		return NULL;
-	}
-
-	snprintf(cmd, strlen(key_hex) + 128,
-		"openssl dgst -sha256 -hmac '%s' -hex < %s 2>/dev/null",
-		key_hex, tmpfile);
-
-	p = popen(cmd, "re");
-	free(cmd);
-	unlink(tmpfile);
-
-	if (!p)
-		return NULL;
-
-	while (fgets(line, sizeof(line), p)) {
-		eq = strstr(line, "= ");
-		if (eq) {
-			eq += 2;
-			end = eq + strlen(eq) - 1;
-			while (end > eq && (*end == '\n' || *end == '\r'
-						|| *end == ' '))
-				*end-- = '\0';
-			result = strdup(eq);
-			break;
-		}
-	}
-
-	pclose(p);
-	return result;
-}
-
-/*
- * Verify HMAC signature.
+ * Verify HMAC-SHA256 signature using bt_crypto (kernel AF_ALG).
  */
 static bool verify_signature(const char *json_path,
 			     const uint8_t *hmac_key, int key_len)
 {
 	char sig_path[1024];
 	size_t sig_len, json_len;
-	char *sig_hex, *json_data, *expected_sig;
+	char *sig_hex, *json_data;
 	char *end;
+	uint8_t computed[32];
+	char computed_hex[65];
+	int i;
 	bool valid;
 
+	if (!crypto)
+		return false;
+
 	snprintf(sig_path, sizeof(sig_path), "%s" SIG_EXT, json_path);
 
 	sig_hex = read_file(sig_path, &sig_len);
@@ -228,21 +173,25 @@ static bool verify_signature(const char *json_path,
 		return false;
 	}
 
-	expected_sig = hmac_sha256(hmac_key, key_len, json_data, json_len);
-	free(json_data);
-
-	if (!expected_sig) {
+	if (!bt_crypto_hmac_sha256(crypto, hmac_key, key_len,
+				(const uint8_t *) json_data, json_len,
+				computed)) {
+		free(json_data);
 		free(sig_hex);
 		return false;
 	}
+	free(json_data);
+
+	for (i = 0; i < 32; i++)
+		snprintf(computed_hex + i * 2, 3, "%02x", computed[i]);
+	computed_hex[64] = '\0';
 
-	valid = (strcmp(sig_hex, expected_sig) == 0);
+	valid = (strcmp(sig_hex, computed_hex) == 0);
 
 	if (!valid)
 		DBG("Signature mismatch for %s", json_path);
 
 	free(sig_hex);
-	free(expected_sig);
 	return valid;
 }
 
@@ -516,6 +465,12 @@ int load_external_quirks(const char *dir)
 	if (!dir)
 		dir = QUIRK_PROFILE_DIR;
 
+	if (!crypto) {
+		crypto = bt_crypto_new();
+		if (!crypto)
+			DBG("quirk-profile: failed to init crypto");
+	}
+
 	d = opendir(dir);
 	if (!d) {
 		DBG("quirk-profile: cannot open %s: %s", dir, strerror(errno));
@@ -586,4 +541,9 @@ void free_external_quirks(void)
 		ext_quirks[i] = NULL;
 	}
 	num_ext_quirks = 0;
+
+	if (crypto) {
+		bt_crypto_unref(crypto);
+		crypto = NULL;
+	}
 }
diff --git a/profiles/input/quirk.c b/profiles/input/quirk.c
index 053f45b..fa5e7e6 100644
--- a/profiles/input/quirk.c
+++ b/profiles/input/quirk.c
@@ -36,23 +36,6 @@ static struct gamepad_quirk *quirks[] = {
 	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)
 {
@@ -69,9 +52,5 @@ int gamepad_quirk_apply(struct input_device *idev,
 	}
 
 	/* Try external quirks */
-	if (external_quirk_match(idev)) {
-		return external_quirk_apply(idev, req);
-	}
-
-	return -1;
+	return external_quirk_apply(idev, req);
 }
diff --git a/profiles/input/quirk.h b/profiles/input/quirk.h
index 08e0bce..f2fc81b 100644
--- a/profiles/input/quirk.h
+++ b/profiles/input/quirk.h
@@ -24,13 +24,10 @@ struct gamepad_quirk {
 			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);
 
diff --git a/profiles/input/quirks/tg170w.c b/profiles/input/quirks/tg170w.c
index d3f37a8..d82c3fa 100644
--- a/profiles/input/quirks/tg170w.c
+++ b/profiles/input/quirks/tg170w.c
@@ -38,9 +38,14 @@
 #include "bluetooth/sdp.h"
 #include "bluetooth/sdp_lib.h"
 
+#include "gdbus/gdbus.h"
+
 #include "src/log.h"
+#include "src/device.h"
+#include "src/service.h"
 
 #include "../quirk.h"
+#include "../device.h"
 
 /*
  * DualShock 4 Bluetooth HID Report Descriptor.
@@ -124,25 +129,6 @@ static const uint8_t tg170w_hid_report_descriptor[] = {
 #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;


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

* [PATCH BlueZ v2 3/3] tools: add bluez-quirkctl for managing quirk profiles
  2026-07-21 14:17 [PATCH BlueZ v2 0/3] Add HID gamepad quirk fallback for broken SDP records Pakrohk
  2026-07-21 14:17 ` [PATCH BlueZ v2 1/3] src: add HMAC-SHA256 to bt_crypto using kernel AF_ALG Pakrohk
  2026-07-21 14:17 ` [PATCH BlueZ v2 2/3] input: add HID gamepad quirk fallback for broken SDP records Pakrohk
@ 2026-07-21 14:17 ` Pakrohk
  2 siblings, 0 replies; 6+ messages in thread
From: Pakrohk @ 2026-07-21 14:17 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.dentz, marcel, Pakrohk

From: Pakrohk <Pakrohk@users.noreply.github.com>

Add bluez-quirkctl, a CLI tool for managing external gamepad quirk
profiles. This tool validates JSON profiles, manages HMAC-SHA256 keys,
and installs/removes signed profiles to the system directory.

Usage:
  bluez-quirkctl install <file.json>   - validate, sign, install
  bluez-quirkctl remove <name>         - uninstall a profile
  bluez-quirkctl list                  - list installed profiles
  bluez-quirkctl validate <file.json>  - check without installing

Security model:
  - Install/remove requires root (checked via getuid())
  - HMAC key auto-generated in /var/lib/bluez/quirks/.hmac_key (0600)
  - Profiles installed to /var/lib/bluez/quirks/ (0644)
  - HMAC-SHA256 computed using bt_crypto (kernel AF_ALG)
  - Symlinks rejected during loading
  - Descriptor size capped at 2048 bytes

The tool links against libbluetooth-internal and libshared-mainloop
for access to bt_crypto_hmac_sha256().

Signed-off-by: Pakrohk <Pakrohk@users.noreply.github.com>
diff --git a/Makefile.tools b/Makefile.tools
index 44da0ce..ecce5b4 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -581,5 +581,6 @@ bin_PROGRAMS += tools/bluez-quirkctl
 
 tools_bluez_quirkctl_SOURCES = tools/quirkctl.c
 tools_bluez_quirkctl_CFLAGS = $(JSONC_CFLAGS)
-tools_bluez_quirkctl_LDADD = $(JSONC_LIBS)
+tools_bluez_quirkctl_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-mainloop.la $(JSONC_LIBS)
 endif
diff --git a/tools/quirkctl.c b/tools/quirkctl.c
index 5c35e97..93f3207 100644
--- a/tools/quirkctl.c
+++ b/tools/quirkctl.c
@@ -30,10 +30,16 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/sendfile.h>
 #include <pwd.h>
 
 #include <json-c/json.h>
 
+#include "src/shared/crypto.h"
+
+static const char *prog;
+static struct bt_crypto *crypto;
+
 #define QUIRK_DIR "/var/lib/bluez/quirks"
 #define USER_QUIRK_DIR "/.config/bluez/quirks"
 #define HMAC_KEY_PATH QUIRK_DIR "/.hmac_key"
@@ -42,7 +48,166 @@
 #define JSON_EXT ".json"
 #define SIG_EXT ".sig"
 
-static const char *prog;
+/*
+ * Copy file using sendfile(2).
+ */
+static int copy_file(const char *src, const char *dst)
+{
+	int fd_in, fd_out;
+	struct stat st;
+	ssize_t sent;
+
+	fd_in = open(src, O_RDONLY);
+	if (fd_in < 0)
+		return -1;
+
+	if (fstat(fd_in, &st) < 0) {
+		close(fd_in);
+		return -1;
+	}
+
+	fd_out = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+	if (fd_out < 0) {
+		close(fd_in);
+		return -1;
+	}
+
+	sent = sendfile(fd_out, fd_in, NULL, st.st_size);
+	close(fd_in);
+	close(fd_out);
+
+	return (sent == st.st_size) ? 0 : -1;
+}
+
+/*
+ * Load HMAC key from disk. Returns key length or -1 on failure.
+ */
+static int load_hmac_key(uint8_t *key, int max_len)
+{
+	FILE *f;
+	long fsize;
+	char *hex;
+	int key_len, i;
+	char *end;
+
+	f = fopen(HMAC_KEY_PATH, "re");
+	if (!f)
+		return -1;
+
+	fseek(f, 0, SEEK_END);
+	fsize = ftell(f);
+	if (fsize < 0 || fsize > 1024) {
+		fclose(f);
+		return -1;
+	}
+	rewind(f);
+
+	hex = malloc(fsize + 1);
+	if (!hex) {
+		fclose(f);
+		return -1;
+	}
+
+	if (fread(hex, 1, fsize, f) != (size_t)fsize) {
+		free(hex);
+		fclose(f);
+		return -1;
+	}
+	fclose(f);
+	hex[fsize] = '\0';
+
+	end = hex + strlen(hex) - 1;
+	while (end > hex && (*end == '\n' || *end == '\r' || *end == ' '))
+		*end-- = '\0';
+
+	key_len = strlen(hex) / 2;
+	if (key_len > max_len)
+		key_len = max_len;
+
+	for (i = 0; i < key_len; i++) {
+		unsigned int byte;
+		char buf[3];
+
+		buf[0] = hex[i * 2];
+		buf[1] = hex[i * 2 + 1];
+		buf[2] = '\0';
+		if (sscanf(buf, "%2x", &byte) != 1) {
+			free(hex);
+			return -1;
+		}
+		key[i] = (uint8_t)byte;
+	}
+
+	free(hex);
+	return key_len;
+}
+
+/*
+ * Compute HMAC-SHA256 using bt_crypto (kernel AF_ALG).
+ * Returns hex string of 32-byte digest, or NULL on failure.
+ */
+static char *compute_hmac(const char *file_path)
+{
+	FILE *f;
+	long fsize;
+	uint8_t *data;
+	uint8_t key[HMAC_KEY_SIZE];
+	uint8_t digest[32];
+	char hex[HMAC_KEY_SIZE * 2 + 1];
+	char *result;
+	int key_len, i;
+
+	if (!crypto)
+		return NULL;
+
+	/* Read the file */
+	f = fopen(file_path, "re");
+	if (!f)
+		return NULL;
+
+	fseek(f, 0, SEEK_END);
+	fsize = ftell(f);
+	if (fsize < 0 || fsize > 1024 * 1024) {
+		fclose(f);
+		return NULL;
+	}
+	rewind(f);
+
+	data = malloc(fsize);
+	if (!data) {
+		fclose(f);
+		return NULL;
+	}
+
+	if (fread(data, 1, fsize, f) != (size_t)fsize) {
+		free(data);
+		fclose(f);
+		return NULL;
+	}
+	fclose(f);
+
+	/* Load the HMAC key */
+	key_len = load_hmac_key(key, HMAC_KEY_SIZE);
+	if (key_len <= 0) {
+		free(data);
+		return NULL;
+	}
+
+	/* Compute HMAC-SHA256 */
+	if (!bt_crypto_hmac_sha256(crypto, key, key_len, data, fsize, digest)) {
+		free(data);
+		return NULL;
+	}
+	free(data);
+
+	/* Convert to hex string */
+	for (i = 0; i < 32; i++)
+		snprintf(hex + i * 2, 3, "%02x", digest[i]);
+	hex[64] = '\0';
+
+	result = strdup(hex);
+	return result;
+}
 
 static void usage(void)
 {
@@ -139,43 +304,6 @@ static int ensure_hmac_key(void)
 	return 0;
 }
 
-/*
- * Compute HMAC-SHA256 via openssl CLI.
- */
-static char *compute_hmac(const char *file_path)
-{
-	char cmd[1024];
-	char line[256];
-	char *result = NULL;
-	FILE *p;
-	char *eq;
-
-	snprintf(cmd, sizeof(cmd),
-		"openssl dgst -sha256 -hmac \"$(cat " HMAC_KEY_PATH ")\" "
-		"-hex < '%s' 2>/dev/null", file_path);
-
-	p = popen(cmd, "r");
-	if (!p)
-		return NULL;
-
-	while (fgets(line, sizeof(line), p)) {
-		eq = strstr(line, "= ");
-		if (eq) {
-			eq += 2;
-			/* trim */
-			char *end = eq + strlen(eq) - 1;
-			while (end > eq && (*end == '\n' || *end == '\r'
-						|| *end == ' '))
-				*end-- = '\0';
-			result = strdup(eq);
-			break;
-		}
-	}
-
-	pclose(p);
-	return result;
-}
-
 /*
  * Validate JSON profile structure.
  */
@@ -390,14 +518,10 @@ static int cmd_install(const char *path)
 		return 1;
 	}
 
-	{
-		char cmd[1024];
-		snprintf(cmd, sizeof(cmd), "cp '%s' '%s'", path, dest);
-		if (system(cmd) != 0) {
-			fprintf(stderr, "Failed to copy profile\n");
-			free(name);
-			return 1;
-		}
+	if (copy_file(path, dest) < 0) {
+		fprintf(stderr, "Failed to copy profile\n");
+		free(name);
+		return 1;
 	}
 
 	if (chmod(dest, 0644) < 0) {
@@ -510,6 +634,8 @@ static int cmd_list(void)
 
 int main(int argc, char *argv[])
 {
+	int ret;
+
 	prog = argv[0];
 
 	if (argc < 2) {
@@ -522,7 +648,14 @@ int main(int argc, char *argv[])
 			fprintf(stderr, "Usage: %s install <file.json>\n", prog);
 			return 1;
 		}
-		return cmd_install(argv[2]);
+		crypto = bt_crypto_new();
+		if (!crypto) {
+			fprintf(stderr, "Failed to init crypto\n");
+			return 1;
+		}
+		ret = cmd_install(argv[2]);
+		bt_crypto_unref(crypto);
+		return ret;
 	}
 
 	if (strcmp(argv[1], "remove") == 0) {
@@ -533,9 +666,8 @@ int main(int argc, char *argv[])
 		return cmd_remove(argv[2]);
 	}
 
-	if (strcmp(argv[1], "list") == 0) {
+	if (strcmp(argv[1], "list") == 0)
 		return cmd_list();
-	}
 
 	if (strcmp(argv[1], "validate") == 0) {
 		if (argc < 3) {


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

* Re: [PATCH BlueZ v2 1/3] src: add HMAC-SHA256 to bt_crypto using kernel AF_ALG
  2026-07-21 14:17 ` [PATCH BlueZ v2 1/3] src: add HMAC-SHA256 to bt_crypto using kernel AF_ALG Pakrohk
@ 2026-07-21 14:28   ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2026-07-21 14:28 UTC (permalink / raw)
  To: Pakrohk; +Cc: linux-bluetooth, marcel, Pakrohk

Hi Pakrohk,

On Tue, Jul 21, 2026 at 10:17 AM Pakrohk <rhpcir@gmail.com> wrote:
>
> From: Pakrohk <Pakrohk@users.noreply.github.com>
>
> Add HMAC-SHA256 computation to the bt_crypto library using the Linux
> kernel AF_ALG interface. This follows the exact same pattern as the
> existing cmac(aes) implementation.
>
> The HMAC-SHA256 function is needed for verifying external quirk profile
> signatures. The previous implementation used popen("openssl dgst ...")
> which had command injection risks and an openssl CLI dependency.
>
> Using kernel AF_ALG:
>   - No external library dependencies
>   - Matches existing BlueZ crypto patterns
>   - No shell invocation or temp files
>   - Works in bluetoothd's process context

Well I guess kernel crypto doesn't want us to add new usage like this
since AF_ALG is to be considered deprecated, even though I have to
agree it is much simpler and probably safer than using an external
crypto library.

> The hmac(sha256) algorithm is available in all modern Linux kernels
> via the crypto API (/proc/crypto).
>
> Signed-off-by: Pakrohk <Pakrohk@users.noreply.github.com>
> diff --git a/src/shared/crypto.c b/src/shared/crypto.c
> index cb99116..f5883bd 100644
> --- a/src/shared/crypto.c
> +++ b/src/shared/crypto.c
> @@ -69,6 +69,7 @@ struct bt_crypto {
>         int ecb_aes;
>         int urandom;
>         int cmac_aes;
> +       int hmac_sha256;
>  };
>
>  static int urandom_setup(void)
> @@ -126,6 +127,28 @@ static int cmac_aes_setup(void)
>         return fd;
>  }
>
> +static int hmac_sha256_setup(void)
> +{
> +       struct sockaddr_alg salg;
> +       int fd;
> +
> +       fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
> +       if (fd < 0)
> +               return -1;
> +
> +       memset(&salg, 0, sizeof(salg));
> +       salg.salg_family = AF_ALG;
> +       strcpy((char *) salg.salg_type, "hash");
> +       strcpy((char *) salg.salg_name, "hmac(sha256)");
> +
> +       if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
> +               close(fd);
> +               return -1;
> +       }
> +
> +       return fd;
> +}
> +
>  static struct bt_crypto *singleton;
>
>  struct bt_crypto *bt_crypto_new(void)
> @@ -159,6 +182,16 @@ struct bt_crypto *bt_crypto_new(void)
>                 return NULL;
>         }
>
> +       singleton->hmac_sha256 = hmac_sha256_setup();
> +       if (singleton->hmac_sha256 < 0) {
> +               close(singleton->cmac_aes);
> +               close(singleton->urandom);
> +               close(singleton->ecb_aes);
> +               free(singleton);
> +               singleton = NULL;
> +               return NULL;
> +       }
> +
>         return bt_crypto_ref(singleton);
>  }
>
> @@ -183,6 +216,7 @@ void bt_crypto_unref(struct bt_crypto *crypto)
>         close(crypto->urandom);
>         close(crypto->ecb_aes);
>         close(crypto->cmac_aes);
> +       close(crypto->hmac_sha256);
>
>         free(crypto);
>         singleton = NULL;
> @@ -1005,3 +1039,35 @@ bool bt_crypto_sirk(struct bt_crypto *crypto, const char *str, uint16_t vendor,
>         /* Encrypt sirk using k as LTK with sef function */
>         return bt_crypto_sef(crypto, k, sirk_plaintext, sirk);
>  }
> +
> +bool bt_crypto_hmac_sha256(struct bt_crypto *crypto,
> +                               const uint8_t *key, size_t key_len,
> +                               const uint8_t *msg, size_t msg_len,
> +                               uint8_t res[32])
> +{
> +       ssize_t len;
> +       int fd;
> +
> +       if (!crypto)
> +               return false;
> +
> +       fd = alg_new(crypto->hmac_sha256, key, key_len);
> +       if (fd < 0)
> +               return false;
> +
> +       len = send(fd, msg, msg_len, 0);
> +       if (len < 0) {
> +               close(fd);
> +               return false;
> +       }
> +
> +       len = read(fd, res, 32);
> +       if (len < 0) {
> +               close(fd);
> +               return false;
> +       }
> +
> +       close(fd);
> +
> +       return true;
> +}
> diff --git a/src/shared/crypto.h b/src/shared/crypto.h
> index d85f807..6b234bc 100644
> --- a/src/shared/crypto.h
> +++ b/src/shared/crypto.h
> @@ -62,3 +62,8 @@ bool bt_crypto_sirk(struct bt_crypto *crypto, const char *str, uint16_t vendor,
>                         uint8_t sirk[16]);
>  bool bt_crypto_rsi(struct bt_crypto *crypto, const uint8_t sirk[16],
>                                         uint8_t rsi[6]);
> +
> +bool bt_crypto_hmac_sha256(struct bt_crypto *crypto,
> +                               const uint8_t *key, size_t key_len,
> +                               const uint8_t *msg, size_t msg_len,
> +                               uint8_t res[32]);
>


-- 
Luiz Augusto von Dentz

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

* Re: [PATCH BlueZ v2 2/3] input: add HID gamepad quirk fallback for broken SDP records
  2026-07-21 14:17 ` [PATCH BlueZ v2 2/3] input: add HID gamepad quirk fallback for broken SDP records Pakrohk
@ 2026-07-21 14:36   ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2026-07-21 14:36 UTC (permalink / raw)
  To: Pakrohk; +Cc: linux-bluetooth, marcel, Pakrohk

Hi Pakrohk,

On Tue, Jul 21, 2026 at 10:17 AM Pakrohk <rhpcir@gmail.com> wrote:
>
> From: Pakrohk <Pakrohk@users.noreply.github.com>
>
> Add a modular gamepad quirk system to profiles/input/ that provides HID
> report descriptor fallbacks when BlueZ's SDP parser fails.
>
> When extract_hid_record() returns -ENOENT for a known gamepad, the quirk
> system checks registered quirks and injects a fallback HID descriptor so
> the kernel's HID driver can create an input device.
>
> Two tiers of quirk support:
>
> Built-in quirks:
>   Hardcoded C entries for known broken controllers. Currently supports
>   DualShock 4 v2 / TG170W (054c:09cc) with a minimal BT HID descriptor
>   matching report ID 0x11 (78 bytes, as expected by hid-playstation).
>
> External quirk profiles:
>   JSON files in /var/lib/bluez/quirks/ with HMAC-SHA256 signatures.
>   Loaded at bluetoothd startup. Enables community-maintained support
>   without BlueZ source modifications.
>
> Architecture:
>   quirk.h     - quirk struct definition and dispatch API
>   quirk.c     - quirk registry and dispatch logic
>   quirk-profile.h/c - external JSON profile loader with HMAC verification
>   quirks/tg170w.c - built-in quirk for DualShock 4 v2
>
> The quirk only activates when SDP parsing fails AND the device matches
> a registered quirk. This does not globally weaken HID parsing.
>
> Multi-factor matching avoids false positives:
>   match = (vendor_id == 0x054c && product_id == 0x09cc)
>        || (device_name == "Wireless Controller"
>            && SDP provider contains "Sony")
>
> Signed-off-by: Pakrohk <Pakrohk@users.noreply.github.com>
> diff --git a/profiles/input/device.c b/profiles/input/device.c
> index 8017e07..0089c3d 100644
> --- a/profiles/input/device.c
> +++ b/profiles/input/device.c
> @@ -1089,11 +1089,8 @@ static int hidp_add_connection(struct input_device *idev)
>
>         err = extract_hid_record(idev, req);
>         if (err < 0) {
> -               /* Try gamepad quirk fallback for known broken devices */
> -               if (gamepad_quirk_match(idev)) {
> -                       DBG("HID SDP failed, trying gamepad quirk");
> -                       err = gamepad_quirk_apply(idev, req);
> -               }
> +               DBG("HID SDP failed, trying gamepad quirk");
> +               err = gamepad_quirk_apply(idev, req);
>         }
>
>         if (err < 0) {
> diff --git a/profiles/input/quirk-profile.c b/profiles/input/quirk-profile.c
> index 8ec3835..4240154 100644
> --- a/profiles/input/quirk-profile.c
> +++ b/profiles/input/quirk-profile.c
> @@ -28,22 +28,20 @@
>
>  #include "bluetooth/bluetooth.h"
>  #include "bluetooth/hidp.h"
> +#include "bluetooth/sdp.h"
> +#include "bluetooth/sdp_lib.h"
>
>  #include "src/log.h"
>
>  #include "quirk.h"
>  #include "quirk-profile.h"
>
> -/* Accessors 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 bool device_name_known(struct btd_device *device);
> -extern void device_get_name(struct btd_device *device,
> -                                       char *name, size_t len);
> +#include "gdbus/gdbus.h"
> +
> +#include "src/device.h"
> +#include "src/service.h"
> +#include "src/shared/crypto.h"
> +#include "device.h"
>
>  #define MAX_EXTERNAL_QUIRKS 32
>  #define HMAC_KEY_PATH QUIRK_PROFILE_DIR "/.hmac_key"
> @@ -67,6 +65,7 @@ struct external_quirk {
>
>  static struct external_quirk *ext_quirks[MAX_EXTERNAL_QUIRKS + 1];
>  static int num_ext_quirks;
> +static struct bt_crypto *crypto;
>
>  /*
>   * Parse a hex string like "0501 0905" into a byte buffer.
> @@ -139,77 +138,23 @@ static char *read_file(const char *path, size_t *out_len)
>  }
>
>  /*
> - * Compute HMAC-SHA256 using openssl CLI.
> - */
> -static char *hmac_sha256(const uint8_t *key, int key_len,
> -                        const void *data, size_t data_len)

This file doesn't even exist upstream, so how come you're modifying
it? Looks like there is something off with this patch-set, beside it
is saying that it doesn't use openssl so this seem to be a left over
or something.

> -{
> -       char key_hex[HMAC_KEY_SIZE * 2 + 1];
> -       char *cmd;
> -       char *result = NULL;
> -       FILE *p, *tmpf;
> -       int i;
> -       const char *tmpfile = "/tmp/.bluez_quirk_hmac_input";
> -       char line[256];
> -       char *eq, *end;
> -
> -       for (i = 0; i < key_len; i++)
> -               snprintf(key_hex + i * 2, 3, "%02x", key[i]);
> -       key_hex[key_len * 2] = '\0';
> -
> -       tmpf = fopen(tmpfile, "we");
> -       if (!tmpf)
> -               return NULL;
> -
> -       fwrite(data, 1, data_len, tmpf);
> -       fclose(tmpf);
> -
> -       cmd = malloc(strlen(key_hex) + 128);
> -       if (!cmd) {
> -               unlink(tmpfile);
> -               return NULL;
> -       }
> -
> -       snprintf(cmd, strlen(key_hex) + 128,
> -               "openssl dgst -sha256 -hmac '%s' -hex < %s 2>/dev/null",
> -               key_hex, tmpfile);
> -
> -       p = popen(cmd, "re");
> -       free(cmd);
> -       unlink(tmpfile);
> -
> -       if (!p)
> -               return NULL;
> -
> -       while (fgets(line, sizeof(line), p)) {
> -               eq = strstr(line, "= ");
> -               if (eq) {
> -                       eq += 2;
> -                       end = eq + strlen(eq) - 1;
> -                       while (end > eq && (*end == '\n' || *end == '\r'
> -                                               || *end == ' '))
> -                               *end-- = '\0';
> -                       result = strdup(eq);
> -                       break;
> -               }
> -       }
> -
> -       pclose(p);
> -       return result;
> -}
> -
> -/*
> - * Verify HMAC signature.
> + * Verify HMAC-SHA256 signature using bt_crypto (kernel AF_ALG).
>   */
>  static bool verify_signature(const char *json_path,
>                              const uint8_t *hmac_key, int key_len)
>  {
>         char sig_path[1024];
>         size_t sig_len, json_len;
> -       char *sig_hex, *json_data, *expected_sig;
> +       char *sig_hex, *json_data;
>         char *end;
> +       uint8_t computed[32];
> +       char computed_hex[65];
> +       int i;
>         bool valid;
>
> +       if (!crypto)
> +               return false;
> +
>         snprintf(sig_path, sizeof(sig_path), "%s" SIG_EXT, json_path);
>
>         sig_hex = read_file(sig_path, &sig_len);
> @@ -228,21 +173,25 @@ static bool verify_signature(const char *json_path,
>                 return false;
>         }
>
> -       expected_sig = hmac_sha256(hmac_key, key_len, json_data, json_len);
> -       free(json_data);
> -
> -       if (!expected_sig) {
> +       if (!bt_crypto_hmac_sha256(crypto, hmac_key, key_len,
> +                               (const uint8_t *) json_data, json_len,
> +                               computed)) {
> +               free(json_data);
>                 free(sig_hex);
>                 return false;
>         }
> +       free(json_data);
> +
> +       for (i = 0; i < 32; i++)
> +               snprintf(computed_hex + i * 2, 3, "%02x", computed[i]);
> +       computed_hex[64] = '\0';
>
> -       valid = (strcmp(sig_hex, expected_sig) == 0);
> +       valid = (strcmp(sig_hex, computed_hex) == 0);
>
>         if (!valid)
>                 DBG("Signature mismatch for %s", json_path);
>
>         free(sig_hex);
> -       free(expected_sig);
>         return valid;
>  }
>
> @@ -516,6 +465,12 @@ int load_external_quirks(const char *dir)
>         if (!dir)
>                 dir = QUIRK_PROFILE_DIR;
>
> +       if (!crypto) {
> +               crypto = bt_crypto_new();
> +               if (!crypto)
> +                       DBG("quirk-profile: failed to init crypto");
> +       }
> +
>         d = opendir(dir);
>         if (!d) {
>                 DBG("quirk-profile: cannot open %s: %s", dir, strerror(errno));
> @@ -586,4 +541,9 @@ void free_external_quirks(void)
>                 ext_quirks[i] = NULL;
>         }
>         num_ext_quirks = 0;
> +
> +       if (crypto) {
> +               bt_crypto_unref(crypto);
> +               crypto = NULL;
> +       }
>  }
> diff --git a/profiles/input/quirk.c b/profiles/input/quirk.c
> index 053f45b..fa5e7e6 100644
> --- a/profiles/input/quirk.c
> +++ b/profiles/input/quirk.c
> @@ -36,23 +36,6 @@ static struct gamepad_quirk *quirks[] = {
>         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)
>  {
> @@ -69,9 +52,5 @@ int gamepad_quirk_apply(struct input_device *idev,
>         }
>
>         /* Try external quirks */
> -       if (external_quirk_match(idev)) {
> -               return external_quirk_apply(idev, req);
> -       }
> -
> -       return -1;
> +       return external_quirk_apply(idev, req);
>  }
> diff --git a/profiles/input/quirk.h b/profiles/input/quirk.h
> index 08e0bce..f2fc81b 100644
> --- a/profiles/input/quirk.h
> +++ b/profiles/input/quirk.h
> @@ -24,13 +24,10 @@ struct gamepad_quirk {
>                         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);
>
> diff --git a/profiles/input/quirks/tg170w.c b/profiles/input/quirks/tg170w.c
> index d3f37a8..d82c3fa 100644
> --- a/profiles/input/quirks/tg170w.c
> +++ b/profiles/input/quirks/tg170w.c
> @@ -38,9 +38,14 @@
>  #include "bluetooth/sdp.h"
>  #include "bluetooth/sdp_lib.h"
>
> +#include "gdbus/gdbus.h"
> +
>  #include "src/log.h"
> +#include "src/device.h"
> +#include "src/service.h"
>
>  #include "../quirk.h"
> +#include "../device.h"
>
>  /*
>   * DualShock 4 Bluetooth HID Report Descriptor.
> @@ -124,25 +129,6 @@ static const uint8_t tg170w_hid_report_descriptor[] = {
>  #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;
>


-- 
Luiz Augusto von Dentz

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

end of thread, other threads:[~2026-07-21 14:37 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-21 14:17 [PATCH BlueZ v2 0/3] Add HID gamepad quirk fallback for broken SDP records Pakrohk
2026-07-21 14:17 ` [PATCH BlueZ v2 1/3] src: add HMAC-SHA256 to bt_crypto using kernel AF_ALG Pakrohk
2026-07-21 14:28   ` Luiz Augusto von Dentz
2026-07-21 14:17 ` [PATCH BlueZ v2 2/3] input: add HID gamepad quirk fallback for broken SDP records Pakrohk
2026-07-21 14:36   ` Luiz Augusto von Dentz
2026-07-21 14:17 ` [PATCH BlueZ v2 3/3] tools: add bluez-quirkctl for managing quirk profiles Pakrohk

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.