1 /* $OpenBSD: imt.c,v 1.4 2018/08/25 20:31:31 jcs Exp $ */ 2 /* 3 * HID-over-i2c multitouch trackpad driver for devices conforming to 4 * Windows Precision Touchpad standard 5 * 6 * https://msdn.microsoft.com/en-us/library/windows/hardware/dn467314%28v=vs.85%29.aspx 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 int size; 83 void *desc; 84 85 if (iha->reportid == IHIDEV_CLAIM_ALLREPORTID) { 86 ihidev_get_report_desc(iha->parent, &desc, &size); 87 if (imt_find_winptp_reports(iha->parent, desc, size, NULL)) 88 return (IMATCH_DEVCLASS_DEVSUBCLASS); 89 } 90 91 return (IMATCH_NONE); 92 } 93 94 int 95 imt_find_winptp_reports(struct ihidev_softc *parent, void *desc, int size, 96 struct imt_softc *sc) 97 { 98 int repid; 99 int input = 0, conf = 0, cap = 0; 100 101 if (sc != NULL) { 102 sc->sc_rep_input = -1; 103 sc->sc_rep_config = -1; 104 sc->sc_rep_cap = -1; 105 } 106 107 for (repid = 0; repid < parent->sc_nrepid; repid++) { 108 if (hid_report_size(desc, size, hid_input, repid) == 0 && 109 hid_report_size(desc, size, hid_output, repid) == 0 && 110 hid_report_size(desc, size, hid_feature, repid) == 0) 111 continue; 112 113 if (hid_is_collection(desc, size, repid, 114 HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHPAD))) { 115 input = 1; 116 if (sc != NULL && sc->sc_rep_input == -1) 117 sc->sc_rep_input = repid; 118 } else if (hid_is_collection(desc, size, repid, 119 HID_USAGE2(HUP_DIGITIZERS, HUD_CONFIG))) { 120 conf = 1; 121 if (sc != NULL && sc->sc_rep_config == -1) 122 sc->sc_rep_config = repid; 123 } 124 125 /* capabilities report could be anywhere */ 126 if (hid_locate(desc, size, HID_USAGE2(HUP_DIGITIZERS, 127 HUD_CONTACT_MAX), repid, hid_feature, NULL, NULL)) { 128 cap = 1; 129 if (sc != NULL && sc->sc_rep_cap == -1) 130 sc->sc_rep_cap = repid; 131 } 132 } 133 134 return (conf && input && cap); 135 } 136 137 void 138 imt_attach(struct device *parent, struct device *self, void *aux) 139 { 140 struct imt_softc *sc = (struct imt_softc *)self; 141 struct hidmt *mt = &sc->sc_mt; 142 struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux; 143 int size; 144 void *desc; 145 146 sc->sc_hdev.sc_intr = imt_intr; 147 sc->sc_hdev.sc_parent = iha->parent; 148 149 ihidev_get_report_desc(iha->parent, &desc, &size); 150 imt_find_winptp_reports(iha->parent, desc, size, sc); 151 152 memset(mt, 0, sizeof(sc->sc_mt)); 153 154 /* assume everything has "natural scrolling" where Y axis is reversed */ 155 mt->sc_flags = HIDMT_REVY; 156 157 mt->hidev_report_type_conv = ihidev_report_type_conv; 158 mt->hidev_get_report = imt_hidev_get_report; 159 mt->hidev_set_report = imt_hidev_set_report; 160 mt->sc_rep_input = sc->sc_rep_input; 161 mt->sc_rep_config = sc->sc_rep_config; 162 mt->sc_rep_cap = sc->sc_rep_cap; 163 164 if (hidmt_setup(self, mt, desc, size) != 0) 165 return; 166 167 hidmt_attach(mt, &imt_accessops); 168 } 169 170 int 171 imt_hidev_get_report(struct device *self, int type, int id, void *data, int len) 172 { 173 struct imt_softc *sc = (struct imt_softc *)self; 174 175 return ihidev_get_report((struct device *)sc->sc_hdev.sc_parent, type, 176 id, data, len); 177 } 178 179 int 180 imt_hidev_set_report(struct device *self, int type, int id, void *data, int len) 181 { 182 struct imt_softc *sc = (struct imt_softc *)self; 183 184 return ihidev_set_report((struct device *)sc->sc_hdev.sc_parent, type, 185 id, data, len); 186 } 187 188 int 189 imt_detach(struct device *self, int flags) 190 { 191 struct imt_softc *sc = (struct imt_softc *)self; 192 struct hidmt *mt = &sc->sc_mt; 193 194 return hidmt_detach(mt, flags); 195 } 196 197 void 198 imt_intr(struct ihidev *dev, void *buf, u_int len) 199 { 200 struct imt_softc *sc = (struct imt_softc *)dev; 201 struct hidmt *mt = &sc->sc_mt; 202 203 if (!mt->sc_enabled) 204 return; 205 206 hidmt_input(mt, (uint8_t *)buf, len); 207 } 208 209 int 210 imt_enable(void *v) 211 { 212 struct imt_softc *sc = v; 213 struct hidmt *mt = &sc->sc_mt; 214 int rv; 215 216 if ((rv = hidmt_enable(mt)) != 0) 217 return rv; 218 219 rv = ihidev_open(&sc->sc_hdev); 220 221 hidmt_set_input_mode(mt, HIDMT_INPUT_MODE_MT_TOUCHPAD); 222 223 return rv; 224 } 225 226 void 227 imt_disable(void *v) 228 { 229 struct imt_softc *sc = v; 230 struct hidmt *mt = &sc->sc_mt; 231 232 hidmt_disable(mt); 233 ihidev_close(&sc->sc_hdev); 234 } 235 236 int 237 imt_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 238 { 239 struct imt_softc *sc = v; 240 struct hidmt *mt = &sc->sc_mt; 241 int rc; 242 243 rc = ihidev_ioctl(&sc->sc_hdev, cmd, data, flag, p); 244 if (rc != -1) 245 return rc; 246 247 return hidmt_ioctl(mt, cmd, data, flag, p); 248 } 249