linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/13] HID: add support of Logitech touchpads and special devices
@ 2014-09-30 17:18 Benjamin Tissoires
  2014-09-30 17:18 ` [PATCH 01/13] HID: fix merge from wacom into the HID tree Benjamin Tissoires
                   ` (13 more replies)
  0 siblings, 14 replies; 24+ messages in thread
From: Benjamin Tissoires @ 2014-09-30 17:18 UTC (permalink / raw)
  To: Jiri Kosina, Nestor Lopez Casado, Andrew de los Reyes
  Cc: linux-kernel, linux-input

Hi,

Well this patch series is really an old guy:
- I started on this back in 2011 during my short appearance at Logitech
- it has been updated a few time since
- part of it is already shipped in ChromeOS
- now is the time to push it upstream (hopefully)

So, basically, this patch series aims at supporting the touchpads made by
Logitech (i.e. report the raw multi touch events), plus it also gives a driver
to enable special "features" of other various Logitech devices as reported
here[1].

There are couple of changes I introduced in this submission compared to what
Logitech and ChromeOS used to see:
- there will be only one driver for HID++ devices: hid-logitech-hidpp (this way,
  we are trying to stay in the line of the HID subsystem, one HID driver per
  vendor)
- hid-logitech-dj only deals with the transport layer, so it does not depend on
  hid-logitech-hidpp
- there is no more callbacks mechanism in hidpp and the specific drivers, all
  is handled in one place
- there is no need of a blacklist in hid-logitech-dj to decide if the device has
  a subdriver or not.
- there is no support of the mice with raw touch. This can be added in a future
  patch series.

I am finally finding this ready to be sent upstream, so I hope some of you would
be kind enough to review the code.

Regarding the features, here is what end user will expect/see:
- all the DJ devices (connected through the unifying wirelees receiver) have a
  proper name now (T400, M325, ...). This name is retrieved from the receiver
  which has a short buffer for it, thus the short name.
- the touchpads input devices will be created once the device is actually
  connected. For bluetooth device, it's a normal HID process. For DJ devices,
  the receiver is created, the hidraw device of the touchpad is created, but the
  input device, with the full name (asked from the device) appears on the first
  connection (or events).
- The TK820 - keyboard/touchpad combo presents now 2 input devices. One for the
  keyboard part, one for the touchpad. So X and libinput can deal with it
  easily.

Jiri, This patch series can be split in 2 if you would like:
- patches 1 to 4 can be scheduled easily for 3.18 IMO
- patches 5 to 13 might need some more reviews, so either 3.18 or 3.19

Cheers,
Benjamin

[1] https://www.mail-archive.com/linux-input@vger.kernel.org/msg11831.html

Benjamin Tissoires (13):
  HID: fix merge from wacom into the HID tree
  HID: core: do not scan reports if the group is already set
  HID: logitech-dj: rely on hid groups to separate receivers from dj
    devices
  HID: logitech-dj: merge header file into the source
  HID: Introduce hidpp, a module to handle Logitech hid++ devices
  HID: logitech: move dj devices to the HID++ module
  HID: logitech-dj: allow transfer of HID++ reports from/to the correct
    dj device
  HID: logitech: allow the DJ device to request the unifying name
  HID: logitech-dj: enable notifications on connect/disconnect
  HID: logitech-hidpp: late bind the input device on wireless connection
  HID: logitech-hidpp: Add Wireless Touchpad T650 support
  HID: logitech-hidpp: add support of the first Logitech Wireless
    Touchpad
  HID: logitech-hidpp: support combo keyboard touchpad TK820

 drivers/hid/Kconfig              |   12 +
 drivers/hid/Makefile             |    1 +
 drivers/hid/hid-core.c           |   21 +-
 drivers/hid/hid-ids.h            |    1 +
 drivers/hid/hid-logitech-dj.c    |  397 ++++++++++---
 drivers/hid/hid-logitech-dj.h    |  125 ----
 drivers/hid/hid-logitech-hidpp.c | 1201 ++++++++++++++++++++++++++++++++++++++
 include/linux/hid.h              |    5 +-
 8 files changed, 1526 insertions(+), 237 deletions(-)
 delete mode 100644 drivers/hid/hid-logitech-dj.h
 create mode 100644 drivers/hid/hid-logitech-hidpp.c

-- 
2.1.0


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 01/13] HID: fix merge from wacom into the HID tree
  2014-09-30 17:18 [PATCH 00/13] HID: add support of Logitech touchpads and special devices Benjamin Tissoires
@ 2014-09-30 17:18 ` Benjamin Tissoires
  2014-09-30 17:18 ` [PATCH 02/13] HID: core: do not scan reports if the group is already set Benjamin Tissoires
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Benjamin Tissoires @ 2014-09-30 17:18 UTC (permalink / raw)
  To: Jiri Kosina, Nestor Lopez Casado, Andrew de los Reyes
  Cc: linux-kernel, linux-input

While merging wacom from the input to the hid tree, some
comments have been duplicated. We can also integrate the
test for Synaptics devices in the switch case below, so
it is clear that there will be only one place for such
quirks.

No functional changes are expected in this commit.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/hid-core.c | 17 +++++++----------
 include/linux/hid.h    |  4 ----
 2 files changed, 7 insertions(+), 14 deletions(-)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index eb50818..6cfecb8 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -780,22 +780,19 @@ static int hid_scan_report(struct hid_device *hid)
 		hid->group = HID_GROUP_MULTITOUCH_WIN_8;
 
 	/*
-	* Vendor specific handlings
-	*/
-	if ((hid->vendor == USB_VENDOR_ID_SYNAPTICS) &&
-	    (hid->group == HID_GROUP_GENERIC) &&
-	    /* only bind to the mouse interface of composite USB devices */
-	    (hid->bus != BUS_USB || hid->type == HID_TYPE_USBMOUSE))
-		/* hid-rmi should take care of them, not hid-generic */
-		hid->group = HID_GROUP_RMI;
-
-	/*
 	 * Vendor specific handlings
 	 */
 	switch (hid->vendor) {
 	case USB_VENDOR_ID_WACOM:
 		hid->group = HID_GROUP_WACOM;
 		break;
+	case USB_VENDOR_ID_SYNAPTICS:
+		if ((hid->group == HID_GROUP_GENERIC) &&
+		    (hid->bus != BUS_USB || hid->type == HID_TYPE_USBMOUSE))
+			/* hid-rmi should only bind to the mouse interface of
+			 * composite USB devices */
+			hid->group = HID_GROUP_RMI;
+		break;
 	}
 
 	vfree(parser);
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 26ee25f..0da2711 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -311,10 +311,6 @@ struct hid_item {
  * Vendor specific HID device groups
  */
 #define HID_GROUP_RMI				0x0100
-
-/*
- * Vendor specific HID device groups
- */
 #define HID_GROUP_WACOM				0x0101
 
 /*
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 02/13] HID: core: do not scan reports if the group is already set
  2014-09-30 17:18 [PATCH 00/13] HID: add support of Logitech touchpads and special devices Benjamin Tissoires
  2014-09-30 17:18 ` [PATCH 01/13] HID: fix merge from wacom into the HID tree Benjamin Tissoires
@ 2014-09-30 17:18 ` Benjamin Tissoires
  2014-09-30 17:18 ` [PATCH 03/13] HID: logitech-dj: rely on hid groups to separate receivers from dj devices Benjamin Tissoires
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Benjamin Tissoires @ 2014-09-30 17:18 UTC (permalink / raw)
  To: Jiri Kosina, Nestor Lopez Casado, Andrew de los Reyes
  Cc: linux-kernel, linux-input

This allows the transport layer (I have in mind hid-logitech-dj and uhid)
to set the group before it is added to the hid bus. This way, it can
bypass the hid_scan_report() call, and choose in advance which driver
will handle the newly created hid device.

Signed-off-by: Benjamin Tisssoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/hid-core.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 6cfecb8..f4323d2 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2532,7 +2532,8 @@ int hid_add_device(struct hid_device *hdev)
 	 * Scan generic devices for group information
 	 */
 	if (hid_ignore_special_drivers ||
-	    !hid_match_id(hdev, hid_have_special_driver)) {
+	    (!hdev->group &&
+	     !hid_match_id(hdev, hid_have_special_driver))) {
 		ret = hid_scan_report(hdev);
 		if (ret)
 			hid_warn(hdev, "bad device descriptor (%d)\n", ret);
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 03/13] HID: logitech-dj: rely on hid groups to separate receivers from dj devices
  2014-09-30 17:18 [PATCH 00/13] HID: add support of Logitech touchpads and special devices Benjamin Tissoires
  2014-09-30 17:18 ` [PATCH 01/13] HID: fix merge from wacom into the HID tree Benjamin Tissoires
  2014-09-30 17:18 ` [PATCH 02/13] HID: core: do not scan reports if the group is already set Benjamin Tissoires
@ 2014-09-30 17:18 ` Benjamin Tissoires
  2014-09-30 17:18 ` [PATCH 04/13] HID: logitech-dj: merge header file into the source Benjamin Tissoires
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Benjamin Tissoires @ 2014-09-30 17:18 UTC (permalink / raw)
  To: Jiri Kosina, Nestor Lopez Casado, Andrew de los Reyes
  Cc: linux-kernel, linux-input

Several benefits here:
- we can drop the macro is_dj_device: I never been really conviced by
  this macro as we could fall into a null pointer anytime. Anyway time
  showed that this never happened.
- we can simplify the hid driver logitech-djdevice, and make it aware
  of any new receiver VID/PID.
- we can use the Wireless PID of the DJ device as the product id of the
  hid device, this way the sysfs will differentiate between different
  DJ devices.

Signed-off-by: Benjamin Tisssoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/hid-logitech-dj.c | 38 ++++++++++----------------------------
 drivers/hid/hid-logitech-dj.h | 10 ----------
 include/linux/hid.h           |  1 +
 3 files changed, 11 insertions(+), 38 deletions(-)

diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index 71f5692..42d38d5 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -256,11 +256,15 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
 	dj_hiddev->dev.parent = &djrcv_hdev->dev;
 	dj_hiddev->bus = BUS_USB;
 	dj_hiddev->vendor = le16_to_cpu(usbdev->descriptor.idVendor);
-	dj_hiddev->product = le16_to_cpu(usbdev->descriptor.idProduct);
+	dj_hiddev->product =
+		(dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB]
+									<< 8) |
+		dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB];
 	snprintf(dj_hiddev->name, sizeof(dj_hiddev->name),
-		"Logitech Unifying Device. Wireless PID:%02x%02x",
-		dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB],
-		dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB]);
+		"Logitech Unifying Device. Wireless PID:%04x",
+		dj_hiddev->product);
+
+	dj_hiddev->group = HID_GROUP_LOGITECH_DJ_DEVICE;
 
 	usb_make_path(usbdev, dj_hiddev->phys, sizeof(dj_hiddev->phys));
 	snprintf(tmpstr, sizeof(tmpstr), ":%d", dj_report->device_index);
@@ -714,9 +718,6 @@ static int logi_dj_probe(struct hid_device *hdev,
 	struct dj_receiver_dev *djrcv_dev;
 	int retval;
 
-	if (is_dj_device((struct dj_device *)hdev->driver_data))
-		return -ENODEV;
-
 	dbg_hid("%s called for ifnum %d\n", __func__,
 		intf->cur_altsetting->desc.bInterfaceNumber);
 
@@ -869,22 +870,6 @@ static void logi_dj_remove(struct hid_device *hdev)
 	hid_set_drvdata(hdev, NULL);
 }
 
-static int logi_djdevice_probe(struct hid_device *hdev,
-			 const struct hid_device_id *id)
-{
-	int ret;
-	struct dj_device *dj_dev = hdev->driver_data;
-
-	if (!is_dj_device(dj_dev))
-		return -ENODEV;
-
-	ret = hid_parse(hdev);
-	if (!ret)
-		ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
-
-	return ret;
-}
-
 static const struct hid_device_id logi_dj_receivers[] = {
 	{HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
 		USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)},
@@ -908,17 +893,14 @@ static struct hid_driver logi_djreceiver_driver = {
 
 
 static const struct hid_device_id logi_dj_devices[] = {
-	{HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
-		USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)},
-	{HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
-		USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2)},
+	{ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+		USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
 	{}
 };
 
 static struct hid_driver logi_djdevice_driver = {
 	.name = "logitech-djdevice",
 	.id_table = logi_dj_devices,
-	.probe = logi_djdevice_probe,
 };
 
 
diff --git a/drivers/hid/hid-logitech-dj.h b/drivers/hid/hid-logitech-dj.h
index daeb0aa..b1208c9 100644
--- a/drivers/hid/hid-logitech-dj.h
+++ b/drivers/hid/hid-logitech-dj.h
@@ -112,14 +112,4 @@ struct dj_device {
 	u8 device_index;
 };
 
