1 /* $OpenBSD: imt.c,v 1.1 2016/01/20 01:26:00 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 ims_hidev_get_report(struct device *, int, int, void *, int); 64 int ims_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_get_report = ims_hidev_get_report; 158 mt->hidev_set_report = ims_hidev_set_report; 159 mt->sc_rep_input = sc->sc_rep_input; 160 mt->sc_rep_config = sc->sc_rep_config; 161 mt->sc_rep_cap = sc->sc_rep_cap; 162 163 if (hidmt_setup(self, mt, desc, size) != 0) 164 return; 165 166 hidmt_attach(mt, &imt_accessops); 167 } 168 169 int 170 ims_hidev_get_report(struct device *self, int type, int id, void *data, int len) 171 { 172 struct imt_softc *sc = (struct imt_softc *)self; 173 174 return ihidev_get_report((struct device *)sc->sc_hdev.sc_parent, type, 175 id, data, len); 176 } 177 178 int 179 ims_hidev_set_report(struct device *self, int type, int id, void *data, int len) 180 { 181 struct imt_softc *sc = (struct imt_softc *)self; 182 183 return ihidev_set_report((struct device *)sc->sc_hdev.sc_parent, type, 184 id, data, len); 185 } 186 187 int 188 imt_detach(struct device *self, int flags) 189 { 190 struct imt_softc *sc = (struct imt_softc *)self; 191 struct hidmt *mt = &sc->sc_mt; 192 193 return hidmt_detach(mt, flags); 194 } 195 196 void 197 imt_intr(struct ihidev *dev, void *buf, u_int len) 198 { 199 struct imt_softc *sc = (struct imt_softc *)dev; 200 struct hidmt *mt = &sc->sc_mt; 201 202 if (!mt->sc_enabled) 203 return; 204 205 hidmt_input(mt, (uint8_t *)buf, len); 206 } 207 208 int 209 imt_enable(void *v) 210 { 211 struct imt_softc *sc = v; 212 struct hidmt *mt = &sc->sc_mt; 213 int rv; 214 215 if ((rv = hidmt_enable(mt)) != 0) 216 return rv; 217 218 rv = ihidev_open(&sc->sc_hdev); 219 220 hidmt_set_input_mode(mt, HIDMT_INPUT_MODE_MT); 221 222 return rv; 223 } 224 225 void 226 imt_disable(void *v) 227 { 228 struct imt_softc *sc = v; 229 struct hidmt *mt = &sc->sc_mt; 230 231 hidmt_disable(mt); 232 ihidev_close(&sc->sc_hdev); 233 } 234 235 int 236 imt_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 237 { 238 struct imt_softc *sc = v; 239 struct hidmt *mt = &sc->sc_mt; 240 int rc; 241 242 rc = ihidev_ioctl(&sc->sc_hdev, cmd, data, flag, p); 243 if (rc != -1) 244 return rc; 245 246 return hidmt_ioctl(mt, cmd, data, flag, p); 247 } 248