diff mbox

[08/11] HID: hid-multitouch: add support for Nexio 42" panel

Message ID 1353684694-5723-9-git-send-email-benjamin.tissoires@gmail.com (mailing list archive)
State New, archived
Delegated to: Jiri Kosina
Headers show

Commit Message

Benjamin Tissoires Nov. 23, 2012, 3:31 p.m. UTC
This device is the worst device I saw. It keeps TipSwitch and InRange
at 1 for fingers that are not touching the panel.
The solution is to rely on the field ContactCount, which is accurate
as the correct information are packed at the begining of the frame.

Unfortunately, CountactCount is most of the time at the end of the report.
The solution is to pick it when we have the whole report in raw_event.

Fortunately, it occurs that this behavior is pretty well compliant
with all the devices I saw so far. We can make this class the default then.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
---
 drivers/hid/hid-ids.h        |  3 ++
 drivers/hid/hid-multitouch.c | 82 ++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 78 insertions(+), 7 deletions(-)

Comments

Benjamin Tissoires Nov. 26, 2012, 8:37 a.m. UTC | #1
On Fri, Nov 23, 2012 at 4:31 PM, Benjamin Tissoires
<benjamin.tissoires@gmail.com> wrote:
> This device is the worst device I saw. It keeps TipSwitch and InRange
> at 1 for fingers that are not touching the panel.
> The solution is to rely on the field ContactCount, which is accurate
> as the correct information are packed at the begining of the frame.
>
> Unfortunately, CountactCount is most of the time at the end of the report.
> The solution is to pick it when we have the whole report in raw_event.
>
> Fortunately, it occurs that this behavior is pretty well compliant
> with all the devices I saw so far. We can make this class the default then.
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
> ---
>  drivers/hid/hid-ids.h        |  3 ++
>  drivers/hid/hid-multitouch.c | 82 ++++++++++++++++++++++++++++++++++++++++----
>  2 files changed, 78 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index b84790a..9dfc465 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -599,6 +599,9 @@
>  #define USB_VENDOR_ID_NEC              0x073e
>  #define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301
>
> +#define USB_VENDOR_ID_NEXIO            0x1870
> +#define USB_DEVICE_ID_NEXIO_MULTITOUCH_420     0x0100

Oops, I made a typo here. The PID should be 0x010d.

I can send a v2 if needed.

Cheers,
Benjamin