-/**
- * is_dj_device - know if the given dj_device is not the receiver.
- * @dj_dev: the dj device to test
- *
- * This macro tests if a struct dj_device pointer is a device created
- * by the bus enumarator.
- */
-#define is_dj_device(dj_dev) \
-	(&(dj_dev)->dj_receiver_dev->hdev->dev == (dj_dev)->hdev->dev.parent)
-
 #endif
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 0da2711..a50a6dd 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -312,6 +312,7 @@ struct hid_item {
  */
 #define HID_GROUP_RMI				0x0100
 #define HID_GROUP_WACOM				0x0101
+#define HID_GROUP_LOGITECH_DJ_DEVICE		0x0102
 
 /*
  * This is the global environment of the parser. This information is
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 04/13] HID: logitech-dj: merge header file into the source
  2014-09-30 17:18 [PATCH 00/13] HID: add support of Logitech touchpads and special devices Benjamin Tissoires
                   ` (2 preceding siblings ...)
  2014-09-30 17:18 ` [PATCH 03/13] HID: logitech-dj: rely on hid groups to separate receivers from dj devices Benjamin Tissoires
@ 2014-09-30 17:18 ` Benjamin Tissoires
  2014-09-30 17:18 ` [PATCH 05/13] HID: Introduce hidpp, a module to handle Logitech hid++ devices Benjamin Tissoires
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Benjamin Tissoires @ 2014-09-30 17:18 UTC (permalink / raw)
  To: Jiri Kosina, Nestor Lopez Casado, Andrew de los Reyes
  Cc: linux-kernel, linux-input

There is no point in keeping the header in a separate file, nobody
but hid-logitech-dj should have access to its content.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/hid-logitech-dj.c |  88 +++++++++++++++++++++++++++++++-
 drivers/hid/hid-logitech-dj.h | 115 ------------------------------------------
 2 files changed, 87 insertions(+), 116 deletions(-)
 delete mode 100644 drivers/hid/hid-logitech-dj.h

diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index 42d38d5..6aea16d 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -26,9 +26,95 @@
 #include <linux/hid.h>
 #include <linux/module.h>
 #include <linux/usb.h>
+#include <linux/kfifo.h>
 #include <asm/unaligned.h>
 #include "hid-ids.h"
-#include "hid-logitech-dj.h"
+
+#define DJ_MAX_PAIRED_DEVICES			6
+#define DJ_MAX_NUMBER_NOTIFICATIONS		8
+#define DJ_RECEIVER_INDEX			0
+#define DJ_DEVICE_INDEX_MIN			1
+#define DJ_DEVICE_INDEX_MAX			6
+
+#define DJREPORT_SHORT_LENGTH			15
+#define DJREPORT_LONG_LENGTH			32
+
+#define REPORT_ID_DJ_SHORT			0x20
+#define REPORT_ID_DJ_LONG			0x21
+
+#define REPORT_TYPE_RFREPORT_LAST		0x1F
+
+/* Command Switch to DJ mode */
+#define REPORT_TYPE_CMD_SWITCH			0x80
+#define CMD_SWITCH_PARAM_DEVBITFIELD		0x00
+#define CMD_SWITCH_PARAM_TIMEOUT_SECONDS	0x01
+#define TIMEOUT_NO_KEEPALIVE			0x00
+
+/* Command to Get the list of Paired devices */
+#define REPORT_TYPE_CMD_GET_PAIRED_DEVICES	0x81
+
+/* Device Paired Notification */
+#define REPORT_TYPE_NOTIF_DEVICE_PAIRED		0x41
+#define SPFUNCTION_MORE_NOTIF_EXPECTED		0x01
+#define SPFUNCTION_DEVICE_LIST_EMPTY		0x02
+#define DEVICE_PAIRED_PARAM_SPFUNCTION		0x00
+#define DEVICE_PAIRED_PARAM_EQUAD_ID_LSB	0x01
+#define DEVICE_PAIRED_PARAM_EQUAD_ID_MSB	0x02
+#define DEVICE_PAIRED_RF_REPORT_TYPE		0x03
+
+/* Device Un-Paired Notification */
+#define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED	0x40
+
+
+/* Connection Status Notification */
+#define REPORT_TYPE_NOTIF_CONNECTION_STATUS	0x42
+#define CONNECTION_STATUS_PARAM_STATUS		0x00
+#define STATUS_LINKLOSS				0x01
+
+/* Error Notification */
+#define REPORT_TYPE_NOTIF_ERROR			0x7F
+#define NOTIF_ERROR_PARAM_ETYPE			0x00
+#define ETYPE_KEEPALIVE_TIMEOUT			0x01
+
+/* supported DJ HID && RF report types */
+#define REPORT_TYPE_KEYBOARD			0x01
+#define REPORT_TYPE_MOUSE			0x02
+#define REPORT_TYPE_CONSUMER_CONTROL		0x03
+#define REPORT_TYPE_SYSTEM_CONTROL		0x04
+#define REPORT_TYPE_MEDIA_CENTER		0x08
+#define REPORT_TYPE_LEDS			0x0E
+
+/* RF Report types bitfield */
+#define STD_KEYBOARD				0x00000002
+#define STD_MOUSE				0x00000004
+#define MULTIMEDIA				0x00000008
+#define POWER_KEYS				0x00000010
+#define MEDIA_CENTER				0x00000100
+#define KBD_LEDS				0x00004000
+
+struct dj_report {
+	u8 report_id;
+	u8 device_index;
+	u8 report_type;
+	u8 report_params[DJREPORT_SHORT_LENGTH - 3];
+};
+
+struct dj_receiver_dev {
+	struct hid_device *hdev;
+	struct dj_device *paired_dj_devices[DJ_MAX_PAIRED_DEVICES +
+					    DJ_DEVICE_INDEX_MIN];
+	struct work_struct work;
+	struct kfifo notif_fifo;
+	spinlock_t lock;
+	bool querying_devices;
+};
+
+struct dj_device {
+	struct hid_device *hdev;
+	struct dj_receiver_dev *dj_receiver_dev;
+	u32 reports_supported;
+	u8 device_index;
+};
 
 /* Keyboard descriptor (1) */
 static const char kbd_descriptor[] = {
diff --git a/drivers/hid/hid-logitech-dj.h b/drivers/hid/hid-logitech-dj.h
deleted file mode 100644
index b1208c9..0000000
--- a/drivers/hid/hid-logitech-dj.h
+++ /dev/null
@@ -1,115 +0,0 @@
-#ifndef __HID_LOGITECH_DJ_H
-#define __HID_LOGITECH_DJ_H
-
-/*
- *  HID driver for Logitech Unifying receivers
- *
- *  Copyright (c) 2011 Logitech
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <linux/kfifo.h>
-
-#define DJ_MAX_PAIRED_DEVICES			6
-#define DJ_MAX_NUMBER_NOTIFICATIONS		8
-#define DJ_RECEIVER_INDEX			0
-#define DJ_DEVICE_INDEX_MIN 			1
-#define DJ_DEVICE_INDEX_MAX 			6
-
-#define DJREPORT_SHORT_LENGTH			15
-#define DJREPORT_LONG_LENGTH			32
-
-#define REPORT_ID_DJ_SHORT			0x20
-#define REPORT_ID_DJ_LONG			0x21
-
-#define REPORT_TYPE_RFREPORT_FIRST		0x01
-#define REPORT_TYPE_RFREPORT_LAST		0x1F
-
-/* Command Switch to DJ mode */
-#define REPORT_TYPE_CMD_SWITCH			0x80
-#define CMD_SWITCH_PARAM_DEVBITFIELD		0x00
-#define CMD_SWITCH_PARAM_TIMEOUT_SECONDS	0x01
-#define TIMEOUT_NO_KEEPALIVE			0x00
-
-/* Command to Get the list of Paired devices */
-#define REPORT_TYPE_CMD_GET_PAIRED_DEVICES	0x81
-
-/* Device Paired Notification */
-#define REPORT_TYPE_NOTIF_DEVICE_PAIRED		0x41
-#define SPFUNCTION_MORE_NOTIF_EXPECTED		0x01
-#define SPFUNCTION_DEVICE_LIST_EMPTY		0x02
-#define DEVICE_PAIRED_PARAM_SPFUNCTION		0x00
-#define DEVICE_PAIRED_PARAM_EQUAD_ID_LSB	0x01
-#define DEVICE_PAIRED_PARAM_EQUAD_ID_MSB	0x02
-#define DEVICE_PAIRED_RF_REPORT_TYPE		0x03
-
-/* Device Un-Paired Notification */
-#define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED	0x40
-
-
-/* Connection Status Notification */
-#define REPORT_TYPE_NOTIF_CONNECTION_STATUS	0x42
-#define CONNECTION_STATUS_PARAM_STATUS		0x00
-#define STATUS_LINKLOSS				0x01
-
-/* Error Notification */
-#define REPORT_TYPE_NOTIF_ERROR			0x7F
-#define NOTIF_ERROR_PARAM_ETYPE			0x00
-#define ETYPE_KEEPALIVE_TIMEOUT			0x01
-
-/* supported DJ HID && RF report types */
-#define REPORT_TYPE_KEYBOARD			0x01
-#define REPORT_TYPE_MOUSE			0x02
-#define REPORT_TYPE_CONSUMER_CONTROL		0x03
-#define REPORT_TYPE_SYSTEM_CONTROL		0x04
-#define REPORT_TYPE_MEDIA_CENTER		0x08
-#define REPORT_TYPE_LEDS			0x0E
-
-/* RF Report types bitfield */
-#define STD_KEYBOARD				0x00000002
-#define STD_MOUSE				0x00000004
-#define MULTIMEDIA				0x00000008
-#define POWER_KEYS				0x00000010
-#define MEDIA_CENTER				0x00000100
-#define KBD_LEDS				0x00004000
-
-struct dj_report {
-	u8 report_id;
-	u8 device_index;
-	u8 report_type;
-	u8 report_params[DJREPORT_SHORT_LENGTH - 3];
-};
-
-struct dj_receiver_dev {
-	struct hid_device *hdev;
-	struct dj_device *paired_dj_devices[DJ_MAX_PAIRED_DEVICES +
-					    DJ_DEVICE_INDEX_MIN];
-	struct work_struct work;
-	struct kfifo notif_fifo;
-	spinlock_t lock;
-	bool querying_devices;
-};
-
-struct dj_device {
-	struct hid_device *hdev;
-	struct dj_receiver_dev *dj_receiver_dev;
-	u32 reports_supported;
-	u8 device_index;
-};
-
-#endif
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 05/13] HID: Introduce hidpp, a module to handle Logitech hid++ devices
  2014-09-30 17:18 [PATCH 00/13] HID: add support of Logitech touchpads and special devices Benjamin Tissoires
                   ` (3 preceding siblings ...)
  2014-09-30 17:18 ` [PATCH 04/13] HID: logitech-dj: merge header file into the source Benjamin Tissoires
@ 2014-09-30 17:18 ` Benjamin Tissoires
  2014-09-30 17:18 ` [PATCH 06/13] HID: logitech: move dj devices to the HID++ module Benjamin Tissoires
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Benjamin Tissoires @ 2014-09-30 17:18 UTC (permalink / raw)
  To: Jiri Kosina, Nestor Lopez Casado, Andrew de los Reyes
  Cc: linux-kernel, linux-input

Logitech devices use a vendor protocol to communicate various
information with the device. This protocol is called HID++,
and an exerpt can be found here:
https://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28&usp=shar

The main difficulty which is related to this protocol is that
it is a synchronous protocol using the input reports.
So when we want to get some information from the device, we need
to wait for a matching input report.

This driver introduce this capabilities to be able to support
the multitouch mode of the Logitech Wireless Touchpad T651
(the bluetooth one). The multitouch data is available directly
from the mouse input reports, and we just need to query the device
on connect about its caracteristics.

HID++ and the touchpad features has a specific reporting mode
which uses pure HID++ reports, but Logitech told us not to use
it for this specific device. During QA, they detected that
some bluetooth input reports where lost, and so the only supported
mode is the pointer mode.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/Kconfig              |  11 +
 drivers/hid/Makefile             |   1 +
 drivers/hid/hid-core.c           |   1 +
 drivers/hid/hid-ids.h            |   1 +
 drivers/hid/hid-logitech-hidpp.c | 842 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 856 insertions(+)
 create mode 100644 drivers/hid/hid-logitech-hidpp.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index f42df4d..6f299cd 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -378,6 +378,17 @@ config HID_LOGITECH_DJ
 	generic USB_HID driver and all incoming events will be multiplexed
 	into a single mouse and a single keyboard device.
 
+config HID_LOGITECH_HIDPP
+	tristate "Logitech HID++ devices support"
+	depends on HID_LOGITECH
+	---help---
+	Support for Logitech devices relyingon the HID++ Logitech specification
+
+	Say Y if you want support for Logitech devices relying on the HID++
+	specification. Such devices are the various Logitech Touchpads (T650,
+	T651, TK820), some mice (Zone Touch mouse), or even keyboards (Solar
+	Keayboard).
+
 config LOGITECH_FF
 	bool "Logitech force feedback support"
 	depends on HID_LOGITECH
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index e2850d8..b102774b 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_HID_LCPOWER)       += hid-lcpower.o
 obj-$(CONFIG_HID_LENOVO)	+= hid-lenovo.o
 obj-$(CONFIG_HID_LOGITECH)	+= hid-logitech.o
 obj-$(CONFIG_HID_LOGITECH_DJ)	+= hid-logitech-dj.o
+obj-$(CONFIG_HID_LOGITECH_HIDPP)	+= hid-logitech-hidpp.o
 obj-$(CONFIG_HID_MAGICMOUSE)    += hid-magicmouse.o
 obj-$(CONFIG_HID_MICROSOFT)	+= hid-microsoft.o
 obj-$(CONFIG_HID_MONTEREY)	+= hid-monterey.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index f4323d2..ee7320a 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1815,6 +1815,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_T651) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index d4673ae..3a5681c 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -575,6 +575,7 @@
 
 #define USB_VENDOR_ID_LOGITECH		0x046d
 #define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
+#define USB_DEVICE_ID_LOGITECH_T651	0xb00c
 #define USB_DEVICE_ID_LOGITECH_RECEIVER	0xc101
 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST  0xc110
 #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
