jcs's openbsd hax
openbsd
at jcs 339 lines 9.6 kB view raw
1/* $OpenBSD: ums.c,v 1.54 2026/01/19 12:20:43 helg Exp $ */ 2/* $NetBSD: ums.c,v 1.60 2003/03/11 16:44:00 augustss Exp $ */ 3 4/* 5 * Copyright (c) 1998 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Lennart Augustsson (lennart@augustsson.net) at 10 * Carlstedt Research & Technology. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34/* 35 * HID spec: https://www.usb.org/sites/default/files/hid1_11.pdf 36 */ 37 38#include <sys/param.h> 39#include <sys/systm.h> 40#include <sys/device.h> 41 42#include <dev/usb/usb.h> 43#include <dev/usb/usbhid.h> 44 45#include <dev/usb/usbdi.h> 46#include <dev/usb/usbdi_util.h> 47#include <dev/usb/usbdevs.h> 48#include <dev/usb/usb_quirks.h> 49#include <dev/usb/uhidev.h> 50 51#include <dev/wscons/wsconsio.h> 52#include <dev/wscons/wsmousevar.h> 53 54#include <dev/hid/hidmsvar.h> 55 56struct ums_softc { 57 struct uhidev sc_hdev; 58 struct hidms sc_ms; 59 uint32_t sc_quirks; 60}; 61 62void ums_intr(struct uhidev *addr, void *ibuf, u_int len); 63 64int ums_enable(void *); 65void ums_disable(void *); 66int ums_ioctl(void *, u_long, caddr_t, int, struct proc *); 67void ums_fix_elecom_descriptor(struct ums_softc *, void *, int, int); 68 69const struct wsmouse_accessops ums_accessops = { 70 ums_enable, 71 ums_ioctl, 72 ums_disable, 73}; 74 75int ums_match(struct device *, void *, void *); 76void ums_attach(struct device *, struct device *, void *); 77int ums_detach(struct device *, int); 78 79struct cfdriver ums_cd = { 80 NULL, "ums", DV_DULL 81}; 82 83const struct cfattach ums_ca = { 84 sizeof(struct ums_softc), ums_match, ums_attach, ums_detach 85}; 86 87int 88ums_match(struct device *parent, void *match, void *aux) 89{ 90 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 91 int size; 92 u_int i; 93 void *desc; 94 95 if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)) { 96 /* 97 * The virtual USB digitizer exposed under Apple Virtualization 98 * uses several report IDs, but only the pointer report is 99 * relevant here. We still claim them all to prevent other 100 * drivers from binding. 101 */ 102 if (uha->uaa->vendor == USB_VENDOR_APPLE && uha->uaa->product == 103 USB_PRODUCT_APPLE_VIRTUAL_DIGITIZER) { 104 for (i = 0; i < uha->nreports; i++) 105 uha->claimed[i] = 1; 106 107 return (UMATCH_VENDOR_PRODUCT); 108 } 109 return (UMATCH_NONE); 110 } 111 112 uhidev_get_report_desc(uha->parent, &desc, &size); 113 114 if (hid_is_collection(desc, size, uha->reportid, 115 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_POINTER))) 116 return (UMATCH_IFACECLASS); 117 118 if (hid_is_collection(desc, size, uha->reportid, 119 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) 120 return (UMATCH_IFACECLASS); 121 122 if (hid_is_collection(desc, size, uha->reportid, 123 HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHSCREEN))) 124 return (UMATCH_IFACECLASS); 125 126 if (hid_is_collection(desc, size, uha->reportid, 127 HID_USAGE2(HUP_DIGITIZERS, HUD_PEN))) 128 return (UMATCH_IFACECLASS); 129 130 return (UMATCH_NONE); 131} 132 133void 134ums_attach(struct device *parent, struct device *self, void *aux) 135{ 136 struct ums_softc *sc = (struct ums_softc *)self; 137 struct hidms *ms = &sc->sc_ms; 138 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 139 struct usb_attach_arg *uaa = uha->uaa; 140 int size, repid; 141 void *desc; 142 u_int32_t qflags = 0; 143 144 sc->sc_hdev.sc_intr = ums_intr; 145 sc->sc_hdev.sc_parent = uha->parent; 146 sc->sc_hdev.sc_udev = uaa->device; 147 148 /* 149 * Use only the pointer report ID for the Apple Virtual USB Digitizer. 150 * The remaining report IDs are claimed but ignored. 151 */ 152 if (uaa->vendor == USB_VENDOR_APPLE && uaa->product == 153 USB_PRODUCT_APPLE_VIRTUAL_DIGITIZER) 154 sc->sc_hdev.sc_report_id = uha->reportid = 1; 155 else 156 sc->sc_hdev.sc_report_id = uha->reportid; 157 158 usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0); 159 160 sc->sc_quirks = usbd_get_quirks(sc->sc_hdev.sc_udev)->uq_flags; 161 uhidev_get_report_desc(uha->parent, &desc, &size); 162 163 if (uaa->vendor == USB_VENDOR_ELECOM) 164 ums_fix_elecom_descriptor(sc, desc, size, uaa->product); 165 166 repid = uha->reportid; 167 sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid); 168 sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid); 169 sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid); 170 171 if (sc->sc_quirks & UQ_MS_REVZ) 172 qflags |= HIDMS_REVZ; 173 if (sc->sc_quirks & UQ_SPUR_BUT_UP) 174 qflags |= HIDMS_SPUR_BUT_UP; 175 if (sc->sc_quirks & UQ_MS_BAD_CLASS) 176 qflags |= HIDMS_MS_BAD_CLASS; 177 if (sc->sc_quirks & UQ_MS_LEADING_BYTE) 178 qflags |= HIDMS_LEADINGBYTE; 179 if (sc->sc_quirks & UQ_MS_VENDOR_BUTTONS) 180 qflags |= HIDMS_VENDOR_BUTTONS; 181 182 if (hidms_setup(self, ms, qflags, uha->reportid, desc, size) != 0) 183 return; 184 185 /* 186 * The Microsoft Wireless Notebook Optical Mouse 3000 Model 1049 has 187 * five Report IDs: 19, 23, 24, 17, 18 (in the order they appear in 188 * report descriptor), it seems that report 17 contains the necessary 189 * mouse information (3-buttons, X, Y, wheel) so we specify it 190 * manually. 191 */ 192 if (uaa->vendor == USB_VENDOR_MICROSOFT && 193 uaa->product == USB_PRODUCT_MICROSOFT_WLNOTEBOOK3) { 194 ms->sc_flags = HIDMS_Z; 195 ms->sc_num_buttons = 3; 196 /* XXX change sc_hdev isize to 5? */ 197 ms->sc_loc_x.pos = 8; 198 ms->sc_loc_y.pos = 16; 199 ms->sc_loc_z.pos = 24; 200 ms->sc_loc_btn[0].pos = 0; 201 ms->sc_loc_btn[1].pos = 1; 202 ms->sc_loc_btn[2].pos = 2; 203 } 204 205 if (sc->sc_quirks & UQ_ALWAYS_OPEN) { 206 /* open uhidev and keep it open */ 207 ums_enable(sc); 208 /* but mark the hidms not in use */ 209 ums_disable(sc); 210 } 211 212 hidms_attach(ms, &ums_accessops); 213} 214 215int 216ums_detach(struct device *self, int flags) 217{ 218 struct ums_softc *sc = (struct ums_softc *)self; 219 struct hidms *ms = &sc->sc_ms; 220 221 return hidms_detach(ms, flags); 222} 223 224void 225ums_intr(struct uhidev *addr, void *buf, u_int len) 226{ 227 struct ums_softc *sc = (struct ums_softc *)addr; 228 struct hidms *ms = &sc->sc_ms; 229 230 if (ms->sc_enabled != 0) 231 hidms_input(ms, (uint8_t *)buf, len); 232} 233 234int 235ums_enable(void *v) 236{ 237 struct ums_softc *sc = v; 238 struct hidms *ms = &sc->sc_ms; 239 int rv; 240 241 if (usbd_is_dying(sc->sc_hdev.sc_udev)) 242 return EIO; 243 244 if ((rv = hidms_enable(ms)) != 0) 245 return rv; 246 247 if ((sc->sc_quirks & UQ_ALWAYS_OPEN) && 248 (sc->sc_hdev.sc_state & UHIDEV_OPEN)) 249 rv = 0; 250 else 251 rv = uhidev_open(&sc->sc_hdev); 252 253 return rv; 254} 255 256void 257ums_disable(void *v) 258{ 259 struct ums_softc *sc = v; 260 struct hidms *ms = &sc->sc_ms; 261 262 hidms_disable(ms); 263 264 if (sc->sc_quirks & UQ_ALWAYS_OPEN) 265 return; 266 267 uhidev_close(&sc->sc_hdev); 268} 269 270int 271ums_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 272{ 273 struct ums_softc *sc = v; 274 struct hidms *ms = &sc->sc_ms; 275 int rc; 276 277 rc = uhidev_ioctl(&sc->sc_hdev, cmd, data, flag, p); 278 if (rc != -1) 279 return rc; 280 rc = hidms_ioctl(ms, cmd, data, flag, p); 281 if (rc != -1) 282 return rc; 283 284 switch (cmd) { 285 case WSMOUSEIO_GTYPE: 286 *(u_int *)data = WSMOUSE_TYPE_USB; 287 return 0; 288 default: 289 return -1; 290 } 291} 292 293/* 294 * Some ELECOM devices present flawed report descriptors. Instead of a 5-bit 295 * field and a 3-bit padding as defined by the descriptor they actually use 296 * 6 or 8 Bits to report button states, see 297 * https://flameeyes.blog/2017/04/24/elecom-deft-and-the-broken-descriptor 298 * This function adapts the Report Count value for the button page, its Usage 299 * Maximum and the report size for the padding bits (at offset 31). 300 */ 301void 302ums_fix_elecom_descriptor(struct ums_softc *sc, void *desc, int size, 303 int product) 304{ 305 static uByte match[] = { /* a descriptor fragment, offset: 12 */ 306 0x95, 0x05, 0x75, 0x01, /* Report Count (5), Report Size (1) */ 307 0x05, 0x09, 0x19, 0x01, /* Usage Page (Button), Usage Minimum (1) */ 308 0x29, 0x05, /* Usage Maximum (5) */ 309 }; 310 uByte *udesc = desc; 311 int nbuttons; 312 313 switch (product) { 314 case USB_PRODUCT_ELECOM_MXT3URBK: /* EX-G Trackballs */ 315 case USB_PRODUCT_ELECOM_MXT3DRBK: 316 case USB_PRODUCT_ELECOM_MXT4DRBK: 317 nbuttons = 6; 318 break; 319 case USB_PRODUCT_ELECOM_MDT1URBK: /* DEFT Trackballs */ 320 case USB_PRODUCT_ELECOM_MDT1DRBK: 321 case USB_PRODUCT_ELECOM_MHT1URBK: /* HUGE Trackballs */ 322 case USB_PRODUCT_ELECOM_MHT1DRBK: 323 nbuttons = 8; 324 break; 325 default: 326 return; 327 } 328 329 if (udesc == NULL || size < 32 330 || memcmp(&udesc[12], match, sizeof(match)) 331 || udesc[30] != 0x75 || udesc[31] != 3) 332 return; 333 334 printf("%s: fixing Elecom report descriptor (buttons: %d)\n", 335 sc->sc_hdev.sc_dev.dv_xname, nbuttons); 336 udesc[13] = nbuttons; 337 udesc[21] = nbuttons; 338 udesc[31] = 8 - nbuttons; 339}