Skip to content

Commit 42bc4f3

Browse files
jwrdegoedebentiss
authored andcommitted
HID: logitech-hidpp: add support for HID++ 1.0 consumer keys reports
All Logitech 27 MHz keyboards and also the MX5000 bluetooth keyboard use Logitech custom usages of 0x10xx in the consumer page. The descriptor for the consumer input-report only declares usages up to 652, so we end up dropping all the input-reports reporting 0x10xx usages without reporting events for these to userspace. This commit adds a descriptor_fixup function for this which changes the usage and logical maximum to 0x107f. Mapping these usages to something other then KEY_UNKNOWN is left to userspace (hwdb). Note: 1. The old descriptor_fixup for this in hid-lg.c used a maximimum of 0x104d this is not high enough, the S520 keyboard battery key sends 0x106f. 2. The descriptor_fixup is flexible so that it works with both the kbd- desc. passed by the logitech-dj code and with bluetooth descriptors. The descriptor_fixup makes most keys work on 27 MHz keyboards, but it is not enough to get all keys to work on 27 MHz keyboards and just the fixup is not enough to get the MX5000 to generate 0x10xx events: 1) The LX501 and MX3000 27 MHz kbds both have a button labelled "media" (called "Media Player" by SetPoint) and a button with a remote-control symbol ("Media Life" in SetPoint) which both send an identical consumer usage-page code (0x0183) making the 2 buttons indistinguishable, switching to HID++ 1.0 consumer keys reports makes the remote-control symbol button generate a 0x10xx Logitech specific code instead. 2) The MX5000 Bluetooth keyboard has 11 keys which report 0x10xx consumer page usages, but unlike 27 MHz devices which happily send 0x10xx codes in their normal consumer-page input-report, the MX5000 honors the maximum of 652 from its descriptor and sends a 0x0000 code (so release) whenever these keys are pressed. When switching to HID++ sub-id 0x03 HID++ 1.0 consumer keys reports these 0x10xx codes do get properly reported. This commit adds support for HID++ 1.0 consumer keys reports and enables this for all 27 MHz keyboards and for the MX5000. Signed-off-by: Hans de Goede <[email protected]> Signed-off-by: Benjamin Tissoires <[email protected]>
1 parent 7457bc1 commit 42bc4f3

File tree

1 file changed

+121
-12
lines changed

1 file changed

+121
-12
lines changed

drivers/hid/hid-logitech-hidpp.c

Lines changed: 121 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
5353
#define HIDPP_REPORT_LONG_LENGTH 20
5454
#define HIDPP_REPORT_VERY_LONG_MAX_LENGTH 64
5555

56+
#define HIDPP_SUB_ID_CONSUMER_VENDOR_KEYS 0x03
5657
#define HIDPP_SUB_ID_ROLLER 0x05
5758
#define HIDPP_SUB_ID_MOUSE_EXTRA_BTNS 0x06
5859

@@ -73,6 +74,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
7374
#define HIDPP_QUIRK_HI_RES_SCROLL_X2121 BIT(28)
7475
#define HIDPP_QUIRK_HIDPP_WHEELS BIT(29)
7576
#define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS BIT(30)
77+
#define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS BIT(31)
7678

7779
/* These are just aliases for now */
7880
#define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS
@@ -2851,6 +2853,71 @@ static void hidpp10_extra_mouse_buttons_populate_input(
28512853
__set_bit(BTN_7, input_dev->keybit);
28522854
}
28532855

