1 /* $OpenBSD: umt.c,v 1.2 2020/08/23 11:08:02 mglocker Exp $ */ 2 /* 3 * USB multitouch touchpad 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-2018 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/usb/usb.h> 30 #include <dev/usb/usbhid.h> 31 #include <dev/usb/usbdi.h> 32 #include <dev/usb/usbdi_util.h> 33 #include <dev/usb/usbdevs.h> 34 #include <dev/usb/uhidev.h> 35 36 #include <dev/wscons/wsconsio.h> 37 #include <dev/wscons/wsmousevar.h> 38 39 #include <dev/hid/hid.h> 40 #include <dev/hid/hidmtvar.h> 41 42 struct umt_softc { 43 struct uhidev sc_hdev; 44 struct hidmt sc_mt; 45 46 int sc_rep_input; 47 int sc_rep_config; 48 int sc_rep_cap; 49 }; 50 51 int umt_enable(void *); 52 int umt_open(struct uhidev *); 53 void umt_intr(struct uhidev *, void *, u_int); 54 void umt_disable(void *); 55 int umt_ioctl(void *, u_long, caddr_t, int, struct proc *); 56 57 const struct wsmouse_accessops umt_accessops = { 58 umt_enable, 59 umt_ioctl, 60 umt_disable, 61 }; 62 63 int umt_match(struct device *, void *, void *); 64 int umt_find_winptp_reports(struct uhidev_softc *, void *, int, 65 struct umt_softc *); 66 void umt_attach(struct device *, struct device *, void *); 67 int umt_hidev_get_report(struct device *, int, int, void *, int); 68 int umt_hidev_set_report(struct device *, int, int, void *, int); 69 int umt_detach(struct device *, int); 70 71 struct cfdriver umt_cd = { 72 NULL, "umt", DV_DULL 73 }; 74 75 const struct cfattach umt_ca = { 76 sizeof(struct umt_softc), 77 umt_match, 78 umt_attach, 79 umt_detach 80 }; 81 82 int 83 umt_match(struct device *parent, void *match, void *aux) 84 { 85 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 86 int size; 87 void *desc; 88 89 if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID) { 90 uhidev_get_report_desc(uha->parent, &desc, &size); 91 if (umt_find_winptp_reports(uha->parent, desc, size, NULL)) 92 return (UMATCH_DEVCLASS_DEVSUBCLASS); 93 } 94 95 return (UMATCH_NONE); 96 } 97 98 int 99 umt_find_winptp_reports(struct uhidev_softc *parent, void *desc, int size, 100 struct umt_softc *sc) 101 { 102 int repid; 103 int input = 0, conf = 0, cap = 0; 104 105 if (sc != NULL) { 106 sc->sc_rep_input = -1; 107 sc->sc_rep_config = -1; 108 sc->sc_rep_cap = -1; 109 } 110 111 for (repid = 0; repid < parent->sc_nrepid; repid++) { 112 if (hid_report_size(desc, size, hid_input, repid) == 0 && 113 hid_report_size(desc, size, hid_output, repid) == 0 && 114 hid_report_size(desc, size, hid_feature, repid) == 0) 115 continue; 116 117 if (hid_is_collection(desc, size, repid, 118 HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHPAD))) { 119 input = 1; 120 if (sc != NULL && sc->sc_rep_input == -1) 121 sc->sc_rep_input = repid; 122 } else if (hid_is_collection(desc, size, repid, 123 HID_USAGE2(HUP_DIGITIZERS, HUD_CONFIG))) { 124 conf = 1; 125 if (sc != NULL && sc->sc_rep_config == -1) 126 sc->sc_rep_config = repid; 127 } 128 129 /* capabilities report could be anywhere */ 130 if (hid_locate(desc, size, HID_USAGE2(HUP_DIGITIZERS, 131 HUD_CONTACT_MAX), repid, hid_feature, NULL, NULL)) { 132 cap = 1; 133 if (sc != NULL && sc->sc_rep_cap == -1) 134 sc->sc_rep_cap = repid; 135 } 136 } 137 138 return (conf && input && cap); 139 } 140 141 void 142 umt_attach(struct device *parent, struct device *self, void *aux) 143 { 144 struct umt_softc *sc = (struct umt_softc *)self; 145 struct hidmt *mt = &sc->sc_mt; 146 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 147 int size; 148 void *desc; 149 150 sc->sc_hdev.sc_intr = umt_intr; 151 sc->sc_hdev.sc_parent = uha->parent; 152 153 usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0); 154 155 uhidev_get_report_desc(uha->parent, &desc, &size); 156 umt_find_winptp_reports(uha->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 = uhidev_report_type_conv; 164 mt->hidev_get_report = umt_hidev_get_report; 165 mt->hidev_set_report = umt_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, &umt_accessops); 174 } 175 176 int 177 umt_hidev_get_report(struct device *self, int type, int id, void *data, int len) 178 { 179 struct umt_softc *sc = (struct umt_softc *)self; 180 int ret; 181 182 ret = uhidev_get_report(sc->sc_hdev.sc_parent, type, id, data, len); 183 return (ret < len); 184 } 185 186 int 187 umt_hidev_set_report(struct device *self, int type, int id, void *data, int len) 188 { 189 struct umt_softc *sc = (struct umt_softc *)self; 190 int ret; 191 192 ret = uhidev_set_report(sc->sc_hdev.sc_parent, type, id, data, len); 193 return (ret < len); 194 } 195 196 int 197 umt_detach(struct device *self, int flags) 198 { 199 struct umt_softc *sc = (struct umt_softc *)self; 200 struct hidmt *mt = &sc->sc_mt; 201 202 return hidmt_detach(mt, flags); 203 } 204 205 void 206 umt_intr(struct uhidev *dev, void *buf, u_int len) 207 { 208 struct umt_softc *sc = (struct umt_softc *)dev; 209 struct hidmt *mt = &sc->sc_mt; 210 211 if (!mt->sc_enabled) 212 return; 213 214 hidmt_input(mt, (uint8_t *)buf, len); 215 } 216 217 int 218 umt_enable(void *v) 219 { 220 struct umt_softc *sc = v; 221 struct hidmt *mt = &sc->sc_mt; 222 int rv; 223 224 if ((rv = hidmt_enable(mt)) != 0) 225 return rv; 226 227 rv = uhidev_open(&sc->sc_hdev); 228 229 hidmt_set_input_mode(mt, HIDMT_INPUT_MODE_MT_TOUCHPAD); 230 231 return rv; 232 } 233 234 void 235 umt_disable(void *v) 236 { 237 struct umt_softc *sc = v; 238 struct hidmt *mt = &sc->sc_mt; 239 240 hidmt_disable(mt); 241 uhidev_close(&sc->sc_hdev); 242 } 243 244 int 245 umt_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 246 { 247 struct umt_softc *sc = v; 248 struct hidmt *mt = &sc->sc_mt; 249 int rc; 250 251 rc = uhidev_ioctl(&sc->sc_hdev, cmd, data, flag, p); 252 if (rc != -1) 253 return rc; 254 255 return hidmt_ioctl(mt, cmd, data, flag, p); 256 } 257