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