1 1.107 hans 2 1.108 hans /* $NetBSD: ums.c,v 1.108 2025/03/25 10:37:39 hans Exp $ */ 3 1.1 augustss 4 1.1 augustss /* 5 1.91 bouyer * Copyright (c) 1998, 2017 The NetBSD Foundation, Inc. 6 1.1 augustss * All rights reserved. 7 1.1 augustss * 8 1.11 augustss * This code is derived from software contributed to The NetBSD Foundation 9 1.43 augustss * by Lennart Augustsson (lennart (at) augustsson.net) at 10 1.11 augustss * Carlstedt Research & Technology. 11 1.1 augustss * 12 1.1 augustss * Redistribution and use in source and binary forms, with or without 13 1.1 augustss * modification, are permitted provided that the following conditions 14 1.1 augustss * are met: 15 1.1 augustss * 1. Redistributions of source code must retain the above copyright 16 1.1 augustss * notice, this list of conditions and the following disclaimer. 17 1.1 augustss * 2. Redistributions in binary form must reproduce the above copyright 18 1.1 augustss * notice, this list of conditions and the following disclaimer in the 19 1.1 augustss * documentation and/or other materials provided with the distribution. 20 1.1 augustss * 21 1.1 augustss * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 1.1 augustss * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 1.1 augustss * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 1.1 augustss * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 1.1 augustss * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 1.1 augustss * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 1.1 augustss * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 1.1 augustss * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 1.1 augustss * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 1.1 augustss * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 1.1 augustss * POSSIBILITY OF SUCH DAMAGE. 32 1.20 augustss */ 33 1.20 augustss 34 1.20 augustss /* 35 1.60 augustss * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf 36 1.1 augustss */ 37 1.52 lukem 38 1.52 lukem #include <sys/cdefs.h> 39 1.108 hans __KERNEL_RCSID(0, "$NetBSD: ums.c,v 1.108 2025/03/25 10:37:39 hans Exp $"); 40 1.90 jakllsch 41 1.90 jakllsch #ifdef _KERNEL_OPT 42 1.90 jakllsch #include "opt_usb.h" 43 1.90 jakllsch #endif 44 1.1 augustss 45 1.1 augustss #include <sys/param.h> 46 1.1 augustss #include <sys/systm.h> 47 1.1 augustss #include <sys/kernel.h> 48 1.1 augustss #include <sys/device.h> 49 1.1 augustss #include <sys/ioctl.h> 50 1.1 augustss #include <sys/file.h> 51 1.1 augustss #include <sys/select.h> 52 1.104 mrg #include <sys/sysctl.h> 53 1.1 augustss #include <sys/proc.h> 54 1.1 augustss #include <sys/vnode.h> 55 1.1 augustss #include <sys/poll.h> 56 1.1 augustss 57 1.1 augustss #include <dev/usb/usb.h> 58 1.1 augustss #include <dev/usb/usbhid.h> 59 1.1 augustss 60 1.1 augustss #include <dev/usb/usbdi.h> 61 1.1 augustss #include <dev/usb/usbdi_util.h> 62 1.1 augustss #include <dev/usb/usbdevs.h> 63 1.1 augustss #include <dev/usb/usb_quirks.h> 64 1.53 augustss #include <dev/usb/uhidev.h> 65 1.104 mrg #include <dev/usb/usbhist.h> 66 1.91 bouyer #include <dev/hid/hid.h> 67 1.91 bouyer #include <dev/hid/hidms.h> 68 1.3 augustss 69 1.104 mrg #ifdef USB_DEBUG 70 1.104 mrg #ifndef UMS_DEBUG 71 1.104 mrg #define umsdebug 0 72 1.1 augustss #else 73 1.104 mrg 74 1.104 mrg #ifndef UMS_DEBUG_DEFAULT 75 1.104 mrg #define UMS_DEBUG_DEFAULT 0 76 1.1 augustss #endif 77 1.1 augustss 78 1.104 mrg static int umsdebug = UMS_DEBUG_DEFAULT; 79 1.104 mrg 80 1.104 mrg SYSCTL_SETUP(sysctl_hw_ums_setup, "sysctl hw.ums setup") 81 1.104 mrg { 82 1.104 mrg int err; 83 1.104 mrg const struct sysctlnode *rnode; 84 1.104 mrg const struct sysctlnode *cnode; 85 1.104 mrg 86 1.104 mrg err = sysctl_createv(clog, 0, NULL, &rnode, 87 1.104 mrg CTLFLAG_PERMANENT, CTLTYPE_NODE, "ums", 88 1.104 mrg SYSCTL_DESCR("ums global controls"), 89 1.104 mrg NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL); 90 1.104 mrg 91 1.104 mrg if (err) 92 1.104 mrg goto fail; 93 1.104 mrg 94 1.104 mrg /* control debugging printfs */ 95 1.104 mrg err = sysctl_createv(clog, 0, &rnode, &cnode, 96 1.104 mrg CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, 97 1.104 mrg "debug", SYSCTL_DESCR("Enable debugging output"), 98 1.104 mrg NULL, 0, &umsdebug, sizeof(umsdebug), CTL_CREATE, CTL_EOL); 99 1.104 mrg if (err) 100 1.104 mrg goto fail; 101 1.104 mrg 102 1.104 mrg return; 103 1.104 mrg fail: 104 1.104 mrg aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err); 105 1.104 mrg } 106 1.104 mrg 107 1.104 mrg #endif /* UMS_DEBUG */ 108 1.104 mrg #endif /* USB_DEBUG */ 109 1.104 mrg 110 1.104 mrg #define DPRINTF(FMT,A,B,C,D) USBHIST_LOGN(umsdebug,1,FMT,A,B,C,D) 111 1.104 mrg #define DPRINTFN(N,FMT,A,B,C,D) USBHIST_LOGN(umsdebug,N,FMT,A,B,C,D) 112 1.104 mrg #define UMSHIST_FUNC() USBHIST_FUNC() 113 1.104 mrg #define UMSHIST_CALLED(name) USBHIST_CALLED(umsdebug) 114 1.104 mrg #define UMSHIST_CALLARGS(FMT,A,B,C,D) \ 115 1.104 mrg USBHIST_CALLARGS(umsdebug,FMT,A,B,C,D) 116 1.104 mrg #define UMSHIST_CALLARGSN(N,FMT,A,B,C,D) \ 117 1.104 mrg USBHIST_CALLARGSN(umsdebug,N,FMT,A,B,C,D) 118 1.104 mrg 119 1.27 augustss #define UMSUNIT(s) (minor(s)) 120 1.16 augustss 121 1.1 augustss struct ums_softc { 122 1.103 riastrad struct uhidev *sc_hdev; 123 1.102 riastrad struct usbd_device *sc_udev; 124 1.91 bouyer struct hidms sc_ms; 125 1.53 augustss 126 1.96 jmcneill bool sc_alwayson; 127 1.96 jmcneill 128 1.91 bouyer int sc_enabled; 129 1.91 bouyer char sc_dying; 130 1.84 christos }; 131 1.84 christos 132 1.103 riastrad Static void ums_intr(void *, void *, u_int); 133 1.1 augustss 134 1.44 augustss Static int ums_enable(void *); 135 1.44 augustss Static void ums_disable(void *); 136 1.89 skrll Static int ums_ioctl(void *, u_long, void *, int, struct lwp *); 137 1.3 augustss 138 1.95 maxv static const struct wsmouse_accessops ums_accessops = { 139 1.3 augustss ums_enable, 140 1.3 augustss ums_ioctl, 141 1.3 augustss ums_disable, 142 1.3 augustss }; 143 1.3 augustss 144 1.95 maxv static int ums_match(device_t, cfdata_t, void *); 145 1.95 maxv static void ums_attach(device_t, device_t, void *); 146 1.95 maxv static void ums_childdet(device_t, device_t); 147 1.95 maxv static int ums_detach(device_t, int); 148 1.95 maxv static int ums_activate(device_t, enum devact); 149 1.93 mrg 150 1.73 cube CFATTACH_DECL2_NEW(ums, sizeof(struct ums_softc), ums_match, ums_attach, 151 1.71 dyoung ums_detach, ums_activate, NULL, ums_childdet); 152 1.1 augustss 153 1.95 maxv static int 154 1.73 cube ums_match(device_t parent, cfdata_t match, void *aux) 155 1.1 augustss { 156 1.53 augustss struct uhidev_attach_arg *uha = aux; 157 1.53 augustss int size; 158 1.1 augustss void *desc; 159 1.57 augustss 160 1.79 jakllsch /* 161 1.79 jakllsch * Some (older) Griffin PowerMate knobs may masquerade as a 162 1.79 jakllsch * mouse, avoid treating them as such, they have only one axis. 163 1.79 jakllsch */ 164 1.89 skrll if (uha->uiaa->uiaa_vendor == USB_VENDOR_GRIFFIN && 165 1.89 skrll uha->uiaa->uiaa_product == USB_PRODUCT_GRIFFIN_POWERMATE) 166 1.89 skrll return UMATCH_NONE; 167 1.79 jakllsch 168 1.53 augustss uhidev_get_report_desc(uha->parent, &desc, &size); 169 1.53 augustss if (!hid_is_collection(desc, size, uha->reportid, 170 1.78 jakllsch HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)) && 171 1.78 jakllsch !hid_is_collection(desc, size, uha->reportid, 172 1.84 christos HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_POINTER)) && 173 1.84 christos !hid_is_collection(desc, size, uha->reportid, 174 1.106 jakllsch HID_USAGE2(HUP_DIGITIZERS, HUD_PEN))) 175 1.89 skrll return UMATCH_NONE; 176 1.1 augustss 177 1.89 skrll return UMATCH_IFACECLASS; 178 1.1 augustss } 179 1.1 augustss 180 1.95 maxv static void 181 1.73 cube ums_attach(device_t parent, device_t self, void *aux) 182 1.1 augustss { 183 1.73 cube struct ums_softc *sc = device_private(self); 184 1.53 augustss struct uhidev_attach_arg *uha = aux; 185 1.99 jmcneill struct hid_data *d; 186 1.99 jmcneill struct hid_item item; 187 1.96 jmcneill int size, error; 188 1.1 augustss void *desc; 189 1.91 bouyer uint32_t quirks; 190 1.1 augustss 191 1.69 jmcneill aprint_naive("\n"); 192 1.69 jmcneill 193 1.103 riastrad sc->sc_hdev = uha->parent; 194 1.102 riastrad sc->sc_udev = uha->uiaa->uiaa_device; 195 1.1 augustss 196 1.102 riastrad quirks = usbd_get_quirks(sc->sc_udev)->uq_flags; 197 1.35 augustss if (quirks & UQ_MS_REVZ) 198 1.91 bouyer sc->sc_ms.flags |= HIDMS_REVZ; 199 1.35 augustss if (quirks & UQ_SPUR_BUT_UP) 200 1.91 bouyer sc->sc_ms.flags |= HIDMS_SPUR_BUT_UP; 201 1.101 macallan if (quirks & UQ_ALWAYS_ON) 202 1.101 macallan sc->sc_alwayson = true; 203 1.84 christos 204 1.70 jmcneill if (!pmf_device_register(self, NULL, NULL)) 205 1.70 jmcneill aprint_error_dev(self, "couldn't establish power handler\n"); 206 1.70 jmcneill 207 1.91 bouyer uhidev_get_report_desc(uha->parent, &desc, &size); 208 1.10 augustss 209 1.91 bouyer if (!hidms_setup(self, &sc->sc_ms, uha->reportid, desc, size)) 210 1.81 dyoung return; 211 1.6 augustss 212 1.89 skrll if (uha->uiaa->uiaa_vendor == USB_VENDOR_MICROSOFT) { 213 1.88 christos int fixpos; 214 1.98 jdolecek int woffset = 8; 215 1.88 christos /* 216 1.88 christos * The Microsoft Wireless Laser Mouse 6000 v2.0 and the 217 1.88 christos * Microsoft Comfort Mouse 2.0 report a bad position for 218 1.88 christos * the wheel and wheel tilt controls -- should be in bytes 219 1.88 christos * 3 & 4 of the report. Fix this if necessary. 220 1.88 christos */ 221 1.89 skrll switch (uha->uiaa->uiaa_product) { 222 1.88 christos case USB_PRODUCT_MICROSOFT_24GHZ_XCVR10: 223 1.88 christos case USB_PRODUCT_MICROSOFT_24GHZ_XCVR20: 224 1.92 maya case USB_PRODUCT_MICROSOFT_NATURAL_6000: 225 1.88 christos fixpos = 24; 226 1.88 christos break; 227 1.98 jdolecek case USB_PRODUCT_MICROSOFT_24GHZ_XCVR80: 228 1.105 hgutch case USB_PRODUCT_MICROSOFT_24GHZ_XCVR90: 229 1.98 jdolecek fixpos = 40; 230 1.98 jdolecek woffset = sc->sc_ms.hidms_loc_z.size; 231 1.98 jdolecek break; 232 1.88 christos case USB_PRODUCT_MICROSOFT_CM6000: 233 1.88 christos fixpos = 40; 234 1.88 christos break; 235 1.88 christos default: 236 1.88 christos fixpos = 0; 237 1.88 christos break; 238 1.88 christos } 239 1.88 christos if (fixpos) { 240 1.91 bouyer if ((sc->sc_ms.flags & HIDMS_Z) && 241 1.91 bouyer sc->sc_ms.hidms_loc_z.pos == 0) 242 1.91 bouyer sc->sc_ms.hidms_loc_z.pos = fixpos; 243 1.91 bouyer if ((sc->sc_ms.flags & HIDMS_W) && 244 1.91 bouyer sc->sc_ms.hidms_loc_w.pos == 0) 245 1.91 bouyer sc->sc_ms.hidms_loc_w.pos = 246 1.98 jdolecek sc->sc_ms.hidms_loc_z.pos + woffset; 247 1.88 christos } 248 1.75 rafal } 249 1.63 augustss 250 1.94 yhardy tpcalib_init(&sc->sc_ms.sc_tpcalib); 251 1.99 jmcneill 252 1.99 jmcneill /* calibrate the pointer if it reports absolute events */ 253 1.99 jmcneill if (sc->sc_ms.flags & HIDMS_ABS) { 254 1.99 jmcneill memset(&sc->sc_ms.sc_calibcoords, 0, sizeof(sc->sc_ms.sc_calibcoords)); 255 1.99 jmcneill sc->sc_ms.sc_calibcoords.maxx = 0; 256 1.99 jmcneill sc->sc_ms.sc_calibcoords.maxy = 0; 257 1.99 jmcneill sc->sc_ms.sc_calibcoords.samplelen = WSMOUSE_CALIBCOORDS_RESET; 258 1.99 jmcneill d = hid_start_parse(desc, size, hid_input); 259 1.99 jmcneill if (d != NULL) { 260 1.99 jmcneill while (hid_get_item(d, &item)) { 261 1.99 jmcneill if (item.kind != hid_input 262 1.99 jmcneill || HID_GET_USAGE_PAGE(item.usage) != HUP_GENERIC_DESKTOP 263 1.103 riastrad || item.report_ID != uha->reportid) 264 1.99 jmcneill continue; 265 1.99 jmcneill if (HID_GET_USAGE(item.usage) == HUG_X) { 266 1.99 jmcneill sc->sc_ms.sc_calibcoords.minx = item.logical_minimum; 267 1.99 jmcneill sc->sc_ms.sc_calibcoords.maxx = item.logical_maximum; 268 1.99 jmcneill } 269 1.99 jmcneill if (HID_GET_USAGE(item.usage) == HUG_Y) { 270 1.99 jmcneill sc->sc_ms.sc_calibcoords.miny = item.logical_minimum; 271 1.99 jmcneill sc->sc_ms.sc_calibcoords.maxy = item.logical_maximum; 272 1.99 jmcneill } 273 1.99 jmcneill } 274 1.99 jmcneill hid_end_parse(d); 275 1.99 jmcneill } 276 1.99 jmcneill tpcalib_ioctl(&sc->sc_ms.sc_tpcalib, WSMOUSEIO_SCALIBCOORDS, 277 1.99 jmcneill (void *)&sc->sc_ms.sc_calibcoords, 0, 0); 278 1.99 jmcneill } 279 1.99 jmcneill 280 1.91 bouyer hidms_attach(self, &sc->sc_ms, &ums_accessops); 281 1.96 jmcneill 282 1.96 jmcneill if (sc->sc_alwayson) { 283 1.103 riastrad error = uhidev_open(sc->sc_hdev, &ums_intr, sc); 284 1.96 jmcneill if (error != 0) { 285 1.96 jmcneill aprint_error_dev(self, 286 1.96 jmcneill "WARNING: couldn't open always-on device\n"); 287 1.96 jmcneill sc->sc_alwayson = false; 288 1.96 jmcneill } 289 1.96 jmcneill } 290 1.16 augustss } 291 1.16 augustss 292 1.95 maxv static int 293 1.81 dyoung ums_activate(device_t self, enum devact act) 294 1.16 augustss { 295 1.73 cube struct ums_softc *sc = device_private(self); 296 1.28 augustss 297 1.28 augustss switch (act) { 298 1.28 augustss case DVACT_DEACTIVATE: 299 1.28 augustss sc->sc_dying = 1; 300 1.76 dyoung return 0; 301 1.76 dyoung default: 302 1.76 dyoung return EOPNOTSUPP; 303 1.28 augustss } 304 1.1 augustss } 305 1.1 augustss 306 1.95 maxv static void 307 1.71 dyoung ums_childdet(device_t self, device_t child) 308 1.71 dyoung { 309 1.71 dyoung struct ums_softc *sc = device_private(self); 310 1.71 dyoung 311 1.91 bouyer KASSERT(sc->sc_ms.hidms_wsmousedev == child); 312 1.91 bouyer sc->sc_ms.hidms_wsmousedev = NULL; 313 1.71 dyoung } 314 1.71 dyoung 315 1.95 maxv static int 316 1.71 dyoung ums_detach(device_t self, int flags) 317 1.1 augustss { 318 1.71 dyoung struct ums_softc *sc = device_private(self); 319 1.25 augustss int rv = 0; 320 1.8 augustss 321 1.104 mrg UMSHIST_FUNC(); 322 1.104 mrg UMSHIST_CALLARGS("ums_detach: sc=%qd flags=%qd\n", 323 1.104 mrg (uintptr_t)sc, flags, 0, 0); 324 1.33 augustss 325 1.96 jmcneill if (sc->sc_alwayson) 326 1.103 riastrad uhidev_close(sc->sc_hdev); 327 1.96 jmcneill 328 1.51 augustss /* No need to do reference counting of ums, wsmouse has all the goo. */ 329 1.91 bouyer if (sc->sc_ms.hidms_wsmousedev != NULL) 330 1.91 bouyer rv = config_detach(sc->sc_ms.hidms_wsmousedev, flags); 331 1.40 augustss 332 1.70 jmcneill pmf_device_deregister(self); 333 1.70 jmcneill 334 1.89 skrll return rv; 335 1.1 augustss } 336 1.1 augustss 337 1.95 maxv Static void 338 1.103 riastrad ums_intr(void *cookie, void *ibuf, u_int len) 339 1.1 augustss { 340 1.103 riastrad struct ums_softc *sc = cookie; 341 1.96 jmcneill 342 1.96 jmcneill if (sc->sc_enabled) 343 1.96 jmcneill hidms_intr(&sc->sc_ms, ibuf, len); 344 1.1 augustss } 345 1.3 augustss 346 1.42 augustss Static int 347 1.44 augustss ums_enable(void *v) 348 1.3 augustss { 349 1.3 augustss struct ums_softc *sc = v; 350 1.96 jmcneill int error = 0; 351 1.16 augustss 352 1.104 mrg UMSHIST_FUNC(); UMSHIST_CALLARGS("sc=%jx\n", (uintptr_t)sc, 0, 0, 0); 353 1.28 augustss 354 1.28 augustss if (sc->sc_dying) 355 1.89 skrll return EIO; 356 1.28 augustss 357 1.3 augustss if (sc->sc_enabled) 358 1.89 skrll return EBUSY; 359 1.3 augustss 360 1.3 augustss sc->sc_enabled = 1; 361 1.91 bouyer sc->sc_ms.hidms_buttons = 0; 362 1.3 augustss 363 1.96 jmcneill if (!sc->sc_alwayson) { 364 1.103 riastrad error = uhidev_open(sc->sc_hdev, &ums_intr, sc); 365 1.96 jmcneill if (error) 366 1.96 jmcneill sc->sc_enabled = 0; 367 1.96 jmcneill } 368 1.87 mlelstv 369 1.87 mlelstv return error; 370 1.3 augustss } 371 1.3 augustss 372 1.42 augustss Static void 373 1.44 augustss ums_disable(void *v) 374 1.3 augustss { 375 1.3 augustss struct ums_softc *sc = v; 376 1.25 augustss 377 1.104 mrg UMSHIST_FUNC(); UMSHIST_CALLARGS("sc=%jx\n", (uintptr_t)sc, 0, 0, 0); 378 1.104 mrg 379 1.107 hans if (!sc->sc_enabled) { 380 1.25 augustss #ifdef DIAGNOSTIC 381 1.25 augustss printf("ums_disable: not enabled\n"); 382 1.107 hans #endif 383 1.25 augustss return; 384 1.25 augustss } 385 1.3 augustss 386 1.108 hans sc->sc_enabled = 0; 387 1.108 hans if (!sc->sc_alwayson) 388 1.108 hans uhidev_close(sc->sc_hdev); 389 1.3 augustss } 390 1.3 augustss 391 1.42 augustss Static int 392 1.68 christos ums_ioctl(void *v, u_long cmd, void *data, int flag, 393 1.99 jmcneill struct lwp *l) 394 1.16 augustss 395 1.3 augustss { 396 1.77 mbalmer struct ums_softc *sc = v; 397 1.99 jmcneill int error; 398 1.99 jmcneill 399 1.99 jmcneill if (sc->sc_ms.flags & HIDMS_ABS) { 400 1.99 jmcneill error = tpcalib_ioctl(&sc->sc_ms.sc_tpcalib, cmd, data, 401 1.99 jmcneill flag, l); 402 1.99 jmcneill if (error != EPASSTHROUGH) 403 1.99 jmcneill return error; 404 1.99 jmcneill } 405 1.77 mbalmer 406 1.3 augustss switch (cmd) { 407 1.3 augustss case WSMOUSEIO_GTYPE: 408 1.91 bouyer if (sc->sc_ms.flags & HIDMS_ABS) 409 1.77 mbalmer *(u_int *)data = WSMOUSE_TYPE_TPANEL; 410 1.77 mbalmer else 411 1.77 mbalmer *(u_int *)data = WSMOUSE_TYPE_USB; 412 1.89 skrll return 0; 413 1.3 augustss } 414 1.16 augustss 415 1.89 skrll return EPASSTHROUGH; 416 1.3 augustss } 417