1 /* $OpenBSD: hidms.c,v 1.10 2023/08/12 20:47:06 miod 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: http://www.usb.org/developers/devclass_docs/HID1_11.pdf 36 */ 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/kernel.h> 41 #include <sys/device.h> 42 #include <sys/ioctl.h> 43 44 #include <dev/wscons/wsconsio.h> 45 #include <dev/wscons/wsmousevar.h> 46 47 #include <dev/hid/hid.h> 48 #include <dev/hid/hidmsvar.h> 49 50 #ifdef HIDMS_DEBUG 51 #define DPRINTF(x) do { if (hidmsdebug) printf x; } while (0) 52 #define DPRINTFN(n,x) do { if (hidmsdebug>(n)) printf x; } while (0) 53 int hidmsdebug = 0; 54 #else 55 #define DPRINTF(x) 56 #define DPRINTFN(n,x) 57 #endif 58 59 #define HIDMS_BUT(i) ((i) == 1 || (i) == 2 ? 3 - (i) : i) 60 61 #define MOUSE_FLAGS_MASK (HIO_CONST | HIO_RELATIVE) 62 #define NOTMOUSE(f) (((f) & MOUSE_FLAGS_MASK) != HIO_RELATIVE) 63 64 void 65 hidms_stylus_hid_parse(struct hidms *ms, struct hid_data *d, 66 struct hid_location *loc_stylus_btn) 67 { 68 struct hid_item h; 69 70 while (hid_get_item(d, &h)) { 71 if (h.kind == hid_endcollection) 72 break; 73 if (h.kind != hid_input || (h.flags & HIO_CONST) != 0) 74 continue; 75 /* All the possible stylus reported usages go here */ 76 #ifdef HIDMS_DEBUG 77 printf("stylus usage: 0x%x\n", h.usage); 78 #endif 79 switch (h.usage) { 80 /* Buttons */ 81 case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_TIP_SWITCH): 82 DPRINTF("Stylus usage tip set\n"); 83 if (ms->sc_num_stylus_buttons >= MAX_BUTTONS) 84 break; 85 loc_stylus_btn[ms->sc_num_stylus_buttons++] = h.loc; 86 ms->sc_flags |= HIDMS_TIP; 87 break; 88 case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_BARREL_SWITCH): 89 DPRINTF("Stylus usage barrel set\n"); 90 if (ms->sc_num_stylus_buttons >= MAX_BUTTONS) 91 break; 92 loc_stylus_btn[ms->sc_num_stylus_buttons++] = h.loc; 93 ms->sc_flags |= HIDMS_BARREL; 94 break; 95 case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, 96 HUD_SECONDARY_BARREL_SWITCH): 97 DPRINTF("Stylus usage secondary barrel set\n"); 98 if (ms->sc_num_stylus_buttons >= MAX_BUTTONS) 99 break; 100 loc_stylus_btn[ms->sc_num_stylus_buttons++] = h.loc; 101 ms->sc_flags |= HIDMS_SEC_BARREL; 102 break; 103 case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_IN_RANGE): 104 DPRINTF("Stylus usage in range set\n"); 105 if (ms->sc_num_stylus_buttons >= MAX_BUTTONS) 106 break; 107 loc_stylus_btn[ms->sc_num_stylus_buttons++] = h.loc; 108 break; 109 case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_QUALITY): 110 DPRINTF("Stylus usage quality set\n"); 111 if (ms->sc_num_stylus_buttons >= MAX_BUTTONS) 112 break; 113 loc_stylus_btn[ms->sc_num_stylus_buttons++] = h.loc; 114 break; 115 /* Axes */ 116 case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_WACOM_X): 117 DPRINTF("Stylus usage x set\n"); 118 ms->sc_loc_x = h.loc; 119 ms->sc_tsscale.minx = h.logical_minimum; 120 ms->sc_tsscale.maxx = h.logical_maximum; 121 ms->sc_flags |= HIDMS_ABSX; 122 break; 123 case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_WACOM_Y): 124 DPRINTF("Stylus usage y set\n"); 125 ms->sc_loc_y = h.loc; 126 ms->sc_tsscale.miny = h.logical_minimum; 127 ms->sc_tsscale.maxy = h.logical_maximum; 128 ms->sc_flags |= HIDMS_ABSY; 129 break; 130 case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_TIP_PRESSURE): 131 DPRINTF("Stylus usage pressure set\n"); 132 ms->sc_loc_z = h.loc; 133 ms->sc_tsscale.minz = h.logical_minimum; 134 ms->sc_tsscale.maxz = h.logical_maximum; 135 ms->sc_flags |= HIDMS_Z; 136 break; 137 case HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_WACOM_DISTANCE): 138 DPRINTF("Stylus usage distance set\n"); 139 ms->sc_loc_w = h.loc; 140 ms->sc_tsscale.minw = h.logical_minimum; 141 ms->sc_tsscale.maxw = h.logical_maximum; 142 ms->sc_flags |= HIDMS_W; 143 break; 144 default: 145 #ifdef HIDMS_DEBUG 146 printf("Unknown stylus usage: 0x%x\n", 147 h.usage); 148 #endif 149 break; 150 } 151 } 152 } 153 154 void 155 hidms_pad_buttons_hid_parse(struct hidms *ms, struct hid_data *d, 156 struct hid_location *loc_pad_btn) 157 { 158 struct hid_item h; 159 160 while (hid_get_item(d, &h)) { 161 if (h.kind == hid_endcollection) 162 break; 163 if (h.kind == hid_input && (h.flags & HIO_CONST) != 0 && 164 h.usage == HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, 165 HUD_WACOM_PAD_BUTTONS00 | ms->sc_num_pad_buttons)) { 166 if (ms->sc_num_pad_buttons >= MAX_BUTTONS) 167 break; 168 loc_pad_btn[ms->sc_num_pad_buttons++] = h.loc; 169 } 170 } 171 } 172 173 int 174 hidms_wacom_setup(struct device *self, struct hidms *ms, void *desc, int dlen) 175 { 176 struct hid_data *hd; 177 int i; 178 struct hid_location loc_pad_btn[MAX_BUTTONS]; 179 struct hid_location loc_stylus_btn[MAX_BUTTONS]; 180 181 ms->sc_flags = 0; 182 183 /* Set x,y,z and w to zero by default */ 184 ms->sc_loc_x.size = 0; 185 ms->sc_loc_y.size = 0; 186 ms->sc_loc_z.size = 0; 187 ms->sc_loc_w.size = 0; 188 189 if ((hd = hid_get_collection_data(desc, dlen, 190 HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_DIGITIZER), 191 HCOLL_APPLICATION))) { 192 DPRINTF("found the global collection\n"); 193 hid_end_parse(hd); 194 if ((hd = hid_get_collection_data(desc, dlen, 195 HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_STYLUS), 196 HCOLL_PHYSICAL))) { 197 DPRINTF("found stylus collection\n"); 198 hidms_stylus_hid_parse(ms, hd, loc_stylus_btn); 199 hid_end_parse(hd); 200 } 201 if ((hd = hid_get_collection_data(desc, dlen, 202 HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_TABLET_FKEYS), 203 HCOLL_PHYSICAL))) { 204 DPRINTF("found tablet keys collection\n"); 205 hidms_pad_buttons_hid_parse(ms, hd, loc_pad_btn); 206 hid_end_parse(hd); 207 } 208 #ifdef notyet 209 if ((hd = hid_get_collection_data(desc, dlen, 210 HID_USAGE2(HUP_WACOM | HUP_DIGITIZERS, HUD_WACOM_BATTERY), 211 HCOLL_PHYSICAL))) { 212 DPRINTF("found battery collection\n"); 213 /* parse and set the battery info */ 214 /* not yet used */ 215 hid_end_parse(hd); 216 } 217 #endif 218 /* 219 * Ignore the device config, it's not really needed; 220 * Ignore the usage 0x10AC which is the debug collection, and 221 * ignore firmware collection and other collections for now. 222 */ 223 } 224 225 /* Map the pad and stylus buttons to mouse buttons */ 226 for (i = 0; i < ms->sc_num_stylus_buttons; i++) 227 memcpy(&ms->sc_loc_btn[i], &loc_stylus_btn[i], 228 sizeof(struct hid_location)); 229 if (ms->sc_num_pad_buttons + ms->sc_num_stylus_buttons >= MAX_BUTTONS) 230 ms->sc_num_pad_buttons = 231 MAX_BUTTONS - ms->sc_num_stylus_buttons; 232 for (; i < ms->sc_num_pad_buttons + ms->sc_num_stylus_buttons; i++) 233 memcpy(&ms->sc_loc_btn[i], &loc_pad_btn[i], 234 sizeof(struct hid_location)); 235 ms->sc_num_buttons = i; 236 DPRINTF("Button information\n"); 237 #ifdef HIDMS_DEBUG 238 for (i = 0; i < ms->sc_num_buttons; i++) 239 printf("size: 0x%x, pos: 0x%x, count: 0x%x\n", 240 ms->sc_loc_btn[i].size, ms->sc_loc_btn[i].pos, 241 ms->sc_loc_btn[i].count); 242 #endif 243 return 0; 244 } 245 246 int 247 hidms_setup(struct device *self, struct hidms *ms, uint32_t quirks, 248 int id, void *desc, int dlen) 249 { 250 struct hid_item h; 251 struct hid_data *d; 252 uint32_t flags; 253 int i, wheel, twheel; 254 255 ms->sc_device = self; 256 ms->sc_rawmode = 1; 257 258 ms->sc_flags = quirks; 259 260 /* We are setting up a Wacom tablet, not a regular mouse */ 261 if (quirks & HIDMS_WACOM_SETUP) 262 return hidms_wacom_setup(self, ms, desc, dlen); 263 264 if (!hid_locate(desc, dlen, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), id, 265 hid_input, &ms->sc_loc_x, &flags)) 266 ms->sc_loc_x.size = 0; 267 268 switch (flags & MOUSE_FLAGS_MASK) { 269 case 0: 270 ms->sc_flags |= HIDMS_ABSX; 271 break; 272 case HIO_RELATIVE: 273 break; 274 default: 275 printf("\n%s: X report 0x%04x not supported\n", 276 self->dv_xname, flags); 277 return ENXIO; 278 } 279 280 if (!hid_locate(desc, dlen, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), id, 281 hid_input, &ms->sc_loc_y, &flags)) 282 ms->sc_loc_y.size = 0; 283 284 switch(flags & MOUSE_FLAGS_MASK) { 285 case 0: 286 ms->sc_flags |= HIDMS_ABSY; 287 break; 288 case HIO_RELATIVE: 289 break; 290 default: 291 printf("\n%s: Y report 0x%04x not supported\n", 292 self->dv_xname, flags); 293 return ENXIO; 294 } 295 296 /* 297 * Try to guess the Z activator: check WHEEL, TWHEEL, and Z, 298 * in that order. 299 */ 300 301 wheel = hid_locate(desc, dlen, 302 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), id, 303 hid_input, &ms->sc_loc_z, &flags); 304 if (wheel == 0) 305 twheel = hid_locate(desc, dlen, 306 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL), id, 307 hid_input, &ms->sc_loc_z, &flags); 308 else 309 twheel = 0; 310 311 if (wheel || twheel) { 312 if (NOTMOUSE(flags)) { 313 DPRINTF(("\n%s: Wheel report 0x%04x not supported\n", 314 self->dv_xname, flags)); 315 ms->sc_loc_z.size = 0; /* Bad Z coord, ignore it */ 316 } else { 317 ms->sc_flags |= HIDMS_Z; 318 /* Wheels need the Z axis reversed. */ 319 ms->sc_flags ^= HIDMS_REVZ; 320 } 321 /* 322 * We might have both a wheel and Z direction; in this case, 323 * report the Z direction on the W axis. 324 * 325 * Otherwise, check for a W direction as an AC Pan input used 326 * on some newer mice. 327 */ 328 if (hid_locate(desc, dlen, 329 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), id, 330 hid_input, &ms->sc_loc_w, &flags)) { 331 if (NOTMOUSE(flags)) { 332 DPRINTF(("\n%s: Z report 0x%04x not supported\n", 333 self->dv_xname, flags)); 334 /* Bad Z coord, ignore it */ 335 ms->sc_loc_w.size = 0; 336 } 337 else 338 ms->sc_flags |= HIDMS_W; 339 } else if (hid_locate(desc, dlen, 340 HID_USAGE2(HUP_CONSUMER, HUC_AC_PAN), id, hid_input, 341 &ms->sc_loc_w, &flags)) { 342 ms->sc_flags |= HIDMS_W; 343 } 344 } else if (hid_locate(desc, dlen, 345 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), id, 346 hid_input, &ms->sc_loc_z, &flags)) { 347 if (NOTMOUSE(flags)) { 348 DPRINTF(("\n%s: Z report 0x%04x not supported\n", 349 self->dv_xname, flags)); 350 ms->sc_loc_z.size = 0; /* Bad Z coord, ignore it */ 351 } else { 352 ms->sc_flags |= HIDMS_Z; 353 } 354 } 355 356 /* 357 * The Microsoft Wireless Intellimouse 2.0 reports its wheel 358 * using 0x0048 (I've called it HUG_TWHEEL) and seems to expect 359 * us to know that the byte after the wheel is the tilt axis. 360 * There are no other HID axis descriptors other than X, Y and 361 * TWHEEL, so we report TWHEEL on the W axis. 362 */ 363 if (twheel) { 364 ms->sc_loc_w = ms->sc_loc_z; 365 ms->sc_loc_w.pos = ms->sc_loc_w.pos + 8; 366 ms->sc_flags |= HIDMS_W | HIDMS_LEADINGBYTE; 367 /* Wheels need their axis reversed. */ 368 ms->sc_flags ^= HIDMS_REVW; 369 } 370 371 /* figure out the number of buttons */ 372 for (i = 1; i <= MAX_BUTTONS; i++) 373 if (!hid_locate(desc, dlen, HID_USAGE2(HUP_BUTTON, i), id, 374 hid_input, &ms->sc_loc_btn[i - 1], NULL)) 375 break; 376 ms->sc_num_buttons = i - 1; 377 378 /* 379 * The Kensington Slimblade reports some of its buttons as binary 380 * inputs in the first vendor usage page (0xff00). Add such inputs 381 * as buttons if the device has this quirk. 382 */ 383 if (ms->sc_flags & HIDMS_VENDOR_BUTTONS) { 384 for (i = 1; ms->sc_num_buttons < MAX_BUTTONS; i++) { 385 if (!hid_locate(desc, dlen, 386 HID_USAGE2(HUP_MICROSOFT, i), id, hid_input, 387 &ms->sc_loc_btn[ms->sc_num_buttons], NULL)) 388 break; 389 ms->sc_num_buttons++; 390 } 391 } 392 393 if (ms->sc_num_buttons < MAX_BUTTONS && 394 hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS, 395 HUD_TIP_SWITCH), id, hid_input, 396 &ms->sc_loc_btn[ms->sc_num_buttons], NULL)){ 397 ms->sc_flags |= HIDMS_TIP; 398 ms->sc_num_buttons++; 399 } 400 401 if (ms->sc_num_buttons < MAX_BUTTONS && 402 hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS, 403 HUD_ERASER), id, hid_input, 404 &ms->sc_loc_btn[ms->sc_num_buttons], NULL)){ 405 ms->sc_flags |= HIDMS_ERASER; 406 ms->sc_num_buttons++; 407 } 408 409 if (ms->sc_num_buttons < MAX_BUTTONS && 410 hid_locate(desc, dlen, HID_USAGE2(HUP_DIGITIZERS, 411 HUD_BARREL_SWITCH), id, hid_input, 412 &ms->sc_loc_btn[ms->sc_num_buttons], NULL)){ 413 ms->sc_flags |= HIDMS_BARREL; 414 ms->sc_num_buttons++; 415 } 416 417 /* 418 * The Microsoft Wireless Notebook Optical Mouse seems to be in worse 419 * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and 420 * all of its other button positions are all off. It also reports that 421 * it has two additional buttons and a tilt wheel. 422 */ 423 if (ms->sc_flags & HIDMS_MS_BAD_CLASS) { 424 /* HIDMS_LEADINGBYTE cleared on purpose */ 425 ms->sc_flags = HIDMS_Z | HIDMS_SPUR_BUT_UP; 426 ms->sc_num_buttons = 3; 427 /* XXX change sc_hdev isize to 5? */ 428 /* 1st byte of descriptor report contains garbage */ 429 ms->sc_loc_x.pos = 16; 430 ms->sc_loc_y.pos = 24; 431 ms->sc_loc_z.pos = 32; 432 ms->sc_loc_btn[0].pos = 8; 433 ms->sc_loc_btn[1].pos = 9; 434 ms->sc_loc_btn[2].pos = 10; 435 } 436 /* Parse descriptors to get touch panel bounds */ 437 d = hid_start_parse(desc, dlen, hid_input); 438 while (hid_get_item(d, &h)) { 439 if (h.kind != hid_input || 440 HID_GET_USAGE_PAGE(h.usage) != HUP_GENERIC_DESKTOP) 441 continue; 442 DPRINTF(("hidms: usage=0x%x range %d..%d\n", 443 h.usage, h.logical_minimum, h.logical_maximum)); 444 switch (HID_GET_USAGE(h.usage)) { 445 case HUG_X: 446 if (ms->sc_flags & HIDMS_ABSX) { 447 ms->sc_tsscale.minx = h.logical_minimum; 448 ms->sc_tsscale.maxx = h.logical_maximum; 449 } 450 break; 451 case HUG_Y: 452 if (ms->sc_flags & HIDMS_ABSY) { 453 ms->sc_tsscale.miny = h.logical_minimum; 454 ms->sc_tsscale.maxy = h.logical_maximum; 455 } 456 break; 457 } 458 } 459 hid_end_parse(d); 460 return 0; 461 } 462 463 void 464 hidms_attach(struct hidms *ms, const struct wsmouse_accessops *ops) 465 { 466 struct wsmousedev_attach_args a; 467 #ifdef HIDMS_DEBUG 468 int i; 469 #endif 470 471 printf(": %d button%s", 472 ms->sc_num_buttons, ms->sc_num_buttons == 1 ? "" : "s"); 473 switch (ms->sc_flags & (HIDMS_Z | HIDMS_W)) { 474 case HIDMS_Z: 475 printf(", Z dir"); 476 break; 477 case HIDMS_W: 478 printf(", W dir"); 479 break; 480 case HIDMS_Z | HIDMS_W: 481 printf(", Z and W dir"); 482 break; 483 } 484 485 if (ms->sc_flags & HIDMS_TIP) 486 printf(", tip"); 487 if (ms->sc_flags & HIDMS_BARREL) 488 printf(", barrel"); 489 if (ms->sc_flags & HIDMS_ERASER) 490 printf(", eraser"); 491 492 printf("\n"); 493 494 #ifdef HIDMS_DEBUG 495 DPRINTF(("hidms_attach: ms=%p\n", ms)); 496 DPRINTF(("hidms_attach: X\t%d/%d\n", 497 ms->sc_loc_x.pos, ms->sc_loc_x.size)); 498 DPRINTF(("hidms_attach: Y\t%d/%d\n", 499 ms->sc_loc_y.pos, ms->sc_loc_y.size)); 500 if (ms->sc_flags & HIDMS_Z) 501 DPRINTF(("hidms_attach: Z\t%d/%d\n", 502 ms->sc_loc_z.pos, ms->sc_loc_z.size)); 503 if (ms->sc_flags & HIDMS_W) 504 DPRINTF(("hidms_attach: W\t%d/%d\n", 505 ms->sc_loc_w.pos, ms->sc_loc_w.size)); 506 for (i = 1; i <= ms->sc_num_buttons; i++) { 507 DPRINTF(("hidms_attach: B%d\t%d/%d\n", 508 i, ms->sc_loc_btn[i - 1].pos, ms->sc_loc_btn[i - 1].size)); 509 } 510 #endif 511 512 a.accessops = ops; 513 a.accesscookie = ms->sc_device; 514 ms->sc_wsmousedev = config_found(ms->sc_device, &a, wsmousedevprint); 515 } 516 517 int 518 hidms_detach(struct hidms *ms, int flags) 519 { 520 int rv = 0; 521 522 DPRINTF(("hidms_detach: ms=%p flags=%d\n", ms, flags)); 523 524 /* No need to do reference counting of hidms, wsmouse has all the goo */ 525 if (ms->sc_wsmousedev != NULL) 526 rv = config_detach(ms->sc_wsmousedev, flags); 527 528 return (rv); 529 } 530 531 void 532 hidms_input(struct hidms *ms, uint8_t *data, u_int len) 533 { 534 int dx, dy, dz, dw; 535 u_int32_t buttons = 0; 536 int i, s; 537 538 DPRINTFN(5,("hidms_input: len=%d\n", len)); 539 540 /* 541 * The Microsoft Wireless Intellimouse 2.0 sends one extra leading 542 * byte of data compared to most USB mice. This byte frequently 543 * switches from 0x01 (usual state) to 0x02. It may be used to 544 * report non-standard events (such as battery life). However, 545 * at the same time, it generates a left click event on the 546 * button byte, where there shouldn't be any. We simply discard 547 * the packet in this case. 548 * 549 * This problem affects the MS Wireless Notebook Optical Mouse, too. 550 * However, the leading byte for this mouse is normally 0x11, and 551 * the phantom mouse click occurs when it's 0x14. 552 */ 553 if (ms->sc_flags & HIDMS_LEADINGBYTE) { 554 if (*data++ == 0x02) 555 return; 556 /* len--; */ 557 } else if (ms->sc_flags & HIDMS_SPUR_BUT_UP) { 558 if (*data == 0x14 || *data == 0x15) 559 return; 560 } 561 562 dx = hid_get_data(data, len, &ms->sc_loc_x); 563 dy = -hid_get_data(data, len, &ms->sc_loc_y); 564 dz = hid_get_data(data, len, &ms->sc_loc_z); 565 dw = hid_get_data(data, len, &ms->sc_loc_w); 566 567 if (ms->sc_flags & HIDMS_ABSY) 568 dy = -dy; 569 if (ms->sc_flags & HIDMS_REVZ) 570 dz = -dz; 571 if (ms->sc_flags & HIDMS_REVW) 572 dw = -dw; 573 574 if (ms->sc_tsscale.swapxy && !ms->sc_rawmode) { 575 int tmp = dx; 576 dx = dy; 577 dy = tmp; 578 } 579 580 if (!ms->sc_rawmode && 581 (ms->sc_tsscale.maxx - ms->sc_tsscale.minx) != 0 && 582 (ms->sc_tsscale.maxy - ms->sc_tsscale.miny) != 0) { 583 /* Scale down to the screen resolution. */ 584 dx = ((dx - ms->sc_tsscale.minx) * ms->sc_tsscale.resx) / 585 (ms->sc_tsscale.maxx - ms->sc_tsscale.minx); 586 dy = ((dy - ms->sc_tsscale.miny) * ms->sc_tsscale.resy) / 587 (ms->sc_tsscale.maxy - ms->sc_tsscale.miny); 588 } 589 590 for (i = 0; i < ms->sc_num_buttons; i++) 591 if (hid_get_data(data, len, &ms->sc_loc_btn[i])) 592 buttons |= (1 << HIDMS_BUT(i)); 593 594 if (dx != 0 || dy != 0 || dz != 0 || dw != 0 || 595 buttons != ms->sc_buttons) { 596 DPRINTFN(10, ("hidms_input: x:%d y:%d z:%d w:%d buttons:0x%x\n", 597 dx, dy, dz, dw, buttons)); 598 ms->sc_buttons = buttons; 599 if (ms->sc_wsmousedev != NULL) { 600 s = spltty(); 601 if (ms->sc_flags & HIDMS_ABSX) { 602 wsmouse_set(ms->sc_wsmousedev, 603 WSMOUSE_ABS_X, dx, 0); 604 dx = 0; 605 } 606 if (ms->sc_flags & HIDMS_ABSY) { 607 wsmouse_set(ms->sc_wsmousedev, 608 WSMOUSE_ABS_Y, dy, 0); 609 dy = 0; 610 } 611 WSMOUSE_INPUT(ms->sc_wsmousedev, 612 buttons, dx, dy, dz, dw); 613 splx(s); 614 } 615 } 616 } 617 618 int 619 hidms_enable(struct hidms *ms) 620 { 621 if (ms->sc_enabled) 622 return EBUSY; 623 624 ms->sc_enabled = 1; 625 ms->sc_buttons = 0; 626 return 0; 627 } 628 629 int 630 hidms_ioctl(struct hidms *ms, u_long cmd, caddr_t data, int flag, 631 struct proc *p) 632 { 633 struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; 634 635 switch (cmd) { 636 case WSMOUSEIO_SCALIBCOORDS: 637 if (!(wsmc->minx >= -32768 && wsmc->maxx >= -32768 && 638 wsmc->miny >= -32768 && wsmc->maxy >= -32768 && 639 wsmc->resx >= 0 && wsmc->resy >= 0 && 640 wsmc->minx < 32768 && wsmc->maxx < 32768 && 641 wsmc->miny < 32768 && wsmc->maxy < 32768 && 642 (wsmc->maxx - wsmc->minx) != 0 && 643 (wsmc->maxy - wsmc->miny) != 0 && 644 wsmc->resx < 32768 && wsmc->resy < 32768 && 645 wsmc->swapxy >= 0 && wsmc->swapxy <= 1 && 646 wsmc->samplelen >= 0 && wsmc->samplelen <= 1)) 647 return (EINVAL); 648 649 ms->sc_tsscale.minx = wsmc->minx; 650 ms->sc_tsscale.maxx = wsmc->maxx; 651 ms->sc_tsscale.miny = wsmc->miny; 652 ms->sc_tsscale.maxy = wsmc->maxy; 653 ms->sc_tsscale.swapxy = wsmc->swapxy; 654 ms->sc_tsscale.resx = wsmc->resx; 655 ms->sc_tsscale.resy = wsmc->resy; 656 ms->sc_rawmode = wsmc->samplelen; 657 return 0; 658 case WSMOUSEIO_GCALIBCOORDS: 659 wsmc->minx = ms->sc_tsscale.minx; 660 wsmc->maxx = ms->sc_tsscale.maxx; 661 wsmc->miny = ms->sc_tsscale.miny; 662 wsmc->maxy = ms->sc_tsscale.maxy; 663 wsmc->swapxy = ms->sc_tsscale.swapxy; 664 wsmc->resx = ms->sc_tsscale.resx; 665 wsmc->resy = ms->sc_tsscale.resy; 666 wsmc->samplelen = ms->sc_rawmode; 667 return 0; 668 case WSMOUSEIO_GTYPE: 669 if (ms->sc_flags & HIDMS_ABSX && ms->sc_flags & HIDMS_ABSY) { 670 *(u_int *)data = WSMOUSE_TYPE_TPANEL; 671 return 0; 672 } 673 /* FALLTHROUGH */ 674 default: 675 return -1; 676 } 677 } 678 679 void 680 hidms_disable(struct hidms *ms) 681 { 682 ms->sc_enabled = 0; 683 } 684