diff options
-rw-r--r-- | sys/arch/amd64/conf/GENERIC | 6 | ||||
-rw-r--r-- | sys/dev/hid/files.hid | 6 | ||||
-rw-r--r-- | sys/dev/hid/hid.h | 17 | ||||
-rw-r--r-- | sys/dev/hid/hidmt.c | 421 | ||||
-rw-r--r-- | sys/dev/hid/hidmtvar.h | 78 | ||||
-rw-r--r-- | sys/dev/i2c/files.i2c | 7 | ||||
-rw-r--r-- | sys/dev/i2c/imt.c | 247 |
7 files changed, 776 insertions, 6 deletions
diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC index ba6dd199f92..fee3c2ab7bf 100644 --- a/sys/arch/amd64/conf/GENERIC +++ b/sys/arch/amd64/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.413 2016/01/14 21:04:20 kettenis Exp $ +# $OpenBSD: GENERIC,v 1.414 2016/01/20 01:26:00 jcs Exp $ # # For further information on compiling OpenBSD kernels, see the config(8) # man page. @@ -151,10 +151,12 @@ sdtemp* at iic? # SO-DIMM (JC-42.4) temperature wbng* at iic? # Winbond W83793G nvt* at iic? # Novoton W83795G ihidev* at iic? # HID-over-i2c -ims* at ihidev? # i2c mouse/trackpad +ims* at ihidev? # HID-over-i2c mouse/trackpad wsmouse* at ims? mux 0 ikbd* at ihidev? # i2c keyboard wskbd* at ikbd? mux 1 +imt* at ihidev? # HID-over-i2c multitouch trackpad +wsmouse* at imt? mux 0 skgpio0 at isa? port 0x680 # Soekris net6501 GPIO and LEDs gpio* at skgpio? diff --git a/sys/dev/hid/files.hid b/sys/dev/hid/files.hid index c2bd1d2ce80..438f076ce5c 100644 --- a/sys/dev/hid/files.hid +++ b/sys/dev/hid/files.hid @@ -1,4 +1,4 @@ -# $OpenBSD: files.hid,v 1.1 2016/01/08 15:54:13 jcs Exp $ +# $OpenBSD: files.hid,v 1.2 2016/01/20 01:26:00 jcs Exp $ # Human Interface Devices @@ -16,3 +16,7 @@ file dev/hid/hidkbd.c hidkbd needs-flag # Mice define hidms file dev/hid/hidms.c hidms + +# Multitouch +define hidmt +file dev/hid/hidmt.c hidmt diff --git a/sys/dev/hid/hid.h b/sys/dev/hid/hid.h index 6840a4a197d..3297e5cec37 100644 --- a/sys/dev/hid/hid.h +++ b/sys/dev/hid/hid.h @@ -1,4 +1,4 @@ -/* $OpenBSD: hid.h,v 1.3 2016/01/20 01:11:50 jcs Exp $ */ +/* $OpenBSD: hid.h,v 1.4 2016/01/20 01:26:00 jcs Exp $ */ /* $NetBSD: hid.h,v 1.8 2002/07/11 21:14:25 augustss Exp $ */ /* $FreeBSD: src/sys/dev/usb/hid.h,v 1.7 1999/11/17 22:33:40 n_hibma Exp $ */ @@ -123,8 +123,10 @@ int hid_is_collection(const void *, int, uint8_t, int32_t); #define HUP_SCALE 0x008c #define HUP_CAMERA_CONTROL 0x0090 #define HUP_ARCADE 0x0091 -#define HUP_APPLE 0x00ff +#define HUP_VENDOR 0x00ff #define HUP_MICROSOFT 0xff00 +/* XXX compat */ +#define HUP_APPLE 0x00ff /* Usages, Power Device */ #define HUP_INAME 0x0001 @@ -348,6 +350,7 @@ int hid_is_collection(const void *, int, uint8_t, int32_t); #define HUD_PEN 0x0002 #define HUD_TOUCHSCREEN 0x0004 #define HUD_TOUCHPAD 0x0005 +#define HUD_CONFIG 0x000e #define HUD_FINGER 0x0022 #define HUD_TIP_PRESSURE 0x0030 #define HUD_BARREL_PRESSURE 0x0031 @@ -372,6 +375,16 @@ int hid_is_collection(const void *, int, uint8_t, int32_t); #define HUD_BARREL_SWITCH 0x0044 #define HUD_ERASER 0x0045 #define HUD_TABLET_PICK 0x0046 +#define HUD_CONFIDENCE 0x0047 +#define HUD_WIDTH 0x0048 +#define HUD_HEIGHT 0x0049 +#define HUD_CONTACTID 0x0051 +#define HUD_INPUT_MODE 0x0052 +#define HUD_DEVICE_INDEX 0x0053 +#define HUD_CONTACTCOUNT 0x0054 +#define HUD_CONTACT_MAX 0x0055 +#define HUD_SCAN_TIME 0x0056 +#define HUD_BUTTON_TYPE 0x0059 /* Usages, LED */ #define HUL_NUM_LOCK 0x0001 diff --git a/sys/dev/hid/hidmt.c b/sys/dev/hid/hidmt.c new file mode 100644 index 00000000000..db74eef90c0 --- /dev/null +++ b/sys/dev/hid/hidmt.c @@ -0,0 +1,421 @@ +/* $OpenBSD: hidmt.c,v 1.1 2016/01/20 01:26:00 jcs Exp $ */ +/* + * HID multitouch driver for devices conforming to Windows Precision Touchpad + * standard + * + * https://msdn.microsoft.com/en-us/library/windows/hardware/dn467314%28v=vs.85%29.aspx + * + * Copyright (c) 2016 joshua stein <jcs@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/malloc.h> + +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wsmousevar.h> + +#include <dev/hid/hid.h> +#include <dev/hid/hidmtvar.h> + +/* #define HIDMT_DEBUG */ + +#ifdef HIDMT_DEBUG +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +int +hidmt_setup(struct device *self, struct hidmt *mt, void *desc, int dlen) +{ + struct hid_location cap; + int32_t d; + uint8_t *rep; + int capsize; + + struct hid_data *hd; + struct hid_item h; + + mt->sc_device = self; + mt->sc_rep_input_size = hid_report_size(desc, dlen, hid_input, + mt->sc_rep_input); + + mt->sc_minx = mt->sc_miny = mt->sc_maxx = mt->sc_maxy = 0; + + capsize = hid_report_size(desc, dlen, hid_feature, mt->sc_rep_cap); + rep = malloc(capsize, M_DEVBUF, M_NOWAIT | M_ZERO); + + if (mt->hidev_get_report(mt->sc_device, hid_feature, mt->sc_rep_cap, + rep, capsize)) { + printf("\n%s: failed getting capability report\n", + self->dv_xname); + return 1; + } + + /* find maximum number of contacts being reported per input report */ + if (!hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACT_MAX), + mt->sc_rep_cap, hid_feature, &cap, NULL)) { + printf("\n%s: can't find maximum contacts\n", self->dv_xname); + return 1; + } + + d = hid_get_udata(rep, capsize, &cap); + if (d > HIDMT_MAX_CONTACTS) { + printf("\n%s: contacts %d > max %d\n", self->dv_xname, d, + HIDMT_MAX_CONTACTS); + return 1; + } + else + mt->sc_num_contacts = d; + + /* find whether this is a clickpad or not */ + if (!hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS, HUD_BUTTON_TYPE), + mt->sc_rep_cap, hid_feature, &cap, NULL)) { + printf("\n%s: can't find button type\n", self->dv_xname); + return 1; + } + + d = hid_get_udata(rep, capsize, &cap); + mt->sc_clickpad = (d == 0); + + /* + * Walk HID descriptor and store usages we care about to know what to + * pluck out of input reports. + */ + + SIMPLEQ_INIT(&mt->sc_inputs); + + hd = hid_start_parse(desc, dlen, hid_input); + while (hid_get_item(hd, &h)) { + struct hidmt_input *input; + + if (h.report_ID != mt->sc_rep_input) + continue; + + switch (h.usage) { + /* contact level usages */ + case HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X): + if (h.physical_minimum < mt->sc_minx) + mt->sc_minx = h.physical_minimum; + if (h.physical_maximum > mt->sc_maxx) + mt->sc_maxx = h.physical_maximum; + + break; + case HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y): + if (h.physical_minimum < mt->sc_miny) + mt->sc_miny = h.physical_minimum; + if (h.physical_maximum > mt->sc_maxy) + mt->sc_maxy = h.physical_maximum; + + break; + case HID_USAGE2(HUP_DIGITIZERS, HUD_TIP_SWITCH): + case HID_USAGE2(HUP_DIGITIZERS, HUD_CONFIDENCE): + case HID_USAGE2(HUP_DIGITIZERS, HUD_WIDTH): + case HID_USAGE2(HUP_DIGITIZERS, HUD_HEIGHT): + case HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTID): + + /* report level usages */ + case HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTCOUNT): + case HID_USAGE2(HUP_BUTTON, 0x01): + break; + default: + continue; + } + + input = malloc(sizeof(*input), M_DEVBUF, M_NOWAIT | M_ZERO); + memcpy(&input->loc, &h.loc, sizeof(struct hid_location)); + input->usage = h.usage; + + SIMPLEQ_INSERT_TAIL(&mt->sc_inputs, input, entry); + } + hid_end_parse(hd); + + if (mt->sc_maxx <= 0 || mt->sc_maxy <= 0) { + printf("\n%s: invalid max X/Y %d/%d\n", self->dv_xname, + mt->sc_maxx, mt->sc_maxy); + return 1; + } + + if (hidmt_set_input_mode(mt, HIDMT_INPUT_MODE_MT)) { + printf("\n%s: switch to multitouch mode failed\n", + self->dv_xname); + return 1; + } + + return 0; +} + +void +hidmt_attach(struct hidmt *mt, const struct wsmouse_accessops *ops) +{ + struct wsmousedev_attach_args a; + + printf(": %spad, %d contact%s\n", + (mt->sc_clickpad ? "click" : "touch"), mt->sc_num_contacts, + (mt->sc_num_contacts == 1 ? "" : "s")); + + a.accessops = ops; + a.accesscookie = mt->sc_device; + mt->sc_wsmousedev = config_found(mt->sc_device, &a, wsmousedevprint); +} + +int +hidmt_detach(struct hidmt *mt, int flags) +{ + int rv = 0; + + if (mt->sc_wsmousedev != NULL) + rv = config_detach(mt->sc_wsmousedev, flags); + + return (rv); +} + +int +hidmt_set_input_mode(struct hidmt *mt, int mode) +{ + return mt->hidev_set_report(mt->sc_device, hid_feature, + mt->sc_rep_config, &mode, 1); +} + +void +hidmt_input(struct hidmt *mt, uint8_t *data, u_int len) +{ + struct hidmt_input *hi; + struct hidmt_contact hc; + int32_t d, firstu = 0; + int contactcount = 0, seencontacts = 0, tips = 0, i, s; + + if (len != mt->sc_rep_input_size) { + DPRINTF(("%s: %s: length %d not %d, ignoring\n", + mt->sc_device->dv_xname, __func__, len, + mt->sc_rep_input_size)); + return; + } + + /* + * "In Parallel mode, devices report all contact information in a + * single packet. Each physical contact is represented by a logical + * collection that is embedded in the top-level collection." + * + * Since additional contacts that were not present will still be in the + * report with contactid=0 but contactids are zero-based, find + * contactcount first. + */ + SIMPLEQ_FOREACH(hi, &mt->sc_inputs, entry) { + if (hi->usage == HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTCOUNT)) + contactcount = hid_get_udata(data, len, + &hi->loc); + } + + if (!contactcount) { + DPRINTF(("%s: %s: no contactcount in report\n", + mt->sc_device->dv_xname, __func__)); + return; + } + + /* + * Walk through each input we know about and fetch its data from the + * report, storing it in a temporary contact. Once we see our first + * usage again, we'll know we saw all usages being presented for that + * contact. + */ + bzero(&hc, sizeof(struct hidmt_contact)); + SIMPLEQ_FOREACH(hi, &mt->sc_inputs, entry) { + d = hid_get_udata(data, len, &hi->loc); + + if (firstu && hi->usage == firstu) { + if (seencontacts < contactcount && + hc.contactid < HIDMT_MAX_CONTACTS) { + hc.seen = 1; + memcpy(&mt->sc_contacts[hc.contactid], &hc, + sizeof(struct hidmt_contact)); + seencontacts++; + } + + bzero(&hc, sizeof(struct hidmt_contact)); + } + else if (!firstu) + firstu = hi->usage; + + switch (hi->usage) { + case HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X): + hc.x = d; + break; + case HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y): + if (mt->sc_flags & HIDMT_REVY) + hc.y = mt->sc_maxy - d; + else + hc.y = d; + break; + case HID_USAGE2(HUP_DIGITIZERS, HUD_TIP_SWITCH): + hc.tip = d; + if (d) + tips++; + break; + case HID_USAGE2(HUP_DIGITIZERS, HUD_CONFIDENCE): + hc.confidence = d; + break; + case HID_USAGE2(HUP_DIGITIZERS, HUD_WIDTH): + hc.width = d; + break; + case HID_USAGE2(HUP_DIGITIZERS, HUD_HEIGHT): + hc.height = d; + break; + case HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTID): + hc.contactid = d; + break; + + /* these will only appear once per report */ + case HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTCOUNT): + contactcount = d; + break; + case HID_USAGE2(HUP_BUTTON, 0x01): + mt->sc_button = (d != 0); + break; + } + } + if (seencontacts < contactcount && hc.contactid < HIDMT_MAX_CONTACTS) { + hc.seen = 1; + memcpy(&mt->sc_contacts[hc.contactid], &hc, + sizeof(struct hidmt_contact)); + seencontacts++; + } + + s = spltty(); + for (i = 0; i < HIDMT_MAX_CONTACTS; i++) { + if (!mt->sc_contacts[i].seen) + continue; + + mt->sc_contacts[i].seen = 0; + + DPRINTF(("%s: %s: contact %d of %d: id %d, x %d, y %d, " + "touch %d, confidence %d, width %d, height %d " + "(button %d)\n", + mt->sc_device->dv_xname, __func__, + i + 1, contactcount, + mt->sc_contacts[i].contactid, + mt->sc_contacts[i].x, + mt->sc_contacts[i].y, + mt->sc_contacts[i].tip, + mt->sc_contacts[i].confidence, + mt->sc_contacts[i].width, + mt->sc_contacts[i].height, + mt->sc_button)); + + if (mt->sc_contacts[i].tip && !mt->sc_contacts[i].confidence) + continue; + + if (mt->sc_wsmode == WSMOUSE_NATIVE) { + int width = 0; + if (mt->sc_contacts[i].tip) { + width = mt->sc_contacts[i].width; + if (width < 50) + width = 50; + } + + wsmouse_input(mt->sc_wsmousedev, mt->sc_button, + (mt->last_x = mt->sc_contacts[i].x), + (mt->last_y = mt->sc_contacts[i].y), + width, tips, + WSMOUSE_INPUT_ABSOLUTE_X | + WSMOUSE_INPUT_ABSOLUTE_Y | + WSMOUSE_INPUT_ABSOLUTE_Z | + WSMOUSE_INPUT_ABSOLUTE_W); + } else { + wsmouse_input(mt->sc_wsmousedev, mt->sc_button, + (mt->last_x - mt->sc_contacts[i].x), + (mt->last_y - mt->sc_contacts[i].y), + 0, 0, + WSMOUSE_INPUT_DELTA); + mt->last_x = mt->sc_contacts[i].x; + mt->last_y = mt->sc_contacts[i].y; + } + + /* + * XXX: wscons can only handle one finger of data + */ + break; + } + + splx(s); +} + +int +hidmt_enable(struct hidmt *mt) +{ + if (mt->sc_enabled) + return EBUSY; + + mt->sc_enabled = 1; + + return 0; +} + +int +hidmt_ioctl(struct hidmt *mt, u_long cmd, caddr_t data, int flag, + struct proc *p) +{ + struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; + int wsmode; + + switch (cmd) { + case WSMOUSEIO_GTYPE: + /* + * So we can specify our own finger/w values to the + * xf86-input-synaptics driver like pms(4) + */ + *(u_int *)data = WSMOUSE_TYPE_ELANTECH; + break; + + case WSMOUSEIO_GCALIBCOORDS: + wsmc->minx = mt->sc_minx; + wsmc->maxx = mt->sc_maxx; + wsmc->miny = mt->sc_miny; + wsmc->maxy = mt->sc_maxy; + wsmc->swapxy = 0; + wsmc->resx = 0; + wsmc->resy = 0; + break; + + case WSMOUSEIO_SETMODE: + wsmode = *(u_int *)data; + if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) { + printf("%s: invalid mode %d\n", + mt->sc_device->dv_xname, wsmode); + return (EINVAL); + } + + DPRINTF(("%s: changing mode to %s\n", mt->sc_device->dv_xname, + (wsmode == WSMOUSE_COMPAT ? "compat" : "native"))); + + mt->sc_wsmode = wsmode; + break; + + default: + return -1; + } + + return 0; +} + +void +hidmt_disable(struct hidmt *mt) +{ + mt->sc_enabled = 0; +} diff --git a/sys/dev/hid/hidmtvar.h b/sys/dev/hid/hidmtvar.h new file mode 100644 index 00000000000..5cf1329d541 --- /dev/null +++ b/sys/dev/hid/hidmtvar.h @@ -0,0 +1,78 @@ +/* $OpenBSD: hidmtvar.h,v 1.1 2016/01/20 01:26:00 jcs Exp $ */ +/* + * Copyright (c) 2016 joshua stein <jcs@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct hidmt_input { + int32_t usage; + struct hid_location loc; + SIMPLEQ_ENTRY(hidmt_input) entry; +}; + +struct hidmt_contact { + int x; + int y; + int width; + int height; + int tip; + int confidence; + int contactid; + + int seen; +}; + +struct hidmt { + int sc_enabled; + uint32_t sc_flags; +#define HIDMT_REVY 0x0001 /* Y-axis is reversed ("natural" scrolling) */ + + struct device *sc_device; + int (*hidev_get_report)(struct device *, int, int, void *, + int); + int (*hidev_set_report)(struct device *, int, int, void *, + int); + + int sc_rep_input; + int sc_rep_input_size; + int sc_rep_config; + int sc_rep_cap; + + SIMPLEQ_HEAD(, hidmt_input) sc_inputs; + + struct device *sc_wsmousedev; + int sc_wsmode; + + int sc_clickpad; + int sc_num_contacts; +#define HIDMT_MAX_CONTACTS 5 + int sc_minx, sc_maxx; + int sc_miny, sc_maxy; + + struct hidmt_contact sc_contacts[HIDMT_MAX_CONTACTS]; + int sc_button; + + int last_x, last_y; +}; + +int hidmt_set_input_mode(struct hidmt *, int); +#define HIDMT_INPUT_MODE_MT 0x3 + +void hidmt_attach(struct hidmt *, const struct wsmouse_accessops *); +int hidmt_detach(struct hidmt *, int); +void hidmt_disable(struct hidmt *); +int hidmt_enable(struct hidmt *); +void hidmt_input(struct hidmt *, uint8_t *, u_int); +int hidmt_ioctl(struct hidmt *, u_long, caddr_t, int, struct proc *); +int hidmt_setup(struct device *, struct hidmt *, void *, int); diff --git a/sys/dev/i2c/files.i2c b/sys/dev/i2c/files.i2c index 2b2fb296422..1248bd2b59d 100644 --- a/sys/dev/i2c/files.i2c +++ b/sys/dev/i2c/files.i2c @@ -1,4 +1,4 @@ -# $OpenBSD: files.i2c,v 1.53 2016/01/14 21:01:49 kettenis Exp $ +# $OpenBSD: files.i2c,v 1.54 2016/01/20 01:26:00 jcs Exp $ # $NetBSD: files.i2c,v 1.3 2003/10/20 16:24:10 briggs Exp $ define i2c {[addr = -1], [size = -1]} @@ -189,3 +189,8 @@ file dev/i2c/ikbd.c ikbd device ims: hid, hidms, wsmousedev attach ims at ihidbus file dev/i2c/ims.c ims + +# HID Multitouch Trackpad +device imt: hid, hidmt, wsmousedev +attach imt at ihidbus +file dev/i2c/imt.c imt diff --git a/sys/dev/i2c/imt.c b/sys/dev/i2c/imt.c new file mode 100644 index 00000000000..bb41444fccc --- /dev/null +++ b/sys/dev/i2c/imt.c @@ -0,0 +1,247 @@ +/* $OpenBSD: imt.c,v 1.1 2016/01/20 01:26:00 jcs Exp $ */ +/* + * HID-over-i2c multitouch trackpad driver for devices conforming to + * Windows Precision Touchpad standard + * + * https://msdn.microsoft.com/en-us/library/windows/hardware/dn467314%28v=vs.85%29.aspx + * + * Copyright (c) 2016 joshua stein <jcs@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/ioctl.h> + +#include <dev/i2c/i2cvar.h> +#include <dev/i2c/ihidev.h> + +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wsmousevar.h> + +#include <dev/hid/hid.h> +#include <dev/hid/hidmtvar.h> + +struct imt_softc { + struct ihidev sc_hdev; + struct hidmt sc_mt; + + int sc_rep_input; + int sc_rep_config; + int sc_rep_cap; +}; + +int imt_enable(void *); +int imt_open(struct ihidev *); +void imt_intr(struct ihidev *, void *, u_int); +void imt_disable(void *); +int imt_ioctl(void *, u_long, caddr_t, int, struct proc *); + +const struct wsmouse_accessops imt_accessops = { + imt_enable, + imt_ioctl, + imt_disable, +}; + +int imt_match(struct device *, void *, void *); +int imt_find_winptp_reports(struct ihidev_softc *, void *, int, + struct imt_softc *); +void imt_attach(struct device *, struct device *, void *); +int ims_hidev_get_report(struct device *, int, int, void *, int); +int ims_hidev_set_report(struct device *, int, int, void *, int); +int imt_detach(struct device *, int); + +struct cfdriver imt_cd = { + NULL, "imt", DV_DULL +}; + +const struct cfattach imt_ca = { + sizeof(struct imt_softc), + imt_match, + imt_attach, + imt_detach +}; + +int +imt_match(struct device *parent, void *match, void *aux) +{ + struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux; + int size; + void *desc; + + if (iha->reportid == IHIDEV_CLAIM_ALLREPORTID) { + ihidev_get_report_desc(iha->parent, &desc, &size); + if (imt_find_winptp_reports(iha->parent, desc, size, NULL)) + return (IMATCH_DEVCLASS_DEVSUBCLASS); + } + + return (IMATCH_NONE); +} + +int +imt_find_winptp_reports(struct ihidev_softc *parent, void *desc, int size, + struct imt_softc *sc) +{ + int repid; + int input = 0, conf = 0, cap = 0; + + if (sc != NULL) { + sc->sc_rep_input = -1; + sc->sc_rep_config = -1; + sc->sc_rep_cap = -1; + } + + for (repid = 0; repid < parent->sc_nrepid; repid++) { + if (hid_report_size(desc, size, hid_input, repid) == 0 && + hid_report_size(desc, size, hid_output, repid) == 0 && + hid_report_size(desc, size, hid_feature, repid) == 0) + continue; + + if (hid_is_collection(desc, size, repid, + HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHPAD))) { + input = 1; + if (sc != NULL && sc->sc_rep_input == -1) + sc->sc_rep_input = repid; + } else if (hid_is_collection(desc, size, repid, + HID_USAGE2(HUP_DIGITIZERS, HUD_CONFIG))) { + conf = 1; + if (sc != NULL && sc->sc_rep_config == -1) + sc->sc_rep_config = repid; + } + + /* capabilities report could be anywhere */ + if (hid_locate(desc, size, HID_USAGE2(HUP_DIGITIZERS, + HUD_CONTACT_MAX), repid, hid_feature, NULL, NULL)) { + cap = 1; + if (sc != NULL && sc->sc_rep_cap == -1) + sc->sc_rep_cap = repid; + } + } + + return (conf && input && cap); +} + +void +imt_attach(struct device *parent, struct device *self, void *aux) +{ + struct imt_softc *sc = (struct imt_softc *)self; + struct hidmt *mt = &sc->sc_mt; + struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux; + int size; + void *desc; + + sc->sc_hdev.sc_intr = imt_intr; + sc->sc_hdev.sc_parent = iha->parent; + + ihidev_get_report_desc(iha->parent, &desc, &size); + imt_find_winptp_reports(iha->parent, desc, size, sc); + + memset(mt, 0, sizeof(sc->sc_mt)); + + /* assume everything has "natural scrolling" where Y axis is reversed */ + mt->sc_flags = HIDMT_REVY; + + mt->hidev_get_report = ims_hidev_get_report; + mt->hidev_set_report = ims_hidev_set_report; + mt->sc_rep_input = sc->sc_rep_input; + mt->sc_rep_config = sc->sc_rep_config; + mt->sc_rep_cap = sc->sc_rep_cap; + + if (hidmt_setup(self, mt, desc, size) != 0) + return; + + hidmt_attach(mt, &imt_accessops); +} + +int +ims_hidev_get_report(struct device *self, int type, int id, void *data, int len) +{ + struct imt_softc *sc = (struct imt_softc *)self; + + return ihidev_get_report((struct device *)sc->sc_hdev.sc_parent, type, + id, data, len); +} + +int +ims_hidev_set_report(struct device *self, int type, int id, void *data, int len) +{ + struct imt_softc *sc = (struct imt_softc *)self; + + return ihidev_set_report((struct device *)sc->sc_hdev.sc_parent, type, + id, data, len); +} + +int +imt_detach(struct device *self, int flags) +{ + struct imt_softc *sc = (struct imt_softc *)self; + struct hidmt *mt = &sc->sc_mt; + + return hidmt_detach(mt, flags); +} + +void +imt_intr(struct ihidev *dev, void *buf, u_int len) +{ + struct imt_softc *sc = (struct imt_softc *)dev; + struct hidmt *mt = &sc->sc_mt; + + if (!mt->sc_enabled) + return; + + hidmt_input(mt, (uint8_t *)buf, len); +} + +int +imt_enable(void *v) +{ + struct imt_softc *sc = v; + struct hidmt *mt = &sc->sc_mt; + int rv; + + if ((rv = hidmt_enable(mt)) != 0) + return rv; + + rv = ihidev_open(&sc->sc_hdev); + + hidmt_set_input_mode(mt, HIDMT_INPUT_MODE_MT); + + return rv; +} + +void +imt_disable(void *v) +{ + struct imt_softc *sc = v; + struct hidmt *mt = &sc->sc_mt; + + hidmt_disable(mt); + ihidev_close(&sc->sc_hdev); +} + +int +imt_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct imt_softc *sc = v; + struct hidmt *mt = &sc->sc_mt; + int rc; + + rc = ihidev_ioctl(&sc->sc_hdev, cmd, data, flag, p); + if (rc != -1) + return rc; + + return hidmt_ioctl(mt, cmd, data, flag, p); +} |