1 /* $OpenBSD: ietp.c,v 1.3 2024/08/18 03:25:04 deraadt Exp $ */ 2 /* 3 * Elan I2C Touchpad driver 4 * 5 * Copyright (c) 2015, 2016 joshua stein <jcs@openbsd.org> 6 * Copyright (c) 2020, 2022 Vladimir Kondratyev <wulf@FreeBSD.org> 7 * Copyright (c) 2023 vladimir serbinenko <phcoder@gmail.com> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 /* Protocol documentation: 23 * https://lkml.indiana.edu/hypermail/linux/kernel/1205.0/02551.html 24 * Based on FreeBSD ietp driver. 25 */ 26 27 #include <sys/param.h> 28 #include <sys/systm.h> 29 #include <sys/device.h> 30 #include <sys/malloc.h> 31 #include <sys/stdint.h> 32 33 #include <dev/i2c/i2cvar.h> 34 #include <dev/i2c/ietp.h> 35 36 #include <dev/wscons/wsconsio.h> 37 #include <dev/wscons/wsmousevar.h> 38 39 /* #define IETP_DEBUG */ 40 41 #ifdef IETP_DEBUG 42 #define DPRINTF(x) printf x 43 #else 44 #define DPRINTF(x) 45 #endif 46 47 enum { 48 I2C_HID_CMD_DESCR = 0x0, 49 I2C_HID_CMD_RESET = 0x1, 50 I2C_HID_CMD_SET_POWER = 0x8, 51 }; 52 53 #define I2C_HID_POWER_ON 0x0 54 #define I2C_HID_POWER_OFF 0x1 55 56 #define IETP_PATTERN 0x0100 57 #define IETP_UNIQUEID 0x0101 58 #define IETP_IC_TYPE 0x0103 59 #define IETP_OSM_VERSION 0x0103 60 #define IETP_NSM_VERSION 0x0104 61 #define IETP_TRACENUM 0x0105 62 #define IETP_MAX_X_AXIS 0x0106 63 #define IETP_MAX_Y_AXIS 0x0107 64 #define IETP_RESOLUTION 0x0108 65 #define IETP_PRESSURE 0x010A 66 67 #define IETP_CONTROL 0x0300 68 #define IETP_CTRL_ABSOLUTE 0x0001 69 #define IETP_CTRL_STANDARD 0x0000 70 71 #define IETP_REPORT_LEN_LO 31 72 #define IETP_REPORT_LEN_HI 36 73 #define IETP_MAX_FINGERS 5 74 75 #define IETP_REPORT_ID_LO 0x5D 76 #define IETP_REPORT_ID_HI 0x60 77 78 #define IETP_TOUCH_INFO 0 79 #define IETP_FINGER_DATA 1 80 #define IETP_FINGER_DATA_LEN 5 81 #define IETP_WH_DATA 31 82 83 #define IETP_TOUCH_LMB (1 << 0) 84 #define IETP_TOUCH_RMB (1 << 1) 85 #define IETP_TOUCH_MMB (1 << 2) 86 87 #define IETP_MAX_PRESSURE 255 88 #define IETP_FWIDTH_REDUCE 90 89 #define IETP_PRESSURE_BASE 25 90 91 int ietp_match(struct device *, void *, void *); 92 void ietp_attach(struct device *, struct device *, void *); 93 int ietp_detach(struct device *, int); 94 int ietp_activate(struct device *, int); 95 96 int ietp_intr(void *); 97 int ietp_reset(struct ietp_softc *); 98 99 int ietp_fetch_descriptor(struct ietp_softc *sc); 100 int ietp_set_power(struct ietp_softc *sc, int power); 101 int ietp_reset_cmd(struct ietp_softc *sc); 102 103 int32_t ietp_res2dpmm(uint8_t, bool); 104 105 int ietp_iic_read_reg(struct ietp_softc *, uint16_t, size_t, void *); 106 int ietp_iic_write_reg(struct ietp_softc *, uint16_t, uint16_t); 107 int ietp_iic_set_absolute_mode(struct ietp_softc *, bool); 108 109 const struct cfattach ietp_ca = { 110 sizeof(struct ietp_softc), 111 ietp_match, 112 ietp_attach, 113 ietp_detach, 114 ietp_activate, 115 }; 116 117 const struct wsmouse_accessops ietp_mouse_access = { 118 ietp_enable, 119 ietp_ioctl, 120 ietp_disable 121 }; 122 123 struct cfdriver ietp_cd = { 124 NULL, "ietp", DV_DULL 125 }; 126 127 int 128 ietp_match(struct device *parent, void *match, void *aux) 129 { 130 struct i2c_attach_args *ia = aux; 131 132 if (strcmp(ia->ia_name, "ietp") == 0) 133 return (1); 134 135 return (0); 136 } 137 138 int32_t 139 ietp_res2dpmm(uint8_t res, bool hi_precision) 140 { 141 int32_t dpi; 142 143 dpi = hi_precision ? 300 + res * 100 : 790 + res * 10; 144 145 return (dpi * 10 /254); 146 } 147 148 void 149 ietp_attach(struct device *parent, struct device *self, void *aux) 150 { 151 struct ietp_softc *sc = (struct ietp_softc *)self; 152 struct i2c_attach_args *ia = aux; 153 uint16_t buf, reg; 154 uint8_t *buf8; 155 uint8_t pattern; 156 struct wsmousedev_attach_args a; 157 struct wsmousehw *hw; 158 159 sc->sc_tag = ia->ia_tag; 160 sc->sc_addr = ia->ia_addr; 161 162 ietp_fetch_descriptor(sc); 163 164 if (ia->ia_intr) { 165 printf(" %s", iic_intr_string(sc->sc_tag, ia->ia_intr)); 166 167 sc->sc_ih = iic_intr_establish(sc->sc_tag, ia->ia_intr, 168 IPL_TTY, ietp_intr, sc, sc->sc_dev.dv_xname); 169 if (sc->sc_ih == NULL) { 170 printf(", can't establish interrupt\n"); 171 return; 172 } 173 } 174 175 sc->sc_buttons = 0; 176 sc->sc_refcnt = 0; 177 178 buf8 = (uint8_t *)&buf; 179 180 if (ietp_iic_read_reg(sc, IETP_UNIQUEID, sizeof(buf), &buf) != 0) { 181 printf(": failed reading product ID\n"); 182 return; 183 } 184 sc->product_id = le16toh(buf); 185 186 if (ietp_iic_read_reg(sc, IETP_PATTERN, sizeof(buf), &buf) != 0) { 187 printf(": failed reading pattern\n"); 188 return; 189 } 190 pattern = buf == 0xFFFF ? 0 : buf8[1]; 191 sc->hi_precision = pattern >= 0x02; 192 193 reg = pattern >= 0x01 ? IETP_IC_TYPE : IETP_OSM_VERSION; 194 if (ietp_iic_read_reg(sc, reg, sizeof(buf), &buf) != 0) { 195 printf(": failed reading IC type\n"); 196 return; 197 } 198 sc->ic_type = pattern >= 0x01 ? be16toh(buf) : buf8[1]; 199 200 if (ietp_iic_read_reg(sc, IETP_NSM_VERSION, sizeof(buf), &buf) != 0) { 201 printf(": failed reading SM version\n"); 202 return; 203 } 204 sc->is_clickpad = (buf8[0] & 0x10) != 0; 205 206 if (ietp_iic_set_absolute_mode(sc, true) != 0) { 207 printf(": failed to set absolute mode\n"); 208 return; 209 } 210 211 if (ietp_iic_read_reg(sc, IETP_MAX_X_AXIS, sizeof(buf), &buf) != 0) { 212 printf(": failed reading max x\n"); 213 return; 214 } 215 sc->max_x = le16toh(buf); 216 217 if (ietp_iic_read_reg(sc, IETP_MAX_Y_AXIS, sizeof(buf), &buf) != 0) { 218 printf(": failed reading max y\n"); 219 return; 220 } 221 sc->max_y = le16toh(buf); 222 223 if (ietp_iic_read_reg(sc, IETP_TRACENUM, sizeof(buf), &buf) != 0) { 224 printf(": failed reading trace info\n"); 225 return; 226 } 227 sc->trace_x = sc->max_x / buf8[0]; 228 sc->trace_y = sc->max_y / buf8[1]; 229 230 if (ietp_iic_read_reg(sc, IETP_PRESSURE, sizeof(buf), &buf) != 0) { 231 printf(": failed reading pressure format\n"); 232 return; 233 } 234 sc->pressure_base = (buf8[0] & 0x10) ? 0 : IETP_PRESSURE_BASE; 235 236 if (ietp_iic_read_reg(sc, IETP_RESOLUTION, sizeof(buf), &buf) != 0) { 237 printf(": failed reading resolution\n"); 238 return; 239 } 240 /* Conversion from internal format to dot per mm */ 241 sc->res_x = ietp_res2dpmm(buf8[0], sc->hi_precision); 242 sc->res_y = ietp_res2dpmm(buf8[1], sc->hi_precision); 243 244 sc->report_id = sc->hi_precision ? 245 IETP_REPORT_ID_HI : IETP_REPORT_ID_LO; 246 sc->report_len = sc->hi_precision ? 247 IETP_REPORT_LEN_HI : IETP_REPORT_LEN_LO; 248 249 sc->sc_ibuf = malloc(IETP_REPORT_LEN_HI + 12, M_DEVBUF, 250 M_NOWAIT | M_ZERO); 251 sc->sc_isize = sc->report_len + 3; 252 253 a.accessops = &ietp_mouse_access; 254 a.accesscookie = sc; 255 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); 256 257 hw = wsmouse_get_hw(sc->sc_wsmousedev); 258 hw->type = WSMOUSE_TYPE_TOUCHPAD; 259 hw->hw_type = sc->is_clickpad ? WSMOUSEHW_CLICKPAD : WSMOUSEHW_TOUCHPAD; 260 hw->x_min = 0; 261 hw->x_max = sc->max_x; 262 hw->y_min = 0; 263 hw->y_max = sc->max_y; 264 hw->h_res = sc->res_x; 265 hw->v_res = sc->res_y; 266 hw->mt_slots = IETP_MAX_FINGERS; 267 268 wsmouse_configure(sc->sc_wsmousedev, NULL, 0); 269 270 /* power down until we're opened */ 271 if (ietp_set_power(sc, I2C_HID_POWER_OFF)) { 272 printf(": failed to power down\n"); 273 return; 274 } 275 276 printf("\n"); 277 278 DPRINTF(("%s: max_x=%d, max_y=%d, %s\n", sc->sc_dev.dv_xname, 279 sc->max_x, sc->max_y, 280 sc->is_clickpad ? "clickpad" : "touchpad")); 281 282 return; 283 } 284 285 int 286 ietp_detach(struct device *self, int flags) 287 { 288 struct ietp_softc *sc = (struct ietp_softc *)self; 289 290 if (sc->sc_ih != NULL) { 291 iic_intr_disestablish(sc->sc_tag, sc->sc_ih); 292 sc->sc_ih = NULL; 293 } 294 295 if (sc->sc_ibuf != NULL) { 296 free(sc->sc_ibuf, M_DEVBUF, sc->sc_isize); 297 sc->sc_ibuf = NULL; 298 } 299 300 return (0); 301 } 302 303 int 304 ietp_activate(struct device *self, int act) 305 { 306 struct ietp_softc *sc = (struct ietp_softc *)self; 307 int rv; 308 309 DPRINTF(("%s(%d)\n", __func__, act)); 310 311 switch (act) { 312 case DVACT_QUIESCE: 313 rv = config_activate_children(self, act); 314 sc->sc_dying = 1; 315 if (ietp_set_power(sc, I2C_HID_POWER_OFF)) 316 printf("%s: failed to power down\n", 317 sc->sc_dev.dv_xname); 318 break; 319 case DVACT_WAKEUP: 320 ietp_reset(sc); 321 sc->sc_dying = 0; 322 rv = config_activate_children(self, act); 323 break; 324 default: 325 rv = config_activate_children(self, act); 326 break; 327 } 328 return rv; 329 } 330 331 void 332 ietp_sleep(struct ietp_softc *sc, int ms) 333 { 334 if (cold) 335 delay(ms * 1000); 336 else 337 tsleep_nsec(&sc, PWAIT, "ietp", MSEC_TO_NSEC(ms)); 338 } 339 340 int 341 ietp_iic_set_absolute_mode(struct ietp_softc *sc, bool enable) 342 { 343 static const struct { 344 uint16_t ic_type; 345 uint16_t product_id; 346 } special_fw[] = { 347 { 0x0E, 0x05 }, { 0x0E, 0x06 }, { 0x0E, 0x07 }, { 0x0E, 0x09 }, 348 { 0x0E, 0x13 }, { 0x08, 0x26 }, 349 }; 350 uint16_t val; 351 int i, error; 352 bool require_wakeup; 353 354 error = 0; 355 356 /* 357 * Some ASUS touchpads need to be powered on to enter absolute mode. 358 */ 359 require_wakeup = false; 360 for (i = 0; i < nitems(special_fw); i++) { 361 if (sc->ic_type == special_fw[i].ic_type && 362 sc->product_id == special_fw[i].product_id) { 363 require_wakeup = true; 364 break; 365 } 366 } 367 368 if (require_wakeup && ietp_set_power(sc, I2C_HID_POWER_ON) != 0) { 369 printf("%s: failed writing poweron command\n", 370 sc->sc_dev.dv_xname); 371 return (EIO); 372 } 373 374 val = enable ? IETP_CTRL_ABSOLUTE : IETP_CTRL_STANDARD; 375 if (ietp_iic_write_reg(sc, IETP_CONTROL, val) != 0) { 376 printf("%s: failed setting absolute mode\n", 377 sc->sc_dev.dv_xname); 378 error = EIO; 379 } 380 381 if (require_wakeup && ietp_set_power(sc, I2C_HID_POWER_OFF) != 0) { 382 printf("%s: failed writing poweroff command\n", 383 sc->sc_dev.dv_xname); 384 error = EIO; 385 } 386 387 return (error); 388 } 389 390 int 391 ietp_iic_read_reg(struct ietp_softc *sc, uint16_t reg, size_t len, void *val) 392 { 393 uint8_t cmd[] = { 394 reg & 0xff, 395 reg >> 8, 396 }; 397 398 return iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 399 &cmd, 2, val, len, 0); 400 } 401 402 int 403 ietp_iic_write_reg(struct ietp_softc *sc, uint16_t reg, uint16_t val) 404 { 405 uint8_t cmd[] = { 406 reg & 0xff, 407 reg >> 8, 408 val & 0xff, 409 val >> 8, 410 }; 411 412 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 413 &cmd, 4, NULL, 0, 0); 414 } 415 416 int 417 ietp_set_power(struct ietp_softc *sc, int power) 418 { 419 int res = 1; 420 uint8_t cmd[] = { 421 htole16(sc->hid_desc.wCommandRegister) & 0xff, 422 htole16(sc->hid_desc.wCommandRegister) >> 8, 423 power, 424 I2C_HID_CMD_SET_POWER, 425 }; 426 427 iic_acquire_bus(sc->sc_tag, 0); 428 429 DPRINTF(("%s: HID command I2C_HID_CMD_SET_POWER(%d)\n", 430 sc->sc_dev.dv_xname, power)); 431 432 /* 22 00 00 08 */ 433 res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 434 &cmd, sizeof(cmd), NULL, 0, 0); 435 436 iic_release_bus(sc->sc_tag, 0); 437 438 return (res); 439 } 440 441 int 442 ietp_reset_cmd(struct ietp_softc *sc) 443 { 444 int res = 1; 445 uint8_t cmd[] = { 446 htole16(sc->hid_desc.wCommandRegister) & 0xff, 447 htole16(sc->hid_desc.wCommandRegister) >> 8, 448 0, 449 I2C_HID_CMD_RESET, 450 }; 451 452 iic_acquire_bus(sc->sc_tag, 0); 453 454 DPRINTF(("%s: HID command I2C_HID_CMD_RESET\n", 455 sc->sc_dev.dv_xname)); 456 457 /* 22 00 00 01 */ 458 res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 459 &cmd, sizeof(cmd), NULL, 0, 0); 460 461 iic_release_bus(sc->sc_tag, 0); 462 463 return (res); 464 } 465 466 int 467 ietp_fetch_descriptor(struct ietp_softc *sc) 468 { 469 int i, res = 1; 470 /* 471 * 5.2.2 - HID Descriptor Retrieval 472 * register is passed from the controller 473 */ 474 uint8_t cmd[] = { 475 1, 476 0, 477 }; 478 479 iic_acquire_bus(sc->sc_tag, 0); 480 481 DPRINTF(("%s: HID command I2C_HID_CMD_DESCR at 0x1\n", 482 sc->sc_dev.dv_xname)); 483 484 /* 20 00 */ 485 res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 486 &cmd, sizeof(cmd), &sc->hid_desc_buf, 487 sizeof(struct i2c_hid_desc), 0); 488 489 DPRINTF(("%s: HID descriptor:", sc->sc_dev.dv_xname)); 490 for (i = 0; i < sizeof(struct i2c_hid_desc); i++) 491 DPRINTF((" %.2x", sc->hid_desc_buf[i])); 492 DPRINTF(("\n")); 493 494 iic_release_bus(sc->sc_tag, 0); 495 496 return (res); 497 } 498 499 int 500 ietp_reset(struct ietp_softc *sc) 501 { 502 DPRINTF(("%s: resetting\n", sc->sc_dev.dv_xname)); 503 504 if (ietp_set_power(sc, I2C_HID_POWER_ON)) { 505 printf("%s: failed to power on\n", sc->sc_dev.dv_xname); 506 return (1); 507 } 508 509 ietp_sleep(sc, 100); 510 511 if (ietp_reset_cmd(sc)) { 512 printf("%s: failed to reset hardware\n", sc->sc_dev.dv_xname); 513 514 ietp_set_power(sc, I2C_HID_POWER_OFF); 515 516 return (1); 517 } 518 519 ietp_sleep(sc, 100); 520 521 return (0); 522 } 523 524 void 525 parse_input(struct ietp_softc *sc, u_char *report, int len) 526 { 527 uint8_t *fdata; 528 int32_t finger; 529 int32_t x, y, p; 530 int buttons = 0; 531 int s; 532 533 /* we seem to get 0 length reports sometimes, ignore them */ 534 if (len == 0) 535 return; 536 if (len != sc->report_len) { 537 printf("%s: wrong report length (%d vs %d expected)", 538 sc->sc_dev.dv_xname, len, (int) sc->report_len); 539 return; 540 } 541 542 s = spltty(); 543 544 buttons = report[IETP_TOUCH_INFO] & 7; 545 546 if (sc->sc_buttons != buttons) { 547 wsmouse_buttons(sc->sc_wsmousedev, buttons); 548 sc->sc_buttons = buttons; 549 } 550 551 for (finger = 0, fdata = report + IETP_FINGER_DATA; 552 finger < IETP_MAX_FINGERS; 553 finger++, fdata += IETP_FINGER_DATA_LEN) { 554 if ((report[IETP_TOUCH_INFO] & (1 << (finger + 3))) != 0) { 555 if (sc->hi_precision) { 556 x = fdata[0] << 8 | fdata[1]; 557 y = fdata[2] << 8 | fdata[3]; 558 } else { 559 x = (fdata[0] & 0xf0) << 4 | fdata[1]; 560 y = (fdata[0] & 0x0f) << 8 | fdata[2]; 561 } 562 563 if (x > sc->max_x || y > sc->max_y) { 564 printf("%s: [%d] x=%d y=%d over max (%d, %d)\n", 565 sc->sc_dev.dv_xname, finger, x, y, 566 sc->max_x, sc->max_y); 567 continue; 568 } 569 570 571 p = MIN((int32_t)fdata[4] + sc->pressure_base, 572 IETP_MAX_PRESSURE); 573 574 } else { 575 x = 0; 576 y = 0; 577 p = 0; 578 } 579 580 DPRINTF(("position: [finger=%d, x=%d, y=%d, p=%d]\n", finger, 581 x, y, p)); 582 wsmouse_mtstate(sc->sc_wsmousedev, finger, x, y, p); 583 } 584 585 wsmouse_input_sync(sc->sc_wsmousedev); 586 587 splx(s); 588 } 589 590 int 591 ietp_intr(void *arg) 592 { 593 struct ietp_softc *sc = arg; 594 int psize, i; 595 u_char *p; 596 u_int rep = 0; 597 598 if (sc->sc_dying) 599 return 1; 600 601 /* 602 * XXX: force I2C_F_POLL for now to avoid dwiic interrupting 603 * while we are interrupting 604 */ 605 606 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 607 iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, NULL, 0, 608 sc->sc_ibuf, letoh16(sc->hid_desc.wMaxInputLength), I2C_F_POLL); 609 iic_release_bus(sc->sc_tag, I2C_F_POLL); 610 611 /* 612 * 6.1.1 - First two bytes are the packet length, which must be less 613 * than or equal to wMaxInputLength 614 */ 615 psize = sc->sc_ibuf[0] | sc->sc_ibuf[1] << 8; 616 if (psize <= 2 || psize > sc->sc_isize) { 617 DPRINTF(("%s: %s: invalid packet size (%d vs. %d)\n", 618 sc->sc_dev.dv_xname, __func__, psize, 619 sc->sc_isize)); 620 return (1); 621 } 622 623 /* 3rd byte is the report id */ 624 p = sc->sc_ibuf + 2; 625 psize -= 2; 626 rep = *p++; 627 psize--; 628 629 DPRINTF(("%s: %s: hid input (rep 0x%x):", sc->sc_dev.dv_xname, __func__, 630 rep)); 631 for (i = 0; i < psize; i++) { 632 DPRINTF((" %.2x", p[i])); 633 } 634 DPRINTF(("\n")); 635 636 if (sc->sc_refcnt && rep == sc->report_id) { 637 parse_input(sc, p, psize); 638 } 639 640 return (1); 641 } 642 643 int 644 ietp_enable(void *dev) 645 { 646 struct ietp_softc *sc = dev; 647 648 DPRINTF(("%s: %s: refcnt=%d\n", sc->sc_dev.dv_xname, 649 __func__, sc->sc_refcnt)); 650 651 if (sc->sc_refcnt++ || sc->sc_isize == 0) 652 return (0); 653 654 /* power on */ 655 ietp_reset(sc); 656 657 return (0); 658 } 659 660 void 661 ietp_disable(void *dev) 662 { 663 struct ietp_softc *sc = dev; 664 DPRINTF(("%s: %s: refcnt=%d\n", sc->sc_dev.dv_xname, 665 __func__, sc->sc_refcnt)); 666 667 if (--sc->sc_refcnt) 668 return; 669 670 /* no sub-devices open, conserve power */ 671 672 if (ietp_set_power(sc, I2C_HID_POWER_OFF)) 673 printf("%s: failed to power down\n", sc->sc_dev.dv_xname); 674 } 675 676 int 677 ietp_ioctl(void *dev, u_long cmd, caddr_t data, int flag, 678 struct proc *p) 679 { 680 struct ietp_softc *sc = dev; 681 struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data; 682 683 switch (cmd) { 684 case WSMOUSEIO_GTYPE: 685 *(u_int *)data = WSMOUSE_TYPE_TOUCHPAD; 686 return 0; 687 688 case WSMOUSEIO_GCALIBCOORDS: 689 wsmc->minx = 0; 690 wsmc->maxx = sc->max_x; 691 wsmc->miny = 0; 692 wsmc->maxy = sc->max_y; 693 wsmc->swapxy = 0; 694 wsmc->resx = sc->res_x; 695 wsmc->resy = sc->res_y; 696 return 0; 697 } 698 return -1; 699 } 700