lkml.org 
[lkml]   [2018]   [Sep]   [7]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH 4/7] HID: logitech-hidpp: get the name and serial of the other non-HID++ node
Date
The non-Unifying receivers forward the input events over one HID++
capable node (the second one) and one which is not HID++ capable (the
first one, the keyboard). We can use the HID++ information to know
which device name and serial is connected to the first node. For that,
delay the probe of the non-HID++ node, retrieve the information when
available and then authorize the non-HID++ node to appear.

Bonus point, this allows gaming mice to have both input nodes named
after the mouse, and not one called "Logitech USB Receiver".

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
drivers/hid/hid-logitech-hidpp.c | 193 +++++++++++++++++++++++++++++++++------
1 file changed, 166 insertions(+), 27 deletions(-)

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 315ef209a0c4..5f96e63dbd87 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -137,6 +137,16 @@ struct hidpp_battery {
bool online;
};

+struct hidpp_shared {
+ struct list_head list;
+ char name[128]; /* Device name */
+ char phys[64]; /* Device physical location */
+ char uniq[64]; /* Device unique identifier (serial #) */
+};
+
+static LIST_HEAD(hidpp_shared_list);
+static DEFINE_MUTEX(hidpp_shared_list_lock);
+
struct hidpp_device {
struct hid_device *hid_dev;
struct mutex send_mutex;
@@ -160,6 +170,8 @@ struct hidpp_device {

struct hidpp_battery battery;
struct hid_scroll_counter vertical_wheel_counter;
+
+ struct hidpp_shared *shared;
};

/* HID++ 1.0 error codes */
@@ -398,6 +410,81 @@ static void hidpp_prefix_name(char **name, int name_length)
*name = new_name;
}

+static void hidpp_remove_shared_data(void *res)
+{
+ struct hidpp_device *hidpp = res;
+
+ mutex_lock(&hidpp_shared_list_lock);
+
+ if (hidpp->shared) {
+ list_del(&hidpp->shared->list);
+ kfree(hidpp->shared);
+ }
+ hidpp->shared = NULL;
+
+ mutex_unlock(&hidpp_shared_list_lock);
+}
+
+static bool hidpp_compare_device_paths(const char *hdev_a, const char *hdev_b)
+{
+ const char separator = '/';
+ int n1 = strrchr(hdev_a, separator) - hdev_a;
+ int n2 = strrchr(hdev_b, separator) - hdev_b;
+
+ if (n1 != n2 || n1 <= 0 || n2 <= 0)
+ return false;
+
+ return !strncmp(hdev_a, hdev_b, n1);
+}
+
+static int hidpp_allocate_shared_data(struct hidpp_device *hidpp)
+{
+ struct hid_device *hdev = hidpp->hid_dev;
+ struct hidpp_shared *shared = NULL;
+
+ mutex_lock(&hidpp_shared_list_lock);
+
+ shared = kzalloc(sizeof(struct hidpp_shared), GFP_KERNEL);
+ if (!shared) {
+ mutex_unlock(&hidpp_shared_list_lock);
+ return -ENOMEM;
+ }
+ memcpy(shared->name, hdev->name, sizeof(shared->name));
+ memcpy(shared->uniq, hdev->uniq, sizeof(shared->uniq));
+ memcpy(shared->phys, hdev->phys, sizeof(shared->phys));
+ list_add_tail(&shared->list, &hidpp_shared_list);
+
+ hidpp->shared = shared;
+
+ mutex_unlock(&hidpp_shared_list_lock);
+
+ return devm_add_action_or_reset(&hidpp->hid_dev->dev,
+ hidpp_remove_shared_data, hidpp);
+}
+
+static int hidpp_get_shared_data(struct hid_device *hdev)
+{
+ struct hidpp_shared *s, *shared = NULL;
+
+ mutex_lock(&hidpp_shared_list_lock);
+
+ list_for_each_entry(s, &hidpp_shared_list, list) {
+ if (hidpp_compare_device_paths(s->phys, hdev->phys))
+ shared = s;
+ }
+
+ if (!shared) {
+ mutex_unlock(&hidpp_shared_list_lock);
+ return -EPROBE_DEFER;
+ }
+
+ memcpy(hdev->name, shared->name, sizeof(hdev->name));
+ memcpy(hdev->uniq, shared->uniq, sizeof(hdev->uniq));
+
+ mutex_unlock(&hidpp_shared_list_lock);
+ return 0;
+}
+
/* -------------------------------------------------------------------------- */
/* HIDP++ 1.0 commands */
/* -------------------------------------------------------------------------- */
@@ -750,48 +837,90 @@ static int hidpp_unifying_get_serial(struct hidpp_device *hidpp, int index,
return 0;
}

