1 /* $OpenBSD: utpms.c,v 1.7 2016/06/05 20:02:36 bru 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/usbdevs.h> 118 #include <dev/usb/uhidev.h> 119 120 #include <dev/wscons/wsconsio.h> 121 #include <dev/wscons/wsmousevar.h> 122 123 /* The amount of data transferred by the USB device. */ 124 #define UTPMS_DATA_LEN 81 125 126 /* The maximum number of sensors. */ 127 #define UTPMS_X_SENSORS 26 128 #define UTPMS_Y_SENSORS 16 129 #define UTPMS_SENSORS (UTPMS_X_SENSORS + UTPMS_Y_SENSORS) 130 131 /* 132 * Parameters for supported devices. For generality, these parameters 133 * can be different for each device. The meanings of the parameters 134 * are as follows. 135 * 136 * type: Type of the trackpad device, used for dmesg output, and 137 * to know some of the device parameters. 138 * 139 * noise: Amount of noise in the computed position. This controls 140 * how large a change must be to get reported, and how 141 * large enough changes are smoothed. A good value can 142 * probably only be found experimentally, but something around 143 * 16 seems suitable. 144 * 145 * product: The product ID of the trackpad. 146 * 147 * 148 * threshold: Accumulated changes less than this are ignored. A good 149 * value could be determined experimentally, but 5 is a 150 * reasonable guess. 151 * 152 * vendor: The vendor ID. Currently USB_VENDOR_APPLE for all devices. 153 * 154 * x_factor: Factor used in computations with X-coordinates. If the 155 * x-resolution of the display is x, this should be 156 * (x + 1) / (x_sensors - 1). Other values work fine, but 157 * then the aspect ratio is not necessarily kept. 158 * 159 * x_sensors: The number of sensors in the X-direction. 160 * 161 * y_factor: As x_factors, but for Y-coordinates. 162 * 163 * y_sensors: The number of sensors in the Y-direction. 164 */ 165 166 struct utpms_dev { 167 int type; /* Type of the trackpad. */ 168 #define FOUNTAIN 0x00 169 #define GEYSER1 0x01 170 #define GEYSER2 0x02 171 int noise; /* Amount of noise in the computed position. */ 172 int threshold; /* Changes less than this are ignored. */ 173 int x_factor; /* Factor used in computation with X-coordinates. */ 174 int x_sensors; /* The number of X-sensors. */ 175 int y_factor; /* Factor used in computation with Y-coordinates. */ 176 int y_sensors; /* The number of Y-sensors. */ 177 uint16_t product; /* Product ID. */ 178 uint16_t vendor; /* The vendor ID. */ 179 }; 180 181 static struct utpms_dev utpms_devices[] = { 182 #define UTPMS_TOUCHPAD(ttype, prod, x_fact, x_sens, y_fact) \ 183 { \ 184 .type = (ttype), \ 185 .vendor = USB_VENDOR_APPLE, \ 186 .product = (prod), \ 187 .noise = 16, \ 188 .threshold = 5, \ 189 .x_factor = (x_fact), \ 190 .x_sensors = (x_sens), \ 191 .y_factor = (y_fact), \ 192 .y_sensors = 16 \ 193 } 194 /* 12 inch PowerBooks */ 195 UTPMS_TOUCHPAD(FOUNTAIN, 0x030a, 69, 16, 52), 196 /* 12 and 14 inch iBook G4 */ 197 UTPMS_TOUCHPAD(GEYSER1, 0x030b, 69, 16, 52), 198 /* 15 inch PowerBooks */ 199 UTPMS_TOUCHPAD(FOUNTAIN, 0x020e, 85, 16, 57), 200 UTPMS_TOUCHPAD(FOUNTAIN, 0x020f, 85, 16, 57), 201 UTPMS_TOUCHPAD(GEYSER2, 0x0214, 90, 15, 107), 202 UTPMS_TOUCHPAD(GEYSER2, 0x0215, 90, 15, 107), 203 UTPMS_TOUCHPAD(GEYSER2, 0x0216, 90, 15, 107), 204 /* 17 inch PowerBooks */ 205 UTPMS_TOUCHPAD(FOUNTAIN, 0x020d, 71, 26, 68), 206 #undef UTPMS_TOUCHPAD 207 }; 208 209 struct utpms_softc { 210 struct uhidev sc_hdev; /* USB parent (got the struct device). */ 211 int sc_type; /* Type of the trackpad */ 212 int sc_datalen; 213 int sc_acc[UTPMS_SENSORS]; /* Accumulated sensor values. */ 214 unsigned char sc_prev[UTPMS_SENSORS]; /* Previous sample. */ 215 unsigned char sc_sample[UTPMS_SENSORS]; /* Current sample. */ 216 struct device *sc_wsmousedev; /* WSMouse device. */ 217 int sc_noise; /* Amount of noise. */ 218 int sc_threshold; /* Threshold value. */ 219 int sc_x; /* Virtual position in horizontal 220 * direction (wsmouse position). */ 221 int sc_x_factor; /* X-coordinate factor. */ 222 int sc_x_raw; /* X-position of finger on trackpad. */ 223 int sc_x_sensors; /* Number of X-sensors. */ 224 int sc_y; /* Virtual position in vertical direction 225 * (wsmouse position). */ 226 int sc_y_factor; /* Y-coordinate factor. */ 227 int sc_y_raw; /* Y-position of finger on trackpad. */ 228 int sc_y_sensors; /* Number of Y-sensors. */ 229 uint32_t sc_buttons; /* Button state. */ 230 uint32_t sc_status; /* Status flags. */ 231 #define UTPMS_ENABLED 1 /* Is the device enabled? */ 232 #define UTPMS_VALID 4 /* Is the previous sample valid? */ 233 }; 234 235 void utpms_intr(struct uhidev *, void *, unsigned int); 236 int utpms_enable(void *); 237 void utpms_disable(void *); 238 int utpms_ioctl(void *, unsigned long, caddr_t, int, struct proc *); 239 void reorder_sample(struct utpms_softc*, unsigned char *, unsigned char *); 240 int compute_delta(struct utpms_softc *, int *, int *, int *, uint32_t *); 241 int detect_pos(int *, int, int, int, int *, int *); 242 int smooth_pos(int, int, int); 243 244 const struct wsmouse_accessops utpms_accessops = { 245 utpms_enable, 246 utpms_ioctl, 247 utpms_disable, 248 }; 249 250 int utpms_match(struct device *, void *, void *); 251 void utpms_attach(struct device *, struct device *, void *); 252 int utpms_detach(struct device *, int); 253 int utpms_activate(struct device *, int); 254 255 struct cfdriver utpms_cd = { 256 NULL, "utpms", DV_DULL 257 }; 258 259 const struct cfattach utpms_ca = { 260 sizeof(struct utpms_softc), utpms_match, utpms_attach, utpms_detach, 261 utpms_activate, 262 }; 263 264 int 265 utpms_match(struct device *parent, void *match, void *aux) 266 { 267 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 268 usb_interface_descriptor_t *id; 269 int i; 270 271 id = usbd_get_interface_descriptor(uha->uaa->iface); 272 if (id == NULL || 273 id->bInterfaceSubClass != UISUBCLASS_BOOT || 274 id->bInterfaceProtocol != UIPROTO_BOOT_MOUSE) 275 return (UMATCH_NONE); 276 277 /* 278 * We just check if the vendor and product IDs have the magic numbers 279 * we expect. 280 */ 281 for (i = 0; i < nitems(utpms_devices); i++) { 282 if (uha->uaa->vendor == utpms_devices[i].vendor && 283 uha->uaa->product == utpms_devices[i].product) 284 return (UMATCH_IFACECLASS); 285 } 286 287 return (UMATCH_NONE); 288 } 289 290 void 291 utpms_attach(struct device *parent, struct device *self, void *aux) 292 { 293 struct utpms_softc *sc = (struct utpms_softc *)self; 294 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 295 struct wsmousedev_attach_args a; 296 struct utpms_dev *pd; 297 usb_device_descriptor_t *udd; 298 int i; 299 uint16_t vendor, product; 300 301 sc->sc_datalen = UTPMS_DATA_LEN; 302 sc->sc_hdev.sc_udev = uha->uaa->device; 303 304 /* Fill in device-specific parameters. */ 305 if ((udd = usbd_get_device_descriptor(uha->parent->sc_udev)) != NULL) { 306 product = UGETW(udd->idProduct); 307 vendor = UGETW(udd->idVendor); 308 for (i = 0; i < nitems(utpms_devices); i++) { 309 pd = &utpms_devices[i]; 310 if (product == pd->product && vendor == pd->vendor) { 311 switch (pd->type) { 312 case FOUNTAIN: 313 printf(": Fountain"); 314 break; 315 case GEYSER1: 316 printf(": Geyser"); 317 break; 318 case GEYSER2: 319 sc->sc_type = GEYSER2; 320 sc->sc_datalen = 64; 321 sc->sc_y_sensors = 9; 322 printf(": Geyser 2"); 323 break; 324 } 325 printf(" Trackpad\n"); 326 sc->sc_noise = pd->noise; 327 sc->sc_threshold = pd->threshold; 328 sc->sc_x_factor = pd->x_factor; 329 sc->sc_x_sensors = pd->x_sensors; 330 sc->sc_y_factor = pd->y_factor; 331 sc->sc_y_sensors = pd->y_sensors; 332 break; 333 } 334 } 335 } 336 if (sc->sc_x_sensors <= 0 || sc->sc_x_sensors > UTPMS_X_SENSORS || 337 sc->sc_y_sensors <= 0 || sc->sc_y_sensors > UTPMS_Y_SENSORS) { 338 printf(": unexpected sensors configuration (%d:%d)\n", 339 sc->sc_x_sensors, sc->sc_y_sensors); 340 return; 341 } 342 343 sc->sc_hdev.sc_intr = utpms_intr; 344 sc->sc_hdev.sc_parent = uha->parent; 345 sc->sc_hdev.sc_report_id = uha->reportid; 346 347 sc->sc_status = 0; 348 349 a.accessops = &utpms_accessops; 350 a.accesscookie = sc; 351 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); 352 } 353 354 int 355 utpms_detach(struct device *self, int flags) 356 { 357 struct utpms_softc *sc = (struct utpms_softc *)self; 358 int ret = 0; 359 360 /* The wsmouse driver does all the work. */ 361 if (sc->sc_wsmousedev != NULL) 362 ret = config_detach(sc->sc_wsmousedev, flags); 363 364 return (ret); 365 } 366 367 int 368 utpms_activate(struct device *self, int act) 369 { 370 struct utpms_softc *sc = (struct utpms_softc *)self; 371 int rv = 0; 372 373 if (act == DVACT_DEACTIVATE) { 374 if (sc->sc_wsmousedev != NULL) 375 rv = config_deactivate(sc->sc_wsmousedev); 376 } 377 378 return (rv); 379 } 380 381 int 382 utpms_enable(void *v) 383 { 384 struct utpms_softc *sc = v; 385 386 /* Check that we are not detaching or already enabled. */ 387 if (sc->sc_status & usbd_is_dying(sc->sc_hdev.sc_udev)) 388 return (EIO); 389 if (sc->sc_status & UTPMS_ENABLED) 390 return (EBUSY); 391 392 sc->sc_status |= UTPMS_ENABLED; 393 sc->sc_status &= ~UTPMS_VALID; 394 sc->sc_buttons = 0; 395 bzero(sc->sc_sample, sizeof(sc->sc_sample)); 396 397 return (uhidev_open(&sc->sc_hdev)); 398 } 399 400 void 401 utpms_disable(void *v) 402 { 403 struct utpms_softc *sc = v; 404 405 if (!(sc->sc_status & UTPMS_ENABLED)) 406 return; 407 408 sc->sc_status &= ~UTPMS_ENABLED; 409 uhidev_close(&sc->sc_hdev); 410 } 411 412 int 413 utpms_ioctl(void *v, unsigned long cmd, caddr_t data, int flag, struct proc *p) 414 { 415 switch (cmd) { 416 case WSMOUSEIO_GTYPE: 417 *(u_int *)data = WSMOUSE_TYPE_USB; 418 return (0); 419 } 420 421 return (-1); 422 } 423 424 void 425 utpms_intr(struct uhidev *addr, void *ibuf, unsigned int len) 426 { 427 struct utpms_softc *sc = (struct utpms_softc *)addr; 428 unsigned char *data; 429 int dx, dy, dz, i, s; 430 uint32_t buttons; 431 432 /* Ignore incomplete data packets. */ 433 if (len != sc->sc_datalen) 434 return; 435 data = ibuf; 436 437 /* The last byte is 1 if the button is pressed and 0 otherwise. */ 438 buttons = !!data[sc->sc_datalen - 1]; 439 440 /* Everything below assumes that the sample is reordered. */ 441 reorder_sample(sc, sc->sc_sample, data); 442 443 /* Is this the first sample? */ 444 if (!(sc->sc_status & UTPMS_VALID)) { 445 sc->sc_status |= UTPMS_VALID; 446 sc->sc_x = sc->sc_y = -1; 447 sc->sc_x_raw = sc->sc_y_raw = -1; 448 memcpy(sc->sc_prev, sc->sc_sample, sizeof(sc->sc_prev)); 449 bzero(sc->sc_acc, sizeof(sc->sc_acc)); 450 return; 451 } 452 /* Accumulate the sensor change while keeping it nonnegative. */ 453 for (i = 0; i < UTPMS_SENSORS; i++) { 454 sc->sc_acc[i] += 455 (signed char)(sc->sc_sample[i] - sc->sc_prev[i]); 456 457 if (sc->sc_acc[i] < 0) 458 sc->sc_acc[i] = 0; 459 } 460 memcpy(sc->sc_prev, sc->sc_sample, sizeof(sc->sc_prev)); 461 462 /* Compute change. */ 463 dx = dy = dz = 0; 464 if (!compute_delta(sc, &dx, &dy, &dz, &buttons)) 465 return; 466 467 /* Report to wsmouse. */ 468 if ((dx != 0 || dy != 0 || dz != 0 || buttons != sc->sc_buttons) && 469 sc->sc_wsmousedev != NULL) { 470 s = spltty(); 471 WSMOUSE_INPUT(sc->sc_wsmousedev, buttons, dx, -dy, dz, 0); 472 splx(s); 473 } 474 sc->sc_buttons = buttons; 475 } 476 477 /* 478 * Reorder the sensor values so that all the X-sensors are before the 479 * Y-sensors in the natural order. Note that this might have to be 480 * rewritten if UTPMS_X_SENSORS or UTPMS_Y_SENSORS change. 481 */ 482 void 483 reorder_sample(struct utpms_softc *sc, unsigned char *to, unsigned char *from) 484 { 485 int i; 486 487 if (sc->sc_type == GEYSER2) { 488 int j; 489 490 bzero(to, UTPMS_SENSORS); 491 for (i = 0, j = 19; i < 20; i += 2, j += 3) { 492 to[i] = from[j]; 493 to[i + 1] = from[j + 1]; 494 } 495 for (i = 0, j = 1; i < 9; i += 2, j += 3) { 496 to[UTPMS_X_SENSORS + i] = from[j]; 497 to[UTPMS_X_SENSORS + i + 1] = from[j + 1]; 498 } 499 } else { 500 for (i = 0; i < 8; i++) { 501 /* X-sensors. */ 502 to[i] = from[5 * i + 2]; 503 to[i + 8] = from[5 * i + 4]; 504 to[i + 16] = from[5 * i + 42]; 505 #if 0 506 /* 507 * XXX This seems to introduce random ventical jumps, 508 * so we ignore these sensors until we figure out 509 * their meaning. 510 */ 511 if (i < 2) 512 to[i + 24] = from[5 * i + 44]; 513 #endif /* 0 */ 514 /* Y-sensors. */ 515 to[i + 26] = from[5 * i + 1]; 516 to[i + 34] = from[5 * i + 3]; 517 } 518 } 519 } 520 521 /* 522 * Compute the change in x, y and z direction, update the button state 523 * (to simulate more than one button, scrolling etc.), and update the 524 * history. Note that dx, dy, dz and buttons are modified only if 525 * corresponding pressure is detected and should thus be initialised 526 * before the call. Return 0 on error. 527 * 528 * XXX Could we report something useful in dz? 529 */ 530 int 531 compute_delta(struct utpms_softc *sc, int *dx, int *dy, int *dz, 532 uint32_t * buttons) 533 { 534 int x_det, y_det, x_raw, y_raw, x_fingers, y_fingers, fingers, x, y; 535 536 x_det = detect_pos(sc->sc_acc, sc->sc_x_sensors, sc->sc_threshold, 537 sc->sc_x_factor, &x_raw, &x_fingers); 538 y_det = detect_pos(sc->sc_acc + UTPMS_X_SENSORS, sc->sc_y_sensors, 539 sc->sc_threshold, sc->sc_y_factor, 540 &y_raw, &y_fingers); 541 fingers = max(x_fingers, y_fingers); 542 543 /* Check the number of fingers and if we have detected a position. */ 544 if (x_det == 0 && y_det == 0) { 545 /* No position detected, resetting. */ 546 bzero(sc->sc_acc, sizeof(sc->sc_acc)); 547 sc->sc_x_raw = sc->sc_y_raw = sc->sc_x = sc->sc_y = -1; 548 } else if (x_det > 0 && y_det > 0) { 549 switch (fingers) { 550 case 1: 551 /* Smooth position. */ 552 if (sc->sc_x_raw >= 0) { 553 sc->sc_x_raw = (3 * sc->sc_x_raw + x_raw) / 4; 554 sc->sc_y_raw = (3 * sc->sc_y_raw + y_raw) / 4; 555 /* 556 * Compute virtual position and change if we 557 * already have a decent position. 558 */ 559 if (sc->sc_x >= 0) { 560 x = smooth_pos(sc->sc_x, sc->sc_x_raw, 561 sc->sc_noise); 562 y = smooth_pos(sc->sc_y, sc->sc_y_raw, 563 sc->sc_noise); 564 *dx = x - sc->sc_x; 565 *dy = y - sc->sc_y; 566 sc->sc_x = x; 567 sc->sc_y = y; 568 } else { 569 /* Initialise virtual position. */ 570 sc->sc_x = sc->sc_x_raw; 571 sc->sc_y = sc->sc_y_raw; 572 } 573 } else { 574 /* Initialise raw position. */ 575 sc->sc_x_raw = x_raw; 576 sc->sc_y_raw = y_raw; 577 } 578 break; 579 case 2: 580 if (*buttons == 1) 581 *buttons = 4; 582 break; 583 case 3: 584 if (*buttons == 1) 585 *buttons = 2; 586 break; 587 } 588 } 589 return (1); 590 } 591 592 /* 593 * Compute the new smoothed position from the previous smoothed position 594 * and the raw position. 595 */ 596 int 597 smooth_pos(int pos_old, int pos_raw, int noise) 598 { 599 int ad, delta; 600 601 delta = pos_raw - pos_old; 602 ad = abs(delta); 603 604 /* Too small changes are ignored. */ 605 if (ad < noise / 2) 606 delta = 0; 607 /* A bit larger changes are smoothed. */ 608 else if (ad < noise) 609 delta /= 4; 610 else if (ad < 2 * noise) 611 delta /= 2; 612 613 return (pos_old + delta); 614 } 615 616 /* 617 * Detect the position of the finger. Returns the total pressure. 618 * The position is returned in pos_ret and the number of fingers 619 * is returned in fingers_ret. The position returned in pos_ret 620 * is in [0, (n_sensors - 1) * factor - 1]. 621 */ 622 int 623 detect_pos(int *sensors, int n_sensors, int threshold, int fact, 624 int *pos_ret, int *fingers_ret) 625 { 626 int i, w, s; 627 628 /* 629 * Compute the number of fingers, total pressure, and weighted 630 * position of the fingers. 631 */ 632 *fingers_ret = 0; 633 w = s = 0; 634 for (i = 0; i < n_sensors; i++) { 635 if (sensors[i] >= threshold) { 636 if (i == 0 || sensors[i - 1] < threshold) 637 *fingers_ret += 1; 638 s += sensors[i]; 639 w += sensors[i] * i; 640 } 641 } 642 643 if (s > 0) 644 *pos_ret = w * fact / s; 645 646 return (s); 647 } 648