1 /* $NetBSD: ums.c,v 1.90 2016/04/27 19:35:17 jakllsch Exp $ */ 2 3 /* 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Lennart Augustsson (lennart@augustsson.net) at 9 * Carlstedt Research & Technology. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf 35 */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: ums.c,v 1.90 2016/04/27 19:35:17 jakllsch Exp $"); 39 40 #ifdef _KERNEL_OPT 41 #include "opt_usb.h" 42 #endif 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/kernel.h> 47 #include <sys/device.h> 48 #include <sys/ioctl.h> 49 #include <sys/file.h> 50 #include <sys/select.h> 51 #include <sys/proc.h> 52 #include <sys/vnode.h> 53 #include <sys/poll.h> 54 55 #include <dev/usb/usb.h> 56 #include <dev/usb/usbhid.h> 57 58 #include <dev/usb/usbdi.h> 59 #include <dev/usb/usbdi_util.h> 60 #include <dev/usb/usbdevs.h> 61 #include <dev/usb/usb_quirks.h> 62 #include <dev/usb/uhidev.h> 63 #include <dev/usb/hid.h> 64 65 #include <dev/wscons/wsconsio.h> 66 #include <dev/wscons/wsmousevar.h> 67 68 #ifdef UMS_DEBUG 69 #define DPRINTF(x) if (umsdebug) printf x 70 #define DPRINTFN(n,x) if (umsdebug>(n)) printf x 71 int umsdebug = 0; 72 #else 73 #define DPRINTF(x) 74 #define DPRINTFN(n,x) 75 #endif 76 77 #define UMS_BUT(i) ((i) == 1 || (i) == 2 ? 3 - (i) : i) 78 79 #define UMSUNIT(s) (minor(s)) 80 81 #define PS2LBUTMASK x01 82 #define PS2RBUTMASK x02 83 #define PS2MBUTMASK x04 84 #define PS2BUTMASK 0x0f 85 86 #define MAX_BUTTONS 31 /* must not exceed size of sc_buttons */ 87 88 struct ums_softc { 89 struct uhidev sc_hdev; 90 91 struct hid_location sc_loc_x, sc_loc_y, sc_loc_z, sc_loc_w; 92 struct hid_location sc_loc_btn[MAX_BUTTONS]; 93 94 int sc_enabled; 95 96 u_int flags; /* device configuration */ 97 #define UMS_Z 0x001 /* z direction available */ 98 #define UMS_SPUR_BUT_UP 0x002 /* spurious button up events */ 99 #define UMS_REVZ 0x004 /* Z-axis is reversed */ 100 #define UMS_W 0x008 /* w direction/tilt available */ 101 #define UMS_ABS 0x010 /* absolute position, touchpanel */ 102 #define UMS_TIP_SWITCH 0x020 /* digitizer tip switch */ 103 #define UMS_SEC_TIP_SWITCH 0x040 /* digitizer secondary tip switch */ 104 #define UMS_BARREL_SWITCH 0x080 /* digitizer barrel switch */ 105 #define UMS_ERASER 0x100 /* digitizer eraser */ 106 107 int nbuttons; 108 109 uint32_t sc_buttons; /* mouse button status */ 110 device_t sc_wsmousedev; 111 112 char sc_dying; 113 }; 114 115 static const struct { 116 u_int feature; 117 u_int flag; 118 } digbut[] = { 119 { HUD_TIP_SWITCH, UMS_TIP_SWITCH }, 120 { HUD_SEC_TIP_SWITCH, UMS_SEC_TIP_SWITCH }, 121 { HUD_BARREL_SWITCH, UMS_BARREL_SWITCH }, 122 { HUD_ERASER, UMS_ERASER }, 123 }; 124 125 #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) 126 127 Static void ums_intr(struct uhidev *, void *, u_int); 128 129 Static int ums_enable(void *); 130 Static void ums_disable(void *); 131 Static int ums_ioctl(void *, u_long, void *, int, struct lwp *); 132 133 const struct wsmouse_accessops ums_accessops = { 134 ums_enable, 135 ums_ioctl, 136 ums_disable, 137 }; 138 139 int ums_match(device_t, cfdata_t, void *); 140 void ums_attach(device_t, device_t, void *); 141 void ums_childdet(device_t, device_t); 142 int ums_detach(device_t, int); 143 int ums_activate(device_t, enum devact); 144 extern struct cfdriver ums_cd; 145 CFATTACH_DECL2_NEW(ums, sizeof(struct ums_softc), ums_match, ums_attach, 146 ums_detach, ums_activate, NULL, ums_childdet); 147 148 int 149 ums_match(device_t parent, cfdata_t match, void *aux) 150 { 151 struct uhidev_attach_arg *uha = aux; 152 int size; 153 void *desc; 154 155 /* 156 * Some (older) Griffin PowerMate knobs may masquerade as a 157 * mouse, avoid treating them as such, they have only one axis. 158 */ 159 if (uha->uiaa->uiaa_vendor == USB_VENDOR_GRIFFIN && 160 uha->uiaa->uiaa_product == USB_PRODUCT_GRIFFIN_POWERMATE) 161 return UMATCH_NONE; 162 163 uhidev_get_report_desc(uha->parent, &desc, &size); 164 if (!hid_is_collection(desc, size, uha->reportid, 165 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)) && 166 !hid_is_collection(desc, size, uha->reportid, 167 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_POINTER)) && 168 !hid_is_collection(desc, size, uha->reportid, 169 HID_USAGE2(HUP_DIGITIZERS, 0x0002))) 170 return UMATCH_NONE; 171 172 return UMATCH_IFACECLASS; 173 } 174 175 void 176 ums_attach(device_t parent, device_t self, void *aux) 177 { 178 struct ums_softc *sc = device_private(self); 179 struct uhidev_attach_arg *uha = aux; 180 struct wsmousedev_attach_args a; 181 int size; 182 void *desc; 183 uint32_t flags, quirks; 184 int i, hl; 185 struct hid_location *zloc; 186 bool isdigitizer; 187 188 aprint_naive("\n"); 189 190 sc->sc_hdev.sc_dev = self; 191 sc->sc_hdev.sc_intr = ums_intr; 192 sc->sc_hdev.sc_parent = uha->parent; 193 sc->sc_hdev.sc_report_id = uha->reportid; 194 195 quirks = usbd_get_quirks(uha->parent->sc_udev)->uq_flags; 196 if (quirks & UQ_MS_REVZ) 197 sc->flags |= UMS_REVZ; 198 if (quirks & UQ_SPUR_BUT_UP) 199 sc->flags |= UMS_SPUR_BUT_UP; 200 201 uhidev_get_report_desc(uha->parent, &desc, &size); 202 203 isdigitizer = hid_is_collection(desc, size, uha->reportid, 204 HID_USAGE2(HUP_DIGITIZERS, 0x0002)); 205 206 if (!pmf_device_register(self, NULL, NULL)) 207 aprint_error_dev(self, "couldn't establish power handler\n"); 208 209 if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), 210 uha->reportid, hid_input, &sc->sc_loc_x, &flags)) { 211 aprint_error("\n%s: mouse has no X report\n", 212 device_xname(sc->sc_hdev.sc_dev)); 213 return; 214 } 215 switch (flags & MOUSE_FLAGS_MASK) { 216 case 0: 217 sc->flags |= UMS_ABS; 218 break; 219 case HIO_RELATIVE: 220 break; 221 default: 222 aprint_error("\n%s: X report 0x%04x not supported\n", 223 device_xname(sc->sc_hdev.sc_dev), flags); 224 return; 225 } 226 227 if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), 228 uha->reportid, hid_input, &sc->sc_loc_y, &flags)) { 229 aprint_error("\n%s: mouse has no Y report\n", 230 device_xname(sc->sc_hdev.sc_dev)); 231 return; 232 } 233 switch (flags & MOUSE_FLAGS_MASK) { 234 case 0: 235 sc->flags |= UMS_ABS; 236 break; 237 case HIO_RELATIVE: 238 break; 239 default: 240 aprint_error("\n%s: Y report 0x%04x not supported\n", 241 device_xname(sc->sc_hdev.sc_dev), flags); 242 return; 243 } 244 245 /* Try the wheel first as the Z activator since it's tradition. */ 246 hl = hid_locate(desc, 247 size, 248 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), 249 uha->reportid, 250 hid_input, 251 &sc->sc_loc_z, 252 &flags); 253 254 zloc = &sc->sc_loc_z; 255 if (hl) { 256 if ((flags & MOUSE_FLAGS_MASK) != HIO_RELATIVE) { 257 aprint_verbose("\n%s: Wheel report 0x%04x not " 258 "supported\n", device_xname(sc->sc_hdev.sc_dev), 259 flags); 260 sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */ 261 } else { 262 sc->flags |= UMS_Z; 263 /* Wheels need the Z axis reversed. */ 264 sc->flags ^= UMS_REVZ; 265 /* Put Z on the W coordinate */ 266 zloc = &sc->sc_loc_w; 267 } 268 } 269 270 hl = hid_locate(desc, 271 size, 272 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), 273 uha->reportid, 274 hid_input, 275 zloc, 276 &flags); 277 278 /* 279 * The horizontal component of the scrollball can also be given by 280 * Application Control Pan in the Consumer page, so if we didnt see 281 * any Z then check that. 282 */ 283 if (!hl) { 284 hl = hid_locate(desc, 285 size, 286 HID_USAGE2(HUP_CONSUMER, HUC_AC_PAN), 287 uha->reportid, 288 hid_input, 289 zloc, 290 &flags); 291 } 292 293 if (hl) { 294 if ((flags & MOUSE_FLAGS_MASK) != HIO_RELATIVE) { 295 aprint_verbose("\n%s: Z report 0x%04x not supported\n", 296 device_xname(sc->sc_hdev.sc_dev), flags); 297 zloc->size = 0; /* Bad Z coord, ignore it */ 298 } else { 299 if (sc->flags & UMS_Z) 300 sc->flags |= UMS_W; 301 else 302 sc->flags |= UMS_Z; 303 } 304 } 305 306 if (uha->uiaa->uiaa_vendor == USB_VENDOR_MICROSOFT) { 307 int fixpos; 308 /* 309 * The Microsoft Wireless Laser Mouse 6000 v2.0 and the 310 * Microsoft Comfort Mouse 2.0 report a bad position for 311 * the wheel and wheel tilt controls -- should be in bytes 312 * 3 & 4 of the report. Fix this if necessary. 313 */ 314 switch (uha->uiaa->uiaa_product) { 315 case USB_PRODUCT_MICROSOFT_24GHZ_XCVR10: 316 case USB_PRODUCT_MICROSOFT_24GHZ_XCVR20: 317 fixpos = 24; 318 break; 319 case USB_PRODUCT_MICROSOFT_CM6000: 320 fixpos = 40; 321 break; 322 default: 323 fixpos = 0; 324 break; 325 } 326 if (fixpos) { 327 if ((sc->flags & UMS_Z) && sc->sc_loc_z.pos == 0) 328 sc->sc_loc_z.pos = fixpos; 329 if ((sc->flags & UMS_W) && sc->sc_loc_w.pos == 0) 330 sc->sc_loc_w.pos = sc->sc_loc_z.pos + 8; 331 } 332 } 333 334 /* figure out the number of buttons */ 335 for (i = 1; i <= MAX_BUTTONS; i++) 336 if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), 337 uha->reportid, hid_input, &sc->sc_loc_btn[i - 1], 0)) 338 break; 339 340 if (isdigitizer) { 341 for (size_t j = 0; j < __arraycount(digbut); j++) { 342 if (hid_locate(desc, size, HID_USAGE2(HUP_DIGITIZERS, 343 digbut[j].feature), uha->reportid, hid_input, 344 &sc->sc_loc_btn[i - 1], 0)) { 345 if (i <= MAX_BUTTONS) { 346 i++; 347 sc->flags |= digbut[j].flag; 348 } else 349 aprint_error_dev(self, 350 "ran out of buttons\n"); 351 } 352 } 353 } 354 sc->nbuttons = i - 1; 355 356 aprint_normal(": %d button%s%s%s%s%s%s%s%s%s\n", 357 sc->nbuttons, sc->nbuttons == 1 ? "" : "s", 358 sc->flags & UMS_W ? ", W" : "", 359 sc->flags & UMS_Z ? " and Z dir" : "", 360 sc->flags & UMS_W ? "s" : "", 361 isdigitizer ? " digitizer" : "", 362 sc->flags & UMS_TIP_SWITCH ? ", tip" : "", 363 sc->flags & UMS_SEC_TIP_SWITCH ? ", sec tip" : "", 364 sc->flags & UMS_BARREL_SWITCH ? ", barrel" : "", 365 sc->flags & UMS_ERASER ? ", eraser" : ""); 366 367 #ifdef UMS_DEBUG 368 DPRINTF(("ums_attach: sc=%p\n", sc)); 369 DPRINTF(("ums_attach: X\t%d/%d\n", 370 sc->sc_loc_x.pos, sc->sc_loc_x.size)); 371 DPRINTF(("ums_attach: Y\t%d/%d\n", 372 sc->sc_loc_y.pos, sc->sc_loc_y.size)); 373 if (sc->flags & UMS_Z) 374 DPRINTF(("ums_attach: Z\t%d/%d\n", 375 sc->sc_loc_z.pos, sc->sc_loc_z.size)); 376 if (sc->flags & UMS_W) 377 DPRINTF(("ums_attach: W\t%d/%d\n", 378 sc->sc_loc_w.pos, sc->sc_loc_w.size)); 379 for (i = 1; i <= sc->nbuttons; i++) { 380 DPRINTF(("ums_attach: B%d\t%d/%d\n", 381 i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size)); 382 } 383 #endif 384 385 a.accessops = &ums_accessops; 386 a.accesscookie = sc; 387 388 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); 389 390 return; 391 } 392 393 int 394 ums_activate(device_t self, enum devact act) 395 { 396 struct ums_softc *sc = device_private(self); 397 398 switch (act) { 399 case DVACT_DEACTIVATE: 400 sc->sc_dying = 1; 401 return 0; 402 default: 403 return EOPNOTSUPP; 404 } 405 } 406 407 void 408 ums_childdet(device_t self, device_t child) 409 { 410 struct ums_softc *sc = device_private(self); 411 412 KASSERT(sc->sc_wsmousedev == child); 413 sc->sc_wsmousedev = NULL; 414 } 415 416 int 417 ums_detach(device_t self, int flags) 418 { 419 struct ums_softc *sc = device_private(self); 420 int rv = 0; 421 422 DPRINTF(("ums_detach: sc=%p flags=%d\n", sc, flags)); 423 424 /* No need to do reference counting of ums, wsmouse has all the goo. */ 425 if (sc->sc_wsmousedev != NULL) 426 rv = config_detach(sc->sc_wsmousedev, flags); 427 428 pmf_device_deregister(self); 429 430 return rv; 431 } 432 433 void 434 ums_intr(struct uhidev *addr, void *ibuf, u_int len) 435 { 436 struct ums_softc *sc = (struct ums_softc *)addr; 437 int dx, dy, dz, dw; 438 uint32_t buttons = 0; 439 int i, flags, s; 440 441 DPRINTFN(5,("ums_intr: len=%d\n", len)); 442 443 flags = WSMOUSE_INPUT_DELTA; /* equals 0 */ 444 445 dx = hid_get_data(ibuf, &sc->sc_loc_x); 446 if (sc->flags & UMS_ABS) { 447 flags |= (WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y); 448 dy = hid_get_data(ibuf, &sc->sc_loc_y); 449 } else 450 dy = -hid_get_data(ibuf, &sc->sc_loc_y); 451 dz = hid_get_data(ibuf, &sc->sc_loc_z); 452 dw = hid_get_data(ibuf, &sc->sc_loc_w); 453 454 if (sc->flags & UMS_REVZ) 455 dz = -dz; 456 for (i = 0; i < sc->nbuttons; i++) 457 if (hid_get_data(ibuf, &sc->sc_loc_btn[i])) 458 buttons |= (1 << UMS_BUT(i)); 459 460 if (dx != 0 || dy != 0 || dz != 0 || dw != 0 || 461 buttons != sc->sc_buttons) { 462 DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d w:%d buttons:0x%x\n", 463 dx, dy, dz, dw, buttons)); 464 sc->sc_buttons = buttons; 465 if (sc->sc_wsmousedev != NULL) { 466 s = spltty(); 467 wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, dz, 468 dw, flags); 469 splx(s); 470 } 471 } 472 } 473 474 Static int 475 ums_enable(void *v) 476 { 477 struct ums_softc *sc = v; 478 int error; 479 480 DPRINTFN(1,("ums_enable: sc=%p\n", sc)); 481 482 if (sc->sc_dying) 483 return EIO; 484 485 if (sc->sc_enabled) 486 return EBUSY; 487 488 sc->sc_enabled = 1; 489 sc->sc_buttons = 0; 490 491 error = uhidev_open(&sc->sc_hdev); 492 if (error) 493 sc->sc_enabled = 0; 494 495 return error; 496 } 497 498 Static void 499 ums_disable(void *v) 500 { 501 struct ums_softc *sc = v; 502 503 DPRINTFN(1,("ums_disable: sc=%p\n", sc)); 504 #ifdef DIAGNOSTIC 505 if (!sc->sc_enabled) { 506 printf("ums_disable: not enabled\n"); 507 return; 508 } 509 #endif 510 511 if (sc->sc_enabled) { 512 sc->sc_enabled = 0; 513 uhidev_close(&sc->sc_hdev); 514 } 515 } 516 517 Static int 518 ums_ioctl(void *v, u_long cmd, void *data, int flag, 519 struct lwp * p) 520 521 { 522 struct ums_softc *sc = v; 523 524 switch (cmd) { 525 case WSMOUSEIO_GTYPE: 526 if (sc->flags & UMS_ABS) 527 *(u_int *)data = WSMOUSE_TYPE_TPANEL; 528 else 529 *(u_int *)data = WSMOUSE_TYPE_USB; 530 return 0; 531 } 532 533 return EPASSTHROUGH; 534 } 535