diff options
author | Hans de Goede <hdegoede@redhat.com> | 2019-04-20 07:21:56 -0400 |
---|---|---|
committer | Benjamin Tissoires <benjamin.tissoires@redhat.com> | 2019-04-23 12:01:31 -0400 |
commit | c9121cf637331b6fc07a60708c6ca0a161e7deb5 (patch) | |
tree | ae9fc67da0a234f0f94c4f6580abcd9f5d812eba | |
parent | f5fb57a74e88bd1788f57bf77d587c91d4dc9d57 (diff) |
HID: logitech-dj: add support for 27 MHz receivers
Most Logitech wireless keyboard and mice using the 27 MHz are hidpp10
devices, add support to logitech-dj for their receivers.
Doing so leads to 2 improvements:
1) All these devices share the same USB product-id for their receiver,
making it impossible to properly map some special keys / buttons
which differ from device to device. Adding support to logitech-dj to
see these as hidpp10 devices allows us to get the actual device-id
from the keyboard / mouse.
2) It enables battery-monitoring of these devices
This patch uses a new HID group for 27Mhz devices, since the logitech-hidpp
code needs to be able to differentiate them from other devices instantiated
by the logitech-dj code.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
-rw-r--r-- | drivers/hid/hid-lg.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-logitech-dj.c | 94 | ||||
-rw-r--r-- | drivers/hid/hid-quirks.c | 1 | ||||
-rw-r--r-- | include/linux/hid.h | 1 |
4 files changed, 94 insertions, 4 deletions
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 5d419a95b6c2..36d725fdb199 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c | |||
@@ -876,8 +876,6 @@ static const struct hid_device_id lg_devices[] = { | |||
876 | .driver_data = LG_RDESC | LG_WIRELESS }, | 876 | .driver_data = LG_RDESC | LG_WIRELESS }, |
877 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER), | 877 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER), |
878 | .driver_data = LG_RDESC | LG_WIRELESS }, | 878 | .driver_data = LG_RDESC | LG_WIRELESS }, |
879 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2), | ||
880 | .driver_data = LG_RDESC | LG_WIRELESS }, | ||
881 | 879 | ||
882 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER), | 880 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER), |
883 | .driver_data = LG_BAD_RELATIVE_KEYS }, | 881 | .driver_data = LG_BAD_RELATIVE_KEYS }, |
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 9ece9aca40a0..3004ca91b76b 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c | |||
@@ -106,6 +106,7 @@ | |||
106 | #define HIDPP_PARAM_DEVICE_INFO 0x01 | 106 | #define HIDPP_PARAM_DEVICE_INFO 0x01 |
107 | #define HIDPP_PARAM_EQUAD_LSB 0x02 | 107 | #define HIDPP_PARAM_EQUAD_LSB 0x02 |
108 | #define HIDPP_PARAM_EQUAD_MSB 0x03 | 108 | #define HIDPP_PARAM_EQUAD_MSB 0x03 |
109 | #define HIDPP_PARAM_27MHZ_DEVID 0x03 | ||
109 | #define HIDPP_DEVICE_TYPE_MASK GENMASK(3, 0) | 110 | #define HIDPP_DEVICE_TYPE_MASK GENMASK(3, 0) |
110 | #define HIDPP_LINK_STATUS_MASK BIT(6) | 111 | #define HIDPP_LINK_STATUS_MASK BIT(6) |
111 | 112 | ||
@@ -120,6 +121,7 @@ enum recvr_type { | |||
120 | recvr_type_dj, | 121 | recvr_type_dj, |
121 | recvr_type_hidpp, | 122 | recvr_type_hidpp, |
122 | recvr_type_gaming_hidpp, | 123 | recvr_type_gaming_hidpp, |
124 | recvr_type_27mhz, | ||
123 | }; | 125 | }; |
124 | 126 | ||
125 | struct dj_report { | 127 | struct dj_report { |
@@ -248,6 +250,44 @@ static const char mse_descriptor[] = { | |||
248 | 0xC0, /* END_COLLECTION */ | 250 | 0xC0, /* END_COLLECTION */ |
249 | }; | 251 | }; |
250 | 252 | ||
253 | /* Mouse descriptor (2) for 27 MHz receiver, only 8 buttons */ | ||
254 | static const char mse_27mhz_descriptor[] = { | ||
255 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ | ||
256 | 0x09, 0x02, /* USAGE (Mouse) */ | ||
257 | 0xA1, 0x01, /* COLLECTION (Application) */ | ||
258 | 0x85, 0x02, /* REPORT_ID = 2 */ | ||
259 | 0x09, 0x01, /* USAGE (pointer) */ | ||
260 | 0xA1, 0x00, /* COLLECTION (physical) */ | ||
261 | 0x05, 0x09, /* USAGE_PAGE (buttons) */ | ||
262 | 0x19, 0x01, /* USAGE_MIN (1) */ | ||
263 | 0x29, 0x08, /* USAGE_MAX (8) */ | ||
264 | 0x15, 0x00, /* LOGICAL_MIN (0) */ | ||
265 | 0x25, 0x01, /* LOGICAL_MAX (1) */ | ||
266 | 0x95, 0x08, /* REPORT_COUNT (8) */ | ||
267 | 0x75, 0x01, /* REPORT_SIZE (1) */ | ||
268 | 0x81, 0x02, /* INPUT (data var abs) */ | ||
269 | 0x05, 0x01, /* USAGE_PAGE (generic desktop) */ | ||
270 | 0x16, 0x01, 0xF8, /* LOGICAL_MIN (-2047) */ | ||
271 | 0x26, 0xFF, 0x07, /* LOGICAL_MAX (2047) */ | ||
272 | 0x75, 0x0C, /* REPORT_SIZE (12) */ | ||
273 | 0x95, 0x02, /* REPORT_COUNT (2) */ | ||
274 | 0x09, 0x30, /* USAGE (X) */ | ||
275 | 0x09, 0x31, /* USAGE (Y) */ | ||
276 | 0x81, 0x06, /* INPUT */ | ||
277 | 0x15, 0x81, /* LOGICAL_MIN (-127) */ | ||
278 | 0x25, 0x7F, /* LOGICAL_MAX (127) */ | ||
279 | 0x75, 0x08, /* REPORT_SIZE (8) */ | ||
280 | 0x95, 0x01, /* REPORT_COUNT (1) */ | ||
281 | 0x09, 0x38, /* USAGE (wheel) */ | ||
282 | 0x81, 0x06, /* INPUT */ | ||
283 | 0x05, 0x0C, /* USAGE_PAGE(consumer) */ | ||
284 | 0x0A, 0x38, 0x02, /* USAGE(AC Pan) */ | ||
285 | 0x95, 0x01, /* REPORT_COUNT (1) */ | ||
286 | 0x81, 0x06, /* INPUT */ | ||
287 | 0xC0, /* END_COLLECTION */ | ||
288 | 0xC0, /* END_COLLECTION */ | ||
289 | }; | ||
290 | |||
251 | /* Gaming Mouse descriptor (2) */ | 291 | /* Gaming Mouse descriptor (2) */ |
252 | static const char mse_high_res_descriptor[] = { | 292 | static const char mse_high_res_descriptor[] = { |
253 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ | 293 | 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ |
@@ -596,7 +636,10 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev, | |||
596 | "Logitech Unifying Device. Wireless PID:%04x", | 636 | "Logitech Unifying Device. Wireless PID:%04x", |
597 | dj_hiddev->product); | 637 | dj_hiddev->product); |
598 | 638 | ||
599 | dj_hiddev->group = HID_GROUP_LOGITECH_DJ_DEVICE; | 639 | if (djrcv_dev->type == recvr_type_27mhz) |
640 | dj_hiddev->group = HID_GROUP_LOGITECH_27MHZ_DEVICE; | ||
641 | else | ||
642 | dj_hiddev->group = HID_GROUP_LOGITECH_DJ_DEVICE; | ||
600 | 643 | ||
601 | memcpy(dj_hiddev->phys, djrcv_hdev->phys, sizeof(djrcv_hdev->phys)); | 644 | memcpy(dj_hiddev->phys, djrcv_hdev->phys, sizeof(djrcv_hdev->phys)); |
602 | snprintf(tmpstr, sizeof(tmpstr), ":%d", device_index); | 645 | snprintf(tmpstr, sizeof(tmpstr), ":%d", device_index); |
@@ -782,6 +825,28 @@ static void logi_hidpp_dev_conn_notif_equad(struct hidpp_event *hidpp_report, | |||
782 | } | 825 | } |
783 | } | 826 | } |
784 | 827 | ||
828 | static void logi_hidpp_dev_conn_notif_27mhz(struct hid_device *hdev, | ||
829 | struct hidpp_event *hidpp_report, | ||
830 | struct dj_workitem *workitem) | ||
831 | { | ||
832 | workitem->type = WORKITEM_TYPE_PAIRED; | ||
833 | workitem->quad_id_lsb = hidpp_report->params[HIDPP_PARAM_27MHZ_DEVID]; | ||
834 | switch (hidpp_report->device_index) { | ||
835 | case 1: /* Index 1 is always a mouse */ | ||
836 | case 2: /* Index 2 is always a mouse */ | ||
837 | workitem->reports_supported |= STD_MOUSE; | ||
838 | break; | ||
839 | case 3: /* Index 3 is always the keyboard */ | ||
840 | case 4: /* Index 4 is used for an optional separate numpad */ | ||
841 | workitem->reports_supported |= STD_KEYBOARD | MULTIMEDIA | | ||
842 | POWER_KEYS; | ||
843 | break; | ||
844 | default: | ||
845 | hid_warn(hdev, "%s: unexpected device-index %d", __func__, | ||
846 | hidpp_report->device_index); | ||
847 | } | ||
848 | } | ||
849 | |||
785 | static void logi_hidpp_recv_queue_notif(struct hid_device *hdev, | 850 | static void logi_hidpp_recv_queue_notif(struct hid_device *hdev, |
786 | struct hidpp_event *hidpp_report) | 851 | struct hidpp_event *hidpp_report) |
787 | { | 852 | { |
@@ -799,6 +864,7 @@ static void logi_hidpp_recv_queue_notif(struct hid_device *hdev, | |||
799 | break; | 864 | break; |
800 | case 0x02: | 865 | case 0x02: |
801 | device_type = "27 Mhz"; | 866 | device_type = "27 Mhz"; |
867 | logi_hidpp_dev_conn_notif_27mhz(hdev, hidpp_report, &workitem); | ||
802 | break; | 868 | break; |
803 | case 0x03: | 869 | case 0x03: |
804 | device_type = "QUAD or eQUAD"; | 870 | device_type = "QUAD or eQUAD"; |
@@ -1186,6 +1252,9 @@ static int logi_dj_ll_parse(struct hid_device *hid) | |||
1186 | if (djdev->dj_receiver_dev->type == recvr_type_gaming_hidpp) | 1252 | if (djdev->dj_receiver_dev->type == recvr_type_gaming_hidpp) |
1187 | rdcat(rdesc, &rsize, mse_high_res_descriptor, | 1253 | rdcat(rdesc, &rsize, mse_high_res_descriptor, |
1188 | sizeof(mse_high_res_descriptor)); | 1254 | sizeof(mse_high_res_descriptor)); |
1255 | else if (djdev->dj_receiver_dev->type == recvr_type_27mhz) | ||
1256 | rdcat(rdesc, &rsize, mse_27mhz_descriptor, | ||
1257 | sizeof(mse_27mhz_descriptor)); | ||
1189 | else | 1258 | else |
1190 | rdcat(rdesc, &rsize, mse_descriptor, | 1259 | rdcat(rdesc, &rsize, mse_descriptor, |
1191 | sizeof(mse_descriptor)); | 1260 | sizeof(mse_descriptor)); |
@@ -1357,6 +1426,25 @@ static int logi_dj_hidpp_event(struct hid_device *hdev, | |||
1357 | spin_lock_irqsave(&djrcv_dev->lock, flags); | 1426 | spin_lock_irqsave(&djrcv_dev->lock, flags); |
1358 | 1427 | ||
1359 | dj_dev = djrcv_dev->paired_dj_devices[device_index]; | 1428 | dj_dev = djrcv_dev->paired_dj_devices[device_index]; |
1429 | |||
1430 | /* | ||
1431 | * With 27 MHz receivers, we do not get an explicit unpair event, | ||
1432 | * remove the old device if the user has paired a *different* device. | ||
1433 | */ | ||
1434 | if (djrcv_dev->type == recvr_type_27mhz && dj_dev && | ||
1435 | hidpp_report->sub_id == REPORT_TYPE_NOTIF_DEVICE_CONNECTED && | ||
1436 | hidpp_report->params[HIDPP_PARAM_PROTO_TYPE] == 0x02 && | ||
1437 | hidpp_report->params[HIDPP_PARAM_27MHZ_DEVID] != | ||
1438 | dj_dev->hdev->product) { | ||
1439 | struct dj_workitem workitem = { | ||
1440 | .device_index = hidpp_report->device_index, | ||
1441 | .type = WORKITEM_TYPE_UNPAIRED, | ||
1442 | }; | ||
1443 | kfifo_in(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem)); | ||
1444 | /* logi_hidpp_recv_queue_notif will queue the work */ | ||
1445 | dj_dev = NULL; | ||
1446 | } | ||
1447 | |||
1360 | if (dj_dev) { | 1448 | if (dj_dev) { |
1361 | logi_dj_recv_forward_report(dj_dev, data, size); | 1449 | logi_dj_recv_forward_report(dj_dev, data, size); |
1362 | } else { | 1450 | } else { |
@@ -1628,6 +1716,10 @@ static const struct hid_device_id logi_dj_receivers[] = { | |||
1628 | HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, | 1716 | HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, |
1629 | USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_GAMING), | 1717 | USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_GAMING), |
1630 | .driver_data = recvr_type_gaming_hidpp}, | 1718 | .driver_data = recvr_type_gaming_hidpp}, |
1719 | { /* Logitech 27 MHz HID++ 1.0 receiver (0xc517) */ | ||
1720 | HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, | ||
1721 | USB_DEVICE_ID_S510_RECEIVER_2), | ||
1722 | .driver_data = recvr_type_27mhz}, | ||
1631 | {} | 1723 | {} |
1632 | }; | 1724 | }; |
1633 | 1725 | ||
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 426753482494..fea7f7ff5ab1 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c | |||
@@ -432,7 +432,6 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
432 | #if IS_ENABLED(CONFIG_HID_LOGITECH) | 432 | #if IS_ENABLED(CONFIG_HID_LOGITECH) |
433 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, | 433 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, |
434 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, | 434 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, |
435 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) }, | ||
436 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) }, | 435 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) }, |
437 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) }, | 436 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) }, |
438 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) }, | 437 | { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) }, |
diff --git a/include/linux/hid.h b/include/linux/hid.h index f9707d1dcb58..9f161fa5cbd4 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h | |||
@@ -382,6 +382,7 @@ struct hid_item { | |||
382 | #define HID_GROUP_WACOM 0x0101 | 382 | #define HID_GROUP_WACOM 0x0101 |
383 | #define HID_GROUP_LOGITECH_DJ_DEVICE 0x0102 | 383 | #define HID_GROUP_LOGITECH_DJ_DEVICE 0x0102 |
384 | #define HID_GROUP_STEAM 0x0103 | 384 | #define HID_GROUP_STEAM 0x0103 |
385 | #define HID_GROUP_LOGITECH_27MHZ_DEVICE 0x0104 | ||
385 | 386 | ||
386 | /* | 387 | /* |
387 | * HID protocol status | 388 | * HID protocol status |