1 /* $OpenBSD: utpms.c,v 1.10 2020/09/11 19:18:01 tobhe 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 id = usbd_get_interface_descriptor(uha->uaa->iface); 273 if (id == NULL || 274 id->bInterfaceSubClass != UISUBCLASS_BOOT || 275 id->bInterfaceProtocol != UIPROTO_BOOT_MOUSE) 276 return (UMATCH_NONE); 277 278 /* 279 * We just check if the vendor and product IDs have the magic numbers 280 * we expect. 281 */ 282 for (i = 0; i < nitems(utpms_devices); i++) { 283 if (uha->uaa->vendor == utpms_devices[i].vendor && 284 uha->uaa->product == utpms_devices[i].product) 285 return (UMATCH_IFACECLASS); 286 } 287 288 return (UMATCH_NONE); 289 } 290 291 void 292 utpms_attach(struct device *parent, struct device *self, void *aux) 293 { 294 struct utpms_softc *sc = (struct utpms_softc *)self; 295 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 296 struct wsmousedev_attach_args a; 297 struct utpms_dev *pd; 298 usb_device_descriptor_t *udd; 299 int i; 300 uint16_t vendor, product; 301 302 sc->sc_datalen = UTPMS_DATA_LEN; 303 sc->sc_hdev.sc_udev = uha->uaa->device; 304 305 usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0); 306 307 /* Fill in device-specific parameters. */ 308 if ((udd = usbd_get_device_descriptor(uha->parent->sc_udev)) != NULL) { 309 product = UGETW(udd->idProduct); 310 vendor = UGETW(udd->idVendor); 311 for (i = 0; i < nitems(utpms_devices); i++) { 312 pd = &utpms_devices[i]; 313 if (product == pd->product && vendor == pd->vendor) { 314 sc->sc_noise = pd->noise; 315 sc->sc_threshold = pd->threshold; 316 sc->sc_x_factor = pd->x_factor; 317 sc->sc_x_sensors = pd->x_sensors; 318 sc->sc_y_factor = pd->y_factor; 319 sc->sc_y_sensors = pd->y_sensors; 320 switch (pd->type) { 321 case FOUNTAIN: 322 printf(": Fountain"); 323 break; 324 case GEYSER1: 325 printf(": Geyser"); 326 break; 327 case GEYSER2: 328 sc->sc_type = GEYSER2; 329 sc->sc_datalen = 64; 330 sc->sc_y_sensors = 9; 331 printf(": Geyser 2"); 332 break; 333 } 334 printf(" Trackpad\n"); 335 break; 336 } 337 } 338 } 339 if (sc->sc_x_sensors <= 0 || sc->sc_x_sensors > UTPMS_X_SENSORS || 340 sc->sc_y_sensors <= 0 || sc->sc_y_sensors > UTPMS_Y_SENSORS) { 341 printf(": unexpected sensors configuration (%d:%d)\n", 342 sc->sc_x_sensors, sc->sc_y_sensors); 343 return; 344 } 345 346 sc->sc_hdev.sc_intr = utpms_intr; 347 sc->sc_hdev.sc_parent = uha->parent; 348 sc->sc_hdev.sc_report_id = uha->reportid; 349 350 sc->sc_status = 0; 351 352 a.accessops = &utpms_accessops; 353 a.accesscookie = sc; 354 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); 355 } 356 357 int 358 utpms_detach(struct device *self, int flags) 359 { 360 struct utpms_softc *sc = (struct utpms_softc *)self; 361 int ret = 0; 362 363 /* The wsmouse driver does all the work. */ 364 if (sc->sc_wsmousedev != NULL) 365 ret = config_detach(sc->sc_wsmousedev, flags); 366 367 return (ret); 368 } 369 370 int 371 utpms_activate(struct device *self, int act) 372 { 373 struct utpms_softc *sc = (struct utpms_softc *)self; 374 int rv = 0; 375 376 if (act == DVACT_DEACTIVATE) { 377 if (sc->sc_wsmousedev != NULL) 378 rv = config_deactivate(sc->sc_wsmousedev); 379 } 380 381 return (rv); 382 } 383 384 int 385 utpms_enable(void *v) 386 { 387 struct utpms_softc *sc = v; 388 389 /* Check that we are not detaching or already enabled. */ 390 if (sc->sc_status & usbd_is_dying(sc->sc_hdev.sc_udev)) 391 return (EIO); 392 if (sc->sc_status & UTPMS_ENABLED) 393 return (EBUSY); 394 395 sc->sc_status |= UTPMS_ENABLED; 396 sc->sc_status &= ~UTPMS_VALID; 397 sc->sc_buttons = 0; 398 bzero(sc->sc_sample, sizeof(sc->sc_sample)); 399 400 return (uhidev_open(&sc->sc_hdev)); 401 } 402 403 void 404 utpms_disable(void *v) 405 { 406 struct utpms_softc *sc = v; 407 408 if (!(sc->sc_status & UTPMS_ENABLED)) 409 return; 410 411 sc->sc_status &= ~UTPMS_ENABLED; 412 uhidev_close(&sc->sc_hdev); 413 } 414 415 int 416 utpms_ioctl(void *v, unsigned long cmd, caddr_t data, int flag, struct proc *p) 417 { 418 switch (cmd) { 419 case WSMOUSEIO_GTYPE: 420 *(u_int *)data = WSMOUSE_TYPE_USB; 421 return (0); 422 } 423 424 return (-1); 425 } 426 427 void 428 utpms_intr(struct uhidev *addr, void *ibuf, unsigned int len) 429 { 430 struct utpms_softc *sc = (struct utpms_softc *)addr; 431 unsigned char *data; 432 int dx, dy, dz, i, s; 433 uint32_t buttons; 434 435 /* Ignore incomplete data packets. */ 436 if (len != sc->sc_datalen) 437 return; 438 data = ibuf; 439 440 /* The last byte is 1 if the button is pressed and 0 otherwise. */ 441 buttons = !!data[sc->sc_datalen - 1]; 442 443 /* Everything below assumes that the sample is reordered. */ 444 reorder_sample(sc, sc->sc_sample, data); 445 446 /* Is this the first sample? */ 447 if (!(sc->sc_status & UTPMS_VALID)) { 448 sc->sc_status |= UTPMS_VALID; 449 sc->sc_x = sc->sc_y = -1; 450 sc->sc_x_raw = sc->sc_y_raw = -1; 451 memcpy(sc->sc_prev, sc->sc_sample, sizeof(sc->sc_prev)); 452 bzero(sc->sc_acc, sizeof(sc->sc_acc)); 453 return; 454 } 455 /* Accumulate the sensor change while keeping it nonnegative. */ 456 for (i = 0; i < UTPMS_SENSORS; i++) { 457 sc->sc_acc[i] += 458 (signed char)(sc->sc_sample[i] - sc->sc_prev[i]); 459 460 if (sc->sc_acc[i] < 0) 461 sc->sc_acc[i] = 0; 462 } 463 memcpy(sc->sc_prev, sc->sc_sample, sizeof(sc->sc_prev)); 464 465 /* Compute change. */ 466 dx = dy = dz = 0; 467 if (!compute_delta(sc, &dx, &dy, &dz, &buttons)) 468 return; 469 470 /* Report to wsmouse. */ 471 if ((dx != 0 || dy != 0 || dz != 0 || buttons != sc->sc_buttons) && 472 sc->sc_wsmousedev != NULL) { 473 s = spltty(); 474 WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, -dy, dz, 0); 475 splx(s); 476 } 477 sc->sc_buttons = buttons; 478 } 479 480 /* 481 * Reorder the sensor values so that all the X-sensors are before the 482 * Y-sensors in the natural order. Note that this might have to be 483 * rewritten if UTPMS_X_SENSORS or UTPMS_Y_SENSORS change. 484 */ 485 void 486 reorder_sample(struct utpms_softc *sc, unsigned char *to, unsigned char *from) 487 { 488 int i; 489 490 if (sc->sc_type == GEYSER2) { 491 int j; 492 493 bzero(to, UTPMS_SENSORS); 494 for (i = 0, j = 19; i < 20; i += 2, j += 3) { 495 to[i] = from[j]; 496 to[i + 1] = from[j + 1]; 497 } 498 for (i = 0, j = 1; i < 9; i += 2, j += 3) { 499 to[UTPMS_X_SENSORS + i] = from[j]; 500 to[UTPMS_X_SENSORS + i + 1] = from[j + 1]; 501 } 502 } else { 503 for (i = 0; i < 8; i++) { 504 /* X-sensors. */ 505 to[i] = from[5 * i + 2]; 506 to[i + 8] = from[5 * i + 4]; 507 to[i + 16] = from[5 * i + 42]; 508 #if 0 509 /* 510 * XXX This seems to introduce random ventical jumps, 511 * so we ignore these sensors until we figure out 512 * their meaning. 513 */ 514 if (i < 2) 515 to[i + 24] = from[5 * i + 44]; 516 #endif /* 0 */ 517 /* Y-sensors. */ 518 to[i + 26] = from[5 * i + 1]; 519 to[i + 34] = from[5 * i + 3]; 520 } 521 } 522 } 523 524 /* 525 * Compute the change in x, y and z direction, update the button state 526 * (to simulate more than one button, scrolling etc.), and update the 527 * history. Note that dx, dy, dz and buttons are modified only if 528 * corresponding pressure is detected and should thus be initialised 529 * before the call. Return 0 on error. 530 * 531 * XXX Could we report something useful in dz? 532 */ 533 int 534 compute_delta(struct utpms_softc *sc, int *dx, int *dy, int *dz, 535 uint32_t * buttons) 536 { 537 int x_det, y_det, x_raw, y_raw, x_fingers, y_fingers, fingers, x, y; 538 539 x_det = detect_pos(sc->sc_acc, sc->sc_x_sensors, sc->sc_threshold, 540 sc->sc_x_factor, &x_raw, &x_fingers); 541 y_det = detect_pos(sc->sc_acc + UTPMS_X_SENSORS, sc->sc_y_sensors, 542 sc->sc_threshold, sc->sc_y_factor, 543 &y_raw, &y_fingers); 544 fingers = max(x_fingers, y_fingers); 545 546 /* Check the number of fingers and if we have detected a position. */ 547 if (x_det == 0 && y_det == 0) { 548 /* No position detected, resetting. */ 549 bzero(sc->sc_acc, sizeof(sc->sc_acc)); 550 sc->sc_x_raw = sc->sc_y_raw = sc->sc_x = sc->sc_y = -1; 551 } else if (x_det > 0 && y_det > 0) { 552 switch (fingers) { 553 case 1: 554 /* Smooth position. */ 555 if (sc->sc_x_raw >= 0) { 556 sc->sc_x_raw = (3 * sc->sc_x_raw + x_raw) / 4; 557 sc->sc_y_raw = (3 * sc->sc_y_raw + y_raw) / 4; 558 /* 559 * Compute virtual position and change if we 560 * already have a decent position. 561 */ 562 if (sc->sc_x >= 0) { 563 x = smooth_pos(sc->sc_x, sc->sc_x_raw, 564 sc->sc_noise); 565 y = smooth_pos(sc->sc_y, sc->sc_y_raw, 566 sc->sc_noise); 567 *dx = x - sc->sc_x; 568 *dy = y - sc->sc_y; 569 sc->sc_x = x; 570 sc->sc_y = y; 571 } else { 572 /* Initialise virtual position. */ 573 sc->sc_x = sc->sc_x_raw; 574 sc->sc_y = sc->sc_y_raw; 575 } 576 } else { 577 /* Initialise raw position. */ 578 sc->sc_x_raw = x_raw; 579 sc->sc_y_raw = y_raw; 580 } 581 break; 582 case 2: 583 if (*buttons == 1) 584 *buttons = 4; 585 break; 586 case 3: 587 if (*buttons == 1) 588 *buttons = 2; 589 break; 590 } 591 } 592 return (1); 593 } 594 595 /* 596 * Compute the new smoothed position from the previous smoothed position 597 * and the raw position. 598 */ 599 int 600 smooth_pos(int pos_old, int pos_raw, int noise) 601 { 602 int ad, delta; 603 604 delta = pos_raw - pos_old; 605 ad = abs(delta); 606 607 /* Too small changes are ignored. */ 608 if (ad < noise / 2) 609 delta = 0; 610 /* A bit larger changes are smoothed. */ 611 else if (ad < noise) 612 delta /= 4; 613 else if (ad < 2 * noise) 614 delta /= 2; 615 616 return (pos_old + delta); 617 } 618 619 /* 620 * Detect the position of the finger. Returns the total pressure. 621 * The position is returned in pos_ret and the number of fingers 622 * is returned in fingers_ret. The position returned in pos_ret 623 * is in [0, (n_sensors - 1) * factor - 1]. 624 */ 625 int 626 detect_pos(int *sensors, int n_sensors, int threshold, int fact, 627 int *pos_ret, int *fingers_ret) 628 { 629 int i, w, s; 630 631 /* 632 * Compute the number of fingers, total pressure, and weighted 633 * position of the fingers. 634 */ 635 *fingers_ret = 0; 636 w = s = 0; 637 for (i = 0; i < n_sensors; i++) { 638 if (sensors[i] >= threshold) { 639 if (i == 0 || sensors[i - 1] < threshold) 640 *fingers_ret += 1; 641 s += sensors[i]; 642 w += sensors[i] * i; 643 } 644 } 645 646 if (s > 0) 647 *pos_ret = w * fact / s; 648 649 return (s); 650 } 651