diff options
-rw-r--r-- | drivers/hid/hid-logitech-hidpp.c | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 81d1bc0feebb..452e5d5d6be7 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c | |||
@@ -33,6 +33,11 @@ module_param(disable_raw_mode, bool, 0644); | |||
33 | MODULE_PARM_DESC(disable_raw_mode, | 33 | MODULE_PARM_DESC(disable_raw_mode, |
34 | "Disable Raw mode reporting for touchpads and keep firmware gestures."); | 34 | "Disable Raw mode reporting for touchpads and keep firmware gestures."); |
35 | 35 | ||
36 | static bool disable_tap_to_click; | ||
37 | module_param(disable_tap_to_click, bool, 0644); | ||
38 | MODULE_PARM_DESC(disable_tap_to_click, | ||
39 | "Disable Tap-To-Click mode reporting for touchpads (only on the K400 currently)."); | ||
40 | |||
36 | #define REPORT_ID_HIDPP_SHORT 0x10 | 41 | #define REPORT_ID_HIDPP_SHORT 0x10 |
37 | #define REPORT_ID_HIDPP_LONG 0x11 | 42 | #define REPORT_ID_HIDPP_LONG 0x11 |
38 | 43 | ||
@@ -41,6 +46,7 @@ MODULE_PARM_DESC(disable_raw_mode, | |||
41 | 46 | ||
42 | #define HIDPP_QUIRK_CLASS_WTP BIT(0) | 47 | #define HIDPP_QUIRK_CLASS_WTP BIT(0) |
43 | #define HIDPP_QUIRK_CLASS_M560 BIT(1) | 48 | #define HIDPP_QUIRK_CLASS_M560 BIT(1) |
49 | #define HIDPP_QUIRK_CLASS_K400 BIT(2) | ||
44 | 50 | ||
45 | /* bits 2..20 are reserved for classes */ | 51 | /* bits 2..20 are reserved for classes */ |
46 | #define HIDPP_QUIRK_CONNECT_EVENTS BIT(21) | 52 | #define HIDPP_QUIRK_CONNECT_EVENTS BIT(21) |
@@ -557,6 +563,52 @@ static char *hidpp_get_device_name(struct hidpp_device *hidpp) | |||
557 | } | 563 | } |
558 | 564 | ||
559 | /* -------------------------------------------------------------------------- */ | 565 | /* -------------------------------------------------------------------------- */ |
566 | /* 0x6010: Touchpad FW items */ | ||
567 | /* -------------------------------------------------------------------------- */ | ||
568 | |||
569 | #define HIDPP_PAGE_TOUCHPAD_FW_ITEMS 0x6010 | ||
570 | |||
571 | #define CMD_TOUCHPAD_FW_ITEMS_SET 0x10 | ||
572 | |||
573 | struct hidpp_touchpad_fw_items { | ||
574 | uint8_t presence; | ||
575 | uint8_t desired_state; | ||
576 | uint8_t state; | ||
577 | uint8_t persistent; | ||
578 | }; | ||
579 | |||
580 | /** | ||
581 | * send a set state command to the device by reading the current items->state | ||
582 | * field. items is then filled with the current state. | ||
583 | */ | ||
584 | static int hidpp_touchpad_fw_items_set(struct hidpp_device *hidpp, | ||
585 | u8 feature_index, | ||
586 | struct hidpp_touchpad_fw_items *items) | ||
587 | { | ||
588 | struct hidpp_report response; | ||
589 | int ret; | ||
590 | u8 *params = (u8 *)response.fap.params; | ||
591 | |||
592 | ret = hidpp_send_fap_command_sync(hidpp, feature_index, | ||
593 | CMD_TOUCHPAD_FW_ITEMS_SET, &items->state, 1, &response); | ||
594 | |||
595 | if (ret > 0) { | ||
596 | hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n", | ||
597 | __func__, ret); | ||
598 | return -EPROTO; | ||
599 | } | ||
600 | if (ret) | ||
601 | return ret; | ||
602 | |||
603 | items->presence = params[0]; | ||
604 | items->desired_state = params[1]; | ||
605 | items->state = params[2]; | ||
606 | items->persistent = params[3]; | ||
607 | |||
608 | return 0; | ||
609 | } | ||
610 | |||
611 | /* -------------------------------------------------------------------------- */ | ||
560 | /* 0x6100: TouchPadRawXY */ | 612 | /* 0x6100: TouchPadRawXY */ |
561 | /* -------------------------------------------------------------------------- */ | 613 | /* -------------------------------------------------------------------------- */ |
562 | 614 | ||
@@ -1136,6 +1188,75 @@ static int m560_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
1136 | return -1; | 1188 | return -1; |
1137 | } | 1189 | } |
1138 | 1190 | ||
1191 | /* ------------------------------------------------------------------------- */ | ||
1192 | /* Logitech K400 devices */ | ||
1193 | /* ------------------------------------------------------------------------- */ | ||
1194 | |||
1195 | /* | ||
1196 | * The Logitech K400 keyboard has an embedded touchpad which is seen | ||
1197 | * as a mouse from the OS point of view. There is a hardware shortcut to disable | ||
1198 | * tap-to-click but the setting is not remembered accross reset, annoying some | ||
1199 | * users. | ||
1200 | * | ||
1201 | * We can toggle this feature from the host by using the feature 0x6010: | ||
1202 | * Touchpad FW items | ||
1203 | */ | ||
1204 | |||
1205 | struct k400_private_data { | ||
1206 | u8 feature_index; | ||
1207 | }; | ||
1208 | |||
1209 | static int k400_disable_tap_to_click(struct hidpp_device *hidpp) | ||
1210 | { | ||
1211 | struct k400_private_data *k400 = hidpp->private_data; | ||
1212 | struct hidpp_touchpad_fw_items items = {}; | ||
1213 | int ret; | ||
1214 | u8 feature_type; | ||
1215 | |||
1216 | if (!k400->feature_index) { | ||
1217 | ret = hidpp_root_get_feature(hidpp, | ||
1218 | HIDPP_PAGE_TOUCHPAD_FW_ITEMS, | ||
1219 | &k400->feature_index, &feature_type); | ||
1220 | if (ret) | ||
1221 | /* means that the device is not powered up */ | ||
1222 | return ret; | ||
1223 | } | ||
1224 | |||
1225 | ret = hidpp_touchpad_fw_items_set(hidpp, k400->feature_index, &items); | ||
1226 | if (ret) | ||
1227 | return ret; | ||
1228 | |||
1229 | return 0; | ||
1230 | } | ||
1231 | |||
1232 | static int k400_allocate(struct hid_device *hdev) | ||
1233 | { | ||
1234 | struct hidpp_device *hidpp = hid_get_drvdata(hdev); | ||
1235 | struct k400_private_data *k400; | ||
1236 | |||
1237 | k400 = devm_kzalloc(&hdev->dev, sizeof(struct k400_private_data), | ||
1238 | GFP_KERNEL); | ||
1239 | if (!k400) | ||
1240 | return -ENOMEM; | ||
1241 | |||
1242 | hidpp->private_data = k400; | ||
1243 | |||
1244 | return 0; | ||
1245 | }; | ||
1246 | |||
1247 | static int k400_connect(struct hid_device *hdev, bool connected) | ||
1248 | { | ||
1249 | struct hidpp_device *hidpp = hid_get_drvdata(hdev); | ||
1250 | |||
1251 | if (!connected) | ||
1252 | return 0; | ||
1253 | |||
1254 | if (!disable_tap_to_click) | ||
1255 | return 0; | ||
1256 | |||
1257 | return k400_disable_tap_to_click(hidpp); | ||
1258 | } | ||
1259 | |||
1139 | /* -------------------------------------------------------------------------- */ | 1260 | /* -------------------------------------------------------------------------- */ |
1140 | /* Generic HID++ devices */ | 1261 | /* Generic HID++ devices */ |
1141 | /* -------------------------------------------------------------------------- */ | 1262 | /* -------------------------------------------------------------------------- */ |
@@ -1332,6 +1453,10 @@ static void hidpp_connect_event(struct hidpp_device *hidpp) | |||
1332 | ret = m560_send_config_command(hdev, connected); | 1453 | ret = m560_send_config_command(hdev, connected); |
1333 | if (ret) | 1454 | if (ret) |
1334 | return; | 1455 | return; |
1456 | } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) { | ||
1457 | ret = k400_connect(hdev, connected); | ||
1458 | if (ret) | ||
1459 | return; | ||
1335 | } | 1460 | } |
1336 | 1461 | ||
1337 | if (!connected || hidpp->delayed_input) | 1462 | if (!connected || hidpp->delayed_input) |
@@ -1416,6 +1541,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
1416 | ret = m560_allocate(hdev); | 1541 | ret = m560_allocate(hdev); |
1417 | if (ret) | 1542 | if (ret) |
1418 | goto allocate_fail; | 1543 | goto allocate_fail; |
1544 | } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) { | ||
1545 | ret = k400_allocate(hdev); | ||
1546 | if (ret) | ||
1547 | goto allocate_fail; | ||
1419 | } | 1548 | } |
1420 | 1549 | ||
1421 | INIT_WORK(&hidpp->work, delayed_work_cb); | 1550 | INIT_WORK(&hidpp->work, delayed_work_cb); |
@@ -1510,6 +1639,10 @@ static const struct hid_device_id hidpp_devices[] = { | |||
1510 | HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, | 1639 | HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, |
1511 | USB_VENDOR_ID_LOGITECH, 0x402d), | 1640 | USB_VENDOR_ID_LOGITECH, 0x402d), |
1512 | .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 }, | 1641 | .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 }, |
1642 | { /* Keyboard logitech K400 */ | ||
1643 | HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, | ||
1644 | USB_VENDOR_ID_LOGITECH, 0x4024), | ||
1645 | .driver_data = HIDPP_QUIRK_CONNECT_EVENTS | HIDPP_QUIRK_CLASS_K400 }, | ||
1513 | 1646 | ||
1514 | { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, | 1647 | { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, |
1515 | USB_VENDOR_ID_LOGITECH, HID_ANY_ID)}, | 1648 | USB_VENDOR_ID_LOGITECH, HID_ANY_ID)}, |