1 /* $OpenBSD: umt.c,v 1.1 2018/08/25 20:31:31 jcs 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/usbdevs.h> 33 #include <dev/usb/uhidev.h> 34 35 #include <dev/wscons/wsconsio.h> 36 #include <dev/wscons/wsmousevar.h> 37 38 #include <dev/hid/hid.h> 39 #include <dev/hid/hidmtvar.h> 40 41 struct umt_softc { 42 struct uhidev sc_hdev; 43 struct hidmt sc_mt; 44 45 int sc_rep_input; 46 int sc_rep_config; 47 int sc_rep_cap; 48 }; 49 50 int umt_enable(void *); 51 int umt_open(struct uhidev *); 52 void umt_intr(struct uhidev *, void *, u_int); 53 void umt_disable(void *); 54 int umt_ioctl(void *, u_long, caddr_t, int, struct proc *); 55 56 const struct wsmouse_accessops umt_accessops = { 57 umt_enable, 58 umt_ioctl, 59 umt_disable, 60 }; 61 62 int umt_match(struct device *, void *, void *); 63 int umt_find_winptp_reports(struct uhidev_softc *, void *, int, 64 struct umt_softc *); 65 void umt_attach(struct device *, struct device *, void *); 66 int umt_hidev_get_report(struct device *, int, int, void *, int); 67 int umt_hidev_set_report(struct device *, int, int, void *, int); 68 int umt_detach(struct device *, int); 69 70 struct cfdriver umt_cd = { 71 NULL, "umt", DV_DULL 72 }; 73 74 const struct cfattach umt_ca = { 75 sizeof(struct umt_softc), 76 umt_match, 77 umt_attach, 78 umt_detach 79 }; 80 81 int 82 umt_match(struct device *parent, void *match, void *aux) 83 { 84 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 85 int size; 86 void *desc; 87 88 if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID) { 89 uhidev_get_report_desc(uha->parent, &desc, &size); 90 if (umt_find_winptp_reports(uha->parent, desc, size, NULL)) 91 return (UMATCH_DEVCLASS_DEVSUBCLASS); 92 } 93 94 return (UMATCH_NONE); 95 } 96 97 int 98 umt_find_winptp_reports(struct uhidev_softc *parent, void *desc, int size, 99 struct umt_softc *sc) 100 { 101 int repid; 102 int input = 0, conf = 0, cap = 0; 103 104 if (sc != NULL) { 105 sc->sc_rep_input = -1; 106 sc->sc_rep_config = -1; 107 sc->sc_rep_cap = -1; 108 } 109 110 for (repid = 0; repid < parent->sc_nrepid; repid++) { 111 if (hid_report_size(desc, size, hid_input, repid) == 0 && 112 hid_report_size(desc, size, hid_output, repid) == 0 && 113 hid_report_size(desc, size, hid_feature, repid) == 0) 114 continue; 115 116 if (hid_is_collection(desc, size, repid, 117 HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHPAD))) { 118 input = 1; 119 if (sc != NULL && sc->sc_rep_input == -1) 120 sc->sc_rep_input = repid; 121 } else if (hid_is_collection(desc, size, repid, 122 HID_USAGE2(HUP_DIGITIZERS, HUD_CONFIG))) { 123 conf = 1; 124 if (sc != NULL && sc->sc_rep_config == -1) 125 sc->sc_rep_config = repid; 126 } 127 128 /* capabilities report could be anywhere */ 129 if (hid_locate(desc, size, HID_USAGE2(HUP_DIGITIZERS, 130 HUD_CONTACT_MAX), repid, hid_feature, NULL, NULL)) { 131 cap = 1; 132 if (sc != NULL && sc->sc_rep_cap == -1) 133 sc->sc_rep_cap = repid; 134 } 135 } 136 137 return (conf && input && cap); 138 } 139 140 void 141 umt_attach(struct device *parent, struct device *self, void *aux) 142 { 143 struct umt_softc *sc = (struct umt_softc *)self; 144 struct hidmt *mt = &sc->sc_mt; 145 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 146 int size; 147 void *desc; 148 149 sc->sc_hdev.sc_intr = umt_intr; 150 sc->sc_hdev.sc_parent = uha->parent; 151 152 uhidev_get_report_desc(uha->parent, &desc, &size); 153 umt_find_winptp_reports(uha->parent, desc, size, sc); 154 155 memset(mt, 0, sizeof(sc->sc_mt)); 156 157 /* assume everything has "natural scrolling" where Y axis is reversed */ 158 mt->sc_flags = HIDMT_REVY; 159 160 mt->hidev_report_type_conv = uhidev_report_type_conv; 161 mt->hidev_get_report = umt_hidev_get_report; 162 mt->hidev_set_report = umt_hidev_set_report; 163 mt->sc_rep_input = sc->sc_rep_input; 164 mt->sc_rep_config = sc->sc_rep_config; 165 mt->sc_rep_cap = sc->sc_rep_cap; 166 167 if (hidmt_setup(self, mt, desc, size) != 0) 168 return; 169 170 hidmt_attach(mt, &umt_accessops); 171 } 172 173 int 174 umt_hidev_get_report(struct device *self, int type, int id, void *data, int len) 175 { 176 struct umt_softc *sc = (struct umt_softc *)self; 177 int ret; 178 179 ret = uhidev_get_report(sc->sc_hdev.sc_parent, type, id, data, len); 180 return (ret < len); 181 } 182 183 int 184 umt_hidev_set_report(struct device *self, int type, int id, void *data, int len) 185 { 186 struct umt_softc *sc = (struct umt_softc *)self; 187 int ret; 188 189 ret = uhidev_set_report(sc->sc_hdev.sc_parent, type, id, data, len); 190 return (ret < len); 191 } 192 193 int 194 umt_detach(struct device *self, int flags) 195 { 196 struct umt_softc *sc = (struct umt_softc *)self; 197 struct hidmt *mt = &sc->sc_mt; 198 199 return hidmt_detach(mt, flags); 200 } 201 202 void 203 umt_intr(struct uhidev *dev, void *buf, u_int len) 204 { 205 struct umt_softc *sc = (struct umt_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 umt_enable(void *v) 216 { 217 struct umt_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 = uhidev_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 umt_disable(void *v) 233 { 234 struct umt_softc *sc = v; 235 struct hidmt *mt = &sc->sc_mt; 236 237 hidmt_disable(mt); 238 uhidev_close(&sc->sc_hdev); 239 } 240 241 int 242 umt_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 243 { 244 struct umt_softc *sc = v; 245 struct hidmt *mt = &sc->sc_mt; 246 int rc; 247 248 rc = uhidev_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