Linux bluetooth development
 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox