Re: [PATCH BlueZ v2 2/3] input: add HID gamepad quirk fallback for broken SDP records
Luiz Augusto von Dentz <[email protected]>
| Newsgroups | org.kernel.vger.linux-bluetooth |
|---|---|
| Message-ID | <CABBYNZJUNQjaeiy6PrDLWZ1wwgfaEFjgozfZF91J=sPq2Uxp_g@mail.gmail.com> |
Hi Pakrohk, On Tue, Jul 21, 2026 at 10:17 AM Pakrohk <[email protected]> wrote: > > From: Pakrohk <[email protected]> > > 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 <[email protected]> > 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