1 /* $OpenBSD: utpms.c,v 1.13 2022/01/09 05:43:02 jsg Exp $ */ 2 3 /* 4 * Copyright (c) 2005, Johan Wall�n 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the copyright holder may not be used to endorse or 16 * promote products derived from this software without specific 17 * prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * The utpms driver provides support for the trackpad on new (post 34 * February 2005) Apple PowerBooks and iBooks that are not standard 35 * USB HID mice. 36 */ 37 38 /* 39 * The protocol (that is, the interpretation of the data generated by 40 * the trackpad) is taken from the Linux appletouch driver version 41 * 0.08 by Johannes Berg, Stelian Pop and Frank Arnold. The method 42 * used to detect fingers on the trackpad is also taken from that 43 * driver. 44 */ 45 46 /* 47 * PROTOCOL: 48 * 49 * The driver transfers continuously 81 byte events. The last byte is 50 * 1 if the button is pressed, and is 0 otherwise. Of the remaining 51 * bytes, 26 + 16 = 42 are sensors detecting pressure in the X or 52 * horizontal, and Y or vertical directions, respectively. On 12 and 53 * 15 inch PowerBooks, only the 16 first sensors in the X-direction 54 * are used. In the X-direction, the sensors correspond to byte 55 * positions 56 * 57 * 2, 7, 12, 17, 22, 27, 32, 37, 4, 9, 14, 19, 24, 29, 34, 39, 42, 58 * 47, 52, 57, 62, 67, 72, 77, 44 and 49; 59 * 60 * in the Y direction, the sensors correspond to byte positions 61 * 62 * 1, 6, 11, 16, 21, 26, 31, 36, 3, 8, 13, 18, 23, 28, 33 and 38. 63 * 64 * The change in the sensor values over time is more interesting than 65 * their absolute values: if the pressure increases, we know that the 66 * finger has just moved there. 67 * 68 * We keep track of the previous sample (of sensor values in the X and 69 * Y directions) and the accumulated change for each sensor. When we 70 * receive a new sample, we add the difference of the new sensor value 71 * and the old value to the accumulated change. If the accumulator 72 * becomes negative, we set it to zero. The effect is that the 73 * accumulator is large for sensors whose pressure has recently 74 * increased. If there is little change in pressure (or if the 75 * pressure decreases), the accumulator drifts back to zero. 76 * 77 * Since there is some fluctuations, we ignore accumulator values 78 * below a threshold. The raw finger position is computed as a 79 * weighted average of the other sensors (the weights are the 80 * accumulated changes). 81 * 82 * For smoothing, we keep track of the previous raw finger position, 83 * and the virtual position reported to wsmouse. The new raw position 84 * is computed as a weighted average of the old raw position and the 85 * computed raw position. Since this still generates some noise, we 86 * compute a new virtual position as a weighted average of the previous 87 * virtual position and the new raw position. The weights are 88 * controlled by the raw change and a noise parameter. The position 89 * is reported as a relative position. 90 */ 91 92 /* 93 * TODO: 94 * 95 * Add support for other drivers of the same type. 96 * 97 * Add support for tapping and two-finger scrolling? The 98 * implementation already detects two fingers, so this should be 99 * relatively easy. 100 * 101 * Implement some of the mouse ioctls? 102 * 103 * Take care of the XXXs. 104 * 105 */ 106 107 #include <sys/param.h> 108 #include <sys/device.h> 109 #include <sys/errno.h> 110 111 #include <sys/ioctl.h> 112 #include <sys/systm.h> 113 #include <sys/tty.h> 114 115 #include <dev/usb/usb.h> 116 #include <dev/usb/usbdi.h> 117 #include <dev/usb/usbdi_util.h> 118 #include <dev/usb/usbdevs.h> 119 #include <dev/usb/uhidev.h> 120 121 #include <dev/wscons/wsconsio.h> 122 #include <dev/wscons/wsmousevar.h> 123 124 /* The amount of data transferred by the USB device. */ 125 #define UTPMS_DATA_LEN 81 126 127 /* The maximum number of sensors. */ 128 #define UTPMS_X_SENSORS 26 129 #define UTPMS_Y_SENSORS 16 130 #define UTPMS_SENSORS (UTPMS_X_SENSORS + UTPMS_Y_SENSORS) 131 132 /* 133 * Parameters for supported devices. For generality, these parameters 134 * can be different for each device. The meanings of the parameters 135 * are as follows. 136 * 137 * type: Type of the trackpad device, used for dmesg output, and 138 * to know some of the device parameters. 139 * 140 * noise: Amount of noise in the computed position. This controls 141 * how large a change must be to get reported, and how 142 * large enough changes are smoothed. A good value can 143 * probably only be found experimentally, but something around 144 * 16 seems suitable. 145 * 146 * product: The product ID of the trackpad. 147 * 148 * 149 * threshold: Accumulated changes less than this are ignored. A good 150 * value could be determined experimentally, but 5 is a 151 * reasonable guess. 152 * 153 * vendor: The vendor ID. Currently USB_VENDOR_APPLE for all devices. 154 * 155 * x_factor: Factor used in computations with X-coordinates. If the 156 * x-resolution of the display is x, this should be 157 * (x + 1) / (x_sensors - 1). Other values work fine, but 158 * then the aspect ratio is not necessarily kept. 159 * 160 * x_sensors: The number of sensors in the X-direction. 161 * 162 * y_factor: As x_factors, but for Y-coordinates. 163 * 164 * y_sensors: The number of sensors in the Y-direction. 165 */ 166 167 struct utpms_dev { 168 int type; /* Type of the trackpad. */ 169 #define FOUNTAIN 0x00 170 #define GEYSER1 0x01 171 #define GEYSER2 0x02 172 int noise; /* Amount of noise in the computed position. */ 173 int threshold; /* Changes less than this are ignored. */ 174 int x_factor; /* Factor used in computation with X-coordinates. */ 175 int x_sensors; /* The number of X-sensors. */ 176 int y_factor; /* Factor used in computation with Y-coordinates. */ 177 int y_sensors; /* The number of Y-sensors. */ 178 uint16_t product; /* Product ID. */ 179 uint16_t vendor; /* The vendor ID. */ 180 }; 181 182 static struct utpms_dev utpms_devices[] = { 183 #define UTPMS_TOUCHPAD(ttype, prod, x_fact, x_sens, y_fact) \ 184 { \ 185 .type = (ttype), \ 186 .vendor = USB_VENDOR_APPLE, \ 187 .product = (prod), \ 188 .noise = 16, \ 189 .threshold = 5, \ 190 .x_factor = (x_fact), \ 191 .x_sensors = (x_sens), \ 192 .y_factor = (y_fact), \ 193 .y_sensors = 16 \ 194 } 195 /* 12 inch PowerBooks */ 196 UTPMS_TOUCHPAD(FOUNTAIN, 0x030a, 69, 16, 52), 197 /* 12 and 14 inch iBook G4 */ 198 UTPMS_TOUCHPAD(GEYSER1, 0x030b, 69, 16, 52), 199 /* 15 inch PowerBooks */ 200 UTPMS_TOUCHPAD(FOUNTAIN, 0x020e, 85, 16, 57), 201 UTPMS_TOUCHPAD(FOUNTAIN, 0x020f, 85, 16, 57), 202 UTPMS_TOUCHPAD(GEYSER2, 0x0214, 90, 15, 107), 203 UTPMS_TOUCHPAD(GEYSER2, 0x0215, 90, 15, 107), 204 UTPMS_TOUCHPAD(GEYSER2, 0x0216, 90, 15, 107), 205 /* 17 inch PowerBooks */ 206 UTPMS_TOUCHPAD(FOUNTAIN, 0x020d, 71, 26, 68), 207 #undef UTPMS_TOUCHPAD 208 }; 209 210 struct utpms_softc { 211 struct uhidev sc_hdev; /* USB parent (got the struct device). */ 212 int sc_type; /* Type of the trackpad */ 213 int sc_datalen; 214 int sc_acc[UTPMS_SENSORS]; /* Accumulated sensor values. */ 215 unsigned char sc_prev[UTPMS_SENSORS]; /* Previous sample. */ 216 unsigned char sc_sample[UTPMS_SENSORS]; /* Current sample. */ 217 struct device *sc_wsmousedev; /* WSMouse device. */ 218 int sc_noise; /* Amount of noise. */ 219 int sc_threshold; /* Threshold value. */ 220 int sc_x; /* Virtual position in horizontal 221 * direction (wsmouse position). */ 222 int sc_x_factor; /* X-coordinate factor. */ 223 int sc_x_raw; /* X-position of finger on trackpad. */ 224 int sc_x_sensors; /* Number of X-sensors. */ 225 int sc_y; /* Virtual position in vertical direction 226 * (wsmouse position). */ 227 int sc_y_factor; /* Y-coordinate factor. */ 228 int sc_y_raw; /* Y-position of finger on trackpad. */ 229 int sc_y_sensors; /* Number of Y-sensors. */ 230 uint32_t sc_buttons; /* Button state. */ 231 uint32_t sc_status; /* Status flags. */ 232 #define UTPMS_ENABLED 1 /* Is the device enabled? */ 233 #define UTPMS_VALID 4 /* Is the previous sample valid? */ 234 }; 235 236 void utpms_intr(struct uhidev *, void *, unsigned int); 237 int utpms_enable(void *); 238 void utpms_disable(void *); 239 int utpms_ioctl(void *, unsigned long, caddr_t, int, struct proc *); 240 void reorder_sample(struct utpms_softc*, unsigned char *, unsigned char *); 241 int compute_delta(struct utpms_softc *, int *, int *, int *, uint32_t *); 242 int detect_pos(int *, int, int, int, int *, int *); 243 int smooth_pos(int, int, int); 244 245 const struct wsmouse_accessops utpms_accessops = { 246 utpms_enable, 247 utpms_ioctl, 248 utpms_disable, 249 }; 250 251 int utpms_match(struct device *, void *, void *); 252 void utpms_attach(struct device *, struct device *, void *); 253 int utpms_detach(struct device *, int); 254 int utpms_activate(struct device *, int); 255 256 struct cfdriver utpms_cd = { 257 NULL, "utpms", DV_DULL 258 }; 259 260 const struct cfattach utpms_ca = { 261 sizeof(struct utpms_softc), utpms_match, utpms_attach, utpms_detach, 262 utpms_activate, 263 }; 264 265 int 266 utpms_match(struct device *parent, void *match, void *aux) 267 { 268 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 269 usb_interface_descriptor_t *id; 270 int i; 271 272 if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)) 273 return (UMATCH_NONE); 274 275 id = usbd_get_interface_descriptor(uha->uaa->iface); 276 if (id == NULL || 277 id->bInterfaceSubClass != UISUBCLASS_BOOT || 278 id->bInterfaceProtocol != UIPROTO_BOOT_MOUSE) 279 return (UMATCH_NONE); 280 281 /* 282 * We just check if the vendor and product IDs have the magic numbers 283 * we expect. 284 */ 285 for (i = 0; i < nitems(utpms_devices); i++) { 286 if (uha->uaa->vendor == utpms_devices[i].vendor && 287 uha->uaa->product == utpms_devices[i].product) 288 return (UMATCH_IFACECLASS); 289 } 290 291 return (UMATCH_NONE); 292 } 293 294 void 295 utpms_attach(struct device *parent, struct device *self, void *aux) 296 { 297 struct utpms_softc *sc = (struct utpms_softc *)self; 298 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 299 struct wsmousedev_attach_args a; 300 struct utpms_dev *pd; 301 usb_device_descriptor_t *udd; 302 int i; 303 uint16_t vendor, product; 304 305 sc->sc_datalen = UTPMS_DATA_LEN; 306 sc->sc_hdev.sc_udev = uha->uaa->device; 307 308 usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0); 309 310 /* Fill in device-specific parameters. */ 311 if ((udd = usbd_get_device_descriptor(uha->parent->sc_udev)) != NULL) { 312 product = UGETW(udd->idProduct); 313 vendor = UGETW(udd->idVendor); 314 for (i = 0; i < nitems(utpms_devices); i++) { 315 pd = &utpms_devices[i]; 316 if (product == pd->product && vendor == pd->vendor) { 317 sc->sc_noise = pd->noise; 318 sc->sc_threshold = pd->threshold; 319 sc->sc_x_factor = pd->x_factor; 320 sc->sc_x_sensors = pd->x_sensors; 321 sc->sc_y_factor = pd->y_factor; 322 sc->sc_y_sensors = pd->y_sensors; 323 switch (pd->type) { 324 case FOUNTAIN: 325 printf(": Fountain"); 326 break; 327 case GEYSER1: 328 printf(": Geyser"); 329 break; 330 case GEYSER2: 331 sc->sc_type = GEYSER2; 332 sc->sc_datalen = 64; 333 sc->sc_y_sensors = 9; 334 printf(": Geyser 2"); 335 break; 336 } 337 printf(" Trackpad\n"); 338 break; 339 } 340 } 341 } 342 if (sc->sc_x_sensors <= 0 || sc->sc_x_sensors > UTPMS_X_SENSORS || 343 sc->sc_y_sensors <= 0 || sc->sc_y_sensors > UTPMS_Y_SENSORS) { 344 printf(": unexpected sensors configuration (%d:%d)\n", 345 sc->sc_x_sensors, sc->sc_y_sensors); 346 return; 347 } 348 349 sc->sc_hdev.sc_intr = utpms_intr; 350 sc->sc_hdev.sc_parent = uha->parent; 351 sc->sc_hdev.sc_report_id = uha->reportid; 352 353 sc->sc_status = 0; 354 355 a.accessops = &utpms_accessops; 356 a.accesscookie = sc; 357 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); 358 } 359 360 int 361 utpms_detach(struct device *self, int flags) 362 { 363 struct utpms_softc *sc = (struct utpms_softc *)self; 364 int ret = 0; 365 366 /* The wsmouse driver does all the work. */ 367 if (sc->sc_wsmousedev != NULL) 368 ret = config_detach(sc->sc_wsmousedev, flags); 369 370 return (ret); 371 } 372 373 int 374 utpms_activate(struct device *self, int act) 375 { 376 struct utpms_softc *sc = (struct utpms_softc *)self; 377 int rv = 0; 378 379 if (act == DVACT_DEACTIVATE) { 380 if (sc->sc_wsmousedev != NULL) 381 rv = config_deactivate(sc->sc_wsmousedev); 382 } 383 384 return (rv); 385 } 386 387 int 388 utpms_enable(void *v) 389 { 390 struct utpms_softc *sc = v; 391 392 /* Check that we are not detaching or already enabled. */ 393 if (sc->sc_status & usbd_is_dying(sc->sc_hdev.sc_udev)) 394 return (EIO); 395 if (sc->sc_status & UTPMS_ENABLED) 396 return (EBUSY); 397 398 sc->sc_status |= UTPMS_ENABLED; 399 sc->sc_status &= ~UTPMS_VALID; 400 sc->sc_buttons = 0; 401 bzero(sc->sc_sample, sizeof(sc->sc_sample)); 402 403 return (uhidev_open(&sc->sc_hdev)); 404 } 405 406 void 407 utpms_disable(void *v) 408 { 409 struct utpms_softc *sc = v; 410 411 if (!(sc->sc_status & UTPMS_ENABLED)) 412 return; 413 414 sc->sc_status &= ~UTPMS_ENABLED; 415 uhidev_close(&sc->sc_hdev); 416 } 417 418 int 419 utpms_ioctl(void *v, unsigned long cmd, caddr_t data, int flag, struct proc *p) 420 { 421 switch (cmd) { 422 case WSMOUSEIO_GTYPE: 423 *(u_int *)data = WSMOUSE_TYPE_USB; 424 return (0); 425 } 426 427 return (-1); 428 } 429 430 void 431 utpms_intr(struct uhidev *addr, void *ibuf, unsigned int len) 432 { 433 struct utpms_softc *sc = (struct utpms_softc *)addr; 434 unsigned char *data; 435 int dx, dy, dz, i, s; 436 uint32_t buttons; 437 438 /* Ignore incomplete data packets. */ 439 if (len != sc->sc_datalen) 440 return; 441 data = ibuf; 442 443 /* The last byte is 1 if the button is pressed and 0 otherwise. */ 444 buttons = !!data[sc->sc_datalen - 1]; 445 446 /* Everything below assumes that the sample is reordered. */ 447 reorder_sample(sc, sc->sc_sample, data); 448 449 /* Is this the first sample? */ 450 if (!(sc->sc_status & UTPMS_VALID)) { 451 sc->sc_status |= UTPMS_VALID; 452 sc->sc_x = sc->sc_y = -1; 453 sc->sc_x_raw = sc->sc_y_raw = -1; 454 memcpy(sc->sc_prev, sc->sc_sample, sizeof(sc->sc_prev)); 455 bzero(sc->sc_acc, sizeof(sc->sc_acc)); 456 return; 457 } 458 /* Accumulate the sensor change while keeping it nonnegative. */ 459 for (i = 0; i < UTPMS_SENSORS; i++) { 460 sc->sc_acc[i] += 461 (signed char)(sc->sc_sample[i] - sc->sc_prev[i]); 462 463 if (sc->sc_acc[i] < 0) 464 sc->sc_acc[i] = 0; 465 } 466 memcpy(sc->sc_prev, sc->sc_sample, sizeof(sc->sc_prev)); 467 468 /* Compute change. */ 469 dx = dy = dz = 0; 470 if (!compute_delta(sc, &dx, &dy, &dz, &buttons)) 471 return; 472 473 /* Report to wsmouse. */ 474 if ((dx != 0 || dy != 0 || dz != 0 || buttons != sc->sc_buttons) && 475 sc->sc_wsmousedev != NULL) { 476 s = spltty(); 477 WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, -dy, dz, 0); 478 splx(s); 479 } 480 sc->sc_buttons = buttons; 481 } 482 483 /* 484 * Reorder the sensor values so that all the X-sensors are before the 485 * Y-sensors in the natural order. Note that this might have to be 486 * rewritten if UTPMS_X_SENSORS or UTPMS_Y_SENSORS change. 487 */ 488 void 489 reorder_sample(struct utpms_softc *sc, unsigned char *to, unsigned char *from) 490 { 491 int i; 492 493 if (sc->sc_type == GEYSER2) { 494 int j; 495 496 bzero(to, UTPMS_SENSORS); 497 for (i = 0, j = 19; i < 20; i += 2, j += 3) { 498 to[i] = from[j]; 499 to[i + 1] = from[j + 1]; 500 } 501 for (i = 0, j = 1; i < 9; i += 2, j += 3) { 502 to[UTPMS_X_SENSORS + i] = from[j]; 503 to[UTPMS_X_SENSORS + i + 1] = from[j + 1]; 504 } 505 } else { 506 for (i = 0; i < 8; i++) { 507 /* X-sensors. */ 508 to[i] = from[5 * i + 2]; 509 to[i + 8] = from[5 * i + 4]; 510 to[i + 16] = from[5 * i + 42]; 511 #if 0 512 /* 513 * XXX This seems to introduce random vertical jumps, 514 * so we ignore these sensors until we figure out 515 * their meaning. 516 */ 517 if (i < 2) 518 to[i + 24] = from[5 * i + 44]; 519 #endif /* 0 */ 520 /* Y-sensors. */ 521 to[i + 26] = from[5 * i + 1]; 522 to[i + 34] = from[5 * i + 3]; 523 } 524 } 525 } 526 527 /* 528 * Compute the change in x, y and z direction, update the button state 529 * (to simulate more than one button, scrolling etc.), and update the 530 * history. Note that dx, dy, dz and buttons are modified only if 531 * corresponding pressure is detected and should thus be initialised 532 * before the call. Return 0 on error. 533 * 534 * XXX Could we report something useful in dz? 535 */ 536 int 537 compute_delta(struct utpms_softc *sc, int *dx, int *dy, int *dz, 538 uint32_t * buttons) 539 { 540 int x_det, y_det, x_raw, y_raw, x_fingers, y_fingers, fingers, x, y; 541 542 x_det = detect_pos(sc->sc_acc, sc->sc_x_sensors, sc->sc_threshold, 543 sc->sc_x_factor, &x_raw, &x_fingers); 544 y_det = detect_pos(sc->sc_acc + UTPMS_X_SENSORS, sc->sc_y_sensors, 545 sc->sc_threshold, sc->sc_y_factor, 546 &y_raw, &y_fingers); 547 fingers = max(x_fingers, y_fingers); 548 549 /* Check the number of fingers and if we have detected a position. */ 550 if (x_det == 0 && y_det == 0) { 551 /* No position detected, resetting. */ 552 bzero(sc->sc_acc, sizeof(sc->sc_acc)); 553 sc->sc_x_raw = sc->sc_y_raw = sc->sc_x = sc->sc_y = -1; 554 } else if (x_det > 0 && y_det > 0) { 555 switch (fingers) { 556 case 1: 557 /* Smooth position. */ 558 if (sc->sc_x_raw >= 0) { 559 sc->sc_x_raw = (3 * sc->sc_x_raw + x_raw) / 4; 560 sc->sc_y_raw = (3 * sc->sc_y_raw + y_raw) / 4; 561 /* 562 * Compute virtual position and change if we 563 * already have a decent position. 564 */ 565 if (sc->sc_x >= 0) { 566 x = smooth_pos(sc->sc_x, sc->sc_x_raw, 567 sc->sc_noise); 568 y = smooth_pos(sc->sc_y, sc->sc_y_raw, 569 sc->sc_noise); 570 *dx = x - sc->sc_x; 571 *dy = y - sc->sc_y; 572 sc->sc_x = x; 573 sc->sc_y = y; 574 } else { 575 /* Initialise virtual position. */ 576 sc->sc_x = sc->sc_x_raw; 577 sc->sc_y = sc->sc_y_raw; 578 } 579 } else { 580 /* Initialise raw position. */ 581 sc->sc_x_raw = x_raw; 582 sc->sc_y_raw = y_raw; 583 } 584 break; 585 case 2: 586 if (*buttons == 1) 587 *buttons = 4; 588 break; 589 case 3: 590 if (*buttons == 1) 591 *buttons = 2; 592 break; 593 } 594 } 595 return (1); 596 } 597 598 /* 599 * Compute the new smoothed position from the previous smoothed position 600 * and the raw position. 601 */ 602 int 603 smooth_pos(int pos_old, int pos_raw, int noise) 604 { 605 int ad, delta; 606 607 delta = pos_raw - pos_old; 608 ad = abs(delta); 609 610 /* Too small changes are ignored. */ 611 if (ad < noise / 2) 612 delta = 0; 613 /* A bit larger changes are smoothed. */ 614 else if (ad < noise) 615 delta /= 4; 616 else if (ad < 2 * noise) 617 delta /= 2; 618 619 return (pos_old + delta); 620 } 621 622 /* 623 * Detect the position of the finger. Returns the total pressure. 624 * The position is returned in pos_ret and the number of fingers 625 * is returned in fingers_ret. The position returned in pos_ret 626 * is in [0, (n_sensors - 1) * factor - 1]. 627 */ 628 int 629 detect_pos(int *sensors, int n_sensors, int threshold, int fact, 630 int *pos_ret, int *fingers_ret) 631 { 632 int i, w, s; 633 634 /* 635 * Compute the number of fingers, total pressure, and weighted 636 * position of the fingers. 637 */ 638 *fingers_ret = 0; 639 w = s = 0; 640 for (i = 0; i < n_sensors; i++) { 641 if (sensors[i] >= threshold) { 642 if (i == 0 || sensors[i - 1] < threshold) 643 *fingers_ret += 1; 644 s += sensors[i] - threshold; 645 w += (sensors[i] - threshold) * i; 646 } 647 } 648 649 if (s > 0) 650 *pos_ret = w * fact / s; 651 652 return (s); 653 } 654