2856+
/* -------------------------------------------------------------------------- */
2857+
/* HID++1.0 kbds which only report 0x10xx consumer usages through sub-id 0x03 */
2858+
/* -------------------------------------------------------------------------- */
2859+
2860+
/* Find the consumer-page input report desc and change Maximums to 0x107f */
2861+
static u8 *hidpp10_consumer_keys_report_fixup(struct hidpp_device *hidpp,
2862+
u8 *_rdesc, unsigned int *rsize)
2863+
{
2864+
/* Note 0 terminated so we can use strnstr to search for this. */
2865+
const char consumer_rdesc_start[] = {
2866+
0x05, 0x0C, /* USAGE_PAGE (Consumer Devices) */
2867+
0x09, 0x01, /* USAGE (Consumer Control) */
2868+
0xA1, 0x01, /* COLLECTION (Application) */
2869+
0x85, 0x03, /* REPORT_ID = 3 */
2870+
0x75, 0x10, /* REPORT_SIZE (16) */
2871+
0x95, 0x02, /* REPORT_COUNT (2) */
2872+
0x15, 0x01, /* LOGICAL_MIN (1) */
2873+
0x26, 0x00 /* LOGICAL_MAX (... */
2874+
};
2875+
char *consumer_rdesc, *rdesc = (char *)_rdesc;
2876+
unsigned int size;
2877+
2878+
consumer_rdesc = strnstr(rdesc, consumer_rdesc_start, *rsize);
2879+
size = *rsize - (consumer_rdesc - rdesc);
2880+
if (consumer_rdesc && size >= 25) {
2881+
consumer_rdesc[15] = 0x7f;
2882+
consumer_rdesc[16] = 0x10;
2883+
consumer_rdesc[20] = 0x7f;
2884+
consumer_rdesc[21] = 0x10;
2885+
}
2886+
return _rdesc;
2887+
}
2888+
2889+
static int hidpp10_consumer_keys_connect(struct hidpp_device *hidpp)
2890+
{
2891+
return hidpp10_set_register(hidpp, HIDPP_REG_ENABLE_REPORTS, 0,
2892+
HIDPP_ENABLE_CONSUMER_REPORT,
2893+
HIDPP_ENABLE_CONSUMER_REPORT);
2894+
}
2895+
2896+
static int hidpp10_consumer_keys_raw_event(struct hidpp_device *hidpp,
2897+
u8 *data, int size)
2898+
{
2899+
u8 consumer_report[5];
2900+
2901+
if (size < 7)
2902+
return 0;
2903+
2904+
if (data[0] != REPORT_ID_HIDPP_SHORT ||
2905+
data[2] != HIDPP_SUB_ID_CONSUMER_VENDOR_KEYS)
2906+
return 0;
2907+
2908+
/*
2909+
* Build a normal consumer report (3) out of the data, this detour
2910+
* is necessary to get some keyboards to report their 0x10xx usages.
2911+
*/
2912+
consumer_report[0] = 0x03;
2913+
memcpy(&consumer_report[1], &data[3], 4);
2914+
/* We are called from atomic context */
2915+
hid_report_raw_event(hidpp->hid_dev, HID_INPUT_REPORT,
2916+
consumer_report, 5, 1);
2917+
2918+
return 1;
2919+
}
2920+
28542921
/* -------------------------------------------------------------------------- */
28552922
/* High-resolution scroll wheels */
28562923
/* -------------------------------------------------------------------------- */
@@ -2886,6 +2953,22 @@ static int hi_res_scroll_enable(struct hidpp_device *hidpp)
28862953
/* Generic HID++ devices */
28872954
/* -------------------------------------------------------------------------- */
28882955

2956+
static u8 *hidpp_report_fixup(struct hid_device *hdev, u8 *rdesc,
2957+
unsigned int *rsize)
2958+
{
2959+
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
2960+
2961+
if (!hidpp)
2962+
return rdesc;
2963+
2964+
/* For 27 MHz keyboards the quirk gets set after hid_parse. */
2965+
if (hdev->group == HID_GROUP_LOGITECH_27MHZ_DEVICE ||
2966+
(hidpp->quirks & HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS))
2967+
rdesc = hidpp10_consumer_keys_report_fixup(hidpp, rdesc, rsize);
2968+
2969+
return rdesc;
2970+
}
2971+
28892972
static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
28902973
struct hid_field *field, struct hid_usage *usage,
28912974
unsigned long **bit, int *max)
@@ -3024,6 +3107,12 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
30243107
return ret;
30253108
}
30263109

3110+
if (hidpp->quirks & HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS) {
3111+
ret = hidpp10_consumer_keys_raw_event(hidpp, data, size);
3112+
if (ret != 0)
3113+
return ret;
3114+
}
3115+
30273116
return 0;
30283117
}
30293118

@@ -3283,6 +3372,12 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
32833372
return;
32843373
}
32853374