new file mode 100644
index 0000000..7dd9163
--- /dev/null
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -0,0 +1,842 @@
+/*
+ *  HIDPP protocol for Logitech Unifying receivers
+ *
+ *  Copyright (c) 2011 Logitech (c)
+ *  Copyright (c) 2012-2013 Google (c)
+ *  Copyright (c) 2013-2014 Red Hat Inc.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/kfifo.h>
+#include <linux/input/mt.h>
+#include <asm/unaligned.h>
+#include "hid-ids.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
+MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>");
+
+#define REPORT_ID_HIDPP_SHORT			0x10
+#define REPORT_ID_HIDPP_LONG			0x11
+
+#define HIDPP_REPORT_SHORT_LENGTH		7
+#define HIDPP_REPORT_LONG_LENGTH		20
+
+#define HIDPP_QUIRK_CLASS_WTP			BIT(0)
+
+/*
+ * There are two hidpp protocols in use, the first version hidpp10 is known
+ * as register access protocol or RAP, the second version hidpp20 is known as
+ * feature access protocol or FAP
+ *
+ * Most older devices (including the Unifying usb receiver) use the RAP protocol
+ * where as most newer devices use the FAP protocol. Both protocols are
+ * compatible with the underlying transport, which could be usb, Unifiying, or
+ * bluetooth. The message lengths are defined by the hid vendor specific report
+ * descriptor for the HIDPP_SHORT report type (total message lenth 7 bytes) and
+ * the HIDPP_LONG report type (total message length 20 bytes)
+ *
+ * The RAP protocol uses both report types, whereas the FAP only uses HIDPP_LONG
+ * messages. The Unifying receiver itself responds to RAP messages (device index
+ * is 0xFF for the receiver), and all messages (short or long) with a device
+ * index between 1 and 6 are passed untouched to the corresponding paired
+ * Unifying device.
+ *
+ * The paired device can be RAP or FAP, it will receive the message untouched
+ * from the Unifiying receiver.
+ */
+
+struct fap {
+	u8 feature_index;
+	u8 funcindex_clientid;
+	u8 params[HIDPP_REPORT_LONG_LENGTH - 4U];
+};
+
+struct rap {
+	u8 sub_id;
+	u8 reg_address;
+	u8 params[HIDPP_REPORT_LONG_LENGTH - 4U];
+};
+
+struct hidpp_report {
+	u8 report_id;
+	u8 device_index;
+	union {
+		struct fap fap;
+		struct rap rap;
+		u8 rawbytes[sizeof(struct fap)];
+	};
+} __packed;
+
+struct hidpp_device {
+	struct hid_device *hid_dev;
+	struct mutex send_mutex;
+	void *send_receive_buf;
+	wait_queue_head_t wait;
+	bool answer_available;
+	u8 protocol_major;
+	u8 protocol_minor;
+
+	void *private_data;
+
+	unsigned long quirks;
+};
+
+
+#define HIDPP_ERROR				0x8f
+#define HIDPP_ERROR_SUCCESS			0x00
+#define HIDPP_ERROR_INVALID_SUBID		0x01
+#define HIDPP_ERROR_INVALID_ADRESS		0x02
+#define HIDPP_ERROR_INVALID_VALUE		0x03
+#define HIDPP_ERROR_CONNECT_FAIL		0x04
+#define HIDPP_ERROR_TOO_MANY_DEVICES		0x05
+#define HIDPP_ERROR_ALREADY_EXISTS		0x06
+#define HIDPP_ERROR_BUSY			0x07
+#define HIDPP_ERROR_UNKNOWN_DEVICE		0x08
+#define HIDPP_ERROR_RESOURCE_ERROR		0x09
+#define HIDPP_ERROR_REQUEST_UNAVAILABLE		0x0a
+#define HIDPP_ERROR_INVALID_PARAM_VALUE		0x0b
+#define HIDPP_ERROR_WRONG_PIN_CODE		0x0c
+
+static int __hidpp_send_report(struct hid_device *hdev,
+				struct hidpp_report *hidpp_report)
+{
+	int fields_count, ret;
+
+	switch (hidpp_report->report_id) {
+	case REPORT_ID_HIDPP_SHORT:
+		fields_count = HIDPP_REPORT_SHORT_LENGTH;
+		break;
+	case REPORT_ID_HIDPP_LONG:
+		fields_count = HIDPP_REPORT_LONG_LENGTH;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	/*
+	 * set the device_index as the receiver, it will be overwritten by
+	 * hid_hw_request if needed
+	 */
+	hidpp_report->device_index = 0xff;
+
+	ret = hid_hw_raw_request(hdev, hidpp_report->report_id,
+		(u8 *)hidpp_report, fields_count, HID_OUTPUT_REPORT,
+		HID_REQ_SET_REPORT);
+
+	return ret == fields_count ? 0 : -1;
+}
+
+static int hidpp_send_message_sync(struct hidpp_device *hidpp,
+	struct hidpp_report *message,
+	struct hidpp_report *response)
+{
+	int ret;
+
+	mutex_lock(&hidpp->send_mutex);
+
+	hidpp->send_receive_buf = response;
+	hidpp->answer_available = false;
+
+	/*
+	 * So that we can later validate the answer when it arrives
+	 * in hidpp_raw_event
+	 */
+	*response = *message;
+
+	ret = __hidpp_send_report(hidpp->hid_dev, message);
+
+	if (ret) {
+		dbg_hid("__hidpp_send_report returned err: %d\n", ret);
+		memset(response, 0, sizeof(struct hidpp_report));
+		goto exit;
+	}
+
+	if (!wait_event_timeout(hidpp->wait, hidpp->answer_available,
+				5*HZ)) {
+		dbg_hid("%s:timeout waiting for response\n", __func__);
+		memset(response, 0, sizeof(struct hidpp_report));
+		ret = -ETIMEDOUT;
+	}
+
+	if (response->report_id == REPORT_ID_HIDPP_SHORT &&
+	    response->fap.feature_index == HIDPP_ERROR) {
+		ret = response->fap.params[1];
+		dbg_hid("__hidpp_send_report got hidpp error %02X\n", ret);
+		goto exit;
+	}
+
+exit:
+	mutex_unlock(&hidpp->send_mutex);
+	return ret;
+
+}
+
+static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp,
+	u8 feat_index, u8 funcindex_clientid, u8 *params, int param_count,
+	struct hidpp_report *response)
+{
+	struct hidpp_report *message = kzalloc(sizeof(struct hidpp_report),
+			GFP_KERNEL);
+	int ret;
+
+	if (param_count > sizeof(message->fap.params))
+		return -EINVAL;
+
+	message->report_id = REPORT_ID_HIDPP_LONG;
+	message->fap.feature_index = feat_index;
+	message->fap.funcindex_clientid = funcindex_clientid;
+	memcpy(&message->fap.params, params, param_count);
+
+	ret = hidpp_send_message_sync(hidpp, message, response);
+	kfree(message);
+	return ret;
+}
+
+static inline bool hidpp_match_answer(struct hidpp_report *question,
+		struct hidpp_report *answer)
+{
+	return (answer->fap.feature_index == question->fap.feature_index) &&
+	   (answer->fap.funcindex_clientid == question->fap.funcindex_clientid);
+}
+
+static inline bool hidpp_match_error(struct hidpp_report *question,
+		struct hidpp_report *answer)
+{
+	return (answer->fap.feature_index == HIDPP_ERROR) &&
+	    (answer->fap.funcindex_clientid == question->fap.feature_index) &&
+	    (answer->fap.params[0] == question->fap.funcindex_clientid);
+}
+
+/* -------------------------------------------------------------------------- */
+/* 0x0000: Root                                                               */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_ROOT					0x0000
+#define HIDPP_PAGE_ROOT_IDX				0x00
+
+#define CMD_ROOT_GET_FEATURE				0x01
+#define CMD_ROOT_GET_PROTOCOL_VERSION			0x11
+
+static int hidpp_root_get_feature(struct hidpp_device *hidpp, u16 feature,
+	u8 *feature_index, u8 *feature_type)
+{
+	struct hidpp_report response;
+	int ret;
+	u8 params[2] = { feature >> 8, feature & 0x00FF };
+
+	ret = hidpp_send_fap_command_sync(hidpp,
+			HIDPP_PAGE_ROOT_IDX,
+			CMD_ROOT_GET_FEATURE,
+			params, 2, &response);
+	if (ret)
+		return ret;
+
+	*feature_index = response.fap.params[0];
+	*feature_type = response.fap.params[1];
+
+	return ret;
+}
+
+static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp)
+{
+	struct hidpp_report response;
+	int ret;
+
+	ret = hidpp_send_fap_command_sync(hidpp,
+			HIDPP_PAGE_ROOT_IDX,
+			CMD_ROOT_GET_PROTOCOL_VERSION,
+			NULL, 0, &response);
+
+	if (ret == 1) {
+		hidpp->protocol_major = 1;
+		hidpp->protocol_minor = 0;
+		return 0;
+	}
+
+	if (ret)
+		return -ret;
+
+	hidpp->protocol_major = response.fap.params[0];
+	hidpp->protocol_minor = response.fap.params[1];
+
+	return ret;
+}
+
+static bool hidpp_is_connected(struct hidpp_device *hidpp)
+{
+	int ret;
+
+	ret = hidpp_root_get_protocol_version(hidpp);
+	if (!ret)
+		hid_dbg(hidpp->hid_dev, "HID++ %u.%u device connected.\n",
+			hidpp->protocol_major, hidpp->protocol_minor);
+	return ret == 0;
+}
+
+/* -------------------------------------------------------------------------- */
+/* 0x0005: GetDeviceNameType                                                  */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_GET_DEVICE_NAME_TYPE			0x0005
+
+#define CMD_GET_DEVICE_NAME_TYPE_GET_COUNT		0x01
+#define CMD_GET_DEVICE_NAME_TYPE_GET_DEVICE_NAME	0x11
+#define CMD_GET_DEVICE_NAME_TYPE_GET_TYPE		0x21
+
+static int hidpp_devicenametype_get_count(struct hidpp_device *hidpp,
+	u8 feature_index, u8 *nameLength)
+{
+	struct hidpp_report response;
+	int ret;
+
+	ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+		CMD_GET_DEVICE_NAME_TYPE_GET_COUNT, NULL, 0, &response);
+
+	if (ret)
+		return -ret;
+
+	*nameLength = response.fap.params[0];
+
+	return ret;
+}
+
+static int hidpp_devicenametype_get_device_name(struct hidpp_device *hidpp,
+	u8 feature_index, u8 char_index, char *device_name, int len_buf)
+{
+	struct hidpp_report response;
+	int ret, i;
+	int count;
+
+	ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+		CMD_GET_DEVICE_NAME_TYPE_GET_DEVICE_NAME, &char_index, 1,
+		&response);
+
+	if (ret)
+		return -ret;
+
+	if (response.report_id == REPORT_ID_HIDPP_LONG)
+		count = HIDPP_REPORT_LONG_LENGTH - 4;
+	else
+		count = HIDPP_REPORT_SHORT_LENGTH - 4;
+
+	if (len_buf < count)
+		count = len_buf;
+
+	for (i = 0; i < count; i++)
+		device_name[i] = response.fap.params[i];
+
+	return count;
+}
+
+static char *hidpp_get_device_name(struct hidpp_device *hidpp, u8 *name_length)
+{
+	u8 feature_type;
+	u8 feature_index;
+	u8 __name_length;
+	char *name;
+	unsigned index = 0;
+	int ret;
+
+	ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_GET_DEVICE_NAME_TYPE,
+		&feature_index, &feature_type);
+	if (ret)
+		goto out_err;
+
+	ret = hidpp_devicenametype_get_count(hidpp, feature_index,
+		&__name_length);
+	if (ret)
+		goto out_err;
+
+	name = kzalloc(__name_length + 1, GFP_KERNEL);
+	if (!name)
+		goto out_err;
+
+	*name_length = __name_length + 1;
+	while (index < __name_length)
+		index += hidpp_devicenametype_get_device_name(hidpp,
+			feature_index, index, name + index,
+			__name_length - index);
+
+	return name;
+
+out_err:
+	*name_length = 0;
+	return NULL;
+}
+
+/* -------------------------------------------------------------------------- */
+/* 0x6100: TouchPadRawXY                                                      */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_TOUCHPAD_RAW_XY			0x6100
+
+#define CMD_TOUCHPAD_GET_RAW_INFO			0x01
+
+#define TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT		0x01
+#define TOUCHPAD_RAW_XY_ORIGIN_UPPER_LEFT		0x03
+
+struct hidpp_touchpad_raw_info {
+	u16 x_size;
+	u16 y_size;
+	u8 z_range;
+	u8 area_range;
+	u8 timestamp_unit;
+	u8 maxcontacts;
+	u8 origin;
+	u16 res;
+};
+
+struct hidpp_touchpad_raw_xy_finger {
+	u8 contact_type;
+	u8 contact_status;
+	u16 x;
+	u16 y;
+	u8 z;
+	u8 area;
+	u8 finger_id;
+};
+
+struct hidpp_touchpad_raw_xy {
+	u16 timestamp;
+	struct hidpp_touchpad_raw_xy_finger fingers[2];
+	u8 spurious_flag;
+	u8 end_of_frame;
+	u8 finger_count;
+	u8 button;
+};
+
+static int hidpp_touchpad_get_raw_info(struct hidpp_device *hidpp,
+	u8 feature_index, struct hidpp_touchpad_raw_info *raw_info)
+{
+	struct hidpp_report response;
+	int ret;
+	u8 *params = (u8 *)response.fap.params;
+
+	ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+		CMD_TOUCHPAD_GET_RAW_INFO, NULL, 0, &response);
+
+	if (ret)
+		return -ret;
+
+	raw_info->x_size = get_unaligned_be16(&params[0]);
+	raw_info->y_size = get_unaligned_be16(&params[2]);
+	raw_info->z_range = params[4];
+	raw_info->area_range = params[5];
+	raw_info->maxcontacts = params[7];
+	raw_info->origin = params[8];
+	/* res is given in unit per inch */
+	raw_info->res = get_unaligned_be16(&params[13]) * 2 / 51;
+
+	return ret;
+}
+
+/* ************************************************************************** */
+/*                                                                            */
+/* Device Support                                                             */
+/*                                                                            */
+/* ************************************************************************** */
+
+/* -------------------------------------------------------------------------- */
+/* Touchpad HID++ devices                                                     */
+/* -------------------------------------------------------------------------- */
+
+struct wtp_data {
+	struct input_dev *input;
+	u16 x_size, y_size;
+	u8 finger_count;
+	u8 mt_feature_index;
+	u8 button_feature_index;
+	u8 maxcontacts;
+	bool flip_y;
+	unsigned int resolution;
+};
+
+static int wtp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	return -1;
+}
+
+static void wtp_input_configured(struct hid_device *hdev,
+				struct hid_input *hidinput)
+{
+	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+	struct wtp_data *wd = hidpp->private_data;
+	struct input_dev *input_dev = hidinput->input;
+
+	__set_bit(EV_ABS, input_dev->evbit);
+	__set_bit(EV_KEY, input_dev->evbit);
+	__clear_bit(EV_REL, input_dev->evbit);
+	__clear_bit(EV_LED, input_dev->evbit);
+
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, wd->x_size, 0, 0);
+	input_abs_set_res(input_dev, ABS_MT_POSITION_X, wd->resolution);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, wd->y_size, 0, 0);
+	input_abs_set_res(input_dev, ABS_MT_POSITION_Y, wd->resolution);
+
+	/* Max pressure is not given by the devices, pick one */
+	input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 50, 0, 0);
+
+	input_set_capability(input_dev, EV_KEY, BTN_LEFT);
+
+	__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+
+	input_mt_init_slots(input_dev, wd->maxcontacts, INPUT_MT_POINTER |
+		INPUT_MT_DROP_UNUSED);
+
+	wd->input = input_dev;
+}
+
+static void wtp_touch_event(struct wtp_data *wd,
+	struct hidpp_touchpad_raw_xy_finger *touch_report)
+{
+	int slot;
+
+	if (!touch_report->finger_id || touch_report->contact_type)
+		/* no actual data */
+		return;
+
+	slot = input_mt_get_slot_by_key(wd->input, touch_report->finger_id);
+
+	input_mt_slot(wd->input, slot);
+	input_mt_report_slot_state(wd->input, MT_TOOL_FINGER,
+					touch_report->contact_status);
+	if (touch_report->contact_status) {
+		input_event(wd->input, EV_ABS, ABS_MT_POSITION_X,
+				touch_report->x);
+		input_event(wd->input, EV_ABS, ABS_MT_POSITION_Y,
+				wd->flip_y ? wd->y_size - touch_report->y :
+					     touch_report->y);
+		input_event(wd->input, EV_ABS, ABS_MT_PRESSURE,
+				touch_report->area);
+	}
+}
+
+static void wtp_send_raw_xy_event(struct hidpp_device *hidpp,
+		struct hidpp_touchpad_raw_xy *raw)
+{
+	struct wtp_data *wd = hidpp->private_data;
+	int i;
+
+	for (i = 0; i < 2; i++)
+		wtp_touch_event(wd, &(raw->fingers[i]));
+
+	if (raw->end_of_frame)
+		input_event(wd->input, EV_KEY, BTN_LEFT, raw->button);
+
+	if (raw->end_of_frame || raw->finger_count <= 2) {
+		input_mt_sync_frame(wd->input);
+		input_sync(wd->input);
+	}
+}
+
+static int wtp_mouse_raw_xy_event(struct hidpp_device *hidpp, u8 *data)
+{
+	struct wtp_data *wd = hidpp->private_data;
+	u8 c1_area = ((data[7] & 0xf) * (data[7] & 0xf) +
+		      (data[7] >> 4) * (data[7] >> 4)) / 2;
+	u8 c2_area = ((data[13] & 0xf) * (data[13] & 0xf) +
+		      (data[13] >> 4) * (data[13] >> 4)) / 2;
+	struct hidpp_touchpad_raw_xy raw = {
+		.timestamp = data[1],
+		.fingers = {
+			{
+				.contact_type = 0,
+				.contact_status = !!data[7],
+				.x = get_unaligned_le16(&data[3]),
+				.y = get_unaligned_le16(&data[5]),
+				.z = c1_area,
+				.area = c1_area,
+				.finger_id = data[2],
+			}, {
+				.contact_type = 0,
+				.contact_status = !!data[13],
+				.x = get_unaligned_le16(&data[9]),
+				.y = get_unaligned_le16(&data[11]),
+				.z = c2_area,
+				.area = c2_area,
+				.finger_id = data[8],
+			}
+		},
+		.finger_count = wd->maxcontacts,
+		.spurious_flag = 0,
+		.end_of_frame = (data[0] >> 7) == 0,
+		.button = data[0] & 0x01,
+	};
+
+	wtp_send_raw_xy_event(hidpp, &raw);
+
+	return 1;
+}
+
+static int wtp_raw_event(struct hid_device *hdev, u8 *data, int size)
+{
+	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+	struct wtp_data *wd = hidpp->private_data;
+
+	if (!wd || !wd->input || (data[0] != 0x02) || size < 21)
+		return 1;
+
+	return wtp_mouse_raw_xy_event(hidpp, &data[7]);
+}
+
+static int wtp_get_config(struct hidpp_device *hidpp)
+{
+	struct wtp_data *wd = hidpp->private_data;
+	struct hidpp_touchpad_raw_info raw_info = {0};
+	u8 feature_type;
+	int ret;
+
+	ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_TOUCHPAD_RAW_XY,
+		&wd->mt_feature_index, &feature_type);
+	if (ret)
+		/* means that the device is not powered up */
+		return ret;
+
+	ret = hidpp_touchpad_get_raw_info(hidpp, wd->mt_feature_index,
+		&raw_info);
+	if (ret)
+		return ret;
+
+	wd->x_size = raw_info.x_size;
+	wd->y_size = raw_info.y_size;
+	wd->maxcontacts = raw_info.maxcontacts;
+	wd->flip_y = raw_info.origin == TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT;
+	wd->resolution = raw_info.res;
+
+	return 0;
+}
+
+static int wtp_allocate(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+	struct wtp_data *wd;
+
+	wd = devm_kzalloc(&hdev->dev, sizeof(struct wtp_data),
+			GFP_KERNEL);
+	if (!wd)
+		return -ENOMEM;
+
+	hidpp->private_data = wd;
+
+	return 0;
+};
+
+/* -------------------------------------------------------------------------- */
+/* Generic HID++ devices                                                      */
+/* -------------------------------------------------------------------------- */
+
+static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+		struct hid_field *field, struct hid_usage *usage,
+		unsigned long **bit, int *max)
+{
+	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+
+	if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
+		return wtp_input_mapping(hdev, hi, field, usage, bit, max);
+
+	return 0;
+}
+
+static void hidpp_input_configured(struct hid_device *hdev,
+				struct hid_input *hidinput)
+{
+	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+
+	if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
+		wtp_input_configured(hdev, hidinput);
+}
+
+static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
+		int size)
+{
+	struct hidpp_report *question = hidpp->send_receive_buf;
+	struct hidpp_report *answer = hidpp->send_receive_buf;
+	struct hidpp_report *report = (struct hidpp_report *)data;
+
+	/*
+	 * If the mutex is locked then we have a pending answer from a
+	 * previoulsly sent command
+	 */
+	if (unlikely(mutex_is_locked(&hidpp->send_mutex))) {
+		/*
+		 * Check for a correct hidpp20 answer or the corresponding
+		 * error
+		 */
+		if (hidpp_match_answer(question, report) ||
+				hidpp_match_error(question, report)) {
+			*answer = *report;
+			hidpp->answer_available = true;
+			wake_up(&hidpp->wait);
+			/*
+			 * This was an answer to a command that this driver sent
+			 * We return 1 to hid-core to avoid forwarding the
+			 * command upstream as it has been treated by the driver
+			 */
+
+			return 1;
+		}
+	}
+
+	if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
+		return wtp_raw_event(hidpp->hid_dev, data, size);
+
+	return 0;
+}
+
+static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
+		u8 *data, int size)
+{
+	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+
+	switch (data[0]) {
+	case REPORT_ID_HIDPP_LONG:
+		if (size != HIDPP_REPORT_LONG_LENGTH) {
+			hid_err(hdev, "received hid++ report of bad size (%d)",
+				size);
+			return 1;
+		}
+		return hidpp_raw_hidpp_event(hidpp, data, size);
+	case REPORT_ID_HIDPP_SHORT:
+		if (size != HIDPP_REPORT_SHORT_LENGTH) {
+			hid_err(hdev, "received hid++ report of bad size (%d)",
+				size);
+			return 1;
+		}
+		return hidpp_raw_hidpp_event(hidpp, data, size);
+	}
+
+	if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
+		return wtp_raw_event(hdev, data, size);
+
+	return 0;
+}
+
+static void hidpp_overwrite_name(struct hid_device *hdev)
+{
+	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+	char *name;
+	u8 name_length;
+
+	name = hidpp_get_device_name(hidpp, &name_length);
+
+	if (!name)
+		hid_err(hdev, "unable to retrieve the name of the device");
+	else
+		snprintf(hdev->name, sizeof(hdev->name), "%s", name);
+
+	kfree(name);
+}
+
+static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	struct hidpp_device *hidpp;
+	int ret;
+	bool connected;
+
+	hidpp = devm_kzalloc(&hdev->dev, sizeof(struct hidpp_device),
+			GFP_KERNEL);
+	if (!hidpp)
+		return -ENOMEM;
+
+	hidpp->hid_dev = hdev;
+	hid_set_drvdata(hdev, hidpp);
+
+	hidpp->quirks = id->driver_data;
+
+	if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
+		ret = wtp_allocate(hdev, id);
+		if (ret)
+			return ret;
+	}
+
+	mutex_init(&hidpp->send_mutex);
+	init_waitqueue_head(&hidpp->wait);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		hid_err(hdev, "%s:parse failed\n", __func__);
+		goto hid_parse_fail;
+	}
+
+	/* Allow incoming packets */
+	hid_device_io_start(hdev);
+
+	connected = hidpp_is_connected(hidpp);
+	if (!connected) {
+		hid_err(hdev, "Device not connected");
+		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);
+
+	if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
+		ret = wtp_get_config(hidpp);
+		if (ret)
+			goto hid_parse_fail;
+	}
+
+	/* Block incoming packets */
+	hid_device_io_stop(hdev);
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+	if (ret) {
+		hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
+		goto hid_hw_start_fail;
+	}
+
+	return ret;
+
+hid_hw_start_fail:
+hid_parse_fail:
+	mutex_destroy(&hidpp->send_mutex);
+	hid_set_drvdata(hdev, NULL);
+	return ret;
+}
+
+static void hidpp_remove(struct hid_device *hdev)
+{
+	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+
+	mutex_destroy(&hidpp->send_mutex);
+	hid_hw_stop(hdev);
+}
+
+static const struct hid_device_id hidpp_devices[] = {
+	{ /* wireless touchpad T651 */
+	  HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
+		USB_DEVICE_ID_LOGITECH_T651),
+	  .driver_data = HIDPP_QUIRK_CLASS_WTP },
+	{}
+};
+
+MODULE_DEVICE_TABLE(hid, hidpp_devices);
+
+static struct hid_driver hidpp_driver = {
+	.name = "logitech-hidpp-device",
+	.id_table = hidpp_devices,
+	.probe = hidpp_probe,
+	.remove = hidpp_remove,
+	.raw_event = hidpp_raw_event,
+	.input_configured = hidpp_input_configured,
+	.input_mapping = hidpp_input_mapping,
+};
+
+module_hid_driver(hidpp_driver);
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 06/13] HID: logitech: move dj devices to the HID++ module
  2014-09-30 17:18 [PATCH 00/13] HID: add support of Logitech touchpads and special devices Benjamin Tissoires
                   ` (4 preceding siblings ...)
  2014-09-30 17:18 ` [PATCH 05/13] HID: Introduce hidpp, a module to handle Logitech hid++ devices Benjamin Tissoires
