Skip to content

Commit 90cdd98

Browse files
bentissJiri Kosina
authored andcommitted
HID: logitech-hidpp: add support to disable tap-to-click on the K400
The Logitech K400 keyboard has an embedded touchpad which is seen as a mouse from the OS point of view. There is a hardware shortcut to disable tap-to-click but the setting is not remembered accross reset, annoying some users. We can toggle this feature from the host by using the feature 0x6010: Touchpad FW items Reported-by: BALATON Zoltan <[email protected]> Tested-by: BALATON Zoltan <[email protected]> Signed-off-by: Benjamin Tissoires <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent 580a7e8 commit 90cdd98

File tree

1 file changed

+133
-0
lines changed

1 file changed

+133
-0
lines changed

drivers/hid/hid-logitech-hidpp.c

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ module_param(disable_raw_mode, bool, 0644);
3333
MODULE_PARM_DESC(disable_raw_mode,
3434
"Disable Raw mode reporting for touchpads and keep firmware gestures.");
3535

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+
3641
#define REPORT_ID_HIDPP_SHORT 0x10
3742
#define REPORT_ID_HIDPP_LONG 0x11
3843

@@ -41,6 +46,7 @@ MODULE_PARM_DESC(disable_raw_mode,
4146

4247
#define HIDPP_QUIRK_CLASS_WTP BIT(0)
4348
#define HIDPP_QUIRK_CLASS_M560 BIT(1)
49+
#define HIDPP_QUIRK_CLASS_K400 BIT(2)
4450

4551
/* bits 2..20 are reserved for classes */
4652
#define HIDPP_QUIRK_CONNECT_EVENTS BIT(21)
@@ -556,6 +562,52 @@ static char *hidpp_get_device_name(struct hidpp_device *hidpp)
556562
return name;
557563
}
558564

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+
559611
/* -------------------------------------------------------------------------- */
560612
/* 0x6100: TouchPadRawXY */
561613
/* -------------------------------------------------------------------------- */
@@ -1136,6 +1188,75 @@ static int m560_input_mapping(struct hid_device *hdev, struct hid_input *hi,
11361188
return -1;
11371189
}
11381190

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+
11391260
/* -------------------------------------------------------------------------- */
11401261
/* Generic HID++ devices */
11411262
/* -------------------------------------------------------------------------- */
@@ -1332,6 +1453,10 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
13321453
ret = m560_send_config_command(hdev, connected);
13331454
if (ret)
13341455
return;
1456+
} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) {
1457+
ret = k400_connect(hdev, connected);
1458+
if (ret)
1459+
return;
13351460
}
13361461

13371462
if (!connected || hidpp->delayed_input)
@@ -1416,6 +1541,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
14161541
ret = m560_allocate(hdev);
14171542
if (ret)
14181543
goto allocate_fail;
1544+
} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) {
1545+
ret = k400_allocate(hdev);
1546+
if (ret)
1547+
goto allocate_fail;
14191548
}
14201549

14211550
INIT_WORK(&hidpp->work, delayed_work_cb);
@@ -1510,6 +1639,10 @@ static const struct hid_device_id hidpp_devices[] = {
15101639
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
15111640
USB_VENDOR_ID_LOGITECH, 0x402d),
15121641
.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 },
15131646

15141647
{ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
15151648
USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},

0 commit comments

Comments
 (0)