1 /* $OpenBSD: imt.c,v 1.5 2020/07/09 21:01:56 jcs Exp $ */ 2 /* 3 * HID-over-i2c multitouch trackpad driver for devices conforming to 4 * Windows Precision Touchpad standard 5 * 6 * https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-precision-touchpad-required-hid-top-level-collections 7 * 8 * Copyright (c) 2016 joshua stein <jcs@openbsd.org> 9 * 10 * Permission to use, copy, modify, and distribute this software for any 11 * purpose with or without fee is hereby granted, provided that the above 12 * copyright notice and this permission notice appear in all copies. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 */ 22 23 #include <sys/param.h> 24 #include <sys/systm.h> 25 #include <sys/kernel.h> 26 #include <sys/device.h> 27 #include <sys/ioctl.h> 28 29 #include <dev/i2c/i2cvar.h> 30 #include <dev/i2c/ihidev.h> 31 32 #include <dev/wscons/wsconsio.h> 33 #include <dev/wscons/wsmousevar.h> 34 35 #include <dev/hid/hid.h> 36 #include <dev/hid/hidmtvar.h> 37 38 struct imt_softc { 39 struct ihidev sc_hdev; 40 struct hidmt sc_mt; 41 42 int sc_rep_input; 43 int sc_rep_config; 44 int sc_rep_cap; 45 }; 46 47 int imt_enable(void *); 48 int imt_open(struct ihidev *); 49 void imt_intr(struct ihidev *, void *, u_int); 50 void imt_disable(void *); 51 int imt_ioctl(void *, u_long, caddr_t, int, struct proc *); 52 53 const struct wsmouse_accessops imt_accessops = { 54 imt_enable, 55 imt_ioctl, 56 imt_disable, 57 }; 58 59 int imt_match(struct device *, void *, void *); 60 int imt_find_winptp_reports(struct ihidev_softc *, void *, int, 61 struct imt_softc *); 62 void imt_attach(struct device *, struct device *, void *); 63 int imt_hidev_get_report(struct device *, int, int, void *, int); 64 int imt_hidev_set_report(struct device *, int, int, void *, int); 65 int imt_detach(struct device *, int); 66 67 struct cfdriver imt_cd = { 68 NULL, "imt", DV_DULL 69 }; 70 71 const struct cfattach imt_ca = { 72 sizeof(struct imt_softc), 73 imt_match, 74 imt_attach, 75 imt_detach 76 }; 77 78 int 79 imt_match(struct device *parent, void *match, void *aux) 80 { 81 struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux; 82 struct imt_softc sc; 83 int size; 84 void *desc; 85 86 if (iha->reportid == IHIDEV_CLAIM_MULTIPLEID) { 87 ihidev_get_report_desc(iha->parent, &desc, &size); 88 if (imt_find_winptp_reports(iha->parent, desc, size, &sc)) { 89 iha->claims[0] = sc.sc_rep_input; 90 iha->claims[1] = sc.sc_rep_config; 91 iha->claims[2] = sc.sc_rep_cap; 92 iha->nclaims = 3; 93 return (IMATCH_DEVCLASS_DEVSUBCLASS); 94 } 95 } 96 97 return (IMATCH_NONE); 98 } 99 100 int 101 imt_find_winptp_reports(struct ihidev_softc *parent, void *desc, int size, 102 struct imt_softc *sc) 103 { 104 int repid; 105 int input = 0, conf = 0, cap = 0; 106 107 if (sc != NULL) { 108 sc->sc_rep_input = -1; 109 sc->sc_rep_config = -1; 110 sc->sc_rep_cap = -1; 111 } 112 113 for (repid = 0; repid < parent->sc_nrepid; repid++) { 114 if (hid_report_size(desc, size, hid_input, repid) == 0 && 115 hid_report_size(desc, size, hid_output, repid) == 0 && 116 hid_report_size(desc, size, hid_feature, repid) == 0) 117 continue; 118 119 if (hid_is_collection(desc, size, repid, 120 HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHPAD))) { 121 input = 1; 122 if (sc != NULL && sc->sc_rep_input == -1) 123 sc->sc_rep_input = repid; 124 } else if (hid_is_collection(desc, size, repid, 125 HID_USAGE2(HUP_DIGITIZERS, HUD_CONFIG))) { 126 conf = 1; 127 if (sc != NULL && sc->sc_rep_config == -1) 128 sc->sc_rep_config = repid; 129 } 130 131 /* capabilities report could be anywhere */ 132 if (hid_locate(desc, size, HID_USAGE2(HUP_DIGITIZERS, 133 HUD_CONTACT_MAX), repid, hid_feature, NULL, NULL)) { 134 cap = 1; 135 if (sc != NULL && sc->sc_rep_cap == -1) 136 sc->sc_rep_cap = repid; 137 } 138 } 139 140 return (conf && input && cap); 141 } 142 143 void 144 imt_attach(struct device *parent, struct device *self, void *aux) 145 { 146 struct imt_softc *sc = (struct imt_softc *)self; 147 struct hidmt *mt = &sc->sc_mt; 148 struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux; 149 int size; 150 void *desc; 151 152 sc->sc_hdev.sc_intr = imt_intr; 153 sc->sc_hdev.sc_parent = iha->parent; 154 155 ihidev_get_report_desc(iha->parent, &desc, &size); 156 imt_find_winptp_reports(iha->parent, desc, size, sc); 157 158 memset(mt, 0, sizeof(sc->sc_mt)); 159 160 /* assume everything has "natural scrolling" where Y axis is reversed */ 161 mt->sc_flags = HIDMT_REVY; 162 163 mt->hidev_report_type_conv = ihidev_report_type_conv; 164 mt->hidev_get_report = imt_hidev_get_report; 165 mt->hidev_set_report = imt_hidev_set_report; 166 mt->sc_rep_input = sc->sc_rep_input; 167 mt->sc_rep_config = sc->sc_rep_config; 168 mt->sc_rep_cap = sc->sc_rep_cap; 169 170 if (hidmt_setup(self, mt, desc, size) != 0) 171 return; 172 173 hidmt_attach(mt, &imt_accessops); 174 } 175 176 int 177 imt_hidev_get_report(struct device *self, int type, int id, void *data, int len) 178 { 179 struct imt_softc *sc = (struct imt_softc *)self; 180 181 return ihidev_get_report((struct device *)sc->sc_hdev.sc_parent, type, 182 id, data, len); 183 } 184 185 int 186 imt_hidev_set_report(struct device *self, int type, int id, void *data, int len) 187 { 188 struct imt_softc *sc = (struct imt_softc *)self; 189 190 return ihidev_set_report((struct device *)sc->sc_hdev.sc_parent, type, 191 id, data, len); 192 } 193 194 int 195 imt_detach(struct device *self, int flags) 196 { 197 struct imt_softc *sc = (struct imt_softc *)self; 198 struct hidmt *mt = &sc->sc_mt; 199 200 return hidmt_detach(mt, flags); 201 } 202 203 void 204 imt_intr(struct ihidev *dev, void *buf, u_int len) 205 { 206 struct imt_softc *sc = (struct imt_softc *)dev; 207 struct hidmt *mt = &sc->sc_mt; 208 209 if (!mt->sc_enabled) 210 return; 211 212 hidmt_input(mt, (uint8_t *)buf, len); 213 } 214 215 int 216 imt_enable(void *v) 217 { 218 struct imt_softc *sc = v; 219 struct hidmt *mt = &sc->sc_mt; 220 int rv; 221 222 if ((rv = hidmt_enable(mt)) != 0) 223 return rv; 224 225 rv = ihidev_open(&sc->sc_hdev); 226 227 hidmt_set_input_mode(mt, HIDMT_INPUT_MODE_MT_TOUCHPAD); 228 229 return rv; 230 } 231 232 void 233 imt_disable(void *v) 234 { 235 struct imt_softc *sc = v; 236 struct hidmt *mt = &sc->sc_mt; 237 238 hidmt_disable(mt); 239 ihidev_close(&sc->sc_hdev); 240 } 241 242 int 243 imt_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 244 { 245 struct imt_softc *sc = v; 246 struct hidmt *mt = &sc->sc_mt; 247 int rc; 248 249 rc = ihidev_ioctl(&sc->sc_hdev, cmd, data, flag, p); 250 if (rc != -1) 251 return rc; 252 253 return hidmt_ioctl(mt, cmd, data, flag, p); 254 } 255