@ 2014-09-30 17:18 ` Benjamin Tissoires
  2014-09-30 17:18 ` [PATCH 07/13] HID: logitech-dj: allow transfer of HID++ reports from/to the correct dj device Benjamin Tissoires
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Benjamin Tissoires @ 2014-09-30 17:18 UTC (permalink / raw)
  To: Jiri Kosina, Nestor Lopez Casado, Andrew de los Reyes
  Cc: linux-kernel, linux-input

Devices connected through the Logitech Wireless Receiver are HID++ devices.
We can handle them here to benefit from this new module and activate
enhaced support of the various wireless touchpad or mice with touch
sensors on them.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/Kconfig              |  1 +
 drivers/hid/hid-logitech-dj.c    | 42 +---------------------------------------
 drivers/hid/hid-logitech-hidpp.c | 21 ++++++++++++--------
 3 files changed, 15 insertions(+), 49 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 6f299cd..dd49a9b 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -371,6 +371,7 @@ config HID_LOGITECH_DJ
 	tristate "Logitech Unifying receivers full support"
 	depends on HIDRAW
 	depends on HID_LOGITECH
+	select HID_LOGITECH_HIDPP
 	---help---
 	Say Y if you want support for Logitech Unifying receivers and devices.
 	Unifying receivers are capable of pairing up to 6 Logitech compliant
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index 6aea16d..45a7eac 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -977,48 +977,8 @@ static struct hid_driver logi_djreceiver_driver = {
 #endif
 };
 
+module_hid_driver(logi_djreceiver_driver);
 
