pbms.c revision 1.20 1 1.20 riastrad /* $Id: pbms.c,v 1.20 2022/03/28 12:43:12 riastradh Exp $ */
2 1.1 christos
3 1.1 christos /*
4 1.1 christos * Copyright (c) 2005, Johan Walln
5 1.1 christos * All rights reserved.
6 1.1 christos *
7 1.1 christos * Redistribution and use in source and binary forms, with or without
8 1.1 christos * modification, are permitted provided that the following conditions are
9 1.1 christos * met:
10 1.1 christos *
11 1.1 christos * 1. Redistributions of source code must retain the above copyright
12 1.1 christos * notice, this list of conditions and the following disclaimer.
13 1.1 christos *
14 1.1 christos * 2. Redistributions in binary form must reproduce the above
15 1.1 christos * copyright notice, this list of conditions and the following
16 1.1 christos * disclaimer in the documentation and/or other materials provided
17 1.1 christos * with the distribution.
18 1.1 christos *
19 1.1 christos * 3. The name of the copyright holder may not be used to endorse or
20 1.1 christos * promote products derived from this software without specific
21 1.1 christos * prior written permission.
22 1.1 christos *
23 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
24 1.1 christos * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 1.1 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 1.1 christos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE
27 1.1 christos * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 1.1 christos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 1.1 christos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
30 1.1 christos * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 1.1 christos * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 1.1 christos * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 1.1 christos * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 1.1 christos */
35 1.1 christos
36 1.1 christos /*
37 1.1 christos * The pbms driver provides support for the trackpad on new (post
38 1.1 christos * February 2005) Apple PowerBooks (and iBooks?) that are not standard
39 1.1 christos * USB HID mice.
40 1.1 christos */
41 1.1 christos
42 1.1 christos /*
43 1.1 christos * The protocol (that is, the interpretation of the data generated by
44 1.1 christos * the trackpad) is taken from the Linux appletouch driver version
45 1.1 christos * 0.08 by Johannes Berg, Stelian Pop and Frank Arnold. The method
46 1.1 christos * used to detect fingers on the trackpad is also taken from that
47 1.1 christos * driver.
48 1.1 christos */
49 1.1 christos
50 1.1 christos /*
51 1.1 christos * To add support for other devices using the same protocol, add an
52 1.1 christos * entry to the pbms_devices table below. See the comments for
53 1.1 christos * pbms_devices and struct pbms_devs.
54 1.1 christos */
55 1.1 christos
56 1.1 christos /*
57 1.1 christos * PROTOCOL:
58 1.1 christos *
59 1.1 christos * The driver transfers continuously 81 byte events. The last byte is
60 1.1 christos * 1 if the button is pressed, and is 0 otherwise. Of the remaining
61 1.1 christos * bytes, 26 + 16 = 42 are sensors detecting pressure in the X or
62 1.1 christos * horizontal, and Y or vertical directions, respectively. On 12 and
63 1.1 christos * 15 inch PowerBooks, only the 16 first sensors in the X-direction
64 1.1 christos * are used. In the X-direction, the sensors correspond to byte
65 1.1 christos * positions
66 1.1 christos *
67 1.1 christos * 2, 7, 12, 17, 22, 27, 32, 37, 4, 9, 14, 19, 24, 29, 34, 39, 42,
68 1.1 christos * 47, 52, 57, 62, 67, 72, 77, 44 and 49;
69 1.1 christos *
70 1.12 phx * In the Y direction, the sensors correspond to byte positions
71 1.1 christos *
72 1.1 christos * 1, 6, 11, 16, 21, 26, 31, 36, 3, 8, 13, 18, 23, 28, 33 and 38.
73 1.1 christos *
74 1.12 phx * On 12 inch iBooks only the 9 first sensors in Y-direction are used.
75 1.1 christos * The change in the sensor values over time is more interesting than
76 1.1 christos * their absolute values: if the pressure increases, we know that the
77 1.1 christos * finger has just moved there.
78 1.1 christos *
79 1.1 christos * We keep track of the previous sample (of sensor values in the X and
80 1.1 christos * Y directions) and the accumulated change for each sensor. When we
81 1.1 christos * receive a new sample, we add the difference of the new sensor value
82 1.1 christos * and the old value to the accumulated change. If the accumulator
83 1.1 christos * becomes negative, we set it to zero. The effect is that the
84 1.1 christos * accumulator is large for sensors whose pressure has recently
85 1.1 christos * increased. If there is little change in pressure (or if the
86 1.1 christos * pressure decreases), the accumulator drifts back to zero.
87 1.1 christos *
88 1.1 christos * Since there is some fluctuations, we ignore accumulator values
89 1.1 christos * below a threshold. The raw finger position is computed as a
90 1.1 christos * weighted average of the other sensors (the weights are the
91 1.1 christos * accumulated changes).
92 1.1 christos *
93 1.1 christos * For smoothing, we keep track of the previous raw finger position,
94 1.1 christos * and the virtual position reported to wsmouse. The new raw position
95 1.1 christos * is computed as a weighted average of the old raw position and the
96 1.1 christos * computed raw position. Since this still generates some noise, we
97 1.1 christos * compute a new virtual position as a weighted average of the previous
98 1.1 christos * virtual position and the new raw position. The weights are
99 1.1 christos * controlled by the raw change and a noise parameter. The position
100 1.1 christos * is reported as a relative position.
101 1.1 christos */
102 1.1 christos
103 1.1 christos /*
104 1.1 christos * TODO:
105 1.1 christos *
106 1.1 christos * Add support for other drivers of the same type.
107 1.1 christos *
108 1.1 christos * Add support for tapping and two-finger scrolling? The
109 1.1 christos * implementation already detects two fingers, so this should be
110 1.1 christos * relatively easy.
111 1.1 christos *
112 1.1 christos * Implement some of the mouse ioctls?
113 1.1 christos *
114 1.1 christos * Take care of the XXXs.
115 1.1 christos *
116 1.1 christos */
117 1.1 christos
118 1.1 christos #include <sys/cdefs.h>
119 1.1 christos
120 1.1 christos #include <sys/param.h>
121 1.1 christos #include <sys/device.h>
122 1.1 christos #include <sys/errno.h>
123 1.1 christos
124 1.1 christos #include <sys/ioctl.h>
125 1.1 christos #include <sys/systm.h>
126 1.1 christos #include <sys/tty.h>
127 1.1 christos
128 1.1 christos #include <dev/usb/usb.h>
129 1.1 christos #include <dev/usb/usbdi.h>
130 1.1 christos #include <dev/usb/usbdevs.h>
131 1.1 christos #include <dev/usb/uhidev.h>
132 1.15 maya #include <dev/hid/hid.h>
133 1.1 christos
134 1.1 christos #include <dev/wscons/wsconsio.h>
135 1.1 christos #include <dev/wscons/wsmousevar.h>
136 1.1 christos
137 1.1 christos /*
138 1.1 christos * Magic numbers.
139 1.1 christos */
140 1.1 christos
141 1.1 christos
142 1.18 andvar /* The amount of data transferred by the USB device. */
143 1.1 christos #define PBMS_DATA_LEN 81
144 1.1 christos
145 1.1 christos /* The maximum number of sensors. */
146 1.1 christos #define PBMS_X_SENSORS 26
147 1.1 christos #define PBMS_Y_SENSORS 16
148 1.1 christos #define PBMS_SENSORS (PBMS_X_SENSORS + PBMS_Y_SENSORS)
149 1.1 christos
150 1.1 christos /*
151 1.1 christos * Parameters for supported devices. For generality, these parameters
152 1.1 christos * can be different for each device. The meanings of the parameters
153 1.1 christos * are as follows.
154 1.1 christos *
155 1.1 christos * desc: A printable description used for dmesg output.
156 1.1 christos *
157 1.1 christos * noise: Amount of noise in the computed position. This controls
158 1.1 christos * how large a change must be to get reported, and how
159 1.1 christos * large enough changes are smoothed. A good value can
160 1.1 christos * probably only be found experimentally, but something around
161 1.1 christos * 16 seems suitable.
162 1.1 christos *
163 1.1 christos * product: The product ID of the trackpad.
164 1.1 christos *
165 1.1 christos *
166 1.1 christos * threshold: Accumulated changes less than this are ignored. A good
167 1.1 christos * value could be determined experimentally, but 5 is a
168 1.1 christos * reasonable guess.
169 1.1 christos *
170 1.1 christos * vendor: The vendor ID. Currently USB_VENDOR_APPLE for all devices.
171 1.1 christos *
172 1.1 christos * x_factor: Factor used in computations with X-coordinates. If the
173 1.1 christos * x-resolution of the display is x, this should be
174 1.1 christos * (x + 1) / (x_sensors - 1). Other values work fine, but
175 1.1 christos * then the aspect ratio is not necessarily kept.
176 1.1 christos *
177 1.1 christos * x_sensors: The number of sensors in the X-direction.
178 1.1 christos *
179 1.1 christos * y_factor: As x_factors, but for Y-coordinates.
180 1.1 christos *
181 1.1 christos * y_sensors: The number of sensors in the Y-direction.
182 1.1 christos */
183 1.1 christos
184 1.1 christos struct pbms_dev {
185 1.1 christos const char *descr; /* Description of the driver (for dmesg). */
186 1.1 christos int noise; /* Amount of noise in the computed position. */
187 1.1 christos int threshold; /* Changes less than this are ignored. */
188 1.1 christos int x_factor; /* Factor used in computation with X-coordinates. */
189 1.1 christos int x_sensors; /* The number of X-sensors. */
190 1.1 christos int y_factor; /* Factor used in computation with Y-coordinates. */
191 1.1 christos int y_sensors; /* The number of Y-sensors. */
192 1.1 christos uint16_t product; /* Product ID. */
193 1.1 christos uint16_t vendor; /* The vendor ID. */
194 1.1 christos };
195 1.1 christos
196 1.1 christos /* Devices supported by this driver. */
197 1.1 christos static struct pbms_dev pbms_devices[] =
198 1.1 christos {
199 1.1 christos #define POWERBOOK_TOUCHPAD(inches, prod, x_fact, x_sens, y_fact) \
200 1.1 christos { \
201 1.1 christos .descr = #inches " inch PowerBook Trackpad", \
202 1.1 christos .vendor = USB_VENDOR_APPLE, \
203 1.1 christos .product = (prod), \
204 1.1 christos .noise = 16, \
205 1.1 christos .threshold = 5, \
206 1.1 christos .x_factor = (x_fact), \
207 1.1 christos .x_sensors = (x_sens), \
208 1.1 christos .y_factor = (y_fact), \
209 1.1 christos .y_sensors = 16 \
210 1.1 christos }
211 1.12 phx /* 12 inch PowerBooks/iBooks */
212 1.1 christos POWERBOOK_TOUCHPAD(12, 0x030a, 69, 16, 52), /* XXX Not tested. */
213 1.12 phx POWERBOOK_TOUCHPAD(12, 0x030b, 73, 15, 96),
214 1.1 christos /* 15 inch PowerBooks */
215 1.1 christos POWERBOOK_TOUCHPAD(15, 0x020e, 85, 16, 57), /* XXX Not tested. */
216 1.1 christos POWERBOOK_TOUCHPAD(15, 0x020f, 85, 16, 57),
217 1.8 aymeric POWERBOOK_TOUCHPAD(15, 0x0215, 90, 15, 107),
218 1.1 christos /* 17 inch PowerBooks */
219 1.1 christos POWERBOOK_TOUCHPAD(17, 0x020d, 71, 26, 68) /* XXX Not tested. */
220 1.1 christos #undef POWERBOOK_TOUCHPAD
221 1.1 christos };
222 1.1 christos
223 1.1 christos /* The number of supported devices. */
224 1.1 christos #define PBMS_NUM_DEVICES (sizeof(pbms_devices) / sizeof(pbms_devices[0]))
225 1.1 christos
226 1.1 christos
227 1.1 christos /*
228 1.1 christos * Types and prototypes.
229 1.1 christos */
230 1.1 christos
231 1.1 christos
232 1.1 christos /* Device data. */
233 1.1 christos struct pbms_softc {
234 1.13 chs struct uhidev sc_hdev; /* USB parent */
235 1.6 aymeric int is_geyser2;
236 1.12 phx int sc_datalen; /* Size of a data packet */
237 1.12 phx int sc_bufusage; /* Number of bytes in sc_databuf */
238 1.1 christos int sc_acc[PBMS_SENSORS]; /* Accumulated sensor values. */
239 1.6 aymeric unsigned char sc_prev[PBMS_SENSORS]; /* Previous sample. */
240 1.6 aymeric unsigned char sc_sample[PBMS_SENSORS]; /* Current sample. */
241 1.12 phx uint8_t sc_databuf[PBMS_DATA_LEN]; /* Buffer for a data packet */
242 1.9 dyoung device_t sc_wsmousedev; /* WSMouse device. */
243 1.1 christos int sc_noise; /* Amount of noise. */
244 1.1 christos int sc_theshold; /* Threshold value. */
245 1.1 christos int sc_x; /* Virtual position in horizontal
246 1.1 christos * direction (wsmouse position). */
247 1.1 christos int sc_x_factor; /* X-coordinate factor. */
248 1.1 christos int sc_x_raw; /* X-position of finger on trackpad. */
249 1.1 christos int sc_x_sensors; /* Number of X-sensors. */
250 1.1 christos int sc_y; /* Virtual position in vertical direction
251 1.1 christos * (wsmouse position). */
252 1.1 christos int sc_y_factor; /* Y-coordinate factor. */
253 1.1 christos int sc_y_raw; /* Y-position of finger on trackpad. */
254 1.1 christos int sc_y_sensors; /* Number of Y-sensors. */
255 1.1 christos uint32_t sc_buttons; /* Button state. */
256 1.1 christos uint32_t sc_status; /* Status flags. */
257 1.1 christos #define PBMS_ENABLED 1 /* Is the device enabled? */
258 1.1 christos #define PBMS_DYING 2 /* Is the device dying? */
259 1.1 christos #define PBMS_VALID 4 /* Is the previous sample valid? */
260 1.1 christos };
261 1.1 christos
262 1.1 christos
263 1.1 christos /* Static function prototypes. */
264 1.1 christos static void pbms_intr(struct uhidev *, void *, unsigned int);
265 1.1 christos static int pbms_enable(void *);
266 1.1 christos static void pbms_disable(void *);
267 1.5 christos static int pbms_ioctl(void *, unsigned long, void *, int, struct lwp *);
268 1.6 aymeric static void reorder_sample(struct pbms_softc *, unsigned char *, unsigned char *);
269 1.1 christos static int compute_delta(struct pbms_softc *, int *, int *, int *, uint32_t *);
270 1.1 christos static int detect_pos(int *, int, int, int, int *, int *);
271 1.1 christos static int smooth_pos(int, int, int);
272 1.1 christos
273 1.1 christos /* Access methods for wsmouse. */
274 1.1 christos const struct wsmouse_accessops pbms_accessops = {
275 1.1 christos pbms_enable,
276 1.1 christos pbms_ioctl,
277 1.1 christos pbms_disable,
278 1.1 christos };
279 1.1 christos
280 1.1 christos /* This take cares also of the basic device registration. */
281 1.9 dyoung int pbms_match(device_t, cfdata_t, void *);
282 1.9 dyoung void pbms_attach(device_t, device_t, void *);
283 1.9 dyoung int pbms_detach(device_t, int);
284 1.9 dyoung void pbms_childdet(device_t, device_t);
285 1.9 dyoung int pbms_activate(device_t, enum devact);
286 1.9 dyoung extern struct cfdriver pbms_cd;
287 1.9 dyoung CFATTACH_DECL2_NEW(pbms, sizeof(struct pbms_softc), pbms_match, pbms_attach,
288 1.9 dyoung pbms_detach, pbms_activate, NULL, pbms_childdet);
289 1.1 christos
290 1.1 christos /*
291 1.1 christos * Basic driver.
292 1.1 christos */
293 1.1 christos
294 1.1 christos
295 1.1 christos /* Try to match the device at some uhidev. */
296 1.1 christos
297 1.1 christos int
298 1.9 dyoung pbms_match(device_t parent, cfdata_t match, void *aux)
299 1.1 christos {
300 1.1 christos struct uhidev_attach_arg *uha = aux;
301 1.1 christos usb_device_descriptor_t *udd;
302 1.1 christos int i;
303 1.1 christos uint16_t vendor, product;
304 1.1 christos
305 1.1 christos /*
306 1.1 christos * We just check if the vendor and product IDs have the magic numbers
307 1.1 christos * we expect.
308 1.1 christos */
309 1.14 skrll if (uha->uiaa->uiaa_proto == UIPROTO_MOUSE &&
310 1.20 riastrad ((udd = usbd_get_device_descriptor(uha->uiaa->uiaa_device))
311 1.20 riastrad != NULL)) {
312 1.1 christos vendor = UGETW(udd->idVendor);
313 1.1 christos product = UGETW(udd->idProduct);
314 1.1 christos for (i = 0; i < PBMS_NUM_DEVICES; i++) {
315 1.1 christos if (vendor == pbms_devices[i].vendor &&
316 1.1 christos product == pbms_devices[i].product)
317 1.1 christos return UMATCH_IFACECLASS;
318 1.1 christos }
319 1.1 christos }
320 1.1 christos return UMATCH_NONE;
321 1.1 christos }
322 1.1 christos
323 1.1 christos
324 1.1 christos /* Attach the device. */
325 1.1 christos
326 1.1 christos void
327 1.9 dyoung pbms_attach(device_t parent, device_t self, void *aux)
328 1.1 christos {
329 1.1 christos struct wsmousedev_attach_args a;
330 1.1 christos struct uhidev_attach_arg *uha = aux;
331 1.1 christos struct pbms_dev *pd;
332 1.8 aymeric struct pbms_softc *sc = device_private(self);
333 1.20 riastrad struct usbd_device *udev;
334 1.1 christos usb_device_descriptor_t *udd;
335 1.1 christos int i;
336 1.1 christos uint16_t vendor, product;
337 1.1 christos
338 1.1 christos sc->sc_hdev.sc_intr = pbms_intr;
339 1.1 christos sc->sc_hdev.sc_parent = uha->parent;
340 1.1 christos sc->sc_hdev.sc_report_id = uha->reportid;
341 1.1 christos
342 1.6 aymeric sc->is_geyser2 = 0;
343 1.6 aymeric sc->sc_datalen = PBMS_DATA_LEN;
344 1.6 aymeric
345 1.1 christos /* Fill in device-specific parameters. */
346 1.20 riastrad udev = uha->uiaa->uiaa_udevice;
347 1.20 riastrad if ((udd = usbd_get_device_descriptor(udev)) != NULL) {
348 1.1 christos product = UGETW(udd->idProduct);
349 1.1 christos vendor = UGETW(udd->idVendor);
350 1.1 christos for (i = 0; i < PBMS_NUM_DEVICES; i++) {
351 1.1 christos pd = &pbms_devices[i];
352 1.1 christos if (product == pd->product && vendor == pd->vendor) {
353 1.1 christos printf(": %s\n", pd->descr);
354 1.1 christos sc->sc_noise = pd->noise;
355 1.1 christos sc->sc_theshold = pd->threshold;
356 1.1 christos sc->sc_x_factor = pd->x_factor;
357 1.1 christos sc->sc_x_sensors = pd->x_sensors;
358 1.1 christos sc->sc_y_factor = pd->y_factor;
359 1.1 christos sc->sc_y_sensors = pd->y_sensors;
360 1.8 aymeric if (product == 0x0215) {
361 1.6 aymeric sc->is_geyser2 = 1;
362 1.8 aymeric sc->sc_datalen = 64;
363 1.6 aymeric sc->sc_y_sensors = 9;
364 1.6 aymeric }
365 1.12 phx else if (product == 0x030b)
366 1.12 phx sc->sc_y_sensors = 9;
367 1.1 christos break;
368 1.1 christos }
369 1.1 christos }
370 1.1 christos }
371 1.1 christos KASSERT(0 <= sc->sc_x_sensors && sc->sc_x_sensors <= PBMS_X_SENSORS);
372 1.1 christos KASSERT(0 <= sc->sc_y_sensors && sc->sc_y_sensors <= PBMS_Y_SENSORS);
373 1.1 christos
374 1.1 christos sc->sc_status = 0;
375 1.1 christos
376 1.1 christos a.accessops = &pbms_accessops;
377 1.1 christos a.accesscookie = sc;
378 1.1 christos
379 1.19 thorpej sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint, CFARGS_NONE);
380 1.1 christos
381 1.10 pooka return;
382 1.1 christos }
383 1.1 christos
384 1.1 christos
385 1.1 christos /* Detach the device. */
386 1.1 christos
387 1.9 dyoung void
388 1.9 dyoung pbms_childdet(device_t self, device_t child)
389 1.1 christos {
390 1.9 dyoung struct pbms_softc *sc = device_private(self);
391 1.1 christos
392 1.9 dyoung if (sc->sc_wsmousedev == child)
393 1.9 dyoung sc->sc_wsmousedev = NULL;
394 1.9 dyoung }
395 1.1 christos
396 1.9 dyoung int
397 1.9 dyoung pbms_detach(device_t self, int flags)
398 1.9 dyoung {
399 1.9 dyoung /* XXX This could not possibly be sufficient! */
400 1.9 dyoung return config_detach_children(self, flags);
401 1.1 christos }
402 1.1 christos
403 1.1 christos
404 1.1 christos /* Activate the device. */
405 1.1 christos
406 1.1 christos int
407 1.9 dyoung pbms_activate(device_t self, enum devact act)
408 1.1 christos {
409 1.9 dyoung struct pbms_softc *sc = device_private(self);
410 1.9 dyoung
411 1.9 dyoung if (act != DVACT_DEACTIVATE)
412 1.9 dyoung return EOPNOTSUPP;
413 1.1 christos
414 1.9 dyoung sc->sc_status |= PBMS_DYING;
415 1.9 dyoung return 0;
416 1.1 christos }
417 1.1 christos
418 1.1 christos
419 1.1 christos /* Enable the device. */
420 1.1 christos
421 1.1 christos static int
422 1.1 christos pbms_enable(void *v)
423 1.1 christos {
424 1.1 christos struct pbms_softc *sc = v;
425 1.1 christos
426 1.1 christos /* Check that we are not detaching or already enabled. */
427 1.1 christos if (sc->sc_status & PBMS_DYING)
428 1.1 christos return EIO;
429 1.1 christos if (sc->sc_status & PBMS_ENABLED)
430 1.1 christos return EBUSY;
431 1.1 christos
432 1.1 christos sc->sc_status |= PBMS_ENABLED;
433 1.1 christos sc->sc_status &= ~PBMS_VALID;
434 1.12 phx sc->sc_bufusage = 0;
435 1.1 christos sc->sc_buttons = 0;
436 1.1 christos memset(sc->sc_sample, 0, sizeof(sc->sc_sample));
437 1.1 christos
438 1.1 christos return uhidev_open(&sc->sc_hdev);
439 1.1 christos }
440 1.1 christos
441 1.1 christos
442 1.1 christos /* Disable the device. */
443 1.1 christos
444 1.1 christos static void
445 1.1 christos pbms_disable(void *v)
446 1.1 christos {
447 1.1 christos struct pbms_softc *sc = v;
448 1.1 christos
449 1.1 christos if (!(sc->sc_status & PBMS_ENABLED))
450 1.1 christos return;
451 1.1 christos
452 1.1 christos sc->sc_status &= ~PBMS_ENABLED;
453 1.1 christos uhidev_close(&sc->sc_hdev);
454 1.1 christos }
455 1.1 christos
456 1.1 christos
457 1.1 christos /* XXX ioctl not implemented. */
458 1.1 christos
459 1.1 christos static int
460 1.5 christos pbms_ioctl(void *v, unsigned long cmd, void *data, int flag, struct lwp *p)
461 1.1 christos {
462 1.1 christos return EPASSTHROUGH;
463 1.1 christos }
464 1.1 christos
465 1.1 christos
466 1.1 christos /*
467 1.1 christos * Interrupts & pointer movement.
468 1.1 christos */
469 1.1 christos
470 1.1 christos
471 1.1 christos /* Handle interrupts. */
472 1.1 christos
473 1.1 christos void
474 1.1 christos pbms_intr(struct uhidev *addr, void *ibuf, unsigned int len)
475 1.1 christos {
476 1.1 christos struct pbms_softc *sc = (struct pbms_softc *)addr;
477 1.12 phx uint8_t *data;
478 1.1 christos int dx, dy, dz, i, s;
479 1.1 christos uint32_t buttons;
480 1.1 christos
481 1.12 phx /*
482 1.12 phx * We may have to construct the full data packet over two or three
483 1.12 phx * sequential interrupts, as the device only sends us chunks of
484 1.12 phx * 32 or 64 bytes of data.
485 1.12 phx * This also requires some synchronization, to make sure we place
486 1.12 phx * the first protocol-byte at the first byte in the bufffer.
487 1.12 phx */
488 1.12 phx if (sc->is_geyser2) {
489 1.12 phx /* XXX Need to check this. */
490 1.12 phx } else {
491 1.12 phx /* the last chunk is always 17 bytes */
492 1.12 phx if (len == 17 && sc->sc_bufusage + len != sc->sc_datalen) {
493 1.12 phx sc->sc_bufusage = 0; /* discard bad packet */
494 1.12 phx return;
495 1.12 phx }
496 1.12 phx }
497 1.1 christos
498 1.12 phx memcpy(sc->sc_databuf + sc->sc_bufusage, ibuf, len);
499 1.12 phx sc->sc_bufusage += len;
500 1.12 phx if (sc->sc_bufusage != sc->sc_datalen)
501 1.12 phx return; /* wait until packet is complete */
502 1.12 phx
503 1.12 phx /* process the now complete protocol and clear the buffer */
504 1.12 phx data = sc->sc_databuf;
505 1.12 phx sc->sc_bufusage = 0;
506 1.6 aymeric #if 0
507 1.12 phx for (i = 0; i < sc->sc_datalen; i++)
508 1.12 phx printf(" %02x", data[i]);
509 1.12 phx printf("\n");
510 1.6 aymeric #endif
511 1.6 aymeric
512 1.1 christos /* The last byte is 1 if the button is pressed and 0 otherwise. */
513 1.6 aymeric buttons = !!data[sc->sc_datalen - 1];
514 1.1 christos
515 1.1 christos /* Everything below assumes that the sample is reordered. */
516 1.6 aymeric reorder_sample(sc, sc->sc_sample, data);
517 1.1 christos
518 1.1 christos /* Is this the first sample? */
519 1.1 christos if (!(sc->sc_status & PBMS_VALID)) {
520 1.1 christos sc->sc_status |= PBMS_VALID;
521 1.1 christos sc->sc_x = sc->sc_y = -1;
522 1.1 christos sc->sc_x_raw = sc->sc_y_raw = -1;
523 1.1 christos memcpy(sc->sc_prev, sc->sc_sample, sizeof(sc->sc_prev));
524 1.1 christos memset(sc->sc_acc, 0, sizeof(sc->sc_acc));
525 1.1 christos return;
526 1.1 christos }
527 1.1 christos /* Accumulate the sensor change while keeping it nonnegative. */
528 1.1 christos for (i = 0; i < PBMS_SENSORS; i++) {
529 1.6 aymeric sc->sc_acc[i] +=
530 1.6 aymeric (signed char) (sc->sc_sample[i] - sc->sc_prev[i]);
531 1.1 christos if (sc->sc_acc[i] < 0)
532 1.1 christos sc->sc_acc[i] = 0;
533 1.1 christos }
534 1.1 christos memcpy(sc->sc_prev, sc->sc_sample, sizeof(sc->sc_prev));
535 1.1 christos
536 1.1 christos /* Compute change. */
537 1.1 christos dx = dy = dz = 0;
538 1.1 christos if (!compute_delta(sc, &dx, &dy, &dz, &buttons))
539 1.1 christos return;
540 1.1 christos
541 1.1 christos /* Report to wsmouse. */
542 1.1 christos if ((dx != 0 || dy != 0 || dz != 0 || buttons != sc->sc_buttons) &&
543 1.1 christos sc->sc_wsmousedev != NULL) {
544 1.1 christos s = spltty();
545 1.4 plunky wsmouse_input(sc->sc_wsmousedev, buttons, dx, -dy, dz, 0,
546 1.1 christos WSMOUSE_INPUT_DELTA);
547 1.1 christos splx(s);
548 1.1 christos }
549 1.1 christos sc->sc_buttons = buttons;
550 1.1 christos }
551 1.1 christos
552 1.1 christos
553 1.1 christos /*
554 1.1 christos * Reorder the sensor values so that all the X-sensors are before the
555 1.1 christos * Y-sensors in the natural order. Note that this might have to be
556 1.1 christos * rewritten if PBMS_X_SENSORS or PBMS_Y_SENSORS change.
557 1.1 christos */
558 1.1 christos
559 1.1 christos static void
560 1.6 aymeric reorder_sample(struct pbms_softc *sc, unsigned char *to, unsigned char *from)
561 1.1 christos {
562 1.1 christos int i;
563 1.1 christos
564 1.6 aymeric if (sc->is_geyser2) {
565 1.6 aymeric int j;
566 1.6 aymeric
567 1.6 aymeric memset(to, 0, PBMS_SENSORS);
568 1.6 aymeric for (i = 0, j = 19; i < 20; i += 2, j += 3) {
569 1.6 aymeric to[i] = from[j];
570 1.6 aymeric to[i + 1] = from[j + 1];
571 1.6 aymeric }
572 1.6 aymeric for (i = 0, j = 1; i < 9; i += 2, j += 3) {
573 1.6 aymeric to[PBMS_X_SENSORS + i] = from[j];
574 1.6 aymeric to[PBMS_X_SENSORS + i + 1] = from[j + 1];
575 1.6 aymeric }
576 1.6 aymeric } else {
577 1.6 aymeric for (i = 0; i < 8; i++) {
578 1.6 aymeric /* X-sensors. */
579 1.6 aymeric to[i] = from[5 * i + 2];
580 1.6 aymeric to[i + 8] = from[5 * i + 4];
581 1.6 aymeric to[i + 16] = from[5 * i + 42];
582 1.6 aymeric #if 0
583 1.6 aymeric /*
584 1.6 aymeric * XXX This seems to introduce random ventical jumps, so
585 1.6 aymeric * we ignore these sensors until we figure out their meaning.
586 1.6 aymeric */
587 1.6 aymeric if (i < 2)
588 1.6 aymeric to[i + 24] = from[5 * i + 44];
589 1.6 aymeric #endif /* 0 */
590 1.6 aymeric /* Y-sensors. */
591 1.6 aymeric to[i + 26] = from[5 * i + 1];
592 1.6 aymeric to[i + 34] = from[5 * i + 3];
593 1.6 aymeric }
594 1.1 christos }
595 1.1 christos }
596 1.1 christos
597 1.1 christos
598 1.1 christos /*
599 1.1 christos * Compute the change in x, y and z direction, update the button state
600 1.1 christos * (to simulate more than one button, scrolling etc.), and update the
601 1.1 christos * history. Note that dx, dy, dz and buttons are modified only if
602 1.1 christos * corresponding pressure is detected and should thus be initialised
603 1.1 christos * before the call. Return 0 on error.
604 1.1 christos */
605 1.1 christos
606 1.1 christos /* XXX Could we report something useful in dz? */
607 1.1 christos
608 1.1 christos static int
609 1.1 christos compute_delta(struct pbms_softc *sc, int *dx, int *dy, int *dz,
610 1.1 christos uint32_t * buttons)
611 1.1 christos {
612 1.1 christos int x_det, y_det, x_raw, y_raw, x_fingers, y_fingers, fingers, x, y;
613 1.1 christos
614 1.1 christos x_det = detect_pos(sc->sc_acc, sc->sc_x_sensors, sc->sc_theshold,
615 1.1 christos sc->sc_x_factor, &x_raw, &x_fingers);
616 1.1 christos y_det = detect_pos(sc->sc_acc + PBMS_X_SENSORS, sc->sc_y_sensors,
617 1.1 christos sc->sc_theshold, sc->sc_y_factor,
618 1.1 christos &y_raw, &y_fingers);
619 1.16 riastrad fingers = uimax(x_fingers, y_fingers);
620 1.1 christos
621 1.1 christos /* Check the number of fingers and if we have detected a position. */
622 1.1 christos if (fingers > 1) {
623 1.1 christos /* More than one finger detected, resetting. */
624 1.1 christos memset(sc->sc_acc, 0, sizeof(sc->sc_acc));
625 1.1 christos sc->sc_x_raw = sc->sc_y_raw = sc->sc_x = sc->sc_y = -1;
626 1.1 christos return 0;
627 1.1 christos } else if (x_det == 0 && y_det == 0) {
628 1.1 christos /* No position detected, resetting. */
629 1.1 christos memset(sc->sc_acc, 0, sizeof(sc->sc_acc));
630 1.1 christos sc->sc_x_raw = sc->sc_y_raw = sc->sc_x = sc->sc_y = -1;
631 1.1 christos } else if (x_det > 0 && y_det > 0) {
632 1.1 christos /* Smooth position. */
633 1.1 christos if (sc->sc_x_raw >= 0) {
634 1.1 christos sc->sc_x_raw = (3 * sc->sc_x_raw + x_raw) / 4;
635 1.1 christos sc->sc_y_raw = (3 * sc->sc_y_raw + y_raw) / 4;
636 1.1 christos /*
637 1.1 christos * Compute virtual position and change if we already
638 1.1 christos * have a decent position.
639 1.1 christos */
640 1.1 christos if (sc->sc_x >= 0) {
641 1.1 christos x = smooth_pos(sc->sc_x, sc->sc_x_raw,
642 1.1 christos sc->sc_noise);
643 1.1 christos y = smooth_pos(sc->sc_y, sc->sc_y_raw,
644 1.1 christos sc->sc_noise);
645 1.1 christos *dx = x - sc->sc_x;
646 1.1 christos *dy = y - sc->sc_y;
647 1.1 christos sc->sc_x = x;
648 1.1 christos sc->sc_y = y;
649 1.1 christos } else {
650 1.1 christos /* Initialise virtual position. */
651 1.1 christos sc->sc_x = sc->sc_x_raw;
652 1.1 christos sc->sc_y = sc->sc_y_raw;
653 1.1 christos }
654 1.1 christos } else {
655 1.1 christos /* Initialise raw position. */
656 1.1 christos sc->sc_x_raw = x_raw;
657 1.1 christos sc->sc_y_raw = y_raw;
658 1.1 christos }
659 1.1 christos }
660 1.1 christos return 1;
661 1.1 christos }
662 1.1 christos
663 1.1 christos
664 1.1 christos /*
665 1.1 christos * Compute the new smoothed position from the previous smoothed position
666 1.1 christos * and the raw position.
667 1.1 christos */
668 1.1 christos
669 1.1 christos static int
670 1.1 christos smooth_pos(int pos_old, int pos_raw, int noise)
671 1.1 christos {
672 1.1 christos int ad, delta;
673 1.1 christos
674 1.1 christos delta = pos_raw - pos_old;
675 1.1 christos ad = abs(delta);
676 1.1 christos
677 1.1 christos /* Too small changes are ignored. */
678 1.1 christos if (ad < noise / 2)
679 1.1 christos delta = 0;
680 1.1 christos /* A bit larger changes are smoothed. */
681 1.1 christos else if (ad < noise)
682 1.1 christos delta /= 4;
683 1.1 christos else if (ad < 2 * noise)
684 1.1 christos delta /= 2;
685 1.1 christos
686 1.1 christos return pos_old + delta;
687 1.1 christos }
688 1.1 christos
689 1.1 christos
690 1.1 christos /*
691 1.1 christos * Detect the position of the finger. Returns the total pressure.
692 1.1 christos * The position is returned in pos_ret and the number of fingers
693 1.1 christos * is returned in fingers_ret. The position returned in pos_ret
694 1.1 christos * is in [0, (n_sensors - 1) * factor - 1].
695 1.1 christos */
696 1.1 christos
697 1.1 christos static int
698 1.1 christos detect_pos(int *sensors, int n_sensors, int threshold, int fact,
699 1.1 christos int *pos_ret, int *fingers_ret)
700 1.1 christos {
701 1.1 christos int i, w, s;
702 1.1 christos
703 1.1 christos /*
704 1.1 christos * Compute the number of fingers, total pressure, and weighted
705 1.1 christos * position of the fingers.
706 1.1 christos */
707 1.1 christos *fingers_ret = 0;
708 1.1 christos w = s = 0;
709 1.1 christos for (i = 0; i < n_sensors; i++) {
710 1.1 christos if (sensors[i] >= threshold) {
711 1.1 christos if (i == 0 || sensors[i - 1] < threshold)
712 1.1 christos *fingers_ret += 1;
713 1.1 christos s += sensors[i];
714 1.1 christos w += sensors[i] * i;
715 1.1 christos }
716 1.1 christos }
717 1.1 christos
718 1.1 christos if (s > 0)
719 1.1 christos *pos_ret = w * fact / s;
720 1.1 christos
721 1.1 christos return s;
722 1.1 christos }
723