summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2019-04-20 07:21:56 -0400
committerBenjamin Tissoires <benjamin.tissoires@redhat.com>2019-04-23 12:01:31 -0400
commitc9121cf637331b6fc07a60708c6ca0a161e7deb5 (patch)
treeae9fc67da0a234f0f94c4f6580abcd9f5d812eba
parentf5fb57a74e88bd1788f57bf77d587c91d4dc9d57 (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.c2
-rw-r--r--drivers/hid/hid-logitech-dj.c94
-rw-r--r--drivers/hid/hid-quirks.c1
-rw-r--r--include/linux/hid.h1
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
125struct dj_report { 127struct 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 */
254static 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) */
252static const char mse_high_res_descriptor[] = { 292static 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
828static 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
785static void logi_hidpp_recv_queue_notif(struct hid_device *hdev, 850static 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