-static const struct hid_device_id logi_dj_devices[] = {
-	{ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
-		USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
-	{}
-};
-
-static struct hid_driver logi_djdevice_driver = {
-	.name = "logitech-djdevice",
-	.id_table = logi_dj_devices,
-};
-
-
-static int __init logi_dj_init(void)
-{
-	int retval;
-
-	dbg_hid("Logitech-DJ:%s\n", __func__);
-
-	retval = hid_register_driver(&logi_djreceiver_driver);
-	if (retval)
-		return retval;
-
-	retval = hid_register_driver(&logi_djdevice_driver);
-	if (retval)
-		hid_unregister_driver(&logi_djreceiver_driver);
-
-	return retval;
-
-}
-
-static void __exit logi_dj_exit(void)
-{
-	dbg_hid("Logitech-DJ:%s\n", __func__);
-
-	hid_unregister_driver(&logi_djdevice_driver);
-	hid_unregister_driver(&logi_djreceiver_driver);
-
-}
-
-module_init(logi_dj_init);
-module_exit(logi_dj_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Logitech");
 MODULE_AUTHOR("Nestor Lopez Casado");
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 7dd9163..48dec39 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -777,15 +777,17 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	hid_device_io_start(hdev);
 
 	connected = hidpp_is_connected(hidpp);
-	if (!connected) {
-		hid_err(hdev, "Device not connected");
-		goto hid_parse_fail;
-	}
+	if (id->group != HID_GROUP_LOGITECH_DJ_DEVICE) {
+		if (!connected) {
+			hid_err(hdev, "Device not connected");
+			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);
+		/* 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);
+	}
 
 	if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
 		ret = wtp_get_config(hidpp);
@@ -824,6 +826,9 @@ static const struct hid_device_id hidpp_devices[] = {
 	  HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
 		USB_DEVICE_ID_LOGITECH_T651),
 	  .driver_data = HIDPP_QUIRK_CLASS_WTP },
+
+	{ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+		USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
 	{}
 };
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 07/13] HID: logitech-dj: allow transfer of HID++ reports from/to the correct dj device
  2014-09-30 17:18 [PATCH 00/13] HID: add support of Logitech touchpads and special devices Benjamin Tissoires
                   ` (5 preceding siblings ...)
  2014-09-30 17:18 ` [PATCH 06/13] HID: logitech: move dj devices to the HID++ module Benjamin Tissoires
@ 2014-09-30 17:18 ` Benjamin Tissoires
  2014-09-30 17:18 ` [PATCH 08/13] HID: logitech: allow the DJ device to request the unifying name Benjamin Tissoires
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Benjamin Tissoires @ 2014-09-30 17:18 UTC (permalink / raw)
  To: Jiri Kosina, Nestor Lopez Casado, Andrew de los Reyes
  Cc: linux-kernel, linux-input

HID++ is a Logitech-specific protocol for communicating with HID
devices. DJ devices implement HID++, and so we can add the HID++
collection in the report descriptor and forward the incoming
reports from the receiver to the appropriate DJ device.

The same can be done in the other way, if someone calls a
.raw_request(), we can forward it to the correct dj device
by overriding the device_index in the HID++ report.

Signed-off-by: Benjamin Tisssoires <benjamin.tissoires@redhat.com>
---
 drivers/hid/hid-logitech-dj.c | 188 +++++++++++++++++++++++++++++++++++-------
 1 file changed, 160 insertions(+), 28 deletions(-)

diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index 45a7eac..feddacd 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -42,6 +42,15 @@
 #define REPORT_ID_DJ_SHORT			0x20
 #define REPORT_ID_DJ_LONG			0x21
 
+#define REPORT_ID_HIDPP_SHORT			0x10
+#define REPORT_ID_HIDPP_LONG			0x11
+
+#define HIDPP_REPORT_SHORT_LENGTH		7
+#define HIDPP_REPORT_LONG_LENGTH		20
+
+#define HIDPP_RECEIVER_INDEX			0xff
+
+#define REPORT_TYPE_RFREPORT_FIRST		0x01
 #define REPORT_TYPE_RFREPORT_LAST		0x1F
 
 /* Command Switch to DJ mode */
@@ -242,6 +251,57 @@ static const char media_descriptor[] = {
 	0xc0,			/* EndCollection                       */
 };				/*                                     */
 
+/* HIDPP descriptor */
+static const char hidpp_descriptor[] = {
+	0x06, 0x00, 0xff,	/* Usage Page (Vendor Defined Page 1)  */
+	0x09, 0x01,		/* Usage (Vendor Usage 1)              */
+	0xa1, 0x01,		/* Collection (Application)            */
+	0x85, 0x10,		/*   Report ID (16)                    */
+	0x75, 0x08,		/*   Report Size (8)                   */
+	0x95, 0x06,		/*   Report Count (6)                  */
+	0x15, 0x00,		/*   Logical Minimum (0)               */
+	0x26, 0xff, 0x00,	/*   Logical Maximum (255)             */
+	0x09, 0x01,		/*   Usage (Vendor Usage 1)            */
+	0x81, 0x00,		/*   Input (Data,Arr,Abs)              */
+	0x09, 0x01,		/*   Usage (Vendor Usage 1)            */
+	0x91, 0x00,		/*   Output (Data,Arr,Abs)             */
+	0xc0,			/* End Collection                      */
+	0x06, 0x00, 0xff,	/* Usage Page (Vendor Defined Page 1)  */
+	0x09, 0x02,		/* Usage (Vendor Usage 2)              */
+	0xa1, 0x01,		/* Collection (Application)            */
+	0x85, 0x11,		/*   Report ID (17)                    */
+	0x75, 0x08,		/*   Report Size (8)                   */
+	0x95, 0x13,		/*   Report Count (19)                 */
+	0x15, 0x00,		/*   Logical Minimum (0)               */
+	0x26, 0xff, 0x00,	/*   Logical Maximum (255)             */
+	0x09, 0x02,		/*   Usage (Vendor Usage 2)            */
+	0x81, 0x00,		/*   Input (Data,Arr,Abs)              */
+	0x09, 0x02,		/*   Usage (Vendor Usage 2)            */
+	0x91, 0x00,		/*   Output (Data,Arr,Abs)             */
+	0xc0,			/* End Collection                      */
+	0x06, 0x00, 0xff,	/* Usage Page (Vendor Defined Page 1)  */
+	0x09, 0x04,		/* Usage (Vendor Usage 0x04)           */
+	0xa1, 0x01,		/* Collection (Application)            */
+	0x85, 0x20,		/*   Report ID (32)                    */
+	0x75, 0x08,		/*   Report Size (8)                   */
+	0x95, 0x0e,		/*   Report Count (14)                 */
+	0x15, 0x00,		/*   Logical Minimum (0)               */
+	0x26, 0xff, 0x00,	/*   Logical Maximum (255)             */
+	0x09, 0x41,		/*   Usage (Vendor Usage 0x41)         */
+	0x81, 0x00,		/*   Input (Data,Arr,Abs)              */
+	0x09, 0x41,		/*   Usage (Vendor Usage 0x41)         */
+	0x91, 0x00,		/*   Output (Data,Arr,Abs)             */
+	0x85, 0x21,		/*   Report ID (33)                    */
+	0x95, 0x1f,		/*   Report Count (31)                 */
+	0x15, 0x00,		/*   Logical Minimum (0)               */
+	0x26, 0xff, 0x00,	/*   Logical Maximum (255)             */
+	0x09, 0x42,		/*   Usage (Vendor Usage 0x42)         */
+	0x81, 0x00,		/*   Input (Data,Arr,Abs)              */
+	0x09, 0x42,		/*   Usage (Vendor Usage 0x42)         */
+	0x91, 0x00,		/*   Output (Data,Arr,Abs)             */
+	0xc0,			/* End Collection                      */
+};
+
 /* Maximum size of all defined hid reports in bytes (including report id) */
 #define MAX_REPORT_SIZE 8
 
@@ -251,7 +311,8 @@ static const char media_descriptor[] = {
 	 sizeof(mse_descriptor) +		\
 	 sizeof(consumer_descriptor) +		\
 	 sizeof(syscontrol_descriptor) +	\
-	 sizeof(media_descriptor))
+	 sizeof(media_descriptor) +	\
+	 sizeof(hidpp_descriptor))
 
 /* Number of possible hid report types that can be created by this driver.
  *
@@ -512,6 +573,13 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
 	}
 }
 
+static void logi_dj_recv_forward_hidpp(struct dj_device *dj_dev, u8 *data,
+				       int size)
+{
+	/* We are called from atomic context (tasklet && djrcv->lock held) */
+	if (hid_input_report(dj_dev->hdev, HID_INPUT_REPORT, data, size, 1))
+		dbg_hid("hid_input_report error\n");
+}
 
 static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev,
 				    struct dj_report *dj_report)
@@ -609,6 +677,16 @@ static int logi_dj_ll_raw_request(struct hid_device *hid,
 	u8 *out_buf;
 	int ret;
 
+	if ((buf[0] == REPORT_ID_HIDPP_SHORT) ||
+	    (buf[0] == REPORT_ID_HIDPP_LONG)) {
+		if (count < 2)
+			return -EINVAL;
+
+		buf[1] = djdev->device_index;
+		return hid_hw_raw_request(djrcv_dev->hdev, reportnum, buf,
+				count, report_type, reqtype);
+	}
+
 	if (buf[0] != REPORT_TYPE_LEDS)
 		return -EINVAL;
 
@@ -687,6 +765,8 @@ static int logi_dj_ll_parse(struct hid_device *hid)
 			__func__, djdev->reports_supported);
 	}
 
+	rdcat(rdesc, &rsize, hidpp_descriptor, sizeof(hidpp_descriptor));
+
 	retval = hid_parse_report(hid, rdesc, rsize);
 	kfree(rdesc);
 
@@ -714,8 +794,7 @@ static struct hid_ll_driver logi_dj_ll_driver = {
 	.raw_request = logi_dj_ll_raw_request,
 };
 
-
-static int logi_dj_raw_event(struct hid_device *hdev,
+static int logi_dj_dj_event(struct hid_device *hdev,
 			     struct hid_report *report, u8 *data,
 			     int size)
 {
@@ -723,36 +802,24 @@ static int logi_dj_raw_event(struct hid_device *hdev,
 	struct dj_report *dj_report = (struct dj_report *) data;
 	unsigned long flags;
 
-	dbg_hid("%s, size:%d\n", __func__, size);
-
-	/* Here we receive all data coming from iface 2, there are 4 cases:
-	 *
-	 * 1) Data should continue its normal processing i.e. data does not
-	 * come from the DJ collection, in which case we do nothing and
-	 * return 0, so hid-core can continue normal processing (will forward
-	 * to associated hidraw device)
+	/*
+	 * Here we receive all data coming from iface 2, there are 3 cases:
 	 *
-	 * 2) Data is from DJ collection, and is intended for this driver i. e.
-	 * data contains arrival, departure, etc notifications, in which case
-	 * we queue them for delayed processing by the work queue. We return 1
-	 * to hid-core as no further processing is required from it.
+	 * 1) Data is intended for this driver i. e. data contains arrival,
+	 * departure, etc notifications, in which case we queue them for delayed
+	 * processing by the work queue. We return 1 to hid-core as no further
+	 * processing is required from it.
 	 *
-	 * 3) Data is from DJ collection, and informs a connection change,
-	 * if the change means rf link loss, then we must send a null report
-	 * to the upper layer to discard potentially pressed keys that may be
-	 * repeated forever by the input layer. Return 1 to hid-core as no
-	 * further processing is required.
+	 * 2) Data informs a connection change, if the change means rf link
+	 * loss, then we must send a null report to the upper layer to discard
+	 * potentially pressed keys that may be repeated forever by the input
+	 * layer. Return 1 to hid-core as no further processing is required.
 	 *
-	 * 4) Data is from DJ collection and is an actual input event from
-	 * a paired DJ device in which case we forward it to the correct hid
-	 * device (via hid_input_report() ) and return 1 so hid-core does not do
-	 * anything else with it.
+	 * 3) Data is an actual input event from a paired DJ device in which
+	 * case we forward it to the correct hid device (via hid_input_report()
+	 * ) and return 1 so hid-core does not anything else with it.
 	 */
 
-	/* case 1) */
-	if (data[0] != REPORT_ID_DJ_SHORT)
-		return false;
-
 	if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) ||
 	    (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) {
 		/*
@@ -797,6 +864,71 @@ out:
 	return true;
 }
 
+static int logi_dj_hidpp_event(struct hid_device *hdev,
+			     struct hid_report *report, u8 *data,
+			     int size)
+{
+	struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
+	struct dj_report *dj_report = (struct dj_report *) data;
+	unsigned long flags;
+	u8 device_index = dj_report->device_index;
+
+	if (device_index == HIDPP_RECEIVER_INDEX)
+		return false;
+
+	/*
+	 * Data is from the HID++ collection, in this case, we forward the
+	 * data to the corresponding child dj device and return 0 to hid-core
+	 * so he data also goes to the hidraw device of the receiver. This
+	 * allows a user space application to implement the full HID++ routing
+	 * via the receiver.
+	 */
+
+	if ((device_index < DJ_DEVICE_INDEX_MIN) ||
+	    (device_index > DJ_DEVICE_INDEX_MAX)) {
+		/*
+		 * Device index is wrong, bail out.
+		 * This driver can ignore safely the receiver notifications,
+		 * so ignore those reports too.
+		 */
+		dev_err(&hdev->dev, "%s: invalid device index:%d\n",
+				__func__, dj_report->device_index);
+		return false;
+	}
+
+	spin_lock_irqsave(&djrcv_dev->lock, flags);
+
+	if (!djrcv_dev->paired_dj_devices[device_index])
+		/* received an event for an unknown device, bail out */
+		goto out;
+
+	logi_dj_recv_forward_hidpp(djrcv_dev->paired_dj_devices[device_index],
+				   data, size);
+
+out:
+	spin_unlock_irqrestore(&djrcv_dev->lock, flags);
+
+	return false;
+}
+
+static int logi_dj_raw_event(struct hid_device *hdev,
+			     struct hid_report *report, u8 *data,
+			     int size)
+{
+	dbg_hid("%s, size:%d\n", __func__, size);
+
+	switch (data[0]) {
+	case REPORT_ID_DJ_SHORT:
+		return logi_dj_dj_event(hdev, report, data, size);
+	case REPORT_ID_HIDPP_SHORT:
+		/* intentional fallthrough */
+	case REPORT_ID_HIDPP_LONG:
+		return logi_dj_hidpp_event(hdev, report, data, size);
+	}
+
+	return false;
+}
+
 static int logi_dj_probe(struct hid_device *hdev,
 			 const struct hid_device_id *id)
 {
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 08/13] HID: logitech: allow the DJ device to request the unifying name
  2014-09-30 17:18 [PATCH 00/13] HID: add support of Logitech touchpads and special devices Benjamin Tissoires
                   ` (6 preceding siblings ...)
  2014-09-30 17:18 ` [PATCH 07/13] HID: logitech-dj: allow transfer of HID++ reports from/to the correct dj device Benjamin Tissoires
@ 2014-09-30 17:18 ` Benjamin Tissoires
  2014-09-30 17:18 ` [PATCH 09/13] HID: logitech-dj: enable notifications on connect/disconnect Benjamin Tissoires
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Benjamin Tissoires @ 2014-09-30 17:18 UTC (permalink / raw)
  To: Jiri Kosina, Nestor Lopez Casado, Andrew de los Reyes
  Cc: linux-kernel, linux-input

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>
---
 drivers/hid/hid-logitech-dj.c    | 24 ++++++++++--
 drivers/hid/hid-logitech-hidpp.c | 80 ++++++++++++++++++++++++++++++++++++++--
 2 files changed, 97 insertions(+), 7 deletions(-)

diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index feddacd..9bc3942 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -667,6 +667,9 @@ static void logi_dj_ll_close(struct hid_device *hid)
 	dbg_hid("%s:%s\n", __func__, hid->phys);
 }
 
+static u8 unifying_name_query[]  = {0x10, 0xff, 0x83, 0xb5, 0x40, 0x00, 0x00};
+static u8 unifying_name_answer[] = {0x11, 0xff, 0x83, 0xb5};
+
 static int logi_dj_ll_raw_request(struct hid_device *hid,
 				  unsigned char reportnum, __u8 *buf,
 				  size_t count, unsigned char report_type,
@@ -682,7 +685,13 @@ static int logi_dj_ll_raw_request(struct hid_device *hid,
 		if (count < 2)
 			return -EINVAL;
 
-		buf[1] = djdev->device_index;
+		/* special case where we should not overwrite
+		 * the device_index */
+		if (count == 7 && !memcmp(buf, unifying_name_query,
+					  sizeof(unifying_name_query)))
+			buf[4] |= djdev->device_index - 1;
+		else
+			buf[1] = djdev->device_index;
 		return hid_hw_raw_request(djrcv_dev->hdev, reportnum, buf,
 				count, report_type, reqtype);
 	}
@@ -873,8 +882,17 @@ static int logi_dj_hidpp_event(struct hid_device *hdev,
 	unsigned long flags;
 	u8 device_index = dj_report->device_index;
 
-	if (device_index == HIDPP_RECEIVER_INDEX)
-		return false;
+	if (device_index == HIDPP_RECEIVER_INDEX) {
+		/* special case were the device wants to know its unifying
+		 * name */
+		if (size == HIDPP_REPORT_LONG_LENGTH &&
+		    !memcmp(data, unifying_name_answer,
+			    sizeof(unifying_name_answer)) &&
+		    ((data[4] & 0xF0) == 0x40))
+			device_index = (data[4] & 0x0F) + 1;
+		else
+			return false;
+	}
 
 	/*
 	 * Data is from the HID++ collection, in this case, we forward the
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 48dec39..e748e45 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)
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 09/13] HID: logitech-dj: enable notifications on connect/disconnect
  2014-09-30 17:18 [PATCH 00/13] HID: add support of Logitech touchpads and special devices Benjamin Tissoires
                   ` (7 preceding siblings ...)
  2014-09-30 17:18 ` [PATCH 08/13] HID: logitech: allow the DJ device to request the unifying name Benjamin Tissoires
@ 2014-09-30 17:18 ` Benjamin Tissoires
  2014-09-30 17:18 ` [PATCH 10/13] HID: logitech-hidpp: late bind the input device on wireless connection Benjamin Tissoires
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Benjamin Tissoires @ 2014-09-30 17:18 UTC (permalink / raw)
  To: Jiri Kosina, Nestor Lopez Casado, Andrew de los Reyes
  Cc: linux-kernel, linux-input

The receiver can send HID++ notifications to the DJ devices when the
physical devices are connected/disconnected.
Enable this feature by default.

This command uses a HID++ command instead of a DJ one, so use a direct
call to usbhid instead of using logi_dj_recv_send_report()

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

diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index 9bc3942..c917ab6 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -630,7 +630,9 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
 static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
 					  unsigned timeout)
 {
+	struct hid_device *hdev = djrcv_dev->hdev;
 	struct dj_report *dj_report;
+	u8 *buf;
 	int retval;
 
 	dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
@@ -642,7 +644,6 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
 	dj_report->report_params[CMD_SWITCH_PARAM_DEVBITFIELD] = 0x3F;
 	dj_report->report_params[CMD_SWITCH_PARAM_TIMEOUT_SECONDS] = (u8)timeout;
 	retval = logi_dj_recv_send_report(djrcv_dev, dj_report);
-	kfree(dj_report);
 
 	/*
 	 * Ugly sleep to work around a USB 3.0 bug when the receiver is still
@@ -651,6 +652,30 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
 	 */
 	msleep(50);
 
+	/*
+	 * Magical bits to set up hidpp notifications when the dj devices
+	 * are connected/disconnected.
+	 *
+	 * We can reuse dj_report because HIDPP_REPORT_SHORT_LENGTH is smaller
+	 * than DJREPORT_SHORT_LENGTH.
+	 */
+	buf = (u8 *)dj_report;
+
+	memset(buf, 0, HIDPP_REPORT_SHORT_LENGTH);
+
+	buf[0] = REPORT_ID_HIDPP_SHORT;
+	buf[1] = 0xFF;
+	buf[2] = 0x80;
+	buf[3] = 0x00;
+	buf[4] = 0x00;
+	buf[5] = 0x09;
+	buf[6] = 0x00;
+
+	hid_hw_raw_request(hdev, REPORT_ID_HIDPP_SHORT, buf,
+			HIDPP_REPORT_SHORT_LENGTH, HID_OUTPUT_REPORT,
+			HID_REQ_SET_REPORT);
+
+	kfree(dj_report);
 	return retval;
 }
 
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 10/13] HID: logitech-hidpp: late bind the input device on wireless connection
  2014-09-30 17:18 [PATCH 00/13] HID: add support of Logitech touchpads and special devices Benjamin Tissoires
                   ` (8 preceding siblings ...)
  2014-09-30 17:18 ` [PATCH 09/13] HID: logitech-dj: enable notifications on connect/disconnect Benjamin Tissoires
@ 2014-09-30 17:18 ` Benjamin Tissoires
  2014-09-30 17:18 ` [PATCH 11/13] HID: logitech-hidpp: Add Wireless Touchpad T650 support Benjamin Tissoires
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Benjamin Tissoires @ 2014-09-30 17:18 UTC (permalink / raw)
  To: Jiri Kosina, Nestor Lopez Casado, Andrew de los Reyes
  Cc: linux-kernel, linux-input

Now that the receiver forwards the connect/disconnect events, we can
know when the device is available to communicate with us.

When it is ready, we can for instance retrieve its full name, which
guarantee that we always have the same name for the DJ device (the DJ
name is somewhat shorter than the HID++ name).

This mechanism is mandatory for the touchpads line, which has the
min/max information stored in the device. This information can only
be retrieved when the device is connected. So we can not populate
the input device until we are sure that the device is connected.

This patch creates a new input device for such devices. However,
this input is not bound to hid directly, so the various drivers
which wants to use it are required to process completely the
incoming reports in .raw_event().

Note that the patch in itself just adds the bits for the next
ones, and this feature is disabled by default.

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

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index e748e45..9561a1f 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -36,6 +36,9 @@ MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>");
 
 #define HIDPP_QUIRK_CLASS_WTP			BIT(0)
 
+/* bits 1..20 are reserved for classes */
+#define HIDPP_QUIRK_DELAYED_INIT		BIT(21)
+
 /*
  * There are two hidpp protocols in use, the first version hidpp10 is known
  * as register access protocol or RAP, the second version hidpp20 is known as
@@ -91,6 +94,11 @@ struct hidpp_device {
 
 	void *private_data;
 
+	struct work_struct work;
+	struct kfifo delayed_work_fifo;
+	atomic_t connected;
+	struct input_dev *delayed_input;
+
 	unsigned long quirks;
 };
 
@@ -110,6 +118,8 @@ struct hidpp_device {
 #define HIDPP_ERROR_INVALID_PARAM_VALUE		0x0b
 #define HIDPP_ERROR_WRONG_PIN_CODE		0x0c
 
+static void hidpp_connect_event(struct hidpp_device *hidpp_dev);
+
 static int __hidpp_send_report(struct hid_device *hdev,
 				struct hidpp_report *hidpp_report)
 {
@@ -230,6 +240,13 @@ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
 	return ret;
 }
 
+static void delayed_work_cb(struct work_struct *work)
+{
+	struct hidpp_device *hidpp = container_of(work, struct hidpp_device,
+							work);
+	hidpp_connect_event(hidpp);
+}
+
 static inline bool hidpp_match_answer(struct hidpp_report *question,
 		struct hidpp_report *answer)
 {
@@ -245,6 +262,12 @@ static inline bool hidpp_match_error(struct hidpp_report *question,
 	    (answer->fap.params[0] == question->fap.funcindex_clientid);
 }
 
+static inline bool hidpp_report_is_connect_event(struct hidpp_report *report)
+{
+	return (report->report_id == REPORT_ID_HIDPP_SHORT) &&
+		(report->rap.sub_id == 0x41);
+}
+
 /* -------------------------------------------------------------------------- */
 /* HIDP++ 1.0 commands                                                        */
 /* -------------------------------------------------------------------------- */
@@ -535,12 +558,10 @@ static int wtp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 	return -1;
 }
 
