jcs's openbsd hax
openbsd
at jcs 236 lines 5.8 kB view raw
1/* $OpenBSD: umt.c,v 1.9 2025/07/21 21:46:40 bru Exp $ */ 2/* 3 * USB multitouch touchpad 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-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/device.h> 26 27#include <dev/usb/usb.h> 28#include <dev/usb/usbhid.h> 29#include <dev/usb/usbdi.h> 30#include <dev/usb/usbdi_util.h> 31#include <dev/usb/usb_quirks.h> 32#include <dev/usb/uhidev.h> 33 34#include <dev/wscons/wsconsio.h> 35#include <dev/wscons/wsmousevar.h> 36 37#include <dev/hid/hid.h> 38#include <dev/hid/hidmtvar.h> 39 40struct umt_softc { 41 struct uhidev sc_hdev; 42 struct hidmt sc_mt; 43 44 int sc_rep_input; 45 int sc_rep_config; 46 int sc_rep_cap; 47 48 u_int32_t sc_quirks; 49}; 50 51int umt_enable(void *); 52void umt_intr(struct uhidev *, void *, u_int); 53void umt_disable(void *); 54int umt_ioctl(void *, u_long, caddr_t, int, struct proc *); 55 56const struct wsmouse_accessops umt_accessops = { 57 umt_enable, 58 umt_ioctl, 59 umt_disable, 60}; 61 62int umt_match(struct device *, void *, void *); 63void umt_attach(struct device *, struct device *, void *); 64int umt_hidev_get_report(struct device *, int, int, void *, int); 65int umt_hidev_set_report(struct device *, int, int, void *, int); 66int umt_detach(struct device *, int); 67 68struct cfdriver umt_cd = { 69 NULL, "umt", DV_DULL 70}; 71 72const struct cfattach umt_ca = { 73 sizeof(struct umt_softc), 74 umt_match, 75 umt_attach, 76 umt_detach 77}; 78 79int 80umt_match(struct device *parent, void *match, void *aux) 81{ 82 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 83 int input = 0, conf = 0, cap = 0; 84 int size; 85 void *desc; 86 87 if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)) { 88 uhidev_get_report_desc(uha->parent, &desc, &size); 89 if (hidmt_find_winptp_reports(desc, size, &input, 90 &conf, &cap)) { 91 uha->claimed[input] = 1; 92 uha->claimed[conf] = 1; 93 uha->claimed[cap] = 1; 94 return (UMATCH_DEVCLASS_DEVSUBCLASS); 95 } 96 } 97 98 return (UMATCH_NONE); 99} 100 101void 102umt_attach(struct device *parent, struct device *self, void *aux) 103{ 104 struct umt_softc *sc = (struct umt_softc *)self; 105 struct hidmt *mt = &sc->sc_mt; 106 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 107 struct usb_attach_arg *uaa = uha->uaa; 108 int size; 109 void *desc; 110 111 sc->sc_hdev.sc_intr = umt_intr; 112 sc->sc_hdev.sc_parent = uha->parent; 113 sc->sc_hdev.sc_udev = uaa->device; 114 115 usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0); 116 117 sc->sc_quirks = usbd_get_quirks(sc->sc_hdev.sc_udev)->uq_flags; 118 119 uhidev_get_report_desc(uha->parent, &desc, &size); 120 hidmt_find_winptp_reports(desc, size, &sc->sc_rep_input, 121 &sc->sc_rep_config, &sc->sc_rep_cap); 122 123 memset(mt, 0, sizeof(sc->sc_mt)); 124 125 /* assume everything has "natural scrolling" where Y axis is reversed */ 126 mt->sc_flags = HIDMT_REVY; 127 128 mt->hidev_report_type_conv = uhidev_report_type_conv; 129 mt->hidev_get_report = umt_hidev_get_report; 130 mt->hidev_set_report = umt_hidev_set_report; 131 mt->sc_rep_input = sc->sc_rep_input; 132 mt->sc_rep_config = sc->sc_rep_config; 133 mt->sc_rep_cap = sc->sc_rep_cap; 134 135 if (hidmt_setup(self, mt, desc, size) != 0) 136 return; 137 138 if (sc->sc_quirks & UQ_ALWAYS_OPEN) { 139 /* open uhidev and keep it open */ 140 umt_enable(sc); 141 /* but mark the hidmt not in use */ 142 umt_disable(sc); 143 } 144 145 hidmt_attach(mt, &umt_accessops); 146} 147 148int 149umt_hidev_get_report(struct device *self, int type, int id, void *data, int len) 150{ 151 struct umt_softc *sc = (struct umt_softc *)self; 152 int ret; 153 154 ret = uhidev_get_report(sc->sc_hdev.sc_parent, type, id, data, len); 155 return (ret < len); 156} 157 158int 159umt_hidev_set_report(struct device *self, int type, int id, void *data, int len) 160{ 161 struct umt_softc *sc = (struct umt_softc *)self; 162 int ret; 163 164 ret = uhidev_set_report(sc->sc_hdev.sc_parent, type, id, data, len); 165 return (ret < len); 166} 167 168int 169umt_detach(struct device *self, int flags) 170{ 171 struct umt_softc *sc = (struct umt_softc *)self; 172 struct hidmt *mt = &sc->sc_mt; 173 174 return hidmt_detach(mt, flags); 175} 176 177void 178umt_intr(struct uhidev *dev, void *buf, u_int len) 179{ 180 struct umt_softc *sc = (struct umt_softc *)dev; 181 struct hidmt *mt = &sc->sc_mt; 182 183 if (!mt->sc_enabled) 184 return; 185 186 hidmt_input(mt, (uint8_t *)buf, len); 187} 188 189int 190umt_enable(void *v) 191{ 192 struct umt_softc *sc = v; 193 struct hidmt *mt = &sc->sc_mt; 194 int rv; 195 196 if ((rv = hidmt_enable(mt)) != 0) 197 return rv; 198 199 if ((sc->sc_quirks & UQ_ALWAYS_OPEN) && 200 (sc->sc_hdev.sc_state & UHIDEV_OPEN)) 201 rv = 0; 202 else 203 rv = uhidev_open(&sc->sc_hdev); 204 205 hidmt_set_input_mode(mt, HIDMT_INPUT_MODE_MT_TOUCHPAD); 206 207 return rv; 208} 209 210void 211umt_disable(void *v) 212{ 213 struct umt_softc *sc = v; 214 struct hidmt *mt = &sc->sc_mt; 215 216 hidmt_disable(mt); 217 218 if (sc->sc_quirks & UQ_ALWAYS_OPEN) 219 return; 220 221 uhidev_close(&sc->sc_hdev); 222} 223 224int 225umt_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 226{ 227 struct umt_softc *sc = v; 228 struct hidmt *mt = &sc->sc_mt; 229 int rc; 230 231 rc = uhidev_ioctl(&sc->sc_hdev, cmd, data, flag, p); 232 if (rc != -1) 233 return rc; 234 235 return hidmt_ioctl(mt, cmd, data, flag, p); 236}