This adds support for Hybrid mode for Windows Precision Touchpads
(ihidev/imt).  If yours only works with one finger, this should fix
that.

This also changes the way SET_REPORTs are sent to put the touchpad
into touchpad mode, now as two bytes.  This is needed on the
touchpad on my Huawei and matches the bytes that Linux outputs.

It also renames the internal hidmt_input struct to avoid conflicting
with hidmt_input function.

I'd appreciate tests on any machines with ihidev/imt to make sure
this doesn't break.  (These are three separate commits but are
combined in one diff for testing.)



Index: sys/dev/hid/hidmt.c
===================================================================
RCS file: /cvs/src/sys/dev/hid/hidmt.c,v
retrieving revision 1.3
diff -u -p -u -p -r1.3 hidmt.c
--- sys/dev/hid/hidmt.c 8 Oct 2017 10:13:42 -0000       1.3
+++ sys/dev/hid/hidmt.c 8 Oct 2017 14:08:09 -0000
@@ -4,6 +4,7 @@
  * standard
  *
  * 
https://msdn.microsoft.com/en-us/library/windows/hardware/dn467314%28v=vs.85%29.aspx
+ * 
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/touchscreen-packet-reporting-modes
  *
  * Copyright (c) 2016 joshua stein <j...@openbsd.org>
  *
@@ -167,7 +168,7 @@ hidmt_setup(struct device *self, struct 
 
        hd = hid_start_parse(desc, dlen, hid_input);
        while (hid_get_item(hd, &h)) {
-               struct hidmt_input *input;
+               struct hidmt_data *input;
 
                if (h.report_ID != mt->sc_rep_input)
                        continue;
@@ -275,16 +276,16 @@ hidmt_detach(struct hidmt *mt, int flags
 }
 
 int
-hidmt_set_input_mode(struct hidmt *mt, int mode)
+hidmt_set_input_mode(struct hidmt *mt, uint16_t mode)
 {
        return mt->hidev_set_report(mt->sc_device, hid_feature,
-           mt->sc_rep_config, &mode, 1);
+           mt->sc_rep_config, &mode, 2);
 }
 
 void
 hidmt_input(struct hidmt *mt, uint8_t *data, u_int len)
 {
-       struct hidmt_input *hi;
+       struct hidmt_data *hi;
        struct hidmt_contact hc;
        int32_t d, firstu = 0;
        int contactcount = 0, seencontacts = 0, tips = 0, i, s, z;
@@ -307,8 +308,29 @@ hidmt_input(struct hidmt *mt, uint8_t *d
         */
        SIMPLEQ_FOREACH(hi, &mt->sc_inputs, entry) {
                if (hi->usage == HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTCOUNT))
-                       contactcount = hid_get_udata(data, len,
-                           &hi->loc);
+                       contactcount = hid_get_udata(data, len, &hi->loc);
+       }
+
+       if (contactcount)
+               mt->sc_cur_contactcount = contactcount;
+       else {
+               /*
+               * "In Hybrid mode, the number of contacts that can be reported
+               * in one report is less than the maximum number of contacts
+               * that the device supports. For example, a device that supports
+               * a maximum of 4 concurrent physical contacts, can set up its
+               * top-level collection to deliver a maximum of two contacts in
+               * one report. If four contact points are present, the device
+               * can break these up into two serial reports that deliver two
+               * contacts each.
+               *
+               * "When a device delivers data in this manner, the Contact
+               * Count usage value in the first report should reflect the
+               * total number of contacts that are being delivered in the
+               * hybrid reports. The other serial reports should have a
+               * contact count of zero (0)."
+               */
+               contactcount = mt->sc_cur_contactcount;
        }
 
        if (!contactcount) {
@@ -373,7 +395,8 @@ hidmt_input(struct hidmt *mt, uint8_t *d
 
                /* these will only appear once per report */
                case HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTCOUNT):
-                       contactcount = d;
+                       if (d)
+                               contactcount = d;
                        break;
                case HID_USAGE2(HUP_BUTTON, 0x01):
                        mt->sc_button = (d != 0);
Index: sys/dev/hid/hidmtvar.h
===================================================================
RCS file: /cvs/src/sys/dev/hid/hidmtvar.h,v
retrieving revision 1.2
diff -u -p -u -p -r1.2 hidmtvar.h
--- sys/dev/hid/hidmtvar.h      8 Oct 2017 10:13:42 -0000       1.2
+++ sys/dev/hid/hidmtvar.h      8 Oct 2017 14:08:09 -0000
@@ -15,10 +15,10 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-struct hidmt_input {
+struct hidmt_data {
        int32_t                 usage;
        struct hid_location     loc;
-       SIMPLEQ_ENTRY(hidmt_input) entry;
+       SIMPLEQ_ENTRY(hidmt_data) entry;
 };
 
 struct hidmt_contact {
@@ -49,7 +49,7 @@ struct hidmt {
        int             sc_rep_config;
        int             sc_rep_cap;
 
-       SIMPLEQ_HEAD(, hidmt_input) sc_inputs;
+       SIMPLEQ_HEAD(, hidmt_data) sc_inputs;
 
        struct device   *sc_wsmousedev;
 
@@ -61,10 +61,11 @@ struct hidmt {
        int             sc_resx, sc_resy;
 
        struct hidmt_contact sc_contacts[HIDMT_MAX_CONTACTS];
+       int             sc_cur_contactcount;
        int             sc_button;
 };
 
-int    hidmt_set_input_mode(struct hidmt *, int);
+int    hidmt_set_input_mode(struct hidmt *, uint16_t);
 #define HIDMT_INPUT_MODE_MT    0x3
 
 void   hidmt_attach(struct hidmt *, const struct wsmouse_accessops *);

Reply via email to