-static void wtp_input_configured(struct hid_device *hdev,
-				struct hid_input *hidinput)
+static void wtp_populate_input(struct hidpp_device *hidpp,
+		struct input_dev *input_dev, bool origin_is_hid_core)
 {
-	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
 	struct wtp_data *wd = hidpp->private_data;
-	struct input_dev *input_dev = hidinput->input;
 
 	__set_bit(EV_ABS, input_dev->evbit);
 	__set_bit(EV_KEY, input_dev->evbit);
@@ -716,13 +737,20 @@ static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 	return 0;
 }
 
+static void hidpp_populate_input(struct hidpp_device *hidpp,
+		struct input_dev *input, bool origin_is_hid_core)
+{
+	if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
+		wtp_populate_input(hidpp, input, origin_is_hid_core);
+}
+
 static void hidpp_input_configured(struct hid_device *hdev,
 				struct hid_input *hidinput)
 {
 	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+	struct input_dev *input = hidinput->input;
 
-	if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
-		wtp_input_configured(hdev, hidinput);
+	hidpp_populate_input(hidpp, input, true);
 }
 
 static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
@@ -756,6 +784,15 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
 		}
 	}
 
+	if (unlikely(hidpp_report_is_connect_event(report))) {
+		atomic_set(&hidpp->connected,
+				!(report->rap.params[0] & (1 << 6)));
+		if ((hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) &&
+		    (schedule_work(&hidpp->work) == 0))
+			dbg_hid("%s: connect event already queued\n", __func__);
+		return 1;
+	}
+
 	if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
 		return wtp_raw_event(hidpp->hid_dev, data, size);
 
@@ -814,11 +851,99 @@ static void hidpp_overwrite_name(struct hid_device *hdev, bool use_unifying)
 	kfree(name);
 }
 
+static int hidpp_input_open(struct input_dev *dev)
+{
+	struct hid_device *hid = input_get_drvdata(dev);
+
+	return hid_hw_open(hid);
+}
+
+static void hidpp_input_close(struct input_dev *dev)
+{
+	struct hid_device *hid = input_get_drvdata(dev);
+
+	hid_hw_close(hid);
+}
+
+static struct input_dev *hidpp_allocate_input(struct hid_device *hdev)
+{
+	struct input_dev *input_dev = devm_input_allocate_device(&hdev->dev);
+
+	if (!input_dev)
+		return NULL;
+
+	input_set_drvdata(input_dev, hdev);
+	input_dev->open = hidpp_input_open;
+	input_dev->close = hidpp_input_close;
+
+	input_dev->name = hdev->name;
+	input_dev->phys = hdev->phys;
+	input_dev->uniq = hdev->uniq;
+	input_dev->id.bustype = hdev->bus;
+	input_dev->id.vendor  = hdev->vendor;
+	input_dev->id.product = hdev->product;
+	input_dev->id.version = hdev->version;
+	input_dev->dev.parent = &hdev->dev;
+
+	return input_dev;
+}
+
+static void hidpp_connect_event(struct hidpp_device *hidpp)
+{
+	struct hid_device *hdev = hidpp->hid_dev;
+	int ret = 0;
+	bool connected = atomic_read(&hidpp->connected);
+	struct input_dev *input;
+	char *name, *devm_name;
+	u8 name_length;
+
+	if (!connected || hidpp->delayed_input)
+		return;
+
+	if (!hidpp->protocol_major) {
+		ret = !hidpp_is_connected(hidpp);
+		if (ret) {
+			hid_err(hdev, "Can not get the protocol version.\n");
+			return;
+		}
+	}
+
+	/* the device is already connected, we can ask for its name and
+	 * protocol */
+	hid_info(hdev, "HID++ %u.%u device connected.\n",
+		 hidpp->protocol_major, hidpp->protocol_minor);
+
+	input = hidpp_allocate_input(hdev);
+	if (!input) {
+		hid_err(hdev, "cannot allocate new input device: %d\n", ret);
+		return;
+	}
+
+	name = hidpp_get_device_name(hidpp, &name_length);
+	if (!name) {
+		hid_err(hdev, "unable to retrieve the name of the device");
+	} else {
+		devm_name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s", name);
+		if (devm_name)
+			input->name = devm_name;
+		kfree(name);
+	}
+
+	hidpp_populate_input(hidpp, input, false);
+
+	ret = input_register_device(input);
+	if (ret)
+		input_free_device(input);
+
+	hidpp->delayed_input = input;
+}
+
 static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
 	struct hidpp_device *hidpp;
 	int ret;
 	bool connected;
+	unsigned int connect_mask = HID_CONNECT_DEFAULT;
 
 	hidpp = devm_kzalloc(&hdev->dev, sizeof(struct hidpp_device),
 			GFP_KERNEL);
@@ -836,6 +961,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 			return ret;
 	}
 
+	INIT_WORK(&hidpp->work, delayed_work_cb);
 	mutex_init(&hidpp->send_mutex);
 	init_waitqueue_head(&hidpp->wait);
 
@@ -860,8 +986,9 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	}
 
 	hidpp_overwrite_name(hdev, id->group == HID_GROUP_LOGITECH_DJ_DEVICE);
+	atomic_set(&hidpp->connected, connected);
 
-	if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
+	if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
 		ret = wtp_get_config(hidpp);
 		if (ret)
 			goto hid_parse_fail;
@@ -870,16 +997,27 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	/* Block incoming packets */
 	hid_device_io_stop(hdev);
 
-	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+	if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
+		connect_mask &= ~HID_CONNECT_HIDINPUT;
+
+	ret = hid_hw_start(hdev, connect_mask);
 	if (ret) {
 		hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
 		goto hid_hw_start_fail;
 	}
 
+	if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) {
+		/* Allow incoming packets */
+		hid_device_io_start(hdev);
+
+		hidpp_connect_event(hidpp);
+	}
+
 	return ret;
 
 hid_hw_start_fail:
 hid_parse_fail:
+	cancel_work_sync(&hidpp->work);
 	mutex_destroy(&hidpp->send_mutex);
 	hid_set_drvdata(hdev, NULL);
 	return ret;
@@ -889,6 +1027,7 @@ static void hidpp_remove(struct hid_device *hdev)
 {
 	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
 
+	cancel_work_sync(&hidpp->work);
 	mutex_destroy(&hidpp->send_mutex);
 	hid_hw_stop(hdev);
 }
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 11/13] HID: logitech-hidpp: Add Wireless Touchpad T650 support
  2014-09-30 17:18 [PATCH 00/13] HID: add support of Logitech touchpads and special devices Benjamin Tissoires
                   ` (9 preceding siblings ...)
  2014-09-30 17:18 ` [PATCH 10/13] HID: logitech-hidpp: late bind the input device on wireless connection Benjamin Tissoires
@ 2014-09-30 17:18 ` Benjamin Tissoires
  2014-09-30 17:18 ` [PATCH 12/13] HID: logitech-hidpp: add support of the first Logitech Wireless Touchpad Benjamin Tissoires
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 24+ messages in thread
From: Benjamin Tissoires @ 2014-09-30 17:18 UTC (permalink / raw)
  To: Jiri Kosina, Nestor Lopez Casado, Andrew de los Reyes
  Cc: linux-kernel, linux-input

All the bits are now in place to add the support of the
Touchpad T650.
The creation/population of the input device is delayed until
the device is ready.

The T650 uses the special HID++ reporting protocol, so activate
this on connect.

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

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 9561a1f..f9a4ec0 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -471,6 +471,9 @@ out_err:
 #define HIDPP_PAGE_TOUCHPAD_RAW_XY			0x6100
 
 #define CMD_TOUCHPAD_GET_RAW_INFO			0x01
+#define CMD_TOUCHPAD_SET_RAW_REPORT_STATE		0x21
+
+#define EVENT_TOUCHPAD_RAW_XY				0x00
 
 #define TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT		0x01
 #define TOUCHPAD_RAW_XY_ORIGIN_UPPER_LEFT		0x03
@@ -530,6 +533,59 @@ static int hidpp_touchpad_get_raw_info(struct hidpp_device *hidpp,
 	return ret;
 }
 
