From nobody Tue Sep 29 04:10:47 2026 Received: from mail-wr1-f41.google.com (mail-wr1-f41.google.com [209.85.221.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2929039020C for ; Wed, 12 Aug 2026 19:59:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786564747; cv=none; b=lyfaEG+HB4pgHWtUGu9SYAnQE0o2dhWyKJ/PJRdTFHdNaOjO3AC+FLgvx4HXvisNU+Yg2OpeXd1xX3JH3XqxnRV3czahii51/8DkljHK3vecsfPbImFELoDR06njYL5FIZtfAxEsFkCnBr0o4U10OM8RNze6HmN38ImB1cQQfgQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786564747; c=relaxed/simple; bh=lFceJ4HBWSBMd+5nGP0i0IRBG9201F6rE2f9MGbnGXU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=afTtKfPFZZdJA2dxotp9t8udSv1forejFQlDDDkpVfsbsaK76M3OuYgBcwbXUkBNfs1KftIM4+BFS1s96Vlod7s8x1jVoyvyZonom39/pEj2nwq9R4Wqr+eNz847frAKlaVO5WM3XSFQ/8Vdyxrv6h99emsQJZ1VLHrNneeASt0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=r12OhtIR; arc=none smtp.client-ip=209.85.221.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="r12OhtIR" Received: by mail-wr1-f41.google.com with SMTP id ffacd0b85a97d-4798bea72f9so723179f8f.1 for ; Wed, 12 Aug 2026 12:59:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786564742; x=1787169542; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=LhLxArGo2q7n/e6yuBeJc1CN2MnQp5unm5C/EjB9ZAc=; b=r12OhtIRijkxEhT71gHx7A3Q4DEr790GFNd5TPJv5dIQn6TWoajARacjL0QDwjV3t3 0o9QqmhTraaxMBXIp7ce9AYsNZ6RsqLcSpeBDU1b4HT2IJIJ5wbLt/9au31WvQ+rWT4i LiARyZPd/f4oYUqYN9p8qdXSx0tJghc1aYuKdgsG6n89YbZns8vgTP8sxQI1sR/6wiq7 0dLjY7Vtl4yhg8ItRVyzvPm+FiTQW0hVghTZR3FsQeESCLncuOHfFTPtHYOs8HQjGzT9 KAi6QNobQo3yEcel1yguefWzAWx2U4AuWIB3jRfjZQpjMimRxDA881DMB4SleqqnJomq MsgQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786564742; x=1787169542; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=LhLxArGo2q7n/e6yuBeJc1CN2MnQp5unm5C/EjB9ZAc=; b=RefIQRBY2BF0vInBJc7KSrluQGXR9bDDBe5TAMQyo5i9bepdYqZs9iybeOx9VblNuj Q0hAyGh0iPRMFmTdIb+BmpTpVnUznxyjOUQaisxZQusWToPzichqyEbfNhpuTNsGMo6p Z5BI9hcbsEzQdgsTwjLqvYfoNHKmNBYee5jliekrjleDGp19iT36QStgwd0d15kEDSjS 7PCJ5vr/e1uFqasEm5wFpEVbRzI6Hj87hVk6A+axLmHzaSrqmjuiIBPHicWNuxUBkM+F 06j5FY4UA3JaC8Me4FMNtCXkRzglnPo/w2fQbrWVWg0EFXeAyFwBdqZ5f8aaQwrjm8s1 Ox4g== X-Forwarded-Encrypted: i=1; AHgh+RoscE7GLjUyzhs+WOWywA1mmhCndcMo4VCpX6jNDZaDY6LWjplxpJVh1zmk2IAU7xmQLAdpbR5wxxJH8nM=@vger.kernel.org X-Gm-Message-State: AOJu0Ywa3UAuCs9XnD3/DgsWUIM8bNRHp7YK5oE/5ZKy0uG6sza474Co f4WHXR7Vv3CH1M6Ut9+Il9Wm6J+sf2RVfdTy27uJJJdJi3BVqQsne47y X-Gm-Gg: AR+sD10Ow1VqfNdhq2cFKuzbDZQ4hjISTIxAZ/UELvd/E1GQRPEX4NEYqP09ttS/BGt 6us4VdWcna+rEirvY3k1aczOze4EG1zBCq0dK+fnZGwHW3X+3AsDOEDtPmpT0JVrHGJzRBbHsi7 gL8GaWm3mXK0liv4D3soZ4MvhasZarWYqZijMGJ/navT57nWAZZAdmhPevFiF4QlM8fhRm5jtCQ 4ahsOEpGzcB47Ekzpg/sm/nxSPu5flCUXY2ZOx4Qm9ivCgokilTRgML5Dcl6+kTyM2mYXxvZiNe ZKy9lBf2c8O6yARpfS2qOeP87MdgDEMiG5s4qJ99LOYA+/RVwPAxh3nbSeZZKw+MHwqnr4ArMH7 HoIzIKs6b+Lr+VPb/kL0kFf42pdchkA30ZqFwelM/lrdgUamwYeleHzkSPd1tCc/HWxYZqwgvh/ 5U054Jei5Y7KSvaMZhy9bs8EuwEWBAKtftKX7LrzYyLG1SZl67iRLUHFHrlSQ7teFiwuW70gF7c LgLYxN96Lxin3XWG/uuDsHZ/w== X-Received: by 2002:a05:6000:25e7:b0:47f:4919:d5b2 with SMTP id ffacd0b85a97d-48159ca615cmr898037f8f.1.1786564741760; Wed, 12 Aug 2026 12:59:01 -0700 (PDT) Received: from localhost (97-115-90-76.ptld.qwest.net. [97.115.90.76]) by smtp.gmail.com with UTF8SMTPSA id ffacd0b85a97d-4815a5c2837sm107792f8f.37.2026.08.12.12.58.59 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 12 Aug 2026 12:59:01 -0700 (PDT) From: Elliot Douglas To: linux-input@vger.kernel.org Cc: lains@riseup.net, hadess@hadess.net, jikos@kernel.org, bentiss@kernel.org, linux-kernel@vger.kernel.org, edouglas7358@gmail.com Subject: [PATCH v3 1/2] HID: logitech-hidpp: add HID++ 2.0 reprogrammable button support Date: Wed, 12 Aug 2026 12:58:41 -0700 Message-ID: <20260812195842.77724-2-edouglas7358@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260812195842.77724-1-edouglas7358@gmail.com> References: <20260812195842.77724-1-edouglas7358@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Some Logitech HID++ 2.0 mice can report diverted reprogrammable controls through HID++ feature 0x1b04, SpecialKeysMseButtons / REPROG_CONTROLS_V4, instead of the normal HID mouse report. Add a quirk-gated event path for those controls. The handler temporarily diverts verified per-product controls, parses divertedButtonsEvent as the current pressed-control list, and reports the corresponding evdev key state for every mapped control. Keep the control mappings in per-product arrays so adding support for another mouse does not change the evdev capabilities advertised by already-supported devices. Documentation for feature 0x1b04 describes divertedButtonsEvent as a list of currently pressed diverted buttons, which is the event format handled here. Link: https://lekensteyn.nl/files/logitech/x1b04_specialkeysmsebuttons.html Signed-off-by: Elliot Douglas Reviewed-by: Bastien Nocera --- drivers/hid/hid-logitech-hidpp.c | 205 +++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hi= dpp.c index 70ba1a5e40d8..f9189e14fb78 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -76,6 +76,7 @@ MODULE_PARM_DESC(disable_tap_to_click, #define HIDPP_QUIRK_HI_RES_SCROLL_1P0 BIT(28) #define HIDPP_QUIRK_WIRELESS_STATUS BIT(29) #define HIDPP_QUIRK_RESET_HI_RES_SCROLL BIT(30) +#define HIDPP_QUIRK_HIDPP_REPROG_CONTROLS_BTNS BIT(31) =20 /* These are just aliases for now */ #define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS @@ -178,6 +179,8 @@ struct hidpp_scroll_counter { unsigned long long last_time; }; =20 +struct hidpp_reprog_control_mapping; + struct hidpp_device { struct hid_device *hid_dev; struct input_dev *input; @@ -205,6 +208,8 @@ struct hidpp_device { struct hidpp_scroll_counter vertical_wheel_counter; =20 u8 wireless_feature_index; + u8 reprog_controls_feature_index; + const struct hidpp_reprog_control_mapping *reprog_controls; =20 int hires_wheel_multiplier; u8 hires_wheel_feature_index; @@ -3601,6 +3606,195 @@ static int hidpp10_extra_mouse_buttons_raw_event(st= ruct hidpp_device *hidpp, return 1; } =20 +/* -----------------------------------------------------------------------= --- */ +/* HID++2.0 reprogrammable controls = */ +/* -----------------------------------------------------------------------= --- */ + +#define HIDPP_PAGE_REPROG_CONTROLS_V4 0x1b04 + +#define HIDPP_REPROG_CONTROLS_GET_COUNT 0x00 +#define HIDPP_REPROG_CONTROLS_GET_CID_INFO 0x10 +#define HIDPP_REPROG_CONTROLS_SET_CONTROL_REPORTING 0x30 + +#define HIDPP_REPROG_CONTROLS_FLAG_MOUSE BIT(0) +#define HIDPP_REPROG_CONTROLS_FLAG_DIVERT BIT(5) + +#define HIDPP_REPROG_CONTROLS_TEMPORARY_DIVERTED BIT(0) +#define HIDPP_REPROG_CONTROLS_CHANGE_TEMPORARY_DIVERT BIT(1) + +#define HIDPP_REPROG_CONTROLS_EVENT_DIVERTED 0x00 + +struct hidpp_reprog_control_mapping { + u16 control; + u16 code; +}; + +static const struct hidpp_reprog_control_mapping * +hidpp20_reprog_controls_get_mappings(struct hidpp_device *hidpp) +{ + return NULL; +} + +static int hidpp20_reprog_controls_get_count(struct hidpp_device *hidpp) +{ + struct hidpp_report response; + u8 feature_index =3D hidpp->reprog_controls_feature_index; + u8 cmd =3D HIDPP_REPROG_CONTROLS_GET_COUNT; + int ret; + + ret =3D hidpp_send_fap_command_sync(hidpp, feature_index, cmd, NULL, 0, + &response); + if (ret > 0) + return -EPROTO; + if (ret) + return ret; + + return response.fap.params[0]; +} + +static int hidpp20_reprog_controls_get_cid_info(struct hidpp_device *hidpp, + u8 index, u16 *control, + u8 *flags) +{ + struct hidpp_report response; + u8 feature_index =3D hidpp->reprog_controls_feature_index; + u8 cmd =3D HIDPP_REPROG_CONTROLS_GET_CID_INFO; + int ret; + + ret =3D hidpp_send_fap_command_sync(hidpp, feature_index, cmd, &index, + sizeof(index), &response); + if (ret > 0) + return -EPROTO; + if (ret) + return ret; + + *control =3D get_unaligned_be16(&response.fap.params[0]); + *flags =3D response.fap.params[4]; + + return 0; +} + +static bool hidpp20_reprog_controls_find_control(struct hidpp_device *hidp= p, + u16 control) +{ + int count, ret; + u16 cid; + u8 flags; + int i; + + count =3D hidpp20_reprog_controls_get_count(hidpp); + if (count < 0) + return false; + + for (i =3D 0; i < count; i++) { + ret =3D hidpp20_reprog_controls_get_cid_info(hidpp, i, &cid, + &flags); + if (ret) + return false; + + if (cid =3D=3D control) + return (flags & HIDPP_REPROG_CONTROLS_FLAG_MOUSE) && + (flags & HIDPP_REPROG_CONTROLS_FLAG_DIVERT); + } + + return false; +} + +static int hidpp20_reprog_controls_set_control_reporting(struct hidpp_devi= ce *hidpp, + u16 control, u8 flags) +{ + struct hidpp_report response; + u8 params[5]; + + put_unaligned_be16(control, ¶ms[0]); + params[2] =3D flags; + put_unaligned_be16(control, ¶ms[3]); + + return hidpp_send_fap_command_sync(hidpp, + hidpp->reprog_controls_feature_index, + HIDPP_REPROG_CONTROLS_SET_CONTROL_REPORTING, + params, sizeof(params), &response); +} + +static void hidpp20_reprog_controls_connect(struct hidpp_device *hidpp) +{ + const struct hidpp_reprog_control_mapping *mapping; + u8 flags =3D HIDPP_REPROG_CONTROLS_TEMPORARY_DIVERTED | + HIDPP_REPROG_CONTROLS_CHANGE_TEMPORARY_DIVERT; + + if (!(hidpp->quirks & HIDPP_QUIRK_HIDPP_REPROG_CONTROLS_BTNS)) + return; + + if (!hidpp->reprog_controls) + return; + + if (hidpp_root_get_feature(hidpp, HIDPP_PAGE_REPROG_CONTROLS_V4, + &hidpp->reprog_controls_feature_index)) + return; + + for (mapping =3D hidpp->reprog_controls; mapping->control; mapping++) { + if (!hidpp20_reprog_controls_find_control(hidpp, mapping->control)) + continue; + + hidpp20_reprog_controls_set_control_reporting(hidpp, + mapping->control, + flags); + } +} + +static int hidpp20_reprog_controls_raw_event(struct hidpp_device *hidpp, + u8 *data, int size) +{ + const struct hidpp_reprog_control_mapping *mapping; + struct hidpp_report *report =3D (struct hidpp_report *)data; + u16 controls[4]; + bool pressed; + unsigned int i, j; + + if (!(hidpp->quirks & HIDPP_QUIRK_HIDPP_REPROG_CONTROLS_BTNS) || + !hidpp->input || + !hidpp->reprog_controls || + hidpp->reprog_controls_feature_index =3D=3D 0xff) + return 0; + + if (size < HIDPP_REPORT_LONG_LENGTH || + report->fap.feature_index !=3D hidpp->reprog_controls_feature_index || + report->fap.funcindex_clientid !=3D HIDPP_REPROG_CONTROLS_EVENT_DIVER= TED) + return 0; + + for (i =3D 0; i < ARRAY_SIZE(controls); i++) + controls[i] =3D get_unaligned_be16(&report->fap.params[i * 2]); + + for (mapping =3D hidpp->reprog_controls; mapping->control; mapping++) { + pressed =3D false; + + for (j =3D 0; j < ARRAY_SIZE(controls); j++) { + if (controls[j] =3D=3D mapping->control) { + pressed =3D true; + break; + } + } + + input_report_key(hidpp->input, mapping->code, pressed); + } + + input_sync(hidpp->input); + + return 1; +} + +static void hidpp20_reprog_controls_populate_input(struct hidpp_device *hi= dpp, + struct input_dev *input_dev) +{ + const struct hidpp_reprog_control_mapping *mapping; + + if (!hidpp->reprog_controls) + return; + + for (mapping =3D hidpp->reprog_controls; mapping->control; mapping++) + input_set_capability(input_dev, EV_KEY, mapping->code); +} + static void hidpp10_extra_mouse_buttons_populate_input( struct hidpp_device *hidpp, struct input_dev *input_dev) { @@ -3859,6 +4053,9 @@ static void hidpp_populate_input(struct hidpp_device = *hidpp, =20 if (hidpp->quirks & HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS) hidpp10_extra_mouse_buttons_populate_input(hidpp, input); + + if (hidpp->quirks & HIDPP_QUIRK_HIDPP_REPROG_CONTROLS_BTNS) + hidpp20_reprog_controls_populate_input(hidpp, input); } =20 static int hidpp_input_configured(struct hid_device *hdev, @@ -3971,6 +4168,10 @@ static int hidpp_raw_hidpp_event(struct hidpp_device= *hidpp, u8 *data, return ret; } =20 + ret =3D hidpp20_reprog_controls_raw_event(hidpp, data, size); + if (ret !=3D 0) + return ret; + if (hidpp->quirks & HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS) { ret =3D hidpp10_consumer_keys_raw_event(hidpp, data, size); if (ret !=3D 0) @@ -4264,6 +4465,8 @@ static void hidpp_connect_event(struct work_struct *w= ork) return; } =20 + hidpp20_reprog_controls_connect(hidpp); + if (hidpp->quirks & HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS) { ret =3D hidpp10_consumer_keys_connect(hidpp); if (ret) @@ -4436,6 +4639,8 @@ static int hidpp_probe(struct hid_device *hdev, const= struct hid_device_id *id) hidpp->hid_dev =3D hdev; hidpp->name =3D hdev->name; hidpp->quirks =3D id->driver_data; + hidpp->reprog_controls_feature_index =3D 0xff; + hidpp->reprog_controls =3D hidpp20_reprog_controls_get_mappings(hidpp); hid_set_drvdata(hdev, hidpp); =20 ret =3D hid_parse(hdev); --=20 2.55.0 From nobody Tue Sep 29 04:10:47 2026 Received: from mail-wr1-f54.google.com (mail-wr1-f54.google.com [209.85.221.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8BB09386453 for ; Wed, 12 Aug 2026 19:59:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786564750; cv=none; b=bKiRxGTjhFlQ1aCQXSructseQxwdEAESnuLjWEKILdeDOmgiRrlM2eryesVAto/q5bLPXQVPxxv6pNYQDbQ18D/luJhAwmiDpP8ImePNWtTZeDqywARq4XknuavLII6r0Wvzlphm/BRQuiqRD5kncYhZRKuz2SmuZjL7UdD+OvQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786564750; c=relaxed/simple; bh=SxhLuZhNDLQB7P8JmA5d0v/iTsujFzWA4xG9cUNjap4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=K71LGOy/UwIwvVnd4nsktJK62FBNLF2Ov7ma3gKXo4DPaRvMCbolu7rqR05yPNFSiBPcLSSKMCL2JYp2ZeZob3QAGZ5WrwTcDFW76iWxSOrzLejIi5KjQ+qdXDdXI+7WXRDesSKuArlQxnZuf9tGiiMqETkZAa9E9yT7TC/fGU4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=K9WKgcL5; arc=none smtp.client-ip=209.85.221.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="K9WKgcL5" Received: by mail-wr1-f54.google.com with SMTP id ffacd0b85a97d-47db714766aso140117f8f.0 for ; Wed, 12 Aug 2026 12:59:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786564746; x=1787169546; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=WQJkp7F9XSMUUp3rtI8dEl5NT/CiVfWiLJXvzZwRDEo=; b=K9WKgcL5euEzMwjN2GJb91Ahgso4DC6pj6zwCfBkVurTTOTsrtXMuJ8shtJx45Ozsp zfNq2t0E73gbNE2p6+H+yCoXOeUYBO2qtQl6BTNKt8gknOiRQqg34uug0Q92U86b6+v3 b27gSr23OOeYcxU0ZQcj3wY0kUt00PHzkEU6QnjgigoJB/PHdh9V5CK0VJilFqo0ksKR M5PiTs9e7vkUjqF5VDRkjQ2uFbyEDx+4WxWPOQ9i4f8jFpZxStewmM2OnfYMXs/4NaLw 1Qrec1OCm9FhZKmaO5ipWxg5wglfbXuekAljR5OMqVaZHOZFn0uf62KbH+O0f3brvR+j vfgw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786564746; x=1787169546; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=WQJkp7F9XSMUUp3rtI8dEl5NT/CiVfWiLJXvzZwRDEo=; b=ag0SfH5x9fTF9L9c50XoK1NvsIaiDQBlzdH0pzaJQy1uaLmqz7yOkS+1ypYjkKuxT3 tEaKiuT4K0jtommwcD7cTnF88QxRy8eirb0R+T6S2StDcL85hrR2ouEZH8ge/5a9jwvI YirWLbAcNnCKh2tHVEsl7x1jEhGTS48/dQlv3ivPD7rVfB6hPkmuw1kpzARViiQ9naj/ fou84u4ozRCL2BfIAZDKuJRmaCxrq2wE6xTztSt4iFPN0xKaS2JpIeQWAexMgIo/S3y9 g/ddM7Tl04EpXQaDY1FqmX2FDL+XcLSwp+53ezy0ERofG21kS9//VjdIjgBmHNIzjdTE TZ0A== X-Forwarded-Encrypted: i=1; AHgh+RoV3O6HYRDaUz0zm6cgwGV0NuDUJ3gFPn+bm+B+V2A+d6ay0quKIdF4PBheeCm96+uySRPW1RMpFdTcsiQ=@vger.kernel.org X-Gm-Message-State: AOJu0YzpLYgWBgnrhT0djhSPMea14HkPiRqmGsW9IuV4voxZunccrZnB A8EYd83RKexhQ4JGwc/eCxn1SFlhWnndORAysmQ7CPmcj0J9z0WjaS1A X-Gm-Gg: AR+sD111mNZIWJygjz4gS4p9QmKyCAHLrRxOD4NIibu91+0yxQQcWWFVw9cA64wCW+O mSEbJss6yz1rQgcr2d5BhT25u74g4DO5MchK9O87MP5ivE5hXDqKoseZfdJmHkVf5LEdsKUBHE4 NbCBsewp0c5VM/rc0n34hGPIjt4tTjI9ecdzgfVTnW8Bygnowcy84coD1t1TfauFdZinzamfDjA xlCnYSDtFveZblUbApSLLrIPv3zN6aBQ25wYSjl9wABLr3G3+Q/CtmKEkaFcGvYtmIjxhvCrK+4 GFi5Q2Y979v4O6wD5a/mVXirRuFvAvpz1WJZQZp7Xi3TQaMCa/dlG18cEgyHHm9D2hShQ/IEf6f DKzqKRi0TEmweE0fLDLMbX1n7+gMJLbYEmBNJ772rKE6OKH4ATDLIYfunxGRlcpe5Coylmb5G/i U7kI7F/1Lqc+535r8uluMsNvdmexkUrL9nQQNar7r1EqbGbuQt1YlAtv0A4dW2GhhCSH20Hmfep 5thFlIfvf08vAws X-Received: by 2002:a05:6000:2883:b0:47f:eb38:26f9 with SMTP id ffacd0b85a97d-4815a502bd1mr325002f8f.8.1786564745846; Wed, 12 Aug 2026 12:59:05 -0700 (PDT) Received: from localhost (97-115-90-76.ptld.qwest.net. [97.115.90.76]) by smtp.gmail.com with UTF8SMTPSA id ffacd0b85a97d-4815a5612b8sm148920f8f.1.2026.08.12.12.59.03 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 12 Aug 2026 12:59:05 -0700 (PDT) From: Elliot Douglas To: linux-input@vger.kernel.org Cc: lains@riseup.net, hadess@hadess.net, jikos@kernel.org, bentiss@kernel.org, linux-kernel@vger.kernel.org, edouglas7358@gmail.com Subject: [PATCH v3 2/2] HID: logitech-hidpp: enable reprogrammable buttons on Signature M650 Date: Wed, 12 Aug 2026 12:58:42 -0700 Message-ID: <20260812195842.77724-3-edouglas7358@gmail.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260812195842.77724-1-edouglas7358@gmail.com> References: <20260812195842.77724-1-edouglas7358@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The Bluetooth Signature M650 exposes its side buttons through the normal mouse report, but the observed events are short click-like events emitted around release rather than physical press/release state. The device appears to use the held side-button state for its built-in gesture and side-button + wheel horizontal-scroll mode. As a result, holding a side button more than approximately 2 seconds can prevent the normal mouse report from emitting a usable button event at all. HID++ REPROG_CONTROLS_V4 diversion for control IDs 0x0053 and 0x0056 provides real press and release timing for those same controls. Logitech documents the Signature M650 side buttons as Back/Forward buttons, so report the diverted controls as BTN_BACK and BTN_FORWARD. The HID++ 0x1b04 documentation lists those control IDs as Back and Forward. The driver still verifies that the controls are present in the device control table and advertised as divertable before changing their reporting mode. Link: https://support.logi.com/hc/en-nz/articles/4414473810583-Getting-Star= ted-Signature-M650 Reviewed-by: Bastien Nocera Signed-off-by: Elliot Douglas --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-logitech-hidpp.c | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 426ff78c1c03..f4a23662888a 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -906,6 +906,7 @@ #define USB_DEVICE_ID_LOGITECH_Z_10_SPK 0x0a07 #define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e #define USB_DEVICE_ID_LOGITECH_T651 0xb00c +#define USB_DEVICE_ID_LOGITECH_SIGNATURE_M650 0xb02a #define USB_DEVICE_ID_LOGITECH_DINOVO_EDGE_KBD 0xb309 #define USB_DEVICE_ID_LOGITECH_CASA_TOUCHPAD 0xbb00 #define USB_DEVICE_ID_LOGITECH_C007 0xc007 diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hi= dpp.c index f9189e14fb78..8cd762e1eb51 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -3624,14 +3624,28 @@ static int hidpp10_extra_mouse_buttons_raw_event(st= ruct hidpp_device *hidpp, =20 #define HIDPP_REPROG_CONTROLS_EVENT_DIVERTED 0x00 =20 +#define HIDPP_REPROG_CONTROL_BACK 0x0053 +#define HIDPP_REPROG_CONTROL_FORWARD 0x0056 + struct hidpp_reprog_control_mapping { u16 control; u16 code; }; =20 +static const struct hidpp_reprog_control_mapping m650_reprog_control_mappi= ngs[] =3D { + { HIDPP_REPROG_CONTROL_BACK, BTN_BACK }, + { HIDPP_REPROG_CONTROL_FORWARD, BTN_FORWARD }, + { } +}; + static const struct hidpp_reprog_control_mapping * hidpp20_reprog_controls_get_mappings(struct hidpp_device *hidpp) { + switch (hidpp->hid_dev->product) { + case USB_DEVICE_ID_LOGITECH_SIGNATURE_M650: + return m650_reprog_control_mappings; + } + return NULL; } =20 @@ -4911,7 +4925,9 @@ static const struct hid_device_id hidpp_devices[] =3D= { { /* MX Vertical mouse over Bluetooth */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb020) }, { /* Signature M650 over Bluetooth */ - HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb02a) }, + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_SIGNATURE_M650), + .driver_data =3D HIDPP_QUIRK_HIDPP_REPROG_CONTROLS_BTNS }, { /* MX Master 3 mouse over Bluetooth */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb023) }, { /* MX Anywhere 3 mouse over Bluetooth */ --=20 2.55.0