jcs's openbsd hax
openbsd
at jcs 650 lines 20 kB view raw
1/* $OpenBSD: utpms.c,v 1.14 2024/05/23 03:21:09 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#include <sys/systm.h> 111 112#include <dev/usb/usb.h> 113#include <dev/usb/usbdi.h> 114#include <dev/usb/usbdi_util.h> 115#include <dev/usb/usbdevs.h> 116#include <dev/usb/uhidev.h> 117 118#include <dev/wscons/wsconsio.h> 119#include <dev/wscons/wsmousevar.h> 120 121/* The amount of data transferred by the USB device. */ 122#define UTPMS_DATA_LEN 81 123 124/* The maximum number of sensors. */ 125#define UTPMS_X_SENSORS 26 126#define UTPMS_Y_SENSORS 16 127#define UTPMS_SENSORS (UTPMS_X_SENSORS + UTPMS_Y_SENSORS) 128 129/* 130 * Parameters for supported devices. For generality, these parameters 131 * can be different for each device. The meanings of the parameters 132 * are as follows. 133 * 134 * type: Type of the trackpad device, used for dmesg output, and 135 * to know some of the device parameters. 136 * 137 * noise: Amount of noise in the computed position. This controls 138 * how large a change must be to get reported, and how 139 * large enough changes are smoothed. A good value can 140 * probably only be found experimentally, but something around 141 * 16 seems suitable. 142 * 143 * product: The product ID of the trackpad. 144 * 145 * 146 * threshold: Accumulated changes less than this are ignored. A good 147 * value could be determined experimentally, but 5 is a 148 * reasonable guess. 149 * 150 * vendor: The vendor ID. Currently USB_VENDOR_APPLE for all devices. 151 * 152 * x_factor: Factor used in computations with X-coordinates. If the 153 * x-resolution of the display is x, this should be 154 * (x + 1) / (x_sensors - 1). Other values work fine, but 155 * then the aspect ratio is not necessarily kept. 156 * 157 * x_sensors: The number of sensors in the X-direction. 158 * 159 * y_factor: As x_factors, but for Y-coordinates. 160 * 161 * y_sensors: The number of sensors in the Y-direction. 162 */ 163 164struct utpms_dev { 165 int type; /* Type of the trackpad. */ 166#define FOUNTAIN 0x00 167#define GEYSER1 0x01 168#define GEYSER2 0x02 169 int noise; /* Amount of noise in the computed position. */ 170 int threshold; /* Changes less than this are ignored. */ 171 int x_factor; /* Factor used in computation with X-coordinates. */ 172 int x_sensors; /* The number of X-sensors. */ 173 int y_factor; /* Factor used in computation with Y-coordinates. */ 174 int y_sensors; /* The number of Y-sensors. */ 175 uint16_t product; /* Product ID. */ 176 uint16_t vendor; /* The vendor ID. */ 177}; 178 179static struct utpms_dev utpms_devices[] = { 180#define UTPMS_TOUCHPAD(ttype, prod, x_fact, x_sens, y_fact) \ 181 { \ 182 .type = (ttype), \ 183 .vendor = USB_VENDOR_APPLE, \ 184 .product = (prod), \ 185 .noise = 16, \ 186 .threshold = 5, \ 187 .x_factor = (x_fact), \ 188 .x_sensors = (x_sens), \ 189 .y_factor = (y_fact), \ 190 .y_sensors = 16 \ 191 } 192 /* 12 inch PowerBooks */ 193 UTPMS_TOUCHPAD(FOUNTAIN, 0x030a, 69, 16, 52), 194 /* 12 and 14 inch iBook G4 */ 195 UTPMS_TOUCHPAD(GEYSER1, 0x030b, 69, 16, 52), 196 /* 15 inch PowerBooks */ 197 UTPMS_TOUCHPAD(FOUNTAIN, 0x020e, 85, 16, 57), 198 UTPMS_TOUCHPAD(FOUNTAIN, 0x020f, 85, 16, 57), 199 UTPMS_TOUCHPAD(GEYSER2, 0x0214, 90, 15, 107), 200 UTPMS_TOUCHPAD(GEYSER2, 0x0215, 90, 15, 107), 201 UTPMS_TOUCHPAD(GEYSER2, 0x0216, 90, 15, 107), 202 /* 17 inch PowerBooks */ 203 UTPMS_TOUCHPAD(FOUNTAIN, 0x020d, 71, 26, 68), 204#undef UTPMS_TOUCHPAD 205}; 206 207struct utpms_softc { 208 struct uhidev sc_hdev; /* USB parent (got the struct device). */ 209 int sc_type; /* Type of the trackpad */ 210 int sc_datalen; 211 int sc_acc[UTPMS_SENSORS]; /* Accumulated sensor values. */ 212 unsigned char sc_prev[UTPMS_SENSORS]; /* Previous sample. */ 213 unsigned char sc_sample[UTPMS_SENSORS]; /* Current sample. */ 214 struct device *sc_wsmousedev; /* WSMouse device. */ 215 int sc_noise; /* Amount of noise. */ 216 int sc_threshold; /* Threshold value. */ 217 int sc_x; /* Virtual position in horizontal 218 * direction (wsmouse position). */ 219 int sc_x_factor; /* X-coordinate factor. */ 220 int sc_x_raw; /* X-position of finger on trackpad. */ 221 int sc_x_sensors; /* Number of X-sensors. */ 222 int sc_y; /* Virtual position in vertical direction 223 * (wsmouse position). */ 224 int sc_y_factor; /* Y-coordinate factor. */ 225 int sc_y_raw; /* Y-position of finger on trackpad. */ 226 int sc_y_sensors; /* Number of Y-sensors. */ 227 uint32_t sc_buttons; /* Button state. */ 228 uint32_t sc_status; /* Status flags. */ 229#define UTPMS_ENABLED 1 /* Is the device enabled? */ 230#define UTPMS_VALID 4 /* Is the previous sample valid? */ 231}; 232 233void utpms_intr(struct uhidev *, void *, unsigned int); 234int utpms_enable(void *); 235void utpms_disable(void *); 236int utpms_ioctl(void *, unsigned long, caddr_t, int, struct proc *); 237void reorder_sample(struct utpms_softc*, unsigned char *, unsigned char *); 238int compute_delta(struct utpms_softc *, int *, int *, int *, uint32_t *); 239int detect_pos(int *, int, int, int, int *, int *); 240int smooth_pos(int, int, int); 241 242const struct wsmouse_accessops utpms_accessops = { 243 utpms_enable, 244 utpms_ioctl, 245 utpms_disable, 246}; 247 248int utpms_match(struct device *, void *, void *); 249void utpms_attach(struct device *, struct device *, void *); 250int utpms_detach(struct device *, int); 251int utpms_activate(struct device *, int); 252 253struct cfdriver utpms_cd = { 254 NULL, "utpms", DV_DULL 255}; 256 257const struct cfattach utpms_ca = { 258 sizeof(struct utpms_softc), utpms_match, utpms_attach, utpms_detach, 259 utpms_activate, 260}; 261 262int 263utpms_match(struct device *parent, void *match, void *aux) 264{ 265 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 266 usb_interface_descriptor_t *id; 267 int i; 268 269 if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)) 270 return (UMATCH_NONE); 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 291void 292utpms_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 357int 358utpms_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 370int 371utpms_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 384int 385utpms_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 403void 404utpms_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 415int 416utpms_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 427void 428utpms_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 */ 485void 486reorder_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 vertical 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 */ 533int 534compute_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 */ 599int 600smooth_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 */ 625int 626detect_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] - threshold; 642 w += (sensors[i] - threshold) * i; 643 } 644 } 645 646 if (s > 0) 647 *pos_ret = w * fact / s; 648 649 return (s); 650}