+static int hidpp_touchpad_set_raw_report_state(struct hidpp_device *hidpp_dev,
+		u8 feature_index, bool send_raw_reports,
+		bool sensor_enhanced_settings)
+{
+	struct hidpp_report response;
+
+	/*
+	 * Params:
+	 *   bit 0 - enable raw
+	 *   bit 1 - 16bit Z, no area
+	 *   bit 2 - enhanced sensitivity
+	 *   bit 3 - width, height (4 bits each) instead of area
+	 *   bit 4 - send raw + gestures (degrades smoothness)
+	 *   remaining bits - reserved
+	 */
+	u8 params = send_raw_reports | (sensor_enhanced_settings << 2);
+
+	return hidpp_send_fap_command_sync(hidpp_dev, feature_index,
+		CMD_TOUCHPAD_SET_RAW_REPORT_STATE, &params, 1, &response);
+}
+
+static void hidpp_touchpad_touch_event(u8 *data,
+	struct hidpp_touchpad_raw_xy_finger *finger)
+{
+	u8 x_m = data[0] << 2;
+	u8 y_m = data[2] << 2;
+
+	finger->x = x_m << 6 | data[1];
+	finger->y = y_m << 6 | data[3];
+
+	finger->contact_type = data[0] >> 6;
+	finger->contact_status = data[2] >> 6;
+
+	finger->z = data[4];
+	finger->area = data[5];
+	finger->finger_id = data[6] >> 4;
+}
+
+static void hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev,
+		u8 *data, struct hidpp_touchpad_raw_xy *raw_xy)
+{
+	memset(raw_xy, 0, sizeof(struct hidpp_touchpad_raw_xy));
+	raw_xy->end_of_frame = data[8] & 0x01;
+	raw_xy->spurious_flag = (data[8] >> 1) & 0x01;
+	raw_xy->finger_count = data[15] & 0x0f;
+	raw_xy->button = (data[8] >> 2) & 0x01;
+
+	if (raw_xy->finger_count) {
+		hidpp_touchpad_touch_event(&data[2], &raw_xy->fingers[0]);
+		hidpp_touchpad_touch_event(&data[9], &raw_xy->fingers[1]);
+	}
+}
+
 /* ************************************************************************** */
 /*                                                                            */
 /* Device Support                                                             */
@@ -672,11 +728,28 @@ static int wtp_raw_event(struct hid_device *hdev, u8 *data, int size)
 {
 	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
 	struct wtp_data *wd = hidpp->private_data;
+	struct hidpp_report *report = (struct hidpp_report *)data;
+	struct hidpp_touchpad_raw_xy raw;
 
-	if (!wd || !wd->input || (data[0] != 0x02) || size < 21)
+	if (!wd || !wd->input)
 		return 1;
 
-	return wtp_mouse_raw_xy_event(hidpp, &data[7]);
+	switch (data[0]) {
+	case 0x02:
+		if (size < 21)
+			return 1;
+		return wtp_mouse_raw_xy_event(hidpp, &data[7]);
+	case REPORT_ID_HIDPP_LONG:
+		if ((report->fap.feature_index != wd->mt_feature_index) ||
+		    (report->fap.funcindex_clientid != EVENT_TOUCHPAD_RAW_XY))
+			return 1;
+		hidpp_touchpad_raw_xy_event(hidpp, data + 4, &raw);
+
+		wtp_send_raw_xy_event(hidpp, &raw);
+		return 0;
+	}
+
+	return 0;
 }
 
 static int wtp_get_config(struct hidpp_device *hidpp)
@@ -721,6 +794,27 @@ static int wtp_allocate(struct hid_device *hdev, const struct hid_device_id *id)
 	return 0;
 };
 
+static void wtp_connect(struct hid_device *hdev, bool connected)
+{
+	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+	struct wtp_data *wd = hidpp->private_data;
+	int ret;
+
+	if (!connected)
+		return;
+
+	if (!wd->x_size) {
+		ret = wtp_get_config(hidpp);
+		if (ret) {
+			hid_err(hdev, "Can not get wtp config: %d\n", ret);
+			return;
+		}
+	}
+
+	hidpp_touchpad_set_raw_report_state(hidpp, wd->mt_feature_index,
+			true, true);
+}
+
 /* -------------------------------------------------------------------------- */
 /* Generic HID++ devices                                                      */
 /* -------------------------------------------------------------------------- */
@@ -897,6 +991,9 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
 	char *name, *devm_name;
 	u8 name_length;
 
+	if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
+		wtp_connect(hdev, connected);
+
 	if (!connected || hidpp->delayed_input)
 		return;
 
@@ -1033,6 +1130,10 @@ static void hidpp_remove(struct hid_device *hdev)
 }
 
 static const struct hid_device_id hidpp_devices[] = {
+	{ /* wireless touchpad T650 */
+	  HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+		USB_VENDOR_ID_LOGITECH, 0x4101),
+	  .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT },
 	{ /* wireless touchpad T651 */
 	  HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
 		USB_DEVICE_ID_LOGITECH_T651),
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 12/13] HID: logitech-hidpp: add support of the first Logitech Wireless Touchpad
  2014-09-30 17:18 [PATCH 00/13] HID: add support of Logitech touchpads and special devices Benjamin Tissoires
                   ` (10 preceding siblings ...)
  2014-09-30 17:18 ` [PATCH 11/13] HID: logitech-hidpp: Add Wireless Touchpad T650 support Benjamin Tissoires
@ 2014-09-30 17:18 ` Benjamin Tissoires
  2014-09-30 17:18 ` [PATCH 13/13] HID: logitech-hidpp: support combo keyboard touchpad TK820 Benjamin Tissoires
  2014-10-01  8:17 ` [PATCH 00/13] HID: add support of Logitech touchpads and special devices Jiri Kosina
  13 siblings, 0 replies; 24+ messages in thread
From: Benjamin Tissoires @ 2014-09-30 17:18 UTC (permalink / raw)
  To: Jiri Kosina, Nestor Lopez Casado, Andrew de los Reyes
  Cc: linux-kernel, linux-input

This touchpad differs from the T650 in several ways:
- the resolution is not correctly returned by the device
- it presents physical buttons, so the button flag in the raw touch report
  is not filled.

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

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index f9a4ec0..17e27e9 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -38,6 +38,7 @@ MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>");
 
 /* bits 1..20 are reserved for classes */
 #define HIDPP_QUIRK_DELAYED_INIT		BIT(21)
+#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS	BIT(22)
 
 /*
  * There are two hidpp protocols in use, the first version hidpp10 is known
@@ -596,6 +597,8 @@ static void hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev,
 /* Touchpad HID++ devices                                                     */
 /* -------------------------------------------------------------------------- */
 
+#define WTP_MANUAL_RESOLUTION				39
+
 struct wtp_data {
 	struct input_dev *input;
 	u16 x_size, y_size;
@@ -634,7 +637,10 @@ static void wtp_populate_input(struct hidpp_device *hidpp,
 
 	input_set_capability(input_dev, EV_KEY, BTN_LEFT);
 
-	__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+	if (hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS)
+		input_set_capability(input_dev, EV_KEY, BTN_RIGHT);
+	else
+		__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
 
 	input_mt_init_slots(input_dev, wd->maxcontacts, INPUT_MT_POINTER |
 		INPUT_MT_DROP_UNUSED);
@@ -676,7 +682,8 @@ static void wtp_send_raw_xy_event(struct hidpp_device *hidpp,
 	for (i = 0; i < 2; i++)
 		wtp_touch_event(wd, &(raw->fingers[i]));
 
-	if (raw->end_of_frame)
+	if (raw->end_of_frame &&
+	    !(hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS))
 		input_event(wd->input, EV_KEY, BTN_LEFT, raw->button);
 
 	if (raw->end_of_frame || raw->finger_count <= 2) {
@@ -736,9 +743,17 @@ static int wtp_raw_event(struct hid_device *hdev, u8 *data, int size)
 
 	switch (data[0]) {
 	case 0x02:
-		if (size < 21)
-			return 1;
-		return wtp_mouse_raw_xy_event(hidpp, &data[7]);
+		if (hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS) {
+			input_event(wd->input, EV_KEY, BTN_LEFT,
+					!!(data[1] & 0x01));
+			input_event(wd->input, EV_KEY, BTN_RIGHT,
+					!!(data[1] & 0x02));
+			input_sync(wd->input);
+		} else {
+			if (size < 21)
+				return 1;
+			return wtp_mouse_raw_xy_event(hidpp, &data[7]);
+		}
 	case REPORT_ID_HIDPP_LONG:
 		if ((report->fap.feature_index != wd->mt_feature_index) ||
 		    (report->fap.funcindex_clientid != EVENT_TOUCHPAD_RAW_XY))
@@ -775,6 +790,8 @@ static int wtp_get_config(struct hidpp_device *hidpp)
 	wd->maxcontacts = raw_info.maxcontacts;
 	wd->flip_y = raw_info.origin == TOUCHPAD_RAW_XY_ORIGIN_LOWER_LEFT;
 	wd->resolution = raw_info.res;
+	if (!wd->resolution)
+		wd->resolution = WTP_MANUAL_RESOLUTION;
 
 	return 0;
 }
@@ -1130,6 +1147,11 @@ static void hidpp_remove(struct hid_device *hdev)
 }
 
 static const struct hid_device_id hidpp_devices[] = {
+	{ /* wireless touchpad */
+	  HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+		USB_VENDOR_ID_LOGITECH, 0x4011),
+	  .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT |
+			 HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS },
 	{ /* wireless touchpad T650 */
 	  HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
 		USB_VENDOR_ID_LOGITECH, 0x4101),
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH 13/13] HID: logitech-hidpp: support combo keyboard touchpad TK820
  2014-09-30 17:18 [PATCH 00/13] HID: add support of Logitech touchpads and special devices Benjamin Tissoires
                   ` (11 preceding siblings ...)
  2014-09-30 17:18 ` [PATCH 12/13] HID: logitech-hidpp: add support of the first Logitech Wireless Touchpad Benjamin Tissoires
@ 2014-09-30 17:18 ` Benjamin Tissoires
  2014-10-01  8:17 ` [PATCH 00/13] HID: add support of Logitech touchpads and special devices Jiri Kosina
  13 siblings, 0 replies; 24+ messages in thread
From: Benjamin Tissoires @ 2014-09-30 17:18 UTC (permalink / raw)
  To: Jiri Kosina, Nestor Lopez Casado, Andrew de los Reyes
  Cc: linux-kernel, linux-input

The TK820 presents both a keyboard and a touchpad on the same
physical (and logical device). Use the generic hid-input
processing for the keyboard part. The keyboard input device is created
when the receiver is plugged in, so no events are missed on connect.

When the device actaully connects, we can set it to use the raw
multitouch reporting to have a consistent user experience accross
all Logitech touchpads.

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

diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 17e27e9..361e97d 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -39,6 +39,7 @@ MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>");
 /* bits 1..20 are reserved for classes */
 #define HIDPP_QUIRK_DELAYED_INIT		BIT(21)
 #define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS	BIT(22)
+#define HIDPP_QUIRK_MULTI_INPUT			BIT(23)
 
 /*
  * There are two hidpp protocols in use, the first version hidpp10 is known
@@ -614,6 +615,12 @@ static int wtp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 		struct hid_field *field, struct hid_usage *usage,
 		unsigned long **bit, int *max)
 {
+	struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+
+	if ((hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT) &&
+	    (field->application == HID_GD_KEYBOARD))
+		return 0;
+
 	return -1;
 }
 
@@ -622,6 +629,10 @@ static void wtp_populate_input(struct hidpp_device *hidpp,
 {
 	struct wtp_data *wd = hidpp->private_data;
 
+	if ((hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT) && origin_is_hid_core)
+		/* this is the generic hid-input call */
+		return;
+
 	__set_bit(EV_ABS, input_dev->evbit);
 	__set_bit(EV_KEY, input_dev->evbit);
 	__clear_bit(EV_REL, input_dev->evbit);
@@ -1114,6 +1125,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
 		connect_mask &= ~HID_CONNECT_HIDINPUT;
 
+	/* Re-enable hidinput for multi-input devices */
+	if (hidpp->quirks & HIDPP_QUIRK_MULTI_INPUT)
+		connect_mask |= HID_CONNECT_HIDINPUT;
+
 	ret = hid_hw_start(hdev, connect_mask);
 	if (ret) {
 		hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
@@ -1160,6 +1175,11 @@ static const struct hid_device_id hidpp_devices[] = {
 	  HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
 		USB_DEVICE_ID_LOGITECH_T651),
 	  .driver_data = HIDPP_QUIRK_CLASS_WTP },
+	{ /* Keyboard TK820 */
+	  HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
+		USB_VENDOR_ID_LOGITECH, 0x4102),
+	  .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_MULTI_INPUT |
+			 HIDPP_QUIRK_CLASS_WTP },
 
 	{ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
 		USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
-- 
2.1.0

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/13] HID: add support of Logitech touchpads and special devices
  2014-09-30 17:18 [PATCH 00/13] HID: add support of Logitech touchpads and special devices Benjamin Tissoires
                   ` (12 preceding siblings ...)
  2014-09-30 17:18 ` [PATCH 13/13] HID: logitech-hidpp: support combo keyboard touchpad TK820 Benjamin Tissoires
@ 2014-10-01  8:17 ` Jiri Kosina
  2014-10-01 13:10   ` Benjamin Tissoires
  13 siblings, 1 reply; 24+ messages in thread
From: Jiri Kosina @ 2014-10-01  8:17 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Nestor Lopez Casado, Andrew de los Reyes, linux-kernel,
	linux-input

On Tue, 30 Sep 2014, Benjamin Tissoires wrote:

> Jiri, This patch series can be split in 2 if you would like:
> - patches 1 to 4 can be scheduled easily for 3.18 IMO
> - patches 5 to 13 might need some more reviews, so either 3.18 or 3.19

Benjamin,

thanks for the summary. I'll try to review 1-4 in time for 3.18, but I 
can't guarantee that.

Do you forsee any issues if the whole lot is queued for 3.19 only?

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/13] HID: add support of Logitech touchpads and special devices
  2014-10-01  8:17 ` [PATCH 00/13] HID: add support of Logitech touchpads and special devices Jiri Kosina
@ 2014-10-01 13:10   ` Benjamin Tissoires
  2014-10-02 18:19     ` Andrew de los Reyes
  0 siblings, 1 reply; 24+ messages in thread
From: Benjamin Tissoires @ 2014-10-01 13:10 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Nestor Lopez Casado, Andrew de los Reyes, linux-kernel,
	linux-input

On Oct 01 2014 or thereabouts, Jiri Kosina wrote:
> On Tue, 30 Sep 2014, Benjamin Tissoires wrote:
> 
> > Jiri, This patch series can be split in 2 if you would like:
> > - patches 1 to 4 can be scheduled easily for 3.18 IMO
> > - patches 5 to 13 might need some more reviews, so either 3.18 or 3.19
> 
> Benjamin,
> 
> thanks for the summary. I'll try to review 1-4 in time for 3.18, but I 
> can't guarantee that.
> 

Thanks

> Do you forsee any issues if the whole lot is queued for 3.19 only?

Nope. It waited since 2011, so it can wait 3 more months :)

