1 /* $OpenBSD: imt.c,v 1.6 2024/05/13 01:15:50 jsg 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 void imt_intr(struct ihidev *, void *, u_int); 49 void imt_disable(void *); 50 int imt_ioctl(void *, u_long, caddr_t, int, struct proc *); 51 52 const struct wsmouse_accessops imt_accessops = { 53 imt_enable, 54 imt_ioctl, 55 imt_disable, 56 }; 57 58 int imt_match(struct device *, void *, void *); 59 int imt_find_winptp_reports(struct ihidev_softc *, void *, int, 60 struct imt_softc *); 61 void imt_attach(struct device *, struct device *, void *); 62 int imt_hidev_get_report(struct device *, int, int, void *, int); 63 int imt_hidev_set_report(struct device *, int, int, void *, int); 64 int imt_detach(struct device *, int); 65 66 struct cfdriver imt_cd = { 67 NULL, "imt", DV_DULL 68 }; 69 70 const struct cfattach imt_ca = { 71 sizeof(struct imt_softc), 72 imt_match, 73 imt_attach, 74 imt_detach 75 }; 76 77 int 78 imt_match(struct device *parent, void *match, void *aux) 79 { 80 struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux; 81 struct imt_softc sc; 82 int size; 83 void *desc; 84 85 if (iha->reportid == IHIDEV_CLAIM_MULTIPLEID) { 86 ihidev_get_report_desc(iha->parent, &desc, &size); 87 if (imt_find_winptp_reports(iha->parent, desc, size, &sc)) { 88 iha->claims[0] = sc.sc_rep_input; 89 iha->claims[1] = sc.sc_rep_config; 90 iha->claims[2] = sc.sc_rep_cap; 91 iha->nclaims = 3; 92 return (IMATCH_DEVCLASS_DEVSUBCLASS); 93 } 94 } 95 96 return (IMATCH_NONE); 97 } 98 99 int 100 imt_find_winptp_reports(struct ihidev_softc *parent, void *desc, int size, 101 struct imt_softc *sc) 102 { 103 int repid; 104 int input = 0, conf = 0, cap = 0; 105 106 if (sc != NULL) { 107 sc->sc_rep_input = -1; 108 sc->sc_rep_config = -1; 109 sc->sc_rep_cap = -1; 110 } 111 112 for (repid = 0; repid < parent->sc_nrepid; repid++) { 113 if (hid_report_size(desc, size, hid_input, repid) == 0 && 114 hid_report_size(desc, size, hid_output, repid) == 0 && 115 hid_report_size(desc, size, hid_feature, repid) == 0) 116 continue; 117 118 if (hid_is_collection(desc, size, repid, 119 HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHPAD))) { 120 input = 1; 121 if (sc != NULL && sc->sc_rep_input == -1) 122 sc->sc_rep_input = repid; 123 } else if (hid_is_collection(desc, size, repid, 124 HID_USAGE2(HUP_DIGITIZERS, HUD_CONFIG))) { 125 conf = 1; 126 if (sc != NULL && sc->sc_rep_config == -1) 127 sc->sc_rep_config = repid; 128 } 129 130 /* capabilities report could be anywhere */ 131 if (hid_locate(desc, size, HID_USAGE2(HUP_DIGITIZERS, 132 HUD_CONTACT_MAX), repid, hid_feature, NULL, NULL)) { 133 cap = 1; 134 if (sc != NULL && sc->sc_rep_cap == -1) 135 sc->sc_rep_cap = repid; 136 } 137 } 138 139 return (conf && input && cap); 140 } 141 142 void 143 imt_attach(struct device *parent, struct device *self, void *aux) 144 { 145 struct imt_softc *sc = (struct imt_softc *)self; 146 struct hidmt *mt = &sc->sc_mt; 147 struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux; 148 int size; 149 void *desc; 150 151 sc->sc_hdev.sc_intr = imt_intr; 152 sc->sc_hdev.sc_parent = iha->parent; 153 154 ihidev_get_report_desc(iha->parent, &desc, &size); 155 imt_find_winptp_reports(iha->parent, desc, size, sc); 156 157 memset(mt, 0, sizeof(sc->sc_mt)); 158 159 /* assume everything has "natural scrolling" where Y axis is reversed */ 160 mt->sc_flags = HIDMT_REVY; 161 162 mt->hidev_report_type_conv = ihidev_report_type_conv; 163 mt->hidev_get_report = imt_hidev_get_report; 164 mt->hidev_set_report = imt_hidev_set_report; 165 mt->sc_rep_input = sc->sc_rep_input; 166 mt->sc_rep_config = sc->sc_rep_config; 167 mt->sc_rep_cap = sc->sc_rep_cap; 168 169 if (hidmt_setup(self, mt, desc, size) != 0) 170 return; 171 172 hidmt_attach(mt, &imt_accessops); 173 } 174 175 int 176 imt_hidev_get_report(struct device *self, int type, int id, void *data, int len) 177 { 178 struct imt_softc *sc = (struct imt_softc *)self; 179 180 return ihidev_get_report((struct device *)sc->sc_hdev.sc_parent, type, 181 id, data, len); 182 } 183 184 int 185 imt_hidev_set_report(struct device *self, int type, int id, void *data, int len) 186 { 187 struct imt_softc *sc = (struct imt_softc *)self; 188 189 return ihidev_set_report((struct device *)sc->sc_hdev.sc_parent, type, 190 id, data, len); 191 } 192 193 int 194 imt_detach(struct device *self, int flags) 195 { 196 struct imt_softc *sc = (struct imt_softc *)self; 197 struct hidmt *mt = &sc->sc_mt; 198 199 return hidmt_detach(mt, flags); 200 } 201 202 void 203 imt_intr(struct ihidev *dev, void *buf, u_int len) 204 { 205 struct imt_softc *sc = (struct imt_softc *)dev; 206 struct hidmt *mt = &sc->sc_mt; 207 208 if (!mt->sc_enabled) 209 return; 210 211 hidmt_input(mt, (uint8_t *)buf, len); 212 } 213 214 int 215 imt_enable(void *v) 216 { 217 struct imt_softc *sc = v; 218 struct hidmt *mt = &sc->sc_mt; 219 int rv; 220 221 if ((rv = hidmt_enable(mt)) != 0) 222 return rv; 223 224 rv = ihidev_open(&sc->sc_hdev); 225 226 hidmt_set_input_mode(mt, HIDMT_INPUT_MODE_MT_TOUCHPAD); 227 228 return rv; 229 } 230 231 void 232 imt_disable(void *v) 233 { 234 struct imt_softc *sc = v; 235 struct hidmt *mt = &sc->sc_mt; 236 237 hidmt_disable(mt); 238 ihidev_close(&sc->sc_hdev); 239 } 240 241 int 242 imt_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 243 { 244 struct imt_softc *sc = v; 245 struct hidmt *mt = &sc->sc_mt; 246 int rc; 247 248 rc = ihidev_ioctl(&sc->sc_hdev, cmd, data, flag, p); 249 if (rc != -1) 250 return rc; 251 252 return hidmt_ioctl(mt, cmd, data, flag, p); 253 } 254