3375+
if (hidpp->quirks & HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS) {
3376+
ret = hidpp10_consumer_keys_connect(hidpp);
3377+
if (ret)
3378+
return;
3379+
}
3380+
32863381
/* the device is already connected, we can ask for its name and
32873382
* protocol */
32883383
if (!hidpp->protocol_major) {
@@ -3415,6 +3510,16 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
34153510
bool connected;
34163511
unsigned int connect_mask = HID_CONNECT_DEFAULT;
34173512

3513+
/* report_fixup needs drvdata to be set before we call hid_parse */
3514+
hidpp = devm_kzalloc(&hdev->dev, sizeof(*hidpp), GFP_KERNEL);
3515+
if (!hidpp)
3516+
return -ENOMEM;
3517+
3518+
hidpp->hid_dev = hdev;
3519+
hidpp->name = hdev->name;
3520+
hidpp->quirks = id->driver_data;
3521+
hid_set_drvdata(hdev, hidpp);
3522+
34183523
ret = hid_parse(hdev);
34193524
if (ret) {
34203525
hid_err(hdev, "%s:parse failed\n", __func__);
@@ -3424,19 +3529,11 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
34243529
/*
34253530
* Make sure the device is HID++ capable, otherwise treat as generic HID
34263531
*/
3427-
if (!hidpp_validate_device(hdev))
3532+
if (!hidpp_validate_device(hdev)) {
3533+
hid_set_drvdata(hdev, NULL);
3534+
devm_kfree(&hdev->dev, hidpp);
34283535
return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
3429-
3430-
hidpp = devm_kzalloc(&hdev->dev, sizeof(struct hidpp_device),
3431-
GFP_KERNEL);
3432-
if (!hidpp)
3433-
return -ENOMEM;
3434-
3435-
hidpp->hid_dev = hdev;
3436-
hidpp->name = hdev->name;
3437-
hid_set_drvdata(hdev, hidpp);
3438-
3439-
hidpp->quirks = id->driver_data;
3536+
}
34403537

34413538
hidpp->very_long_report_length =
34423539
hidpp_get_report_length(hdev, REPORT_ID_HIDPP_VERY_LONG);
@@ -3451,6 +3548,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
34513548
hidpp->quirks |= HIDPP_QUIRK_HIDPP_WHEELS |
34523549
HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS;
34533550

3551+
if (id->group == HID_GROUP_LOGITECH_27MHZ_DEVICE &&
3552+
hidpp_application_equals(hdev, HID_GD_KEYBOARD))
3553+
hidpp->quirks |= HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS;
3554+
34543555
if (disable_raw_mode) {
34553556
hidpp->quirks &= ~HIDPP_QUIRK_CLASS_WTP;
34563557
hidpp->quirks &= ~HIDPP_QUIRK_NO_HIDINPUT;
@@ -3628,6 +3729,9 @@ static const struct hid_device_id hidpp_devices[] = {
36283729
{ /* Solar Keyboard Logitech K750 */
36293730
LDJ_DEVICE(0x4002),
36303731
.driver_data = HIDPP_QUIRK_CLASS_K750 },
3732+
{ /* Keyboard MX5000 (Bluetooth-receiver in HID proxy mode) */
3733+
LDJ_DEVICE(0xb305),
3734+
.driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
36313735

36323736
{ LDJ_DEVICE(HID_ANY_ID) },
36333737

@@ -3652,6 +3756,10 @@ static const struct hid_device_id hidpp_devices[] = {
36523756
{ /* Logitech G920 Wheel over USB */
36533757
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
36543758
.driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
3759+
3760+
{ /* MX5000 keyboard over Bluetooth */
3761+
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb305),
3762+
.driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
36553763
{}
36563764
};
36573765

@@ -3665,6 +3773,7 @@ static const struct hid_usage_id hidpp_usages[] = {
36653773
static struct hid_driver hidpp_driver = {
36663774
.name = "logitech-hidpp-device",
36673775
.id_table = hidpp_devices,
3776+
.report_fixup = hidpp_report_fixup,
36683777
.probe = hidpp_probe,
36693778
.remove = hidpp_remove,
36703779
.raw_event = hidpp_raw_event,

0 commit comments

Comments
 (0)