HID: logitech-hidpp: fix Signature M650 side button timing
Subsystems (3)
cover HID: logitech-hidpp: fix Signature M650 side button timing
The Logitech Signature M650 over Bluetooth exposes its side buttons in the normal mouse report, but the reported BTN_SIDE/BTN_EXTRA events are short click-like events emitted around button release rather than physical press/release events with the real hold duration. The device appears to reserve the held side-button state for a built-in gesture mode: holding a side button long enough, or holding it while using the wheel for horizontal scrolling, can mean the normal mouse report never emits a usable side-button press at all. That makes the buttons unusable for standard Linux hold actions such as push-to-talk, drag modifiers, or remapping rules that depend on key-up timing.
When HID++ 2.0 feature 0x1b04, SpecialKeysMseButtons / REPROG_CONTROLS_V4, temporarily diverts the same controls, the device sends diverted-control notifications with real press and release timing. This series adds quirk-gated support for those notifications and enables it for the Bluetooth Signature M650.
Before enabling diversion, the driver verifies that each mapped control is present in the device's HID++ control table and is advertised as a divertable mouse control.
The diverted M650 controls are reported as BTN_BACK and BTN_FORWARD. Logitech's Signature M650 getting-started page labels these physical controls as Back/Forward buttons and describes their default page-navigation behavior:
https://support.logi.com/hc/en-nz/articles/4414473810583-Getting-Started-Signature-M650
The reprogrammable-control support is per-product and parses the full HID++ divertedButtonsEvent pressed-control list, so it can support devices with more buttons without relying on a single last-control release heuristic. Only the Signature M650 opts in for now. Other Logitech devices should only be enabled after their HID++ control IDs and divertedButtonsEvent behavior are captured and verified.
There is evidence that this is not unique to the M650. A prior MX Anywhere 3 patch used the same HID++ feature to fix thumb buttons that only activated on release, and Logitech documents side-button + wheel horizontal scrolling for both the MX Anywhere 3/3S and Signature M650. Solaar's device reports and rules documentation also show HID++ divertable back/forward controls on MX Master 3 and MX Master 3S class devices. This series remains conservative and only enables the device tested here.
Tested with a Logitech Signature M650 L over Bluetooth, HID ID 0005:046D:B02A. Baseline evtest showed short release-time BTN_SIDE/BTN_EXTRA events. Earlier local testing of the same HID++ diversion path showed real hold-duration press/release events, including holds longer than 4 seconds for both buttons.
Elliot Douglas (2):
HID: logitech-hidpp: add HID++ 2.0 reprogrammable button support
HID: logitech-hidpp: enable reprogrammable buttons on Signature M650
drivers/hid/hid-logitech-hidpp.c | 236 ++++++++++++++++++++++++++++++- 1 file changed, 235 insertions(+), 1 deletion(-)
base-commit: f0866517be9345d8245d32b722574b8aecccb348
Signature · 1 line
2.54.0
PATCH 1/2 HID: logitech-hidpp: add HID++ 2.0 reprogrammable button support
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 profiles 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.
--- drivers/hid/hid-logitech-hidpp.c | 215 +++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+)
drivers/hid/hid-logitech-hidpp.c +215 -0
@@ -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)/* These are just aliases for now */ #define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS
@@ -205,6 +206,7 @@ struct hidpp_device {struct hidpp_scroll_counter vertical_wheel_counter;u8 wireless_feature_index; +u8 reprog_controls_feature_index;int hires_wheel_multiplier;u8 hires_wheel_feature_index;
@@ -3601,6 +3603,209 @@ static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp,return 1 ; } +/* -------------------------------------------------------------------------- */ +/* 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; +}; + +struct hidpp_reprog_controls_profile { +const struct hidpp_reprog_control_mapping *mappings; +unsigned int mapping_count; +}; + +static const struct hidpp_reprog_controls_profile * +hidpp20_reprog_controls_get_profile (struct hidpp_device *hidpp) +{ +return NULL ; +} + +static int hidpp20_reprog_controls_get_count (struct hidpp_device *hidpp) +{ +struct hidpp_report response; +u8 feature_index = hidpp->reprog_controls_feature_index; +u8 cmd =HIDPP_REPROG_CONTROLS_GET_COUNT ; +int ret; + + ret =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 = hidpp->reprog_controls_feature_index; +u8 cmd =HIDPP_REPROG_CONTROLS_GET_CID_INFO ; +int ret; + + ret =hidpp_send_fap_command_sync (hidpp, feature_index, cmd, &index, +sizeof (index), &response); +if (ret >0 ) +return -EPROTO ; +if (ret) +return ret; + + *control =get_unaligned_be16 (&response.fap.params[0 ]); + *flags = response.fap.params[4 ]; + +return 0 ; +} + +static bool hidpp20_reprog_controls_find_control (struct hidpp_device *hidpp, +u16 control) +{ +int count, ret; +u16 cid; +u8 flags; +int i; + + count =hidpp20_reprog_controls_get_count (hidpp); +if (count <0 ) +return false; + +for (i =0 ; i < count; i++) { + ret =hidpp20_reprog_controls_get_cid_info (hidpp, i, &cid, + &flags); +if (ret) +return false; + +if (cid == 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_device *hidpp, +u16 control,u8 flags) +{ +struct hidpp_report response; +u8 params[5 ]; + +put_unaligned_be16 (control, ¶ms[0 ]); + params[2 ] = 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_controls_profile *profile; +u8 flags =HIDPP_REPROG_CONTROLS_TEMPORARY_DIVERTED | +HIDPP_REPROG_CONTROLS_CHANGE_TEMPORARY_DIVERT ; +unsigned int i; + +if (!(hidpp->quirks &HIDPP_QUIRK_HIDPP_REPROG_CONTROLS_BTNS )) +return ; + + profile =hidpp20_reprog_controls_get_profile (hidpp); +if (!profile) +return ; + +if (hidpp_root_get_feature (hidpp,HIDPP_PAGE_REPROG_CONTROLS_V4 , + &hidpp->reprog_controls_feature_index)) +return ; + +for (i =0 ; i < profile->mapping_count; i++) { +u16 control = profile->mappings[i].control; + +if (!hidpp20_reprog_controls_find_control (hidpp, control)) +continue ; + +hidpp20_reprog_controls_set_control_reporting (hidpp, control, flags); + } +} + +static int hidpp20_reprog_controls_raw_event (struct hidpp_device *hidpp, +u8 *data,int size) +{ +const struct hidpp_reprog_controls_profile *profile; +const struct hidpp_reprog_control_mapping *mapping; +struct hidpp_report *report = (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_feature_index ==0xff ) +return 0 ; + + profile =hidpp20_reprog_controls_get_profile (hidpp); +if (!profile) +return 0 ; + +if (size <HIDPP_REPORT_LONG_LENGTH || + report->fap.feature_index != hidpp->reprog_controls_feature_index || + report->fap.funcindex_clientid !=HIDPP_REPROG_CONTROLS_EVENT_DIVERTED ) +return 0 ; + +for (i =0 ; i <ARRAY_SIZE (controls); i++) + controls[i] =get_unaligned_be16 (&report->fap.params[i *2 ]); + +for (i =0 ; i < profile->mapping_count; i++) { + mapping = &profile->mappings[i]; + pressed = false; + +for (j =0 ; j <ARRAY_SIZE (controls); j++) { +if (controls[j] == mapping->control) { + pressed = 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 *hidpp, +struct input_dev *input_dev) +{ +const struct hidpp_reprog_controls_profile *profile; +unsigned int i; + + profile =hidpp20_reprog_controls_get_profile (hidpp); +if (!profile) +return ; + +for (i =0 ; i < profile->mapping_count; i++) +input_set_capability (input_dev,EV_KEY , profile->mappings[i].code); +} +static void hidpp10_extra_mouse_buttons_populate_input (struct hidpp_device *hidpp,struct input_dev *input_dev) {
@@ -3859,6 +4064,9 @@ static void hidpp_populate_input(struct hidpp_device *hidpp,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); } static int hidpp_input_configured(struct hid_device *hdev,
@@ -3971,6 +4179,10 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,return ret; } + ret =hidpp20_reprog_controls_raw_event (hidpp, data, size); +if (ret !=0 ) +return ret; +if (hidpp->quirks &HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS ) { ret =hidpp10_consumer_keys_raw_event (hidpp, data, size);if (ret !=0 )
@@ -4264,6 +4476,8 @@ static void hidpp_connect_event(struct work_struct *work)return ; } +hidpp20_reprog_controls_connect (hidpp); +if (hidpp->quirks &HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS ) { ret =hidpp10_consumer_keys_connect (hidpp);if (ret)
@@ -4436,6 +4650,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) hidpp->hid_dev = hdev; hidpp->name = hdev->name; hidpp->quirks = id->driver_data; + hidpp->reprog_controls_feature_index =0xff ;hid_set_drvdata (hdev, hidpp); ret =hid_parse (hdev);
Signature · 1 line
2.54.0
Bastien Nocera
reviewerRe: [PATCH 1/2] HID: logitech-hidpp: add HID++ 2.0 reprogrammable button support
Quoted 4 lines · Elliot Douglas
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.
Quoted 8 lines
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 profiles so adding support for another mouse does not change the evdev capabilities advertised by already-supported devices.
How does this forced setting work/clash with the programmable buttons in Solaar?
I've added some inline comments below.
Quoted 51 lines
Documentation for feature
0x1b04describes divertedButtonsEvent as a list of currently pressed diverted buttons, which is the event format handled here.Signed-off-by Elliot Douglas <[email protected]>---
drivers/hid/hid-logitech-hidpp.c | 215 +++++++++++++++++++++++++++++++1 file changed, 215 insertions(+)drivers/hid/hid-logitech-hidpp.c +1
-0@@ -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)
/* These are just aliases for now */
#defineHIDPP_QUIRK_KBD_SCROLL_WHEELHIDPP_QUIRK_HIDPP_WHEELSdrivers/hid/hid-logitech-hidpp.c +0
-0@@ -205,6 +206,7 @@ struct hidpp_device { struct hidpp_scroll_counter vertical_wheel_counter;
u8wireless_feature_index;drivers/hid/hid-logitech-hidpp.c +1
-0+u8 reprog_controls_feature_index;
inthires_wheel_multiplier;
u8hires_wheel_feature_index;drivers/hid/hid-logitech-hidpp.c +0
-0@@ -3601,6 +3603,209 @@ static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp, return 1; }drivers/hid/hid-logitech-hidpp.c +15
-0+/* -------------------------------------------------------------------------- */ +/* 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)
drivers/hid/hid-logitech-hidpp.c static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp, · line 3630
Quoted 10 lines
drivers/hid/hid-logitech-hidpp.c +10
-0+ +#define HIDPP_REPROG_CONTROLS_EVENT_DIVERTED 0x00 + +struct hidpp_reprog_control_mapping { +u16 control; +u16 code; +}; + +struct hidpp_reprog_controls_profile { +const struct hidpp_reprog_control_mapping *mappings;
probably needs a __counted_by(), or maybe as it's static, it might be better to not require an intermediate struct, and return a NULL- terminated array instead.
Quoted 91 lines
drivers/hid/hid-logitech-hidpp.c +91
-0+unsigned int mapping_count; +}; + +static const struct hidpp_reprog_controls_profile * +hidpp20_reprog_controls_get_profile (struct hidpp_device *hidpp) +{ +return NULL ; +} + +static int hidpp20_reprog_controls_get_count (struct hidpp_device *hidpp) +{ +struct hidpp_report response; +u8 feature_index = hidpp->reprog_controls_feature_index; +u8 cmd =HIDPP_REPROG_CONTROLS_GET_COUNT ; +int ret; + + ret =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 = hidpp->reprog_controls_feature_index; +u8 cmd =HIDPP_REPROG_CONTROLS_GET_CID_INFO ; +int ret; + + ret =hidpp_send_fap_command_sync (hidpp, feature_index, cmd, &index, + sizeof(index), &response); +if (ret >0 ) +return -EPROTO ; +if (ret) +return ret; + + *control =get_unaligned_be16 (&response.fap.params[0 ]); + *flags = response.fap.params[4 ]; + +return 0 ; +} + +static bool hidpp20_reprog_controls_find_control (struct hidpp_device *hidpp, +u16 control) +{ +int count, ret; +u16 cid; +u8 flags; +int i; + + count =hidpp20_reprog_controls_get_count (hidpp); +if (count <0 ) +return false; + +for (i =0 ; i < count; i++) { + ret =hidpp20_reprog_controls_get_cid_info (hidpp, i, &cid, + &flags); +if (ret) +return false; + +if (cid == 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_device *hidpp, +u16 control,u8 flags) +{ +struct hidpp_report response; +u8 params[5 ]; + +put_unaligned_be16 (control, ¶ms[0 ]); + params[2 ] = 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)
drivers/hid/hid-logitech-hidpp.c static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp, · line 3731
Quoted 10 lines
drivers/hid/hid-logitech-hidpp.c +10
-0+{ +const struct hidpp_reprog_controls_profile *profile; +u8 flags =HIDPP_REPROG_CONTROLS_TEMPORARY_DIVERTED | + HIDPP_REPROG_CONTROLS_CHANGE_TEMPORARY_DIVERT; +unsigned int i; + +if (!(hidpp->quirks &HIDPP_QUIRK_HIDPP_REPROG_CONTROLS_BTNS )) +return ; + + profile =hidpp20_reprog_controls_get_profile (hidpp);
Could the profile be cached in the hidpp_device struct?
Quoted 118 lines
drivers/hid/hid-logitech-hidpp.c +77
-0+if (!profile) +return ; + +if (hidpp_root_get_feature (hidpp,HIDPP_PAGE_REPROG_CONTROLS_V4 , + &hidpp->reprog_controls_feature_index)) +return ; + +for (i =0 ; i < profile->mapping_count; i++) { +u16 control = profile->mappings[i].control; + +if (!hidpp20_reprog_controls_find_control (hidpp, control)) +continue ; + +hidpp20_reprog_controls_set_control_reporting (hidpp, control, flags); + } +} + +static int hidpp20_reprog_controls_raw_event (struct hidpp_device *hidpp, + u8 *data, int size) +{ +const struct hidpp_reprog_controls_profile *profile; +const struct hidpp_reprog_control_mapping *mapping; +struct hidpp_report *report = (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_feature_index == 0xff) +return 0 ; + + profile =hidpp20_reprog_controls_get_profile (hidpp); +if (!profile) +return 0 ; + +if (size <HIDPP_REPORT_LONG_LENGTH || + report->fap.feature_index != hidpp->reprog_controls_feature_index || + report->fap.funcindex_clientid != HIDPP_REPROG_CONTROLS_EVENT_DIVERTED) +return 0 ; + +for (i =0 ; i <ARRAY_SIZE (controls); i++) + controls[i] =get_unaligned_be16 (&report->fap.params[i *2 ]); + +for (i =0 ; i < profile->mapping_count; i++) { + mapping = &profile->mappings[i]; + pressed = false; + +for (j =0 ; j <ARRAY_SIZE (controls); j++) { +if (controls[j] == mapping->control) { + pressed = 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 *hidpp, + struct input_dev *input_dev) +{ +const struct hidpp_reprog_controls_profile *profile; +unsigned int i; + + profile =hidpp20_reprog_controls_get_profile (hidpp); +if (!profile) +return ; + +for (i =0 ; i < profile->mapping_count; i++) +input_set_capability (input_dev,EV_KEY , profile->mappings[i].code); +} +static void hidpp10_extra_mouse_buttons_populate_input(
structhidpp_device*hidpp, structinput_dev*input_dev) {drivers/hid/hid-logitech-hidpp.c +0
-0@@ -3859,6 +4064,9 @@ static void hidpp_populate_input(struct hidpp_device *hidpp,
if (hidpp->quirks&HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS)hidpp10_extra_mouse_buttons_populate_input(hidpp, input);drivers/hid/hid-logitech-hidpp.c +3
-0+ +if (hidpp->quirks &HIDPP_QUIRK_HIDPP_REPROG_CONTROLS_BTNS ) +hidpp20_reprog_controls_populate_input (hidpp, input);}
static int hidpp_input_configured(structhid_device*hdev,drivers/hid/hid-logitech-hidpp.c +0
-0@@ -3971,6 +4179,10 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, return ret; }drivers/hid/hid-logitech-hidpp.c +4
-0+ ret =hidpp20_reprog_controls_raw_event (hidpp, data, size); +if (ret !=0 ) +return ret; +if (
hidpp->quirks&HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS) { ret =hidpp10_consumer_keys_raw_event(hidpp, data, size); if (ret != 0)drivers/hid/hid-logitech-hidpp.c +0
-0@@ -4264,6 +4476,8 @@ static void hidpp_connect_event(struct work_struct *work) return; }drivers/hid/hid-logitech-hidpp.c +2
-0+hidpp20_reprog_controls_connect (hidpp); +if (
hidpp->quirks&HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS) { ret =hidpp10_consumer_keys_connect(hidpp); if (ret)drivers/hid/hid-logitech-hidpp.c +1
-0@@ -4436,6 +4650,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) hidpp->hid_dev = hdev; hidpp->name = hdev->name; hidpp->quirks = id->driver_data; + hidpp->reprog_controls_feature_index =0xff ; hid_set_drvdata(hdev, hidpp);
ret =hid_parse(hdev);
Re: [PATCH 1/2] HID: logitech-hidpp: add HID++ 2.0 reprogrammable button support
Thanks, that makes sense.
For Solaar, this is not continuously forced. The kernel only programs temporary diversion when the device connects. Solaar can still issue HID++ commands through hidraw, so if Solaar changes reporting for the same controls afterwards, the last writer wins.
If Solaar takes over those controls for custom actions, the kernel would stop receiving the diverted button notifications for normal evdev reporting until the kernel diverts the controls again, for example after reconnect. While the controls remain diverted, hidraw clients should still receive the raw HID++ reports.
I have addressed the inline comments locally for v2:
- replaced the profile/count wrapper with NULL-terminated mapping arrays
- cached the selected mapping pointer in struct hidpp_device
I'll wait for Benjamin's input to send Patch v2.
Quoted 308 lines · Bastien Nocera
Quoted 4 lines · Elliot Douglas
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.Quoted 8 lines
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 profiles so adding support for another mouse does not change the evdev capabilities advertised by already-supported devices.
How does this forced setting work/clash with the programmable buttons in Solaar?
I've added some inline comments below.
Quoted 51 lines
Documentation for feature
0x1b04describes divertedButtonsEvent as a list of currently pressed diverted buttons, which is the event format handled here.Signed-off-by Elliot Douglas <[email protected]>--- drivers/hid/hid-logitech-hidpp.c | 215 +++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+)drivers/hid/hid-logitech-hidpp.c +17
-0@@ -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)/* These are just aliases for now */ #define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS@@ -205,6 +206,7 @@ struct hidpp_device {struct hidpp_scroll_counter vertical_wheel_counter;u8 wireless_feature_index; +u8 reprog_controls_feature_index;int hires_wheel_multiplier;u8 hires_wheel_feature_index;@@ -3601,6 +3603,209 @@ static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp,return 1 ; } +/* -------------------------------------------------------------------------- */ +/* 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)drivers/hid/hid-logitech-hidpp.c static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp, · line 3630
Quoted 10 lines
drivers/hid/hid-logitech-hidpp.c +10
-0+ +#define HIDPP_REPROG_CONTROLS_EVENT_DIVERTED 0x00 + +struct hidpp_reprog_control_mapping { +u16 control; +u16 code; +}; + +struct hidpp_reprog_controls_profile { +const struct hidpp_reprog_control_mapping *mappings;probably needs a
__counted_by(), or maybe as it's static, it might be better to not require an intermediate struct, and return a NULL- terminated array instead.Quoted 91 lines
drivers/hid/hid-logitech-hidpp.c +91
-0+unsigned int mapping_count; +}; + +static const struct hidpp_reprog_controls_profile * +hidpp20_reprog_controls_get_profile (struct hidpp_device *hidpp) +{ +return NULL ; +} + +static int hidpp20_reprog_controls_get_count (struct hidpp_device *hidpp) +{ +struct hidpp_report response; +u8 feature_index = hidpp->reprog_controls_feature_index; +u8 cmd =HIDPP_REPROG_CONTROLS_GET_COUNT ; +int ret; + + ret =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 = hidpp->reprog_controls_feature_index; +u8 cmd =HIDPP_REPROG_CONTROLS_GET_CID_INFO ; +int ret; + + ret =hidpp_send_fap_command_sync (hidpp, feature_index, cmd, &index, +sizeof (index), &response); +if (ret >0 ) +return -EPROTO ; +if (ret) +return ret; + + *control =get_unaligned_be16 (&response.fap.params[0 ]); + *flags = response.fap.params[4 ]; + +return 0 ; +} + +static bool hidpp20_reprog_controls_find_control (struct hidpp_device *hidpp, +u16 control) +{ +int count, ret; +u16 cid; +u8 flags; +int i; + + count =hidpp20_reprog_controls_get_count (hidpp); +if (count <0 ) +return false; + +for (i =0 ; i < count; i++) { + ret =hidpp20_reprog_controls_get_cid_info (hidpp, i, &cid, + &flags); +if (ret) +return false; + +if (cid == 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_device *hidpp, +u16 control,u8 flags) +{ +struct hidpp_report response; +u8 params[5 ]; + +put_unaligned_be16 (control, ¶ms[0 ]); + params[2 ] = 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)drivers/hid/hid-logitech-hidpp.c static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp, · line 3731
Quoted 10 lines
drivers/hid/hid-logitech-hidpp.c +10
-0+{ +const struct hidpp_reprog_controls_profile *profile; +u8 flags =HIDPP_REPROG_CONTROLS_TEMPORARY_DIVERTED | +HIDPP_REPROG_CONTROLS_CHANGE_TEMPORARY_DIVERT ; +unsigned int i; + +if (!(hidpp->quirks &HIDPP_QUIRK_HIDPP_REPROG_CONTROLS_BTNS )) +return ; + + profile =hidpp20_reprog_controls_get_profile (hidpp);Could the profile be cached in the
hidpp_devicestruct?Quoted 118 lines
drivers/hid/hid-logitech-hidpp.c +87
-0+if (!profile) +return ; + +if (hidpp_root_get_feature (hidpp,HIDPP_PAGE_REPROG_CONTROLS_V4 , + &hidpp->reprog_controls_feature_index)) +return ; + +for (i =0 ; i < profile->mapping_count; i++) { +u16 control = profile->mappings[i].control; + +if (!hidpp20_reprog_controls_find_control (hidpp, control)) +continue ; + +hidpp20_reprog_controls_set_control_reporting (hidpp, control, flags); + } +} + +static int hidpp20_reprog_controls_raw_event (struct hidpp_device *hidpp, +u8 *data,int size) +{ +const struct hidpp_reprog_controls_profile *profile; +const struct hidpp_reprog_control_mapping *mapping; +struct hidpp_report *report = (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_feature_index ==0xff ) +return 0 ; + + profile =hidpp20_reprog_controls_get_profile (hidpp); +if (!profile) +return 0 ; + +if (size <HIDPP_REPORT_LONG_LENGTH || + report->fap.feature_index != hidpp->reprog_controls_feature_index || + report->fap.funcindex_clientid !=HIDPP_REPROG_CONTROLS_EVENT_DIVERTED ) +return 0 ; + +for (i =0 ; i <ARRAY_SIZE (controls); i++) + controls[i] =get_unaligned_be16 (&report->fap.params[i *2 ]); + +for (i =0 ; i < profile->mapping_count; i++) { + mapping = &profile->mappings[i]; + pressed = false; + +for (j =0 ; j <ARRAY_SIZE (controls); j++) { +if (controls[j] == mapping->control) { + pressed = 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 *hidpp, +struct input_dev *input_dev) +{ +const struct hidpp_reprog_controls_profile *profile; +unsigned int i; + + profile =hidpp20_reprog_controls_get_profile (hidpp); +if (!profile) +return ; + +for (i =0 ; i < profile->mapping_count; i++) +input_set_capability (input_dev,EV_KEY , profile->mappings[i].code); +} +static void hidpp10_extra_mouse_buttons_populate_input (struct hidpp_device *hidpp,struct input_dev *input_dev) {@@ -3859,6 +4064,9 @@ static void hidpp_populate_input(struct hidpp_device *hidpp,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); } static int hidpp_input_configured(struct hid_device *hdev,@@ -3971,6 +4179,10 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,return ret; } + ret =hidpp20_reprog_controls_raw_event (hidpp, data, size); +if (ret !=0 ) +return ret; +if (hidpp->quirks &HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS ) { ret =hidpp10_consumer_keys_raw_event (hidpp, data, size);if (ret !=0 )@@ -4264,6 +4476,8 @@ static void hidpp_connect_event(struct work_struct *work)return ; } +hidpp20_reprog_controls_connect (hidpp); +if (hidpp->quirks &HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS ) { ret =hidpp10_consumer_keys_connect (hidpp);if (ret)@@ -4436,6 +4650,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) hidpp->hid_dev = hdev; hidpp->name = hdev->name; hidpp->quirks = id->driver_data; + hidpp->reprog_controls_feature_index =0xff ;hid_set_drvdata (hdev, hidpp); ret =hid_parse (hdev);
Re: [PATCH 1/2] HID: logitech-hidpp: add HID++ 2.0 reprogrammable button support
Just wanted to poke on this thread again, Benjamin or Bastien, what is needed to push this forward or should I send the v2 at this point?
Thanks,
Elliot
Quoted 330 lines · Elliot Douglas
Thanks, that makes sense.
For Solaar, this is not continuously forced. The kernel only programs temporary diversion when the device connects. Solaar can still issue HID++ commands through hidraw, so if Solaar changes reporting for the same controls afterwards, the last writer wins.
If Solaar takes over those controls for custom actions, the kernel would stop receiving the diverted button notifications for normal evdev reporting until the kernel diverts the controls again, for example after reconnect. While the controls remain diverted, hidraw clients should still receive the raw HID++ reports.
I have addressed the inline comments locally for v2:
- replaced the profile/count wrapper with NULL-terminated mapping arrays
- cached the selected mapping pointer in structhidpp_deviceI'll wait for Benjamin's input to send Patch v2.
Quoted 308 lines · Bastien Nocera
Quoted 4 lines · Elliot Douglas
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.Quoted 8 lines
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 profiles so adding support for another mouse does not change the evdev capabilities advertised by already-supported devices.
How does this forced setting work/clash with the programmable buttons in Solaar?
I've added some inline comments below.
Quoted 51 lines
Documentation for feature
0x1b04describes divertedButtonsEvent as a list of currently pressed diverted buttons, which is the event format handled here.Signed-off-by Elliot Douglas <[email protected]>--- drivers/hid/hid-logitech-hidpp.c | 215 +++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+)drivers/hid/hid-logitech-hidpp.c +17
-0@@ -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)/* These are just aliases for now */ #define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS@@ -205,6 +206,7 @@ struct hidpp_device {struct hidpp_scroll_counter vertical_wheel_counter;u8 wireless_feature_index; +u8 reprog_controls_feature_index;int hires_wheel_multiplier;u8 hires_wheel_feature_index;@@ -3601,6 +3603,209 @@ static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp,return 1 ; } +/* -------------------------------------------------------------------------- */ +/* 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)drivers/hid/hid-logitech-hidpp.c static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp, · line 3630
Quoted 10 lines
drivers/hid/hid-logitech-hidpp.c +10
-0+ +#define HIDPP_REPROG_CONTROLS_EVENT_DIVERTED 0x00 + +struct hidpp_reprog_control_mapping { +u16 control; +u16 code; +}; + +struct hidpp_reprog_controls_profile { +const struct hidpp_reprog_control_mapping *mappings;probably needs a
__counted_by(), or maybe as it's static, it might be better to not require an intermediate struct, and return a NULL- terminated array instead.Quoted 91 lines
drivers/hid/hid-logitech-hidpp.c +91
-0+unsigned int mapping_count; +}; + +static const struct hidpp_reprog_controls_profile * +hidpp20_reprog_controls_get_profile (struct hidpp_device *hidpp) +{ +return NULL ; +} + +static int hidpp20_reprog_controls_get_count (struct hidpp_device *hidpp) +{ +struct hidpp_report response; +u8 feature_index = hidpp->reprog_controls_feature_index; +u8 cmd =HIDPP_REPROG_CONTROLS_GET_COUNT ; +int ret; + + ret =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 = hidpp->reprog_controls_feature_index; +u8 cmd =HIDPP_REPROG_CONTROLS_GET_CID_INFO ; +int ret; + + ret =hidpp_send_fap_command_sync (hidpp, feature_index, cmd, &index, +sizeof (index), &response); +if (ret >0 ) +return -EPROTO ; +if (ret) +return ret; + + *control =get_unaligned_be16 (&response.fap.params[0 ]); + *flags = response.fap.params[4 ]; + +return 0 ; +} + +static bool hidpp20_reprog_controls_find_control (struct hidpp_device *hidpp, +u16 control) +{ +int count, ret; +u16 cid; +u8 flags; +int i; + + count =hidpp20_reprog_controls_get_count (hidpp); +if (count <0 ) +return false; + +for (i =0 ; i < count; i++) { + ret =hidpp20_reprog_controls_get_cid_info (hidpp, i, &cid, + &flags); +if (ret) +return false; + +if (cid == 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_device *hidpp, +u16 control,u8 flags) +{ +struct hidpp_report response; +u8 params[5 ]; + +put_unaligned_be16 (control, ¶ms[0 ]); + params[2 ] = 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)drivers/hid/hid-logitech-hidpp.c static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp, · line 3731
Quoted 10 lines
drivers/hid/hid-logitech-hidpp.c +10
-0+{ +const struct hidpp_reprog_controls_profile *profile; +u8 flags =HIDPP_REPROG_CONTROLS_TEMPORARY_DIVERTED | +HIDPP_REPROG_CONTROLS_CHANGE_TEMPORARY_DIVERT ; +unsigned int i; + +if (!(hidpp->quirks &HIDPP_QUIRK_HIDPP_REPROG_CONTROLS_BTNS )) +return ; + + profile =hidpp20_reprog_controls_get_profile (hidpp);Could the profile be cached in the
hidpp_devicestruct?Quoted 118 lines
drivers/hid/hid-logitech-hidpp.c +87
-0+if (!profile) +return ; + +if (hidpp_root_get_feature (hidpp,HIDPP_PAGE_REPROG_CONTROLS_V4 , + &hidpp->reprog_controls_feature_index)) +return ; + +for (i =0 ; i < profile->mapping_count; i++) { +u16 control = profile->mappings[i].control; + +if (!hidpp20_reprog_controls_find_control (hidpp, control)) +continue ; + +hidpp20_reprog_controls_set_control_reporting (hidpp, control, flags); + } +} + +static int hidpp20_reprog_controls_raw_event (struct hidpp_device *hidpp, +u8 *data,int size) +{ +const struct hidpp_reprog_controls_profile *profile; +const struct hidpp_reprog_control_mapping *mapping; +struct hidpp_report *report = (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_feature_index ==0xff ) +return 0 ; + + profile =hidpp20_reprog_controls_get_profile (hidpp); +if (!profile) +return 0 ; + +if (size <HIDPP_REPORT_LONG_LENGTH || + report->fap.feature_index != hidpp->reprog_controls_feature_index || + report->fap.funcindex_clientid !=HIDPP_REPROG_CONTROLS_EVENT_DIVERTED ) +return 0 ; + +for (i =0 ; i <ARRAY_SIZE (controls); i++) + controls[i] =get_unaligned_be16 (&report->fap.params[i *2 ]); + +for (i =0 ; i < profile->mapping_count; i++) { + mapping = &profile->mappings[i]; + pressed = false; + +for (j =0 ; j <ARRAY_SIZE (controls); j++) { +if (controls[j] == mapping->control) { + pressed = 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 *hidpp, +struct input_dev *input_dev) +{ +const struct hidpp_reprog_controls_profile *profile; +unsigned int i; + + profile =hidpp20_reprog_controls_get_profile (hidpp); +if (!profile) +return ; + +for (i =0 ; i < profile->mapping_count; i++) +input_set_capability (input_dev,EV_KEY , profile->mappings[i].code); +} +static void hidpp10_extra_mouse_buttons_populate_input (struct hidpp_device *hidpp,struct input_dev *input_dev) {@@ -3859,6 +4064,9 @@ static void hidpp_populate_input(struct hidpp_device *hidpp,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); } static int hidpp_input_configured(struct hid_device *hdev,@@ -3971,6 +4179,10 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,return ret; } + ret =hidpp20_reprog_controls_raw_event (hidpp, data, size); +if (ret !=0 ) +return ret; +if (hidpp->quirks &HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS ) { ret =hidpp10_consumer_keys_raw_event (hidpp, data, size);if (ret !=0 )@@ -4264,6 +4476,8 @@ static void hidpp_connect_event(struct work_struct *work)return ; } +hidpp20_reprog_controls_connect (hidpp); +if (hidpp->quirks &HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS ) { ret =hidpp10_consumer_keys_connect (hidpp);if (ret)@@ -4436,6 +4650,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) hidpp->hid_dev = hdev; hidpp->name = hdev->name; hidpp->quirks = id->driver_data; + hidpp->reprog_controls_feature_index =0xff ;hid_set_drvdata (hdev, hidpp); ret =hid_parse (hdev);
Bastien Nocera
reviewerRe: [PATCH 1/2] HID: logitech-hidpp: add HID++ 2.0 reprogrammable button support
Quoted 3 lines · Elliot Douglas
Just wanted to poke on this thread again, Benjamin or Bastien, what is needed to
push this forward or should I send the v2 at this point?
You're definitely better off sending a new version when there's no feedback incoming, it acts as a gentle reminder and avoids another back and forth if there's no further comments (apart from the ones in the original review).
Quoted 412 lines
Thanks,
ElliotQuoted 406 lines · Elliot Douglas
Thanks, that makes sense.
For Solaar, this is not continuously forced. The kernel only programs
temporary diversion when the device connects. Solaar can still issue HID++
commands through hidraw, so if Solaar changes reporting for the same controls
afterwards, the last writer wins.If Solaar takes over those controls for custom actions, the kernel would stop
receiving the diverted button notifications for normal evdev reporting until
the kernel diverts the controls again, for example after reconnect. While the
controls remain diverted, hidraw clients should still receive the raw HID++
reports.I have addressed the inline comments locally for v2:
- replaced the profile/count wrapper with NULL-terminated mapping arrays
- cached the selected mapping pointer in structhidpp_deviceI'll wait for Benjamin's input to send Patch v2.
Quoted 375 lines · Bastien Nocera
Quoted 12 lines · Elliot Douglas
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.Quoted 5 lines
Keep the control mappings in per-product profiles so adding support for
another mouse does not change the evdev capabilities advertised by
already-supported devices.How does this forced setting work/clash with the programmable buttons
in Solaar?I've added some inline comments below.
Quoted 64 lines
Documentation for feature
0x1b04describes divertedButtonsEvent as a list
of currently pressed diverted buttons, which is the event format handled
here.Link:
https://lekensteyn.nl/files/logitech/x1b04_specialkeysmsebuttons.htmlSigned-off-by Elliot Douglas <[email protected]>---
drivers/hid/hid-logitech-hidpp.c | 215
+++++++++++++++++++++++++++++++1 file changed, 215 insertions(+)drivers/hid/hid-logitech-hidpp.c +2
-0@@ -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) /* These are just aliases for now */ #define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS@@ -205,6 +206,7 @@ struct hidpp_device { struct hidpp_scroll_counter vertical_wheel_counter; u8 wireless_feature_index; + u8 reprog_controls_feature_index; int hires_wheel_multiplier; u8 hires_wheel_feature_index;@@ -3601,6 +3603,209 @@ static inthidpp10_extra_mouse_buttons_raw_event(struct
hidpp_device*hidpp,
return 1;
}drivers/hid/hid-logitech-hidpp.c +2
-1+/* ----------------------------------------------------------- --------------- */ +/* HID++2.0 reprogrammable controls */
drivers/hid/hid-logitech-hidpp.c +3
-1+/* ----------------------------------------------------------- --------------- */ + +#define HIDPP_PAGE_REPROG_CONTROLS_V4
0x1b04drivers/hid/hid-logitech-hidpp.c +2
-0+ +#define HIDPP_REPROG_CONTROLS_GET_COUNT
0x00drivers/hid/hid-logitech-hidpp.c +8
-0+#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)
drivers/hid/hid-logitech-hidpp.c static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp, · line 3630
Quoted 10 lines
drivers/hid/hid-logitech-hidpp.c +10
-0+ +#define HIDPP_REPROG_CONTROLS_EVENT_DIVERTED 0x00 + +struct hidpp_reprog_control_mapping { + u16 control; + u16 code; +}; + +struct hidpp_reprog_controls_profile { + const struct hidpp_reprog_control_mapping *mappings;probably needs a
__counted_by(), or maybe as it's static, it might be
better to not require an intermediate struct, and return a NULL- terminated array instead.Quoted 117 lines
+ unsigned int
mapping_count;drivers/hid/hid-logitech-hidpp.c +4
-0+}; + +static const struct hidpp_reprog_controls_profile * +hidpp20_reprog_controls_get_profile(struct hidpp_device*hidpp)
drivers/hid/hid-logitech-hidpp.c +5
-0+{ + return NULL; +} + +static int hidpp20_reprog_controls_get_count(struct
hidpp_device*hidpp)drivers/hid/hid-logitech-hidpp.c +7
-0+{ + struct hidpp_report response; + u8 feature_index = hidpp->reprog_controls_feature_index; + u8 cmd = HIDPP_REPROG_CONTROLS_GET_COUNT; + int ret; + + ret = hidpp_send_fap_command_sync(hidpp, feature_index,cmd, NULL, 0,
+ &response); + if (ret > 0)
+ return-EPROTO;
+ if (ret)
+ return ret;drivers/hid/hid-logitech-hidpp.c +5
-0+ + return response.fap.params[0]; +} + +static int hidpp20_reprog_controls_get_cid_info(struct
hidpp_device*hidpp,
+ u8 index, u16 *control,
+ u8 *flags)drivers/hid/hid-logitech-hidpp.c +7
-0+{ + struct hidpp_report response; + u8 feature_index = hidpp->reprog_controls_feature_index; + u8 cmd = HIDPP_REPROG_CONTROLS_GET_CID_INFO; + int ret; + + ret = hidpp_send_fap_command_sync(hidpp, feature_index,cmd, &index,
+ sizeof(index), &response);
+ if (ret > 0)
+ return-EPROTO;
+ if (ret)
+ return ret;drivers/hid/hid-logitech-hidpp.c +8
-0+ + *control = get_unaligned_be16(&response.fap.params[0]); + *flags = response.fap.params[4]; + + return 0; +} + +static bool hidpp20_reprog_controls_find_control(struct
hidpp_device*hidpp,
+ u16 control)drivers/hid/hid-logitech-hidpp.c +12
-0+{ + int count, ret; + u16 cid; + u8 flags; + int i; + + count = hidpp20_reprog_controls_get_count(hidpp); + if (count < 0) + return false; + + for (i = 0; i < count; i++) { + ret = hidpp20_reprog_controls_get_cid_info(hidpp,i, &cid,
+
&flags);
+ if (ret)
+ return false;
+
+ if (cid == control)
+ return (flags &HIDPP_REPROG_CONTROLS_FLAG_MOUSE) &&
+ (flags &HIDPP_REPROG_CONTROLS_FLAG_DIVERT);
+ }drivers/hid/hid-logitech-hidpp.c +5
-0+ + return false; +} + +static inthidpp20_reprog_controls_set_control_reporting(struct
hidpp_device*hidpp,
+ u16 control, u8 flags)drivers/hid/hid-logitech-hidpp.c +10
-0+{ + struct hidpp_report response; + u8 params[5]; + + put_unaligned_be16(control, ¶ms[0]); + params[2] = flags; + put_unaligned_be16(control, ¶ms[3]); + + return hidpp_send_fap_command_sync(hidpp, + hidpp-Quoted 1 line
reprog_controls_feature_index,+
HIDPP_REPROG_CONTROLS_SET_CONTROL_REPORTING,
+ params,
sizeof(params), &response);drivers/hid/hid-logitech-hidpp.c +3
-0+} + +static void hidpp20_reprog_controls_connect(struct
hidpp_device*hidpp)drivers/hid/hid-logitech-hidpp.c +7
-0+{ + const struct hidpp_reprog_controls_profile *profile; + u8 flags = HIDPP_REPROG_CONTROLS_TEMPORARY_DIVERTED | + HIDPP_REPROG_CONTROLS_CHANGE_TEMPORARY_DIVERT; + unsigned int i; + + if (!(hidpp->quirks &Quoted 4 lines
HIDPP_QUIRK_HIDPP_REPROG_CONTROLS_BTNS))
+ return;
+
+ profile =hidpp20_reprog_controls_get_profile(hidpp);Could the profile be cached in the
hidpp_devicestruct?Quoted 145 lines
+ if (!profile)
+ return;
+
+ if (hidpp_root_get_feature(hidpp,HIDPP_PAGE_REPROG_CONTROLS_V4,
+ &hidpp-Quoted 1 line
reprog_controls_feature_index))+ return;
drivers/hid/hid-logitech-hidpp.c +5
-0+ + for (i = 0; i < profile->mapping_count; i++) { + u16 control = profile->mappings[i].control; + + if (!hidpp20_reprog_controls_find_control(hidpp,control))
+ continue;
+
+hidpp20_reprog_controls_set_control_reporting(hidpp, control, flags);
+ }drivers/hid/hid-logitech-hidpp.c +3
-0+} + +static int hidpp20_reprog_controls_raw_event(struct
hidpp_device*hidpp,
+ u8 *data, int size) +{
+ const structhidpp_reprog_controls_profile*profile; + const structhidpp_reprog_control_mapping*mapping; + structhidpp_report*report = (structhidpp_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_feature_index==0xff) + return 0;drivers/hid/hid-logitech-hidpp.c +7
-0+ + profile = hidpp20_reprog_controls_get_profile(hidpp); + if (!profile) + return 0; + + if (size < HIDPP_REPORT_LONG_LENGTH || + report->fap.feature_index != hidpp-Quoted 1 line
reprog_controls_feature_index||+
report->fap.funcindex_clientid!=HIDPP_REPROG_CONTROLS_EVENT_DIVERTED)
+ return 0;
+
+ for (i = 0; i <ARRAY_SIZE(controls); i++) + controls[i] = get_unaligned_be16(&report-Quoted 1 line
fap.params[i * 2]);
drivers/hid/hid-logitech-hidpp.c +13
-0+ + for (i = 0; i < profile->mapping_count; i++) { + mapping = &profile->mappings[i]; + pressed = false; + + for (j = 0; j < ARRAY_SIZE(controls); j++) { + if (controls[j] == mapping->control) { + pressed = true; + break; + } + } + + input_report_key(hidpp->input, mapping->code,pressed);
+ }drivers/hid/hid-logitech-hidpp.c +7
-0+ + input_sync(hidpp->input); + + return 1; +} + +static void hidpp20_reprog_controls_populate_input(struct
hidpp_device*hidpp,
+ structinput_dev*input_dev)drivers/hid/hid-logitech-hidpp.c +10
-0+{ + const struct hidpp_reprog_controls_profile *profile; + unsigned int i; + + profile = hidpp20_reprog_controls_get_profile(hidpp); + if (!profile) + return; + + for (i = 0; i < profile->mapping_count; i++) + input_set_capability(input_dev, EV_KEY, profile-Quoted 1 line
mappings[i].code);
drivers/hid/hid-logitech-hidpp.c +2
-0+} +static void
hidpp10_extra_mouse_buttons_populate_input( struct hidpp_device *hidpp, struct input_dev *input_dev)
{drivers/hid/hid-logitech-hidpp.c +3
-0@@ -3859,6 +4064,9 @@ static void hidpp_populate_input(struct hidpp_device *hidpp, 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);
}static int hidpp_input_configured(struct
hid_device*hdev,drivers/hid/hid-logitech-hidpp.c +4
-0@@ -3971,6 +4179,10 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, return ret; } + ret = hidpp20_reprog_controls_raw_event(hidpp, data, size); + if (ret != 0) + return ret; +if (
hidpp->quirks&HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS) {
ret =hidpp10_consumer_keys_raw_event(hidpp, data, size);
if (ret != 0)drivers/hid/hid-logitech-hidpp.c +2
-0@@ -4264,6 +4476,8 @@ static void hidpp_connect_event(struct work_struct *work) return; } + hidpp20_reprog_controls_connect(hidpp); +if (
hidpp->quirks&HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS) {
ret =hidpp10_consumer_keys_connect(hidpp); if (ret)drivers/hid/hid-logitech-hidpp.c +1
-0@@ -4436,6 +4650,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) hidpp->hid_dev = hdev; hidpp->name = hdev->name; hidpp->quirks = id->driver_data; + hidpp->reprog_controls_feature_index = 0xff; hid_set_drvdata(hdev, hidpp);ret =
hid_parse(hdev);
PATCH 2/2 HID: logitech-hidpp: enable reprogrammable buttons on Signature M650
In reply to Elliot DouglasThe 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 long enough 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.
--- drivers/hid/hid-logitech-hidpp.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-)
drivers/hid/hid-logitech-hidpp.c +20 -1
@@ -3621,6 +3621,9 @@ static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp,#define HIDPP_REPROG_CONTROLS_EVENT_DIVERTED 0x00 +#define HIDPP_REPROG_CONTROL_M650_BACK 0x0053 +#define HIDPP_REPROG_CONTROL_M650_FORWARD 0x0056 +struct hidpp_reprog_control_mapping {u16 control;u16 code;
@@ -3631,9 +3634,24 @@ struct hidpp_reprog_controls_profile {unsigned int mapping_count; }; +static const struct hidpp_reprog_control_mapping m650_reprog_control_mappings[] = { + {HIDPP_REPROG_CONTROL_M650_BACK ,BTN_BACK }, + {HIDPP_REPROG_CONTROL_M650_FORWARD ,BTN_FORWARD }, +}; + +static const struct hidpp_reprog_controls_profile m650_reprog_controls_profile = { + .mappings = m650_reprog_control_mappings, + .mapping_count =ARRAY_SIZE (m650_reprog_control_mappings), +}; +static const struct hidpp_reprog_controls_profile *hidpp20_reprog_controls_get_profile (struct hidpp_device *hidpp) { +switch (hidpp->hid_dev->product) { +case 0xb02a : +return &m650_reprog_controls_profile; + } +return NULL ; }
@@ -4921,7 +4939,8 @@ static const struct hid_device_id hidpp_devices[] = { {/* 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, 0xb02a), + .driver_data = 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 */
Signature · 1 line
2.54.0
Bastien Nocera
reviewerRe: [PATCH 2/2] HID: logitech-hidpp: enable reprogrammable buttons on Signature M650
Hey Elliot,
I have an m650 on hand on with which I should be able to test your patch next week, once we've gone through a first round of reviews.
Benjamin, is there something I can capture locally that could make it into the HID tests for this feature?
Inline comments below
Quoted 36 lines · Elliot Douglas
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 long enough can prevent the normal mouse report from
emitting a usable button event at all.HID++
REPROG_CONTROLS_V4diversion for control IDs0x0053and0x0056provides 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 asBTN_BACKandBTN_FORWARD.Link:
https://support.logi.com/hc/en-nz/articles/4414473810583-Getting-Started-Signature-M650Signed-off-by Elliot Douglas <[email protected]>---
drivers/hid/hid-logitech-hidpp.c | 21 ++++++++++++++++++++-1 file changed, 20 insertions(+), 1 deletion(-)drivers/hid/hid- renamed from drivers/hid/hid-logitech-hidpp.c +0
-0
logitech-hidpp.c
index24c9cfaa4f37..80108778ee80100644drivers/hid/hid-logitech-hidpp.c +0
-0@@ -3621,6 +3621,9 @@ static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp,
#defineHIDPP_REPROG_CONTROLS_EVENT_DIVERTED0x00
drivers/hid/hid-logitech-hidpp.c static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp, · line 3625
Quoted 2 lines
drivers/hid/hid-logitech-hidpp.c +2
-0+#define HIDPP_REPROG_CONTROL_M650_BACK 0x0053 +#define HIDPP_REPROG_CONTROL_M650_FORWARD 0x0056
I read through the 0x1b04 docs, and I'm trying to understand whether those CID numbers change in different hardware, or if they're hardcoded (a back button will always have the 83/0x53 value).
Seems to me that it's the latter? The back button is listed in the Example Control ID table in the 0x1b04 docs.
Quoted 23 lines
+
structhidpp_reprog_control_mapping{
u16 control;
u16 code;drivers/hid/hid-logitech-hidpp.c +0
-0@@ -3631,9 +3634,24 @@ struct hidpp_reprog_controls_profile { unsigned int mapping_count; };
+static const structhidpp_reprog_control_mapping
m650_reprog_control_mappings[] = {drivers/hid/hid-logitech-hidpp.c +5
-0+ { HIDPP_REPROG_CONTROL_M650_BACK, BTN_BACK }, + { HIDPP_REPROG_CONTROL_M650_FORWARD, BTN_FORWARD }, +}; + +static const struct hidpp_reprog_controls_profile
m650_reprog_controls_profile= {drivers/hid/hid-logitech-hidpp.c +4
-0+ .mappings = m650_reprog_control_mappings, + .mapping_count = ARRAY_SIZE(m650_reprog_control_mappings), +}; +static const struct
hidpp_reprog_controls_profile*hidpp20_reprog_controls_get_profile(struct hidpp_device *hidpp){
drivers/hid/hid-logitech-hidpp.c struct hidpp_reprog_controls_profile { · line 3651
Quoted 2 lines
drivers/hid/hid-logitech-hidpp.c +2
-0+switch (hidpp->hid_dev->product) { +case 0xb02a :
You probably want a define for that constant.
Quoted 17 lines
drivers/hid/hid-logitech-hidpp.c +3
-0+return &m650_reprog_controls_profile; + } +return NULL;
}drivers/hid/hid-logitech-hidpp.c +0
-0@@ -4921,7 +4939,8 @@ static const struct hid_device_id hidpp_devices[] = {{ /* MX Vertical mouse over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb020)}, { /* Signature M650 over Bluetooth */drivers/hid/hid-logitech-hidpp.c +2
-1- HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb02a) }, + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb02a), + .driver_data = 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 */
v2 cover HID: logitech-hidpp: fix Signature M650 side button timing
In reply to Elliot DouglasThe Logitech Signature M650 over Bluetooth exposes its side buttons in the normal mouse report, but the reported BTN_SIDE/BTN_EXTRA events are short click-like events emitted around button release rather than physical press/release events with the real hold duration. The device appears to reserve the held side-button state for a built-in gesture mode: holding a side button long enough, or holding it while using the wheel for horizontal scrolling, can mean the normal mouse report never emits a usable side-button press at all. That makes the buttons unusable for standard Linux hold actions such as push-to-talk, drag modifiers, or remapping rules that depend on key-up timing.
When HID++ 2.0 feature 0x1b04, SpecialKeysMseButtons / REPROG_CONTROLS_V4, temporarily diverts the same controls, the device sends diverted-control notifications with real press and release timing. This series adds quirk-gated support for those notifications and enables it for the Bluetooth Signature M650.
Before enabling diversion, the driver verifies that each mapped control is present in the device's HID++ control table and is advertised as a divertable mouse control.
The kernel only programs temporary diversion when the device connects. It does not continuously force the setting. A userspace HID++ tool such as Solaar can still issue HID++ commands through hidraw; if Solaar changes the reporting mode for the same controls afterwards, the last writer wins. That means Solaar can still take over those controls for custom actions, but the kernel will no longer receive the diverted button notifications for normal evdev reporting until the kernel diverts the controls again, for example after reconnect. While the controls remain diverted, hidraw clients should still receive the raw reports and the kernel reports the matching evdev state.
The diverted M650 controls are reported as BTN_BACK and BTN_FORWARD. Logitech's Signature M650 getting-started page labels these physical controls as Back/Forward buttons and describes their default page-navigation behavior:
https://support.logi.com/hc/en-nz/articles/4414473810583-Getting-Started-Signature-M650
The reprogrammable-control support is per-product and parses the full HID++ divertedButtonsEvent pressed-control list, so it can support devices with more buttons without relying on a single last-control release heuristic. Only the Signature M650 opts in for now. Other Logitech devices should only be enabled after their HID++ control IDs and divertedButtonsEvent behavior are captured and verified.
There is evidence that this is not unique to the M650. A prior MX Anywhere 3 patch used the same HID++ feature to fix thumb buttons that only activated on release, and Logitech documents side-button + wheel horizontal scrolling for both the MX Anywhere 3/3S and Signature M650. Solaar's device reports and rules documentation also show HID++ divertable back/forward controls on MX Master 3 and MX Master 3S class devices. This series remains conservative and only enables the device tested here.
Tested with a Logitech Signature M650 L over Bluetooth, HID ID 0005:046D:B02A. Baseline evtest showed short release-time BTN_SIDE/BTN_EXTRA events. Earlier local testing of the same HID++ diversion path showed real hold-duration press/release events, including holds longer than 4 seconds for both buttons.
Changes in v2:
- Replace the profile/count wrapper with NULL-terminated mapping arrays.
- Cache the selected reprogrammable-control mapping in struct hidpp_device.
- Add a named constant for the M650 Bluetooth product ID.
- Use common Back/Forward names for the 0x0053/0x0056 HID++ control IDs.
Elliot Douglas (2):
HID: logitech-hidpp: add HID++ 2.0 reprogrammable button support
HID: logitech-hidpp: enable reprogrammable buttons on Signature M650
drivers/hid/hid-logitech-hidpp.c | 225 ++++++++++++++++++++++++++++++- 1 file changed, 224 insertions(+), 1 deletion(-)
base-commit: f0866517be9345d8245d32b722574b8aecccb348
Signature · 1 line
2.54.0
PATCH v2 1/2 HID: logitech-hidpp: add HID++ 2.0 reprogrammable button support
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.
--- drivers/hid/hid-logitech-hidpp.c | 205 +++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+)
drivers/hid/hid-logitech-hidpp.c +205 -0
@@ -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)/* 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; }; +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;u8 wireless_feature_index; +u8 reprog_controls_feature_index; +const struct hidpp_reprog_control_mapping *reprog_controls;int hires_wheel_multiplier;u8 hires_wheel_feature_index;
@@ -3601,6 +3606,195 @@ static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp,return 1 ; } +/* -------------------------------------------------------------------------- */ +/* 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 = hidpp->reprog_controls_feature_index; +u8 cmd =HIDPP_REPROG_CONTROLS_GET_COUNT ; +int ret; + + ret =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 = hidpp->reprog_controls_feature_index; +u8 cmd =HIDPP_REPROG_CONTROLS_GET_CID_INFO ; +int ret; + + ret =hidpp_send_fap_command_sync (hidpp, feature_index, cmd, &index, +sizeof (index), &response); +if (ret >0 ) +return -EPROTO ; +if (ret) +return ret; + + *control =get_unaligned_be16 (&response.fap.params[0 ]); + *flags = response.fap.params[4 ]; + +return 0 ; +} + +static bool hidpp20_reprog_controls_find_control (struct hidpp_device *hidpp, +u16 control) +{ +int count, ret; +u16 cid; +u8 flags; +int i; + + count =hidpp20_reprog_controls_get_count (hidpp); +if (count <0 ) +return false; + +for (i =0 ; i < count; i++) { + ret =hidpp20_reprog_controls_get_cid_info (hidpp, i, &cid, + &flags); +if (ret) +return false; + +if (cid == 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_device *hidpp, +u16 control,u8 flags) +{ +struct hidpp_report response; +u8 params[5 ]; + +put_unaligned_be16 (control, ¶ms[0 ]); + params[2 ] = 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 =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 = 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 = (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 ==0xff ) +return 0 ; + +if (size <HIDPP_REPORT_LONG_LENGTH || + report->fap.feature_index != hidpp->reprog_controls_feature_index || + report->fap.funcindex_clientid !=HIDPP_REPROG_CONTROLS_EVENT_DIVERTED ) +return 0 ; + +for (i =0 ; i <ARRAY_SIZE (controls); i++) + controls[i] =get_unaligned_be16 (&report->fap.params[i *2 ]); + +for (mapping = hidpp->reprog_controls; mapping->control; mapping++) { + pressed = false; + +for (j =0 ; j <ARRAY_SIZE (controls); j++) { +if (controls[j] == mapping->control) { + pressed = 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 *hidpp, +struct input_dev *input_dev) +{ +const struct hidpp_reprog_control_mapping *mapping; + +if (!hidpp->reprog_controls) +return ; + +for (mapping = 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,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); } 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; } + ret =hidpp20_reprog_controls_raw_event (hidpp, data, size); +if (ret !=0 ) +return ret; +if (hidpp->quirks &HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS ) { ret =hidpp10_consumer_keys_raw_event (hidpp, data, size);if (ret !=0 )
@@ -4264,6 +4465,8 @@ static void hidpp_connect_event(struct work_struct *work)return ; } +hidpp20_reprog_controls_connect (hidpp); +if (hidpp->quirks &HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS ) { ret =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 = hdev; hidpp->name = hdev->name; hidpp->quirks = id->driver_data; + hidpp->reprog_controls_feature_index =0xff ; + hidpp->reprog_controls =hidpp20_reprog_controls_get_mappings (hidpp);hid_set_drvdata (hdev, hidpp); ret =hid_parse (hdev);
Signature · 1 line
2.54.0
[email protected]
AI reviewRe: [PATCH v2 1/2] HID: logitech-hidpp: add HID++ 2.0 reprogrammable button support
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Dangling pointer hidpp->input leads to Use-After-Free if input_register_device() fails.
- [High] Unbounded 64-byte structure copy in hidpp_raw_hidpp_event causes a Kernel Heap Out-of-Bounds Read. --
commit 13dc21807e8eb6f76d9029e89353e4bd2e6aa1c3
Author: Elliot Douglas <[email protected]>
Subject: HID: logitech-hidpp: add HID++ 2.0 reprogrammable button support
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.
Quoted 4 lines
drivers/hid/hid-logitech-hidpp.c +0
-0
[ ... ]
Quoted 1 line
drivers/hid/hid-logitech-hidpp.c +0
-0@@ -3601,6 +3606,195 @@ static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp,
[ ... ]
drivers/hid/hid-logitech-hidpp.c static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp, · line 3755
Quoted 11 lines
drivers/hid/hid-logitech-hidpp.c +11
-0+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 = (struct hidpp_report *)data; +u16 controls[4 ]; +bool pressed; +unsigned int i, j; + + if (!(hidpp->quirks & HIDPP_QUIRK_HIDPP_REPROG_CONTROLS_BTNS) || + !hidpp->input ||
[Severity: High]
This is a pre-existing issue, but could this lead to a use-after-free?
Looking at hidpp_connect_event(), if input_register_device() fails, it cleans up by calling input_free_device() but leaves the hidpp->input pointer dangling:
ret = input_register_device(input);
if (ret) {
input_free_device(input);
return;
}Because hid_device_io_start() has already enabled incoming packets before this point, could a newly arriving event trigger this new hidpp20_reprog_controls_raw_event() handler, pass the !hidpp->input check, and dereference the freed pointer?
Quoted 3 lines
drivers/hid/hid-logitech-hidpp.c +3
-0+ !hidpp->reprog_controls || + hidpp->reprog_controls_feature_index == 0xff) +return 0 ;
[ ... ]
drivers/hid/hid-logitech-hidpp.c static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, · line 4174
Quoted 8 lines
drivers/hid/hid-logitech-hidpp.c +4
-0@@ -3971,6 +4168,10 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,return ret; } + ret =hidpp20_reprog_controls_raw_event (hidpp, data, size); +if (ret !=0 ) +return ret; +
[Severity: High]
This is a pre-existing issue, but does hidpp_raw_hidpp_event() perform an out-of-bounds read on the incoming data buffer?
Earlier in this function, if the packet matches a pending question, it does a direct structure assignment:
if (hidpp_match_answer(question, report) ||
hidpp_match_error(question, report)) {
*answer = *report;Since struct hidpp_report is a 64-byte packed structure, won't this pointer assignment perform a fixed 64-byte copy regardless of the actual size of the received buffer (which could be as small as 7 bytes for a REPORT_ID_HIDPP_SHORT packet)?
Could this copy adjacent heap memory into the answer buffer, potentially leaking it when the data is parsed later?
Quoted 3 lines
drivers/hid/hid-logitech-hidpp.c +0
-0if (hidpp->quirks &HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS ) { ret =hidpp10_consumer_keys_raw_event (hidpp, data, size);if (ret !=0 )
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
PATCH v2 2/2 HID: logitech-hidpp: enable reprogrammable buttons on Signature M650
In reply to Elliot DouglasThe 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 long enough 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.
--- drivers/hid/hid-logitech-hidpp.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)
drivers/hid/hid-logitech-hidpp.c +19 -1
@@ -3624,14 +3624,30 @@ static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp,#define HIDPP_REPROG_CONTROLS_EVENT_DIVERTED 0x00 +#define HIDPP_REPROG_CONTROL_BACK 0x0053 +#define HIDPP_REPROG_CONTROL_FORWARD 0x0056 + +#define HIDPP_PRODUCT_SIGNATURE_M650 0xb02a +struct hidpp_reprog_control_mapping {u16 control;u16 code; }; +static const struct hidpp_reprog_control_mapping m650_reprog_control_mappings[] = { + {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 HIDPP_PRODUCT_SIGNATURE_M650 : +return m650_reprog_control_mappings; + } +return NULL ; }
@@ -4911,7 +4927,9 @@ static const struct hid_device_id hidpp_devices[] = { {/* 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, + HIDPP_PRODUCT_SIGNATURE_M650), + .driver_data = 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 */
Signature · 1 line
2.54.0
Re: [PATCH v2 2/2] HID: logitech-hidpp: enable reprogrammable buttons on Signature M650
Quoted 9 lines · Elliot Douglas
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 long enough
s/long enough/more than approximately 2 seconds/ ?
Quoted 16 lines
can prevent the normal mouse report from
emitting a usable button event at all.HID++
REPROG_CONTROLS_V4diversion for control IDs0x0053and0x0056provides 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 asBTN_BACKandBTN_FORWARD.The HID++
0x1b04documentation 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.
Quoted 3 lines
Link:
https://support.logi.com/hc/en-nz/articles/4414473810583-Getting-Started-Signature-M650Signed-off-by Elliot Douglas <[email protected]>
Tested with my M650 mouse connected through Bluetooth.
Quoted 14 lines
---
drivers/hid/hid-logitech-hidpp.c | 20 +++++++++++++++++++-1 file changed, 19 insertions(+), 1 deletion(-)drivers/hid/hid- renamed from drivers/hid/hid-logitech-hidpp.c +0
-0
logitech-hidpp.c
indexf9189e14fb78..e71dd2457e80100644drivers/hid/hid-logitech-hidpp.c +0
-0@@ -3624,14 +3624,30 @@ static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp,
#defineHIDPP_REPROG_CONTROLS_EVENT_DIVERTED0x00
drivers/hid/hid-logitech-hidpp.c static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp, · line 3630
Quoted 4 lines
drivers/hid/hid-logitech-hidpp.c +4
-0+#define HIDPP_REPROG_CONTROL_BACK 0x0053 +#define HIDPP_REPROG_CONTROL_FORWARD 0x0056 + +#define HIDPP_PRODUCT_SIGNATURE_M650 0xb02a
If you want to add a product ID constant, please do it in hid-ids.h instead.
Quoted 36 lines
+
structhidpp_reprog_control_mapping{
u16 control;
u16 code;
};
+static const structhidpp_reprog_control_mapping
m650_reprog_control_mappings[] = {
+ {HIDPP_REPROG_CONTROL_BACK,BTN_BACK},
+ {HIDPP_REPROG_CONTROL_FORWARD,BTN_FORWARD},
+ { }drivers/hid/hid-logitech-hidpp.c +2
-0+}; +static const struct
hidpp_reprog_control_mapping*hidpp20_reprog_controls_get_mappings(struct hidpp_device *hidpp){
+ switch (hidpp->hid_dev->product) {
+ caseHIDPP_PRODUCT_SIGNATURE_M650:
+ returnm650_reprog_control_mappings;
+ }
+
return NULL;
}drivers/hid/hid-logitech-hidpp.c +0
-0@@ -4911,7 +4927,9 @@ static const struct hid_device_id hidpp_devices[] = {{ /* 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,
+HIDPP_PRODUCT_SIGNATURE_M650),
+ .driver_data =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 */
Re: [PATCH v2 2/2] HID: logitech-hidpp: enable reprogrammable buttons on Signature M650
Thanks for the feedback. I just left for a trip, but I'll be back and should be able to work on it on Tuesday, August 11th.
Quoted 96 lines · Bastien Nocera
Quoted 9 lines · Elliot Douglas
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 long enough
s/long enough/more than approximately 2 seconds/ ?
Quoted 16 lines
can prevent the normal mouse report from
emitting a usable button event at all.HID++
REPROG_CONTROLS_V4diversion for control IDs0x0053and0x0056provides 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 asBTN_BACKandBTN_FORWARD.The HID++
0x1b04documentation 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.Quoted 3 lines
Link:
https://support.logi.com/hc/en-nz/articles/4414473810583-Getting-Started-Signature-M650Signed-off-by Elliot Douglas <[email protected]>Tested with my M650 mouse connected through Bluetooth.
Reviewed-by Bastien Nocera <[email protected]>Quoted 10 lines
--- drivers/hid/hid-logitech-hidpp.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)drivers/hid/hid- renamed from drivers/hid/hid-logitech-hidpp.c +0
-0
logitech-hidpp.c
indexf9189e14fb78..e71dd2457e80100644drivers/hid/hid-logitech-hidpp.c static int · line 3631
Quoted 8 lines
drivers/hid/hid-logitech-hidpp.c +4
-0@@ -3624,14 +3624,30 @@ static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp,#define HIDPP_REPROG_CONTROLS_EVENT_DIVERTED 0x00 +#define HIDPP_REPROG_CONTROL_BACK 0x0053 +#define HIDPP_REPROG_CONTROL_FORWARD 0x0056 + +#define HIDPP_PRODUCT_SIGNATURE_M650 0xb02aIf you want to add a product ID constant, please do it in
hid-ids.hinstead.Quoted 36 lines
drivers/hid/hid-logitech-hidpp.c +12
-0+struct hidpp_reprog_control_mapping {u16 control;u16 code; }; +static const struct hidpp_reprog_control_mapping m650_reprog_control_mappings[] = { + {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 HIDPP_PRODUCT_SIGNATURE_M650 : +return m650_reprog_control_mappings; + } +return NULL ;}
drivers/hid/hid-logitech-hidpp.c +3
-1@@ -4911,7 +4927,9 @@ static const struct hid_device_id hidpp_devices[] = { {/* 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, + HIDPP_PRODUCT_SIGNATURE_M650), + .driver_data = 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 */
Re: [PATCH v2 2/2] HID: logitech-hidpp: enable reprogrammable buttons on Signature M650
In reply to Bastien NoceraQuoted 8 lines · Bastien Nocera
Quoted 6 lines · Elliot Douglas
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.
Quoted 5 lines
Quoted 3 lines
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 long enough
s/long enough/more than approximately 2 seconds/ ?
It was about 2 seconds, I measured it.
Quoted 38 lines
Quoted 16 lines
can prevent the normal mouse report from
emitting a usable button event at all.HID++
REPROG_CONTROLS_V4diversion for control IDs0x0053and0x0056provides 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 asBTN_BACKandBTN_FORWARD.The HID++
0x1b04documentation 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.Quoted 3 lines
Link:
https://support.logi.com/hc/en-nz/articles/4414473810583-Getting-Started-Signature-M650Signed-off-by Elliot Douglas <[email protected]>Tested with my M650 mouse connected through Bluetooth.
Reviewed-by Bastien Nocera <[email protected]>Quoted 14 lines
--- drivers/hid/hid-logitech-hidpp.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)drivers/hid/hid- renamed from drivers/hid/hid-logitech-hidpp.c +0
-0
logitech-hidpp.c
indexf9189e14fb78..e71dd2457e80100644drivers/hid/hid-logitech-hidpp.c +0
-0@@ -3624,14 +3624,30 @@ static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp,#define HIDPP_REPROG_CONTROLS_EVENT_DIVERTED 0x00
Quoted 7 lines
drivers/hid/hid-logitech-hidpp.c static int hidpp10_extra_mouse_buttons_raw_event(struct hidpp_device *hidpp, · line 3630
Quoted 4 lines
drivers/hid/hid-logitech-hidpp.c +4
-0+#define HIDPP_REPROG_CONTROL_BACK 0x0053 +#define HIDPP_REPROG_CONTROL_FORWARD 0x0056 + +#define HIDPP_PRODUCT_SIGNATURE_M650 0xb02aIf you want to add a product ID constant, please do it in
hid-ids.hinstead.
I'll address this in the next patch revision.
Quoted 36 lines
Quoted 36 lines
drivers/hid/hid-logitech-hidpp.c +12
-0+struct hidpp_reprog_control_mapping {u16 control;u16 code; }; +static const struct hidpp_reprog_control_mapping m650_reprog_control_mappings[] = { + {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 HIDPP_PRODUCT_SIGNATURE_M650 : +return m650_reprog_control_mappings; + } +return NULL ;}
drivers/hid/hid-logitech-hidpp.c +3
-1@@ -4911,7 +4927,9 @@ static const struct hid_device_id hidpp_devices[] = { {/* 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, + HIDPP_PRODUCT_SIGNATURE_M650), + .driver_data = 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 */
Applied, thanks.
Signature · 2 lines
Jiri Kosina SUSE Labs