aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-logitech-hidpp.c
diff options
context:
space:
mode:
authorBenjamin Tissoires <benjamin.tissoires@redhat.com>2014-09-30 13:18:30 -0400
committerJiri Kosina <jkosina@suse.cz>2014-10-29 10:51:40 +0100
commit33797820af98cde5c7cee00d00f0d8e255ea199f (patch)
tree2d85919e568b520986c52fed4af86d077f755bf9 /drivers/hid/hid-logitech-hidpp.c
parentHID: logitech-dj: allow transfer of HID++ reports from/to the correct dj device (diff)
downloadlinux-dev-33797820af98cde5c7cee00d00f0d8e255ea199f.tar.xz
linux-dev-33797820af98cde5c7cee00d00f0d8e255ea199f.zip
HID: logitech: allow the DJ device to request the unifying name
The names of the DJ devices are stored in the receiver. These names can be retrieved through a HID++ command. However, the protocol says that you have to ask the receiver for that, not the device iteself. Introduce a special case in the DJ handling where a device can request its unifying name, and when such a name is given, forward it also to the corresponding device. On the HID++ side, the receiver talks only HID++ 1.0, so we need to implement this part of the protocol in the module. Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Tested-by: Andrew de los Reyes <adlr@chromium.org> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hid-logitech-hidpp.c')
-rw-r--r--drivers/hid/hid-logitech-hidpp.c80
1 files changed, 76 insertions, 4 deletions
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 48dec394dd38..e748e45b5b2f 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -205,6 +205,31 @@ static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp,
return ret;
}
+static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
+ u8 report_id, u8 sub_id, u8 reg_address, u8 *params, int param_count,
+ struct hidpp_report *response)
+{
+ struct hidpp_report *message = kzalloc(sizeof(struct hidpp_report),
+ GFP_KERNEL);
+ int ret;
+
+ if ((report_id != REPORT_ID_HIDPP_SHORT) &&
+ (report_id != REPORT_ID_HIDPP_LONG))
+ return -EINVAL;
+
+ if (param_count > sizeof(message->rap.params))
+ return -EINVAL;
+
+ message->report_id = report_id;
+ message->rap.sub_id = sub_id;
+ message->rap.reg_address = reg_address;
+ memcpy(&message->rap.params, params, param_count);
+
+ ret = hidpp_send_message_sync(hidpp_dev, message, response);
+ kfree(message);
+ return ret;
+}
+
static inline bool hidpp_match_answer(struct hidpp_report *question,
struct hidpp_report *answer)
{
@@ -221,6 +246,45 @@ static inline bool hidpp_match_error(struct hidpp_report *question,
}
/* -------------------------------------------------------------------------- */
+/* HIDP++ 1.0 commands */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_SET_REGISTER 0x80
+#define HIDPP_GET_REGISTER 0x81
+#define HIDPP_SET_LONG_REGISTER 0x82
+#define HIDPP_GET_LONG_REGISTER 0x83
+
+#define HIDPP_REG_PAIRING_INFORMATION 0xB5
+#define DEVICE_NAME 0x40
+
+static char *hidpp_get_unifying_name(struct hidpp_device *hidpp_dev)
+{
+ struct hidpp_report response;
+ int ret;
+ /* hid-logitech-dj is in charge of setting the right device index */
+ u8 params[1] = { DEVICE_NAME };
+ char *name;
+ int len;
+
+ ret = hidpp_send_rap_command_sync(hidpp_dev,
+ REPORT_ID_HIDPP_SHORT,
+ HIDPP_GET_LONG_REGISTER,
+ HIDPP_REG_PAIRING_INFORMATION,
+ params, 1, &response);
+ if (ret)
+ return NULL;
+
+ len = response.rap.params[1];
+
+ name = kzalloc(len + 1, GFP_KERNEL);
+ if (!name)
+ return NULL;
+
+ memcpy(name, &response.rap.params[2], len);
+ return name;
+}
+
+/* -------------------------------------------------------------------------- */
/* 0x0000: Root */
/* -------------------------------------------------------------------------- */
@@ -726,13 +790,21 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
return 0;
}
-static void hidpp_overwrite_name(struct hid_device *hdev)
+static void hidpp_overwrite_name(struct hid_device *hdev, bool use_unifying)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
char *name;
u8 name_length;
- name = hidpp_get_device_name(hidpp, &name_length);
+ if (use_unifying)
+ /*
+ * the device is connected through an Unifying receiver, and
+ * might not be already connected.
+ * Ask the receiver for its name.
+ */
+ name = hidpp_get_unifying_name(hidpp);
+ else
+ name = hidpp_get_device_name(hidpp, &name_length);
if (!name)
hid_err(hdev, "unable to retrieve the name of the device");
@@ -783,12 +855,12 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto hid_parse_fail;
}
- /* the device is connected, we can ask for its name */
hid_info(hdev, "HID++ %u.%u device connected.\n",
hidpp->protocol_major, hidpp->protocol_minor);
- hidpp_overwrite_name(hdev);
}
+ hidpp_overwrite_name(hdev, id->group == HID_GROUP_LOGITECH_DJ_DEVICE);
+
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
ret = wtp_get_config(hidpp);
if (ret)