> +
>  #define USB_VENDOR_ID_NEXTWINDOW       0x1926
>  #define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN   0x0003
>
> diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
> index 50fb79f..36cf346 100644
> --- a/drivers/hid/hid-multitouch.c
> +++ b/drivers/hid/hid-multitouch.c
> @@ -33,6 +33,8 @@
>  #include <linux/usb.h>
>  #include <linux/input/mt.h>
>  #include <linux/string.h>
> +#include <asm/unaligned.h>
> +#include <asm/byteorder.h>
>  #include "usbhid/usbhid.h"
>
>
> @@ -55,6 +57,7 @@ MODULE_LICENSE("GPL");
>  #define MT_QUIRK_NO_AREA               (1 << 9)
>  #define MT_QUIRK_IGNORE_DUPLICATES     (1 << 10)
>  #define MT_QUIRK_HOVERING              (1 << 11)
> +#define MT_QUIRK_CONTACT_COUNT_ACCURATE        (1 << 12)
>
>  struct mt_slot {
>         __s32 x, y, cx, cy, p, w, h;
> @@ -84,6 +87,8 @@ struct mt_device {
>         struct mt_class mtclass;        /* our mt device class */
>         struct mt_fields *fields;       /* temporary placeholder for storing the
>                                            multitouch fields */
> +       struct hid_field *contactcount; /* the hid_field contact count that
> +                                          will be picked in mt_raw_event */
>         unsigned last_field_index;      /* last field index of the report */
>         unsigned last_pen_field_index;  /* last field index of the pen report */
>         unsigned last_slot_field;       /* the last field of a slot */
> @@ -148,7 +153,9 @@ static int cypress_compute_slot(struct mt_device *td)
>  }
>
>  static struct mt_class mt_classes[] = {
> -       { .name = MT_CLS_DEFAULT},
> +       { .name = MT_CLS_DEFAULT,
> +               .quirks = MT_QUIRK_ALWAYS_VALID |
> +                       MT_QUIRK_CONTACT_COUNT_ACCURATE },
>         { .name = MT_CLS_NSMU,
>                 .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP },
>         { .name = MT_CLS_SERIAL,
> @@ -487,6 +494,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
>                         td->last_field_index = field->index;
>                         return 1;
>                 case HID_DG_CONTACTCOUNT:
> +                       td->contactcount = field;
>                         td->last_field_index = field->index;
>                         return 1;
>                 case HID_DG_CONTACTMAX:
> @@ -554,6 +562,10 @@ static int mt_compute_slot(struct mt_device *td, struct input_dev *input)
>   */
>  static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
>  {
> +       if ((td->mtclass.quirks & MT_QUIRK_CONTACT_COUNT_ACCURATE) &&
> +           td->num_received >= td->num_expected)
> +               return;
> +
>         if (td->curvalid || (td->mtclass.quirks & MT_QUIRK_ALWAYS_VALID)) {
>                 int slotnum = mt_compute_slot(td, input);
>                 struct mt_slot *s = &td->curdata;
> @@ -665,12 +677,6 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
>                         td->curdata.h = value;
>                         break;
>                 case HID_DG_CONTACTCOUNT:
> -                       /*
> -                        * Includes multi-packet support where subsequent
> -                        * packets are sent with zero contactcount.
> -                        */
> -                       if (value)
> -                               td->num_expected = value;
>                         break;
>                 case HID_DG_TOUCH:
>                         /* do nothing */
> @@ -700,6 +706,62 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
>         return 1;
>  }
>
> +/*
> + * Extract/implement a data field from/to a little endian report (bit array).
> + * Copied from hid-core.c.
> + *
> + * Code sort-of follows HID spec:
> + *     http://www.usb.org/developers/devclass_docs/HID1_11.pdf
> + *
> + * While the USB HID spec allows unlimited length bit fields in "report
> + * descriptors", most devices never use more than 16 bits.
> + * One model of UPS is claimed to report "LINEV" as a 32-bit field.
> + * Search linux-kernel and linux-usb-devel archives for "hid-core extract".
> + */
> +
> +static __u32 extract(const struct hid_device *hid, __u8 *report,
> +                    unsigned offset, unsigned n)
> +{
> +       u64 x;
> +
> +       if (n > 32)
> +               hid_warn(hid, "extract() called with n (%d) > 32! (%s)\n",
> +                        n, current->comm);
> +
> +       report += offset >> 3;  /* adjust byte index */
> +       offset &= 7;            /* now only need bit offset into one byte */
> +       x = get_unaligned_le64(report);
> +       x = (x >> offset) & ((1ULL << n) - 1);  /* extract bit field */
> +       return (u32) x;
> +}
> +
> +
> +static int mt_raw_event(struct hid_device *hid, struct hid_report *report,
> +                       u8 *data, int size)
> +{
> +       struct mt_device *td = hid_get_drvdata(hid);
> +       struct hid_field *field = td->contactcount;
> +       unsigned value;
> +
> +       if (field && report->id == field->report->id) {
> +               /*
> +                * Pick in advance the field HID_DG_CONTACTCOUNT as it is
> +                * often placed at the end of the report.
> +                */
> +               if (report->id)
> +                       data++;
> +               value = extract(hid, data, field->report_offset,
> +                               field->report_size);
> +               /*
> +                * Includes multi-packet support where subsequent
> +                * packets are sent with zero contactcount.
> +                */
> +               if (value)
> +                       td->num_expected = value;
> +       }
> +       return 0;
> +}
> +
>  static void mt_set_input_mode(struct hid_device *hdev)
>  {
>         struct mt_device *td = hid_get_drvdata(hdev);
> @@ -1110,6 +1172,11 @@ static const struct hid_device_id mt_devices[] = {
>                 MT_USB_DEVICE(USB_VENDOR_ID_TURBOX,
>                         USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART) },
>
> +       /* Nexio panels */
> +       { .driver_data = MT_CLS_DEFAULT,
> +               MT_USB_DEVICE(USB_VENDOR_ID_NEXIO,
> +                       USB_DEVICE_ID_NEXIO_MULTITOUCH_420)},
> +
>         /* Panasonic panels */
>         { .driver_data = MT_CLS_PANASONIC,
>                 MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC,
> @@ -1247,6 +1314,7 @@ static struct hid_driver mt_driver = {
>         .feature_mapping = mt_feature_mapping,
>         .usage_table = mt_grabbed_usages,
>         .event = mt_event,
> +       .raw_event = mt_raw_event,
>  #ifdef CONFIG_PM
>         .reset_resume = mt_reset_resume,
>         .resume = mt_resume,
> --
> 1.8.0
>
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index b84790a..9dfc465 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -599,6 +599,9 @@ 
 #define USB_VENDOR_ID_NEC		0x073e
 #define USB_DEVICE_ID_NEC_USB_GAME_PAD	0x0301
 
+#define USB_VENDOR_ID_NEXIO		0x1870
+#define USB_DEVICE_ID_NEXIO_MULTITOUCH_420	0x0100
+
 #define USB_VENDOR_ID_NEXTWINDOW	0x1926
 #define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN	0x0003
 
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 50fb79f..36cf346 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -33,6 +33,8 @@ 
 #include <linux/usb.h>
 #include <linux/input/mt.h>
 #include <linux/string.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
 #include "usbhid/usbhid.h"
 
 
@@ -55,6 +57,7 @@  MODULE_LICENSE("GPL");
 #define MT_QUIRK_NO_AREA		(1 << 9)
 #define MT_QUIRK_IGNORE_DUPLICATES	(1 << 10)
 #define MT_QUIRK_HOVERING		(1 << 11)
+#define MT_QUIRK_CONTACT_COUNT_ACCURATE	(1 << 12)
 
 struct mt_slot {
 	__s32 x, y, cx, cy, p, w, h;
@@ -84,6 +87,8 @@  struct mt_device {
 	struct mt_class mtclass;	/* our mt device class */
 	struct mt_fields *fields;	/* temporary placeholder for storing the
 					   multitouch fields */
+	struct hid_field *contactcount;	/* the hid_field contact count that
+					   will be picked in mt_raw_event */
 	unsigned last_field_index;	/* last field index of the report */
 	unsigned last_pen_field_index;	/* last field index of the pen report */
 	unsigned last_slot_field;	/* the last field of a slot */
@@ -148,7 +153,9 @@  static int cypress_compute_slot(struct mt_device *td)
 }
 
 static struct mt_class mt_classes[] = {
-	{ .name = MT_CLS_DEFAULT},
+	{ .name = MT_CLS_DEFAULT,
+		.quirks = MT_QUIRK_ALWAYS_VALID |
+			MT_QUIRK_CONTACT_COUNT_ACCURATE },
 	{ .name = MT_CLS_NSMU,
 		.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP },
 	{ .name = MT_CLS_SERIAL,
@@ -487,6 +494,7 @@  static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 			td->last_field_index = field->index;
 			return 1;
 		case HID_DG_CONTACTCOUNT:
+			td->contactcount = field;
 			td->last_field_index = field->index;
 			return 1;
 		case HID_DG_CONTACTMAX:
@@ -554,6 +562,10 @@  static int mt_compute_slot(struct mt_device *td, struct input_dev *input)
  */
 static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
 {
+	if ((td->mtclass.quirks & MT_QUIRK_CONTACT_COUNT_ACCURATE) &&
+	    td->num_received >= td->num_expected)
+		return;
+
 	if (td->curvalid || (td->mtclass.quirks & MT_QUIRK_ALWAYS_VALID)) {
 		int slotnum = mt_compute_slot(td, input);
 		struct mt_slot *s = &td->curdata;
@@ -665,12 +677,6 @@  static int mt_event(struct hid_device *hid, struct hid_field *field,
 			td->curdata.h = value;
 			break;
 		case HID_DG_CONTACTCOUNT:
-			/*
-			 * Includes multi-packet support where subsequent
-			 * packets are sent with zero contactcount.
-			 */
-			if (value)
-				td->num_expected = value;
 			break;
 		case HID_DG_TOUCH:
 			/* do nothing */
@@ -700,6 +706,62 @@  static int mt_event(struct hid_device *hid, struct hid_field *field,
 	return 1;
 }
 
+/*
+ * Extract/implement a data field from/to a little endian report (bit array).
+ * Copied from hid-core.c.
+ *
+ * Code sort-of follows HID spec:
+ *     http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ *
+ * While the USB HID spec allows unlimited length bit fields in "report
+ * descriptors", most devices never use more than 16 bits.
+ * One model of UPS is claimed to report "LINEV" as a 32-bit field.
+ * Search linux-kernel and linux-usb-devel archives for "hid-core extract".
+ */
+
+static __u32 extract(const struct hid_device *hid, __u8 *report,
+		     unsigned offset, unsigned n)
+{
+	u64 x;
+
+	if (n > 32)
+		hid_warn(hid, "extract() called with n (%d) > 32! (%s)\n",
+			 n, current->comm);
+
+	report += offset >> 3;  /* adjust byte index */
+	offset &= 7;            /* now only need bit offset into one byte */
+	x = get_unaligned_le64(report);
+	x = (x >> offset) & ((1ULL << n) - 1);  /* extract bit field */
+	return (u32) x;
+}
+
+
+static int mt_raw_event(struct hid_device *hid, struct hid_report *report,
+			u8 *data, int size)
+{
+	struct mt_device *td = hid_get_drvdata(hid);
+	struct hid_field *field = td->contactcount;
+	unsigned value;
+
+	if (field && report->id == field->report->id) {
+		/*
+		 * Pick in advance the field HID_DG_CONTACTCOUNT as it is
+		 * often placed at the end of the report.
+		 */
+		if (report->id)
+			data++;
+		value = extract(hid, data, field->report_offset,
+				field->report_size);
+		/*
+		 * Includes multi-packet support where subsequent
+		 * packets are sent with zero contactcount.
+		 */
+		if (value)
+			td->num_expected = value;
+	}
+	return 0;
+}
+
 static void mt_set_input_mode(struct hid_device *hdev)
 {
 	struct mt_device *td = hid_get_drvdata(hdev);
@@ -1110,6 +1172,11 @@  static const struct hid_device_id mt_devices[] = {
 		MT_USB_DEVICE(USB_VENDOR_ID_TURBOX,
 			USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART) },
 
+	/* Nexio panels */
+	{ .driver_data = MT_CLS_DEFAULT,
+		MT_USB_DEVICE(USB_VENDOR_ID_NEXIO,
+			USB_DEVICE_ID_NEXIO_MULTITOUCH_420)},
+
 	/* Panasonic panels */
 	{ .driver_data = MT_CLS_PANASONIC,
 		MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC,
@@ -1247,6 +1314,7 @@  static struct hid_driver mt_driver = {
 	.feature_mapping = mt_feature_mapping,
 	.usage_table = mt_grabbed_usages,
 	.event = mt_event,
+	.raw_event = mt_raw_event,
 #ifdef CONFIG_PM
 	.reset_resume = mt_reset_resume,
 	.resume = mt_resume,