xref: /openbsd/sys/dev/usb/utpms.c (revision 4bdff4be)
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