+static int hidpp_unifying_get_name_and_uniq(struct hidpp_device *hidpp,
+ int device_index, u16 quadid, char name[128], char uniq[64])
+{
+ const char *hidpp_name;
+ u32 serial;
+ int ret;
+
+ ret = hidpp_unifying_get_serial(hidpp, device_index, &serial);
+ if (ret)
+ return ret;
+
+ snprintf(uniq, 64, "%04x-%4phD", quadid, &serial);
+ dbg_hid("HID++ Unifying: Got serial: '%s' for device %d\n", uniq,
+ device_index);
+
+ hidpp_name = hidpp_unifying_get_name(hidpp, device_index);
+ if (!hidpp_name)
+ return -EIO;
+
+ snprintf(name, 128, "%s", hidpp_name);
+ dbg_hid("HID++ Unifying: Got name: '%s' for device %d\n", name,
+ device_index);
+
+ kfree(hidpp_name);
+ return 0;
+}
+
static int hidpp_unifying_init(struct hidpp_device *hidpp)
{
struct hid_device *hdev = hidpp->hid_dev;
- const char *name;
- u32 serial;
u16 quadid, q;
- int device_index, ret;
+ int device_index, i, ret;
u8 type = 0;

device_index = 0;
quadid = hdev->product;

if (hidpp->quirks & HIDPP_QUIRK_RECEIVER) {
- /*
- * On the receiver, the HID++ collection is on the mouse
- * interface, so match only there.
- */
- ret = hidpp_unifying_get_quadid(hidpp, 2, &q, &type);
- if (!ret && !hidpp_unifying_type_is_keyboard(type)) {
- device_index = 2;
- quadid = q;
+ ret = hidpp_allocate_shared_data(hidpp);
+ if (ret)
+ return ret;
+
+ /* The device indexes go from 1 to 2 */
+ for (i = 1; i <= 2; i++) {
+ ret = hidpp_unifying_get_quadid(hidpp, i, &q, &type);
+ if (ret)
+ continue;
+ /*
+ * On the receiver, the HID++ collection is on the mouse
+ * interface, so match only there.
+ */
+ if (!hidpp_unifying_type_is_keyboard(type)) {
+ device_index = i;
+ quadid = q;
+ }
+
+ /*
+ * The first device will be reported through the
+ * first hidraw node (the non HID++ one).
+ */
+ if (i == 1) {
+ ret = hidpp_unifying_get_name_and_uniq(hidpp, i,
+ q, hidpp->shared->name,
+ hidpp->shared->uniq);
+ if (ret)
+ return ret;
+ }
}
hidpp_unifying_enable_notifications(hidpp);
}

- ret = hidpp_unifying_get_serial(hidpp, device_index, &serial);
- if (ret)
- return ret;
-
- snprintf(hdev->uniq, sizeof(hdev->uniq), "%04x-%4phD", quadid, &serial);
- dbg_hid("HID++ Unifying: Got serial: %s\n", hdev->uniq);
-
- name = hidpp_unifying_get_name(hidpp, device_index);
- if (!name)
- return -EIO;
-
- snprintf(hdev->name, sizeof(hdev->name), "%s", name);
- dbg_hid("HID++ Unifying: Got name: %s\n", name);
+ if (device_index != 1) {
+ ret = hidpp_unifying_get_name_and_uniq(hidpp, device_index,
+ quadid, hdev->name,
+ hdev->uniq);
+ if (ret)
+ return ret;
+ } else {
+ memcpy(hdev->name, hidpp->shared->name, sizeof(hdev->name));
+ memcpy(hdev->uniq, hidpp->shared->uniq, sizeof(hdev->uniq));
+ }

hidpp->device_index = device_index;

- kfree(name);
return 0;
}

@@ -3299,10 +3428,20 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
}

/*
- * Make sure the device is HID++ capable, otherwise treat as generic HID
+ * Make sure the device is HID++ capable, otherwise treat as generic
+ * HID.
+ * Special case for manually affected Non Unifying receiver where we
+ * want to link the interfaces together to have a proper name instead
+ * of "Logitech USB Receiver".
*/
- if (!hidpp_validate_device(hdev))
+ if (!hidpp_validate_device(hdev)) {
+ if (id->driver_data & HIDPP_QUIRK_RECEIVER) {
+ ret = hidpp_get_shared_data(hdev);
+ if (ret)
+ return ret;
+ }
return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ }

hidpp = devm_kzalloc(&hdev->dev, sizeof(struct hidpp_device),
GFP_KERNEL);
--
2.14.3
\
 
 \ /
  Last update: 2018-09-07 12:35    [W:0.106 / U:0.000 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site