Cheers,
Benjamin


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/13] HID: add support of Logitech touchpads and special devices
  2014-10-01 13:10   ` Benjamin Tissoires
@ 2014-10-02 18:19     ` Andrew de los Reyes
  2014-10-03 11:09       ` Jiri Kosina
  0 siblings, 1 reply; 24+ messages in thread
From: Andrew de los Reyes @ 2014-10-02 18:19 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, Nestor Lopez Casado, linux-kernel, Linux Input

I've tested these patches successfully on a Chromebook Pixel with the
following devices:

- T650 touchpad
- TK820 keyboard/touchpad
- Original WTP touchpad

I also tested a touch mouse and as expected it continued to function
as a plain USB mouse. The TK820 was indeed broken into two devices,
with autorepeat enabled for the keyboard only. The other two devices
behaved as expected with no perceived regressions.

In short, from a functionality perspective I'm happy with these
patches. I will let others discuss code structure.

-andrew

On Wed, Oct 1, 2014 at 6:10 AM, Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
> On Oct 01 2014 or thereabouts, Jiri Kosina wrote:
>> On Tue, 30 Sep 2014, Benjamin Tissoires wrote:
>>
>> > Jiri, This patch series can be split in 2 if you would like:
>> > - patches 1 to 4 can be scheduled easily for 3.18 IMO
>> > - patches 5 to 13 might need some more reviews, so either 3.18 or 3.19
>>
>> Benjamin,
>>
>> thanks for the summary. I'll try to review 1-4 in time for 3.18, but I
>> can't guarantee that.
>>
>
> Thanks
>
>> Do you forsee any issues if the whole lot is queued for 3.19 only?
>
> Nope. It waited since 2011, so it can wait 3 more months :)
>
> Cheers,
> Benjamin
>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/13] HID: add support of Logitech touchpads and special devices
  2014-10-02 18:19     ` Andrew de los Reyes
@ 2014-10-03 11:09       ` Jiri Kosina
  2014-10-03 14:44         ` Andrew de los Reyes
  2014-10-28 20:05         ` Benjamin Tissoires
  0 siblings, 2 replies; 24+ messages in thread
From: Jiri Kosina @ 2014-10-03 11:09 UTC (permalink / raw)
  To: Andrew de los Reyes
  Cc: Benjamin Tissoires, Nestor Lopez Casado, linux-kernel,
	Linux Input

On Thu, 2 Oct 2014, Andrew de los Reyes wrote:

> I've tested these patches successfully on a Chromebook Pixel with the
> following devices:
> 
> - T650 touchpad
> - TK820 keyboard/touchpad
> - Original WTP touchpad
> 
> I also tested a touch mouse and as expected it continued to function
> as a plain USB mouse. The TK820 was indeed broken into two devices,
> with autorepeat enabled for the keyboard only. The other two devices
> behaved as expected with no perceived regressions.
> 
> In short, from a functionality perspective I'm happy with these
> patches. I will let others discuss code structure.

Thank you. Can I use

	Tested-by: Andrew de los Reyes <andrew-vger@gizmolabs.org>

once / if I am merging this patchset?

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/13] HID: add support of Logitech touchpads and special devices
  2014-10-03 11:09       ` Jiri Kosina
@ 2014-10-03 14:44         ` Andrew de los Reyes
  2014-10-28 20:05         ` Benjamin Tissoires
  1 sibling, 0 replies; 24+ messages in thread
From: Andrew de los Reyes @ 2014-10-03 14:44 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Benjamin Tissoires, Nestor Lopez Casado, linux-kernel,
	Linux Input

Sure, but I would prefer to use my chromium account:

Tested-by: Andrew de los Reyes <adlr@chromium.org>

On Fri, Oct 3, 2014 at 4:09 AM, Jiri Kosina <jkosina@suse.cz> wrote:
> On Thu, 2 Oct 2014, Andrew de los Reyes wrote:
>
>> I've tested these patches successfully on a Chromebook Pixel with the
>> following devices:
>>
>> - T650 touchpad
>> - TK820 keyboard/touchpad
>> - Original WTP touchpad
>>
>> I also tested a touch mouse and as expected it continued to function
>> as a plain USB mouse. The TK820 was indeed broken into two devices,
>> with autorepeat enabled for the keyboard only. The other two devices
>> behaved as expected with no perceived regressions.
>>
>> In short, from a functionality perspective I'm happy with these
>> patches. I will let others discuss code structure.
>
> Thank you. Can I use
>
>         Tested-by: Andrew de los Reyes <andrew-vger@gizmolabs.org>
>
> once / if I am merging this patchset?
>
> --
> Jiri Kosina
> SUSE Labs

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/13] HID: add support of Logitech touchpads and special devices
  2014-10-03 11:09       ` Jiri Kosina
  2014-10-03 14:44         ` Andrew de los Reyes
@ 2014-10-28 20:05         ` Benjamin Tissoires
  2014-10-29  9:49           ` Jiri Kosina
  1 sibling, 1 reply; 24+ messages in thread
From: Benjamin Tissoires @ 2014-10-28 20:05 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Andrew de los Reyes, Benjamin Tissoires, Nestor Lopez Casado,
	linux-kernel@vger.kernel.org, Linux Input

On Fri, Oct 3, 2014 at 7:09 AM, Jiri Kosina <jkosina@suse.cz> wrote:
> On Thu, 2 Oct 2014, Andrew de los Reyes wrote:
>
>> I've tested these patches successfully on a Chromebook Pixel with the
>> following devices:
>>
>> - T650 touchpad
>> - TK820 keyboard/touchpad
>> - Original WTP touchpad
>>
>> I also tested a touch mouse and as expected it continued to function
>> as a plain USB mouse. The TK820 was indeed broken into two devices,
>> with autorepeat enabled for the keyboard only. The other two devices
>> behaved as expected with no perceived regressions.
>>
>> In short, from a functionality perspective I'm happy with these
>> patches. I will let others discuss code structure.
>
> Thank you. Can I use
>
>         Tested-by: Andrew de los Reyes <andrew-vger@gizmolabs.org>
>
> once / if I am merging this patchset?
>

I am pretty sure this is not required too, but I am raising this one
again in Jiri's pile of things to review.
Sorry Jiri, I did not forget about it :)

Cheers,
Benjamin

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/13] HID: add support of Logitech touchpads and special devices
  2014-10-28 20:05         ` Benjamin Tissoires
@ 2014-10-29  9:49           ` Jiri Kosina
  2014-10-29 14:41             ` Benjamin Tissoires
  0 siblings, 1 reply; 24+ messages in thread
From: Jiri Kosina @ 2014-10-29  9:49 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Andrew de los Reyes, Benjamin Tissoires, Nestor Lopez Casado,
	linux-kernel@vger.kernel.org, Linux Input

On Tue, 28 Oct 2014, Benjamin Tissoires wrote:

> >> I've tested these patches successfully on a Chromebook Pixel with the
> >> following devices:
> >>
> >> - T650 touchpad
> >> - TK820 keyboard/touchpad
> >> - Original WTP touchpad
> >>
> >> I also tested a touch mouse and as expected it continued to function
> >> as a plain USB mouse. The TK820 was indeed broken into two devices,
> >> with autorepeat enabled for the keyboard only. The other two devices
> >> behaved as expected with no perceived regressions.
> >>
> >> In short, from a functionality perspective I'm happy with these
> >> patches. I will let others discuss code structure.
> >
> > Thank you. Can I use
> >
> >         Tested-by: Andrew de los Reyes <andrew-vger@gizmolabs.org>
> >
> > once / if I am merging this patchset?
> >
> 
> I am pretty sure this is not required too, but I am raising this one
> again in Jiri's pile of things to review.
> Sorry Jiri, I did not forget about it :)

It absolutely hasn't been forgotten, I've been just a bit busy during past 
weeks.

I am almost done with the review, expect to hear from me in a 1-2 days 
timeframe regarding this series.

Thanks for patience,

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/13] HID: add support of Logitech touchpads and special devices
  2014-10-29  9:49           ` Jiri Kosina
@ 2014-10-29 14:41             ` Benjamin Tissoires
  2014-10-29 16:14               ` Jiri Kosina
  0 siblings, 1 reply; 24+ messages in thread
From: Benjamin Tissoires @ 2014-10-29 14:41 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Andrew de los Reyes, Benjamin Tissoires, Nestor Lopez Casado,
	linux-kernel@vger.kernel.org, Linux Input

On Wed, Oct 29, 2014 at 5:49 AM, Jiri Kosina <jkosina@suse.cz> wrote:
> On Tue, 28 Oct 2014, Benjamin Tissoires wrote:
>
>> >> I've tested these patches successfully on a Chromebook Pixel with the
>> >> following devices:
>> >>
>> >> - T650 touchpad
>> >> - TK820 keyboard/touchpad
>> >> - Original WTP touchpad
>> >>
>> >> I also tested a touch mouse and as expected it continued to function
>> >> as a plain USB mouse. The TK820 was indeed broken into two devices,
>> >> with autorepeat enabled for the keyboard only. The other two devices
>> >> behaved as expected with no perceived regressions.
>> >>
>> >> In short, from a functionality perspective I'm happy with these
>> >> patches. I will let others discuss code structure.
>> >
>> > Thank you. Can I use
>> >
>> >         Tested-by: Andrew de los Reyes <andrew-vger@gizmolabs.org>
>> >
>> > once / if I am merging this patchset?
>> >
>>
>> I am pretty sure this is not required too, but I am raising this one
>> again in Jiri's pile of things to review.
>> Sorry Jiri, I did not forget about it :)
>
> It absolutely hasn't been forgotten, I've been just a bit busy during past
> weeks.

No worries. I definitively understand. Being close to the merge window
is never a good time to send you patches and I am sure SuSE asks you a
lot also :)

>
> I am almost done with the review, expect to hear from me in a 1-2 days
> timeframe regarding this series.

Cool, thanks. That is much appreciated.

Cheers,
Benjamin

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/13] HID: add support of Logitech touchpads and special devices
  2014-10-29 14:41             ` Benjamin Tissoires
@ 2014-10-29 16:14               ` Jiri Kosina
  2014-10-29 18:14                 ` Benjamin Tissoires
  0 siblings, 1 reply; 24+ messages in thread
From: Jiri Kosina @ 2014-10-29 16:14 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Andrew de los Reyes, Benjamin Tissoires, Nestor Lopez Casado,
	linux-kernel@vger.kernel.org, Linux Input

On Wed, 29 Oct 2014, Benjamin Tissoires wrote:

> No worries. I definitively understand. Being close to the merge window 
> is never a good time to send you patches and I am sure SuSE asks you a 
> lot also :)

Indeed, sometimes the flow of inbound things is just unstoppable :)

> > I am almost done with the review, expect to hear from me in a 1-2 days 
> > timeframe regarding this series.
> 
> Cool, thanks. That is much appreciated.

I have now applied the whole patchset and pushed out.

Thanks,

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 00/13] HID: add support of Logitech touchpads and special devices
  2014-10-29 16:14               ` Jiri Kosina
@ 2014-10-29 18:14                 ` Benjamin Tissoires
  0 siblings, 0 replies; 24+ messages in thread
From: Benjamin Tissoires @ 2014-10-29 18:14 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: Andrew de los Reyes, Benjamin Tissoires, Nestor Lopez Casado,
	linux-kernel@vger.kernel.org, Linux Input

On Wed, Oct 29, 2014 at 12:14 PM, Jiri Kosina <jkosina@suse.cz> wrote:
> On Wed, 29 Oct 2014, Benjamin Tissoires wrote:
>
>> No worries. I definitively understand. Being close to the merge window
>> is never a good time to send you patches and I am sure SuSE asks you a
>> lot also :)
>
> Indeed, sometimes the flow of inbound things is just unstoppable :)
>
>> > I am almost done with the review, expect to hear from me in a 1-2 days
>> > timeframe regarding this series.
>>
>> Cool, thanks. That is much appreciated.
>
> I have now applied the whole patchset and pushed out.
>

That was much faster than expected!!! Thanks a lot.

Cheers,
Benjamin

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2014-10-29 18:14 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-09-30 17:18 [PATCH 00/13] HID: add support of Logitech touchpads and special devices Benjamin Tissoires
2014-09-30 17:18 ` [PATCH 01/13] HID: fix merge from wacom into the HID tree Benjamin Tissoires
2014-09-30 17:18 ` [PATCH 02/13] HID: core: do not scan reports if the group is already set Benjamin Tissoires
2014-09-30 17:18 ` [PATCH 03/13] HID: logitech-dj: rely on hid groups to separate receivers from dj devices Benjamin Tissoires
2014-09-30 17:18 ` [PATCH 04/13] HID: logitech-dj: merge header file into the source Benjamin Tissoires
2014-09-30 17:18 ` [PATCH 05/13] HID: Introduce hidpp, a module to handle Logitech hid++ devices Benjamin Tissoires
2014-09-30 17:18 ` [PATCH 06/13] HID: logitech: move dj devices to the HID++ module Benjamin Tissoires
2014-09-30 17:18 ` [PATCH 07/13] HID: logitech-dj: allow transfer of HID++ reports from/to the correct dj device Benjamin Tissoires
2014-09-30 17:18 ` [PATCH 08/13] HID: logitech: allow the DJ device to request the unifying name Benjamin Tissoires
2014-09-30 17:18 ` [PATCH 09/13] HID: logitech-dj: enable notifications on connect/disconnect Benjamin Tissoires
2014-09-30 17:18 ` [PATCH 10/13] HID: logitech-hidpp: late bind the input device on wireless connection Benjamin Tissoires
2014-09-30 17:18 ` [PATCH 11/13] HID: logitech-hidpp: Add Wireless Touchpad T650 support Benjamin Tissoires
2014-09-30 17:18 ` [PATCH 12/13] HID: logitech-hidpp: add support of the first Logitech Wireless Touchpad Benjamin Tissoires
2014-09-30 17:18 ` [PATCH 13/13] HID: logitech-hidpp: support combo keyboard touchpad TK820 Benjamin Tissoires
2014-10-01  8:17 ` [PATCH 00/13] HID: add support of Logitech touchpads and special devices Jiri Kosina
2014-10-01 13:10   ` Benjamin Tissoires
2014-10-02 18:19     ` Andrew de los Reyes
2014-10-03 11:09       ` Jiri Kosina
2014-10-03 14:44         ` Andrew de los Reyes
2014-10-28 20:05         ` Benjamin Tissoires
2014-10-29  9:49           ` Jiri Kosina
2014-10-29 14:41             ` Benjamin Tissoires
2014-10-29 16:14               ` Jiri Kosina
2014-10-29 18:14                 ` Benjamin Tissoires

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).