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