[BACK]Return to tpms.c CVS log [TXT][DIR] Up to [local] / sys / arch / macppc / dev

Annotation of sys/arch/macppc/dev/tpms.c, Revision 1.1

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

CVSweb