1/*
2 * Copyright (c) 2014 The DragonFly Project.  All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com> and was subsequently ported,
6 * modified and enhanced for FreeBSD by Michael Gmelin <freebsd@grem.de>.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in
16 *    the documentation and/or other materials provided with the
17 *    distribution.
18 * 3. Neither the name of The DragonFly Project nor the names of its
19 *    contributors may be used to endorse or promote products derived
20 *    from this software without specific, prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD: stable/11/sys/dev/cyapa/cyapa.c 310072 2016-12-14 16:27:28Z avg $");
38
39/*
40 * CYAPA - Cypress APA trackpad with I2C Interface driver
41 *
42 * Based on DragonFlyBSD's cyapa driver, which referenced the linux
43 * cyapa.c driver to figure out the bootstrapping and commands.
44 *
45 * Unable to locate any datasheet for the device.
46 *
47 *
48 * Trackpad layout:
49 *
50 *                2/3               1/3
51 *       +--------------------+------------+
52 *       |                    |   Middle   |
53 *       |                    |   Button   |
54 *       |       Left         |            |
55 *       |      Button        +------------+
56 *       |                    |   Right    |
57 *       |                    |   Button   |
58 *       +--------------------+............|
59 *       |     Thumb/Button Area           | 15%
60 *       +---------------------------------+
61 *
62 *
63 *                             FEATURES
64 *
65 * IMPS/2 emulation       - Emulates the IntelliMouse protocol.
66 *
67 * Jitter supression      - Implements 2-pixel hysteresis with memory.
68 *
69 * Jump detecion          - Detect jumps caused by touchpad.
70 *
71 * Two finger scrolling   - Use two fingers for Z axis scrolling.
72 *
73 * Button down/2nd finger - While one finger clicks and holds down the
74 *                          touchpad, the second one can be used to move
75 *                          the mouse cursor. Useful for drawing or
76 *                          selecting text.
77 *
78 * Thumb/Button Area      - The lower 15%* of the trackpad will not affect
79 *                          the mouse cursor position. This allows for high
80 *                          precision clicking, by controlling the cursor
81 *                          with the index finger and pushing/holding the
82 *                          pad down with the thumb.
83 *                          * can be changed using sysctl
84 *
85 * Track-pad button       - Push physical button. Left 2/3rds of the pad
86 *                          will issue a LEFT button event, upper right
87 *                          corner will issue a MIDDLE button event,
88 *                          lower right corner will issue a RIGHT button
89 *                          event. Optional tap support can be enabled
90 *                          and configured using sysctl.
91 *
92 *                              WARNINGS
93 *
94 * These trackpads get confused when three or more fingers are down on the
95 * same horizontal axis and will start to glitch the finger detection.
96 * Removing your hand for a few seconds will allow the trackpad to
97 * recalibrate.  Generally speaking, when using three or more fingers
98 * please try to place at least one finger off-axis (a little above or
99 * below) the other two.
100 */
101
102#include <sys/param.h>
103#include <sys/bus.h>
104#include <sys/conf.h>
105#include <sys/event.h>
106#include <sys/fcntl.h>
107#include <sys/kernel.h>
108#include <sys/kthread.h>
109#include <sys/lock.h>
110#include <sys/lockmgr.h>
111#include <sys/malloc.h>
112#include <sys/mbuf.h>
113#include <sys/module.h>
114#include <sys/mouse.h>
115#include <sys/mutex.h>
116#include <sys/poll.h>
117#include <sys/selinfo.h>
118#include <sys/sysctl.h>
119#include <sys/sysctl.h>
120#include <sys/systm.h>
121#include <sys/systm.h>
122#include <sys/uio.h>
123#include <sys/vnode.h>
124
125#include <dev/iicbus/iiconf.h>
126#include <dev/iicbus/iicbus.h>
127#include <dev/cyapa/cyapa.h>
128
129#include "iicbus_if.h"
130#include "bus_if.h"
131#include "device_if.h"
132
133#define CYAPA_BUFSIZE	128			/* power of 2 */
134#define CYAPA_BUFMASK	(CYAPA_BUFSIZE - 1)
135
136#define ZSCALE		15
137
138#define TIME_TO_IDLE	(hz * 10)
139#define TIME_TO_RESET	(hz * 3)
140
141static MALLOC_DEFINE(M_CYAPA, "cyapa", "CYAPA device data");
142
143struct cyapa_fifo {
144	int	rindex;
145	int	windex;
146	char	buf[CYAPA_BUFSIZE];
147};
148
149struct cyapa_softc {
150	device_t dev;
151	int	count;			/* >0 if device opened */
152	struct cdev *devnode;
153	struct selinfo selinfo;
154	struct mtx mutex;
155
156	int	cap_resx;
157	int	cap_resy;
158	int	cap_phyx;
159	int	cap_phyy;
160	uint8_t	cap_buttons;
161
162	int	detaching;		/* driver is detaching */
163	int	poll_thread_running;	/* poll thread is running */
164
165	/* PS/2 mouse emulation */
166	int	track_x;		/* current tracking */
167	int	track_y;
168	int	track_z;
169	int	track_z_ticks;
170	uint16_t track_but;
171	char	track_id;		/* first finger id */
172	int	track_nfingers;
173	int	delta_x;		/* accumulation -> report */
174	int	delta_y;
175	int	delta_z;
176	int	fuzz_x;
177	int	fuzz_y;
178	int	fuzz_z;
179	int	touch_x;		/* touch down coordinates */
180	int	touch_y;
181	int	touch_z;
182	int	finger1_ticks;
183	int	finger2_ticks;
184	int	finger3_ticks;
185	uint16_t reported_but;
186
187	struct cyapa_fifo rfifo;	/* device->host */
188	struct cyapa_fifo wfifo;	/* host->device */
189	uint8_t	ps2_cmd;		/* active p2_cmd waiting for data */
190	uint8_t ps2_acked;
191	int	active_tick;
192	int	data_signal;
193	int	blocked;
194	int	isselect;
195	int	reporting_mode;		/* 0=disabled 1=enabled */
196	int	scaling_mode;		/* 0=1:1 1=2:1 */
197	int	remote_mode;		/* 0 for streaming mode */
198	int	zenabled;		/* z-axis enabled (mode 1 or 2) */
199	mousehw_t hw;			/* hardware information */
200	mousemode_t mode;		/* mode */
201	int	poll_ticks;
202};
203
204struct cyapa_cdevpriv {
205	struct cyapa_softc *sc;
206};
207
208#define CYPOLL_SHUTDOWN	0x0001
209
210static void cyapa_poll_thread(void *arg);
211static int cyapa_raw_input(struct cyapa_softc *sc, struct cyapa_regs *regs,
212    int freq);
213static void cyapa_set_power_mode(struct cyapa_softc *sc, int mode);
214
215static int fifo_empty(struct cyapa_softc *sc, struct cyapa_fifo *fifo);
216static size_t fifo_ready(struct cyapa_softc *sc, struct cyapa_fifo *fifo);
217static char *fifo_read(struct cyapa_softc *sc, struct cyapa_fifo *fifo,
218    size_t n);
219static char *fifo_write(struct cyapa_softc *sc, struct cyapa_fifo *fifo,
220    size_t n);
221static uint8_t fifo_read_char(struct cyapa_softc *sc,
222    struct cyapa_fifo *fifo);
223static void fifo_write_char(struct cyapa_softc *sc, struct cyapa_fifo *fifo,
224    uint8_t c);
225static size_t fifo_space(struct cyapa_softc *sc, struct cyapa_fifo *fifo);
226static void fifo_reset(struct cyapa_softc *sc, struct cyapa_fifo *fifo);
227
228static int cyapa_fuzz(int delta, int *fuzz);
229
230static int cyapa_idle_freq = 1;
231SYSCTL_INT(_debug, OID_AUTO, cyapa_idle_freq, CTLFLAG_RW,
232	    &cyapa_idle_freq, 0, "Scan frequency in idle mode");
233static int cyapa_slow_freq = 20;
234SYSCTL_INT(_debug, OID_AUTO, cyapa_slow_freq, CTLFLAG_RW,
235	    &cyapa_slow_freq, 0, "Scan frequency in slow mode ");
236static int cyapa_norm_freq = 100;
237SYSCTL_INT(_debug, OID_AUTO, cyapa_norm_freq, CTLFLAG_RW,
238	    &cyapa_norm_freq, 0, "Normal scan frequency");
239static int cyapa_minpressure = 12;
240SYSCTL_INT(_debug, OID_AUTO, cyapa_minpressure, CTLFLAG_RW,
241	    &cyapa_minpressure, 0, "Minimum pressure to detect finger");
242static int cyapa_enable_tapclick = 0;
243SYSCTL_INT(_debug, OID_AUTO, cyapa_enable_tapclick, CTLFLAG_RW,
244	    &cyapa_enable_tapclick, 0, "Enable tap to click");
245static int cyapa_tapclick_min_ticks = 1;
246SYSCTL_INT(_debug, OID_AUTO, cyapa_tapclick_min_ticks, CTLFLAG_RW,
247	    &cyapa_tapclick_min_ticks, 0, "Minimum tap duration for click");
248static int cyapa_tapclick_max_ticks = 8;
249SYSCTL_INT(_debug, OID_AUTO, cyapa_tapclick_max_ticks, CTLFLAG_RW,
250	    &cyapa_tapclick_max_ticks, 0, "Maximum tap duration for click");
251static int cyapa_move_min_ticks = 4;
252SYSCTL_INT(_debug, OID_AUTO, cyapa_move_min_ticks, CTLFLAG_RW,
253	    &cyapa_move_min_ticks, 0,
254	    "Minimum ticks before cursor position is changed");
255static int cyapa_scroll_wait_ticks = 0;
256SYSCTL_INT(_debug, OID_AUTO, cyapa_scroll_wait_ticks, CTLFLAG_RW,
257	    &cyapa_scroll_wait_ticks, 0,
258	    "Wait N ticks before starting to scroll");
259static int cyapa_scroll_stick_ticks = 15;
260SYSCTL_INT(_debug, OID_AUTO, cyapa_scroll_stick_ticks, CTLFLAG_RW,
261	    &cyapa_scroll_stick_ticks, 0,
262	    "Prevent cursor move on single finger for N ticks after scroll");
263static int cyapa_thumbarea_percent = 15;
264SYSCTL_INT(_debug, OID_AUTO, cyapa_thumbarea_percent, CTLFLAG_RW,
265	    &cyapa_thumbarea_percent, 0,
266	    "Size of bottom thumb area in percent");
267
268static int cyapa_debug = 0;
269SYSCTL_INT(_debug, OID_AUTO, cyapa_debug, CTLFLAG_RW,
270	    &cyapa_debug, 0, "Enable debugging");
271static int cyapa_reset = 0;
272SYSCTL_INT(_debug, OID_AUTO, cyapa_reset, CTLFLAG_RW,
273	    &cyapa_reset, 0, "Reset track pad");
274
275static int
276cyapa_read_bytes(device_t dev, uint8_t reg, uint8_t *val, int cnt)
277{
278	uint16_t addr = iicbus_get_addr(dev);
279	struct iic_msg msgs[] = {
280	     { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
281	     { addr, IIC_M_RD, cnt, val },
282	};
283
284	return (iicbus_transfer(dev, msgs, nitems(msgs)));
285}
286
287static int
288cyapa_write_bytes(device_t dev, uint8_t reg, const uint8_t *val, int cnt)
289{
290	uint16_t addr = iicbus_get_addr(dev);
291	struct iic_msg msgs[] = {
292	     { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
293	     { addr, IIC_M_WR | IIC_M_NOSTART, cnt, __DECONST(uint8_t *, val) },
294	};
295
296	return (iicbus_transfer(dev, msgs, nitems(msgs)));
297}
298
299static void
300cyapa_lock(struct cyapa_softc *sc)
301{
302
303	mtx_lock(&sc->mutex);
304}
305
306static void
307cyapa_unlock(struct cyapa_softc *sc)
308{
309
310	mtx_unlock(&sc->mutex);
311}
312
313#define	CYAPA_LOCK_ASSERT(sc)	mtx_assert(&(sc)->mutex, MA_OWNED);
314
315/*
316 * Notify if possible receive data ready.  Must be called
317 * with sc->mutex held (cyapa_lock(sc)).
318 */
319static void
320cyapa_notify(struct cyapa_softc *sc)
321{
322
323	CYAPA_LOCK_ASSERT(sc);
324
325	if (sc->data_signal || !fifo_empty(sc, &sc->rfifo)) {
326		KNOTE_LOCKED(&sc->selinfo.si_note, 0);
327		if (sc->blocked || sc->isselect) {
328			if (sc->blocked) {
329			    sc->blocked = 0;
330			    wakeup(&sc->blocked);
331			}
332			if (sc->isselect) {
333			    sc->isselect = 0;
334			    selwakeup(&sc->selinfo);
335			}
336		}
337	}
338}
339
340/*
341 * Initialize the device
342 */
343static int
344init_device(device_t dev, struct cyapa_cap *cap, int probe)
345{
346	static char bl_exit[] = {
347		0x00, 0xff, 0xa5, 0x00, 0x01,
348		0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
349	static char bl_deactivate[] = {
350		0x00, 0xff, 0x3b, 0x00, 0x01,
351		0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
352	struct cyapa_boot_regs boot;
353	int error;
354	int retries;
355
356	/* Get status */
357	error = cyapa_read_bytes(dev, CMD_BOOT_STATUS,
358	    (void *)&boot, sizeof(boot));
359	if (error)
360		goto done;
361
362	/*
363	 * Bootstrap the device if necessary.  It can take up to 2 seconds
364	 * for the device to fully initialize.
365	 */
366	retries = 20;
367	while ((boot.stat & CYAPA_STAT_RUNNING) == 0 && retries > 0) {
368		if (boot.boot & CYAPA_BOOT_BUSY) {
369			/* Busy, wait loop. */
370		} else if (boot.error & CYAPA_ERROR_BOOTLOADER) {
371			/* Magic */
372			error = cyapa_write_bytes(dev, CMD_BOOT_STATUS,
373			    bl_deactivate, sizeof(bl_deactivate));
374			if (error)
375				goto done;
376		} else {
377			/* Magic */
378			error = cyapa_write_bytes(dev, CMD_BOOT_STATUS,
379			    bl_exit, sizeof(bl_exit));
380			if (error)
381				goto done;
382		}
383		pause("cyapab1", (hz * 2) / 10);
384		--retries;
385		error = cyapa_read_bytes(dev, CMD_BOOT_STATUS,
386		    (void *)&boot, sizeof(boot));
387		if (error)
388			goto done;
389	}
390
391	if (retries == 0) {
392		device_printf(dev, "Unable to bring device out of bootstrap\n");
393		error = ENXIO;
394		goto done;
395	}
396
397	/* Check identity */
398	if (cap) {
399		error = cyapa_read_bytes(dev, CMD_QUERY_CAPABILITIES,
400		    (void *)cap, sizeof(*cap));
401
402		if (strncmp(cap->prod_ida, "CYTRA", 5) != 0) {
403			device_printf(dev, "Product ID \"%5.5s\" mismatch\n",
404			    cap->prod_ida);
405			error = ENXIO;
406		}
407	}
408	error = cyapa_read_bytes(dev, CMD_BOOT_STATUS,
409	    (void *)&boot, sizeof(boot));
410
411	if (probe == 0)		/* official init */
412		device_printf(dev, "cyapa init status %02x\n", boot.stat);
413	else if (probe == 2)
414		device_printf(dev, "cyapa reset status %02x\n", boot.stat);
415
416done:
417	if (error)
418		device_printf(dev, "Unable to initialize\n");
419	return (error);
420}
421
422static int cyapa_probe(device_t);
423static int cyapa_attach(device_t);
424static int cyapa_detach(device_t);
425static void cyapa_cdevpriv_dtor(void*);
426
427static devclass_t cyapa_devclass;
428
429static device_method_t cyapa_methods[] = {
430	/* device interface */
431	DEVMETHOD(device_probe,		cyapa_probe),
432	DEVMETHOD(device_attach,	cyapa_attach),
433	DEVMETHOD(device_detach,	cyapa_detach),
434
435	DEVMETHOD_END
436};
437
438static driver_t cyapa_driver = {
439	"cyapa",
440	cyapa_methods,
441	sizeof(struct cyapa_softc),
442};
443
444static	d_open_t	cyapaopen;
445static	d_ioctl_t	cyapaioctl;
446static	d_read_t	cyaparead;
447static	d_write_t	cyapawrite;
448static	d_kqfilter_t	cyapakqfilter;
449static	d_poll_t	cyapapoll;
450
451static struct cdevsw cyapa_cdevsw = {
452	.d_version =	D_VERSION,
453	.d_open =	cyapaopen,
454	.d_ioctl =	cyapaioctl,
455	.d_read =	cyaparead,
456	.d_write =	cyapawrite,
457	.d_kqfilter =	cyapakqfilter,
458	.d_poll =	cyapapoll,
459};
460
461static int
462cyapa_probe(device_t dev)
463{
464	struct cyapa_cap cap;
465	int addr;
466	int error;
467
468	addr = iicbus_get_addr(dev);
469
470	/*
471	 * 0x67 - cypress trackpad on the acer c720
472	 * (other devices might use other ids).
473	 */
474	if (addr != 0xce)
475		return (ENXIO);
476
477	error = init_device(dev, &cap, 1);
478	if (error != 0)
479		return (ENXIO);
480
481	device_set_desc(dev, "Cypress APA I2C Trackpad");
482
483	return (BUS_PROBE_VENDOR);
484}
485
486static int
487cyapa_attach(device_t dev)
488{
489	struct cyapa_softc *sc;
490	struct cyapa_cap cap;
491	int unit;
492	int addr;
493
494	sc = device_get_softc(dev);
495	sc->reporting_mode = 1;
496
497	unit = device_get_unit(dev);
498	addr = iicbus_get_addr(dev);
499
500	if (init_device(dev, &cap, 0))
501		return (ENXIO);
502
503	mtx_init(&sc->mutex, "cyapa", NULL, MTX_DEF);
504
505	sc->dev = dev;
506
507	knlist_init_mtx(&sc->selinfo.si_note, &sc->mutex);
508
509	sc->cap_resx = ((cap.max_abs_xy_high << 4) & 0x0F00) |
510	    cap.max_abs_x_low;
511	sc->cap_resy = ((cap.max_abs_xy_high << 8) & 0x0F00) |
512	    cap.max_abs_y_low;
513	sc->cap_phyx = ((cap.phy_siz_xy_high << 4) & 0x0F00) |
514	    cap.phy_siz_x_low;
515	sc->cap_phyy = ((cap.phy_siz_xy_high << 8) & 0x0F00) |
516	    cap.phy_siz_y_low;
517	sc->cap_buttons = cap.buttons;
518
519	device_printf(dev, "%5.5s-%6.6s-%2.2s buttons=%c%c%c res=%dx%d\n",
520	    cap.prod_ida, cap.prod_idb, cap.prod_idc,
521	    ((cap.buttons & CYAPA_FNGR_LEFT) ? 'L' : '-'),
522	    ((cap.buttons & CYAPA_FNGR_MIDDLE) ? 'M' : '-'),
523	    ((cap.buttons & CYAPA_FNGR_RIGHT) ? 'R' : '-'),
524	    sc->cap_resx, sc->cap_resy);
525
526	sc->hw.buttons = 5;
527	sc->hw.iftype = MOUSE_IF_PS2;
528	sc->hw.type = MOUSE_MOUSE;
529	sc->hw.model = MOUSE_MODEL_INTELLI;
530	sc->hw.hwid = addr;
531
532	sc->mode.protocol = MOUSE_PROTO_PS2;
533	sc->mode.rate = 100;
534	sc->mode.resolution = 4;
535	sc->mode.accelfactor = 1;
536	sc->mode.level = 0;
537	sc->mode.packetsize = MOUSE_PS2_PACKETSIZE;
538
539	/* Setup input event tracking */
540	cyapa_set_power_mode(sc, CMD_POWER_MODE_IDLE);
541
542	/* Start the polling thread */
543	 kthread_add(cyapa_poll_thread, sc, NULL, NULL,
544	    0, 0, "cyapa-poll");
545
546	sc->devnode = make_dev(&cyapa_cdevsw, unit,
547	    UID_ROOT, GID_WHEEL, 0600, "cyapa%d", unit);
548
549	sc->devnode->si_drv1 = sc;
550
551	return (0);
552}
553
554static int
555cyapa_detach(device_t dev)
556{
557	struct cyapa_softc *sc;
558
559	sc = device_get_softc(dev);
560
561	/* Cleanup poller thread */
562	cyapa_lock(sc);
563	while (sc->poll_thread_running) {
564		sc->detaching = 1;
565		mtx_sleep(&sc->detaching, &sc->mutex, PCATCH, "cyapadet", hz);
566	}
567	cyapa_unlock(sc);
568
569	destroy_dev(sc->devnode);
570
571	knlist_clear(&sc->selinfo.si_note, 0);
572	seldrain(&sc->selinfo);
573	knlist_destroy(&sc->selinfo.si_note);
574
575	mtx_destroy(&sc->mutex);
576
577	return (0);
578}
579
580/*
581 * USER DEVICE I/O FUNCTIONS
582 */
583static int
584cyapaopen(struct cdev *dev, int oflags, int devtype, struct thread *td)
585{
586	struct cyapa_cdevpriv *priv;
587	int error;
588
589	priv = malloc(sizeof(*priv), M_CYAPA, M_WAITOK | M_ZERO);
590	priv->sc = dev->si_drv1;
591
592	error = devfs_set_cdevpriv(priv, cyapa_cdevpriv_dtor);
593	if (error == 0) {
594		cyapa_lock(priv->sc);
595		priv->sc->count++;
596		cyapa_unlock(priv->sc);
597	}
598	else
599		free(priv, M_CYAPA);
600
601	return (error);
602}
603
604static void
605cyapa_cdevpriv_dtor(void *data)
606{
607	struct cyapa_cdevpriv *priv;
608
609	priv = data;
610	KASSERT(priv != NULL, ("cyapa cdevpriv should not be NULL!"));
611
612	cyapa_lock(priv->sc);
613	priv->sc->count--;
614	cyapa_unlock(priv->sc);
615
616	free(priv, M_CYAPA);
617}
618
619static int
620cyaparead(struct cdev *dev, struct uio *uio, int ioflag)
621{
622	struct cyapa_softc *sc;
623	int error;
624	int didread;
625	size_t n;
626	char* ptr;
627
628	sc = dev->si_drv1;
629	/* If buffer is empty, load a new event if it is ready */
630	cyapa_lock(sc);
631again:
632	if (fifo_empty(sc, &sc->rfifo) &&
633	    (sc->data_signal || sc->delta_x || sc->delta_y ||
634	     sc->track_but != sc->reported_but)) {
635		uint8_t c0;
636		uint16_t but;
637		int delta_x;
638		int delta_y;
639		int delta_z;
640
641		/* Accumulate delta_x, delta_y */
642		sc->data_signal = 0;
643		delta_x = sc->delta_x;
644		delta_y = sc->delta_y;
645		delta_z = sc->delta_z;
646		if (delta_x > 255) {
647			delta_x = 255;
648			sc->data_signal = 1;
649		}
650		if (delta_x < -256) {
651			delta_x = -256;
652			sc->data_signal = 1;
653		}
654		if (delta_y > 255) {
655			delta_y = 255;
656			sc->data_signal = 1;
657		}
658		if (delta_y < -256) {
659			delta_y = -256;
660			sc->data_signal = 1;
661		}
662		if (delta_z > 255) {
663			delta_z = 255;
664			sc->data_signal = 1;
665		}
666		if (delta_z < -256) {
667			delta_z = -256;
668			sc->data_signal = 1;
669		}
670		but = sc->track_but;
671
672		/* Adjust baseline for next calculation */
673		sc->delta_x -= delta_x;
674		sc->delta_y -= delta_y;
675		sc->delta_z -= delta_z;
676		sc->reported_but = but;
677
678		/*
679		 * Fuzz reduces movement jitter by introducing some
680		 * hysteresis.  It operates without cumulative error so
681		 * if you swish around quickly and return your finger to
682		 * where it started, so to will the mouse.
683		 */
684		delta_x = cyapa_fuzz(delta_x, &sc->fuzz_x);
685		delta_y = cyapa_fuzz(delta_y, &sc->fuzz_y);
686		delta_z = cyapa_fuzz(delta_z, &sc->fuzz_z);
687
688		/*
689		 * Generate report
690		 */
691		c0 = 0;
692		if (delta_x < 0)
693			c0 |= 0x10;
694		if (delta_y < 0)
695			c0 |= 0x20;
696		c0 |= 0x08;
697		if (but & CYAPA_FNGR_LEFT)
698			c0 |= 0x01;
699		if (but & CYAPA_FNGR_MIDDLE)
700			c0 |= 0x04;
701		if (but & CYAPA_FNGR_RIGHT)
702			c0 |= 0x02;
703
704		fifo_write_char(sc, &sc->rfifo, c0);
705		fifo_write_char(sc, &sc->rfifo, (uint8_t)delta_x);
706		fifo_write_char(sc, &sc->rfifo, (uint8_t)delta_y);
707		switch(sc->zenabled) {
708		case 1:
709			/* Z axis all 8 bits */
710			fifo_write_char(sc, &sc->rfifo, (uint8_t)delta_z);
711			break;
712		case 2:
713			/*
714			 * Z axis low 4 bits + 4th button and 5th button
715			 * (high 2 bits must be left 0).  Auto-scale
716			 * delta_z to fit to avoid a wrong-direction
717			 * overflow (don't try to retain the remainder).
718			 */
719			while (delta_z > 7 || delta_z < -8)
720				delta_z >>= 1;
721			c0 = (uint8_t)delta_z & 0x0F;
722			fifo_write_char(sc, &sc->rfifo, c0);
723			break;
724		default:
725			/* basic PS/2 */
726			break;
727		}
728		cyapa_notify(sc);
729	}
730
731	/* Blocking / Non-blocking */
732	error = 0;
733	didread = (uio->uio_resid == 0);
734
735	while ((ioflag & IO_NDELAY) == 0 && fifo_empty(sc, &sc->rfifo)) {
736		if (sc->data_signal)
737			goto again;
738		sc->blocked = 1;
739		error = mtx_sleep(&sc->blocked, &sc->mutex, PCATCH, "cyablk", 0);
740		if (error)
741			break;
742	}
743
744	/* Return any buffered data */
745	while (error == 0 && uio->uio_resid &&
746	    (n = fifo_ready(sc, &sc->rfifo)) > 0) {
747		if (n > uio->uio_resid)
748			n = uio->uio_resid;
749		ptr = fifo_read(sc, &sc->rfifo, 0);
750		cyapa_unlock(sc);
751		error = uiomove(ptr, n, uio);
752		cyapa_lock(sc);
753		if (error)
754			break;
755		fifo_read(sc, &sc->rfifo, n);
756		didread = 1;
757	}
758	cyapa_unlock(sc);
759
760	if (error == 0 && didread == 0) {
761		error = EWOULDBLOCK;
762	}
763	return (didread ? 0 : error);
764}
765
766static int
767cyapawrite(struct cdev *dev, struct uio *uio, int ioflag)
768{
769	struct cyapa_softc *sc;
770	int error;
771	int cmd_completed;
772	size_t n;
773	uint8_t c0;
774	char* ptr;
775
776	sc = dev->si_drv1;
777again:
778	/*
779	 * Copy data from userland.  This will also cross-over the end
780	 * of the fifo and keep filling.
781	 */
782	cyapa_lock(sc);
783	while ((n = fifo_space(sc, &sc->wfifo)) > 0 && uio->uio_resid) {
784		if (n > uio->uio_resid)
785			n = uio->uio_resid;
786		ptr = fifo_write(sc, &sc->wfifo, 0);
787		cyapa_unlock(sc);
788		error = uiomove(ptr, n, uio);
789		cyapa_lock(sc);
790		if (error)
791			break;
792		fifo_write(sc, &sc->wfifo, n);
793	}
794
795	/* Handle commands */
796	cmd_completed = (fifo_ready(sc, &sc->wfifo) != 0);
797	while (fifo_ready(sc, &sc->wfifo) && cmd_completed && error == 0) {
798		if (sc->ps2_cmd == 0)
799			sc->ps2_cmd = fifo_read_char(sc, &sc->wfifo);
800		switch(sc->ps2_cmd) {
801		case 0xE6:
802			/* SET SCALING 1:1 */
803			sc->scaling_mode = 0;
804			fifo_write_char(sc, &sc->rfifo, 0xFA);
805			break;
806		case 0xE7:
807			/* SET SCALING 2:1 */
808			sc->scaling_mode = 1;
809			fifo_write_char(sc, &sc->rfifo, 0xFA);
810			break;
811		case 0xE8:
812			/* SET RESOLUTION +1 byte */
813			if (sc->ps2_acked == 0) {
814				sc->ps2_acked = 1;
815				fifo_write_char(sc, &sc->rfifo, 0xFA);
816			}
817			if (fifo_ready(sc, &sc->wfifo) == 0) {
818				cmd_completed = 0;
819				break;
820			}
821			sc->mode.resolution = fifo_read_char(sc, &sc->wfifo);
822			fifo_write_char(sc, &sc->rfifo, 0xFA);
823			break;
824		case 0xE9:
825			/*
826			 * STATUS REQUEST
827			 *
828			 * byte1:
829			 *	bit 7	0
830			 *	bit 6	Mode	(1=remote mode, 0=stream mode)
831			 *	bit 5	Enable	(data reporting enabled)
832			 *	bit 4	Scaling	(0=1:1 1=2:1)
833			 *	bit 3	0
834			 *	bit 2	LEFT BUTTON    (1 if pressed)
835			 *	bit 1	MIDDLE BUTTON  (1 if pressed)
836			 *	bit 0	RIGHT BUTTON   (1 if pressed)
837			 *
838			 * byte2: resolution counts/mm
839			 * byte3: sample rate
840			 */
841			c0 = 0;
842			if (sc->remote_mode)
843				c0 |= 0x40;
844			if (sc->reporting_mode)
845				c0 |= 0x20;
846			if (sc->scaling_mode)
847				c0 |= 0x10;
848			if (sc->track_but & CYAPA_FNGR_LEFT)
849				c0 |= 0x04;
850			if (sc->track_but & CYAPA_FNGR_MIDDLE)
851				c0 |= 0x02;
852			if (sc->track_but & CYAPA_FNGR_RIGHT)
853				c0 |= 0x01;
854			fifo_write_char(sc, &sc->rfifo, 0xFA);
855			fifo_write_char(sc, &sc->rfifo, c0);
856			fifo_write_char(sc, &sc->rfifo, 0x00);
857			fifo_write_char(sc, &sc->rfifo, 100);
858			break;
859		case 0xEA:
860			/* Set stream mode and reset movement counters */
861			sc->remote_mode = 0;
862			fifo_write_char(sc, &sc->rfifo, 0xFA);
863			sc->delta_x = 0;
864			sc->delta_y = 0;
865			sc->delta_z = 0;
866			break;
867		case 0xEB:
868			/*
869			 * Read Data (if in remote mode).  If not in remote
870			 * mode force an event.
871			 */
872			fifo_write_char(sc, &sc->rfifo, 0xFA);
873			sc->data_signal = 1;
874			break;
875		case 0xEC:
876			/* Reset Wrap Mode (ignored) */
877			fifo_write_char(sc, &sc->rfifo, 0xFA);
878			break;
879		case 0xEE:
880			/* Set Wrap Mode (ignored) */
881			fifo_write_char(sc, &sc->rfifo, 0xFA);
882			break;
883		case 0xF0:
884			/* Set Remote Mode */
885			sc->remote_mode = 1;
886			fifo_write_char(sc, &sc->rfifo, 0xFA);
887			sc->delta_x = 0;
888			sc->delta_y = 0;
889			sc->delta_z = 0;
890			break;
891		case 0xF2:
892			/*
893			 * Get Device ID
894			 *
895			 * If we send 0x00 - normal PS/2 mouse, no Z-axis
896			 *
897			 * If we send 0x03 - Intellimouse, data packet has
898			 * an additional Z movement byte (8 bits signed).
899			 * (also reset movement counters)
900			 *
901			 * If we send 0x04 - Now includes z-axis and the
902			 * 4th and 5th mouse buttons.
903			 */
904			fifo_write_char(sc, &sc->rfifo, 0xFA);
905			switch(sc->zenabled) {
906			case 1:
907				fifo_write_char(sc, &sc->rfifo, 0x03);
908				break;
909			case 2:
910				fifo_write_char(sc, &sc->rfifo, 0x04);
911				break;
912			default:
913				fifo_write_char(sc, &sc->rfifo, 0x00);
914				break;
915			}
916			sc->delta_x = 0;
917			sc->delta_y = 0;
918			sc->delta_z = 0;
919			break;
920		case 0xF3:
921			/*
922			 * Set Sample Rate
923			 *
924			 * byte1: the sample rate
925			 */
926			if (sc->ps2_acked == 0) {
927				sc->ps2_acked = 1;
928				fifo_write_char(sc, &sc->rfifo, 0xFA);
929			}
930			if (fifo_ready(sc, &sc->wfifo) == 0) {
931				cmd_completed = 0;
932				break;
933			}
934			sc->mode.rate = fifo_read_char(sc, &sc->wfifo);
935			fifo_write_char(sc, &sc->rfifo, 0xFA);
936
937			/*
938			 * zenabling sequence: 200,100,80 (device id 0x03)
939			 *		       200,200,80 (device id 0x04)
940			 *
941			 * We support id 0x03 (no 4th or 5th button).
942			 * We support id 0x04 (w/ 4th and 5th button).
943			 */
944			if (sc->zenabled == 0 && sc->mode.rate == 200)
945				sc->zenabled = -1;
946			else if (sc->zenabled == -1 && sc->mode.rate == 100)
947				sc->zenabled = -2;
948			else if (sc->zenabled == -1 && sc->mode.rate == 200)
949				sc->zenabled = -3;
950			else if (sc->zenabled == -2 && sc->mode.rate == 80)
951				sc->zenabled = 1;	/* z-axis mode */
952			else if (sc->zenabled == -3 && sc->mode.rate == 80)
953				sc->zenabled = 2;	/* z-axis+but4/5 */
954			if (sc->mode.level)
955				sc->zenabled = 1;
956			break;
957		case 0xF4:
958			/* Enable data reporting.  Only effects stream mode. */
959			fifo_write_char(sc, &sc->rfifo, 0xFA);
960			sc->reporting_mode = 1;
961			break;
962		case 0xF5:
963			/*
964			 * Disable data reporting.  Only effects stream mode
965			 * and is ignored right now.
966			 */
967			fifo_write_char(sc, &sc->rfifo, 0xFA);
968			sc->reporting_mode = 1;
969			break;
970		case 0xF6:
971			/*
972			 * SET DEFAULTS
973			 *
974			 * (reset sampling rate, resolution, scaling and
975			 *  enter stream mode)
976			 */
977			fifo_write_char(sc, &sc->rfifo, 0xFA);
978			sc->mode.rate = 100;
979			sc->mode.resolution = 4;
980			sc->scaling_mode = 0;
981			sc->reporting_mode = 1;
982			sc->remote_mode = 0;
983			sc->delta_x = 0;
984			sc->delta_y = 0;
985			sc->delta_z = 0;
986			/* signal */
987			break;
988		case 0xFE:
989			/*
990			 * RESEND
991			 *
992			 * Force a resend by guaranteeing that reported_but
993			 * differs from track_but.
994			 */
995			fifo_write_char(sc, &sc->rfifo, 0xFA);
996			sc->data_signal = 1;
997			break;
998		case 0xFF:
999			/*
1000			 * RESET
1001			 */
1002			fifo_reset(sc, &sc->rfifo);	/* should we do this? */
1003			fifo_reset(sc, &sc->wfifo);	/* should we do this? */
1004			fifo_write_char(sc, &sc->rfifo, 0xFA);
1005			sc->delta_x = 0;
1006			sc->delta_y = 0;
1007			sc->delta_z = 0;
1008			sc->zenabled = 0;
1009			sc->mode.level = 0;
1010			break;
1011		default:
1012			printf("unknown command %02x\n", sc->ps2_cmd);
1013			break;
1014		}
1015		if (cmd_completed) {
1016			sc->ps2_cmd = 0;
1017			sc->ps2_acked = 0;
1018		}
1019		cyapa_notify(sc);
1020	}
1021	cyapa_unlock(sc);
1022	if (error == 0 && (cmd_completed || uio->uio_resid))
1023		goto again;
1024	return (error);
1025}
1026
1027static void cyapafiltdetach(struct knote *);
1028static int cyapafilt(struct knote *, long);
1029
1030static struct filterops cyapa_filtops = {
1031	    .f_isfd = 1,
1032	    .f_detach = cyapafiltdetach,
1033	    .f_event = cyapafilt
1034};
1035
1036static int
1037cyapakqfilter(struct cdev *dev, struct knote *kn)
1038{
1039	struct cyapa_softc *sc;
1040	struct knlist *knlist;
1041
1042	sc = dev->si_drv1;
1043
1044	switch(kn->kn_filter) {
1045	case EVFILT_READ:
1046		kn->kn_fop = &cyapa_filtops;
1047		kn->kn_hook = (void *)sc;
1048		break;
1049	default:
1050		return (EOPNOTSUPP);
1051	}
1052	knlist = &sc->selinfo.si_note;
1053	knlist_add(knlist, kn, 0);
1054
1055	return (0);
1056}
1057
1058static int
1059cyapapoll(struct cdev *dev, int events, struct thread *td)
1060{
1061	struct cyapa_softc *sc;
1062	int revents;
1063
1064	sc = dev->si_drv1;
1065	revents = 0;
1066
1067	cyapa_lock(sc);
1068	if (events & (POLLIN | POLLRDNORM)) {
1069		if (sc->data_signal || !fifo_empty(sc, &sc->rfifo))
1070			revents = events & (POLLIN | POLLRDNORM);
1071		else {
1072			sc->isselect = 1;
1073			selrecord(td, &sc->selinfo);
1074		}
1075	}
1076	cyapa_unlock(sc);
1077
1078	return (revents);
1079}
1080
1081static void
1082cyapafiltdetach(struct knote *kn)
1083{
1084	struct cyapa_softc *sc;
1085	struct knlist *knlist;
1086
1087	sc = (struct cyapa_softc *)kn->kn_hook;
1088
1089	knlist = &sc->selinfo.si_note;
1090	knlist_remove(knlist, kn, 0);
1091}
1092
1093static int
1094cyapafilt(struct knote *kn, long hint)
1095{
1096	struct cyapa_softc *sc;
1097	int ready;
1098
1099	sc = (struct cyapa_softc *)kn->kn_hook;
1100
1101	cyapa_lock(sc);
1102	ready = fifo_ready(sc, &sc->rfifo) || sc->data_signal;
1103	cyapa_unlock(sc);
1104
1105	return (ready);
1106}
1107
1108static int
1109cyapaioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
1110{
1111	struct cyapa_softc *sc;
1112	int error;
1113
1114	sc = dev->si_drv1;
1115	error = 0;
1116
1117	cyapa_lock(sc);
1118	switch (cmd) {
1119	case MOUSE_GETHWINFO:
1120		*(mousehw_t *)data = sc->hw;
1121		if (sc->mode.level == 0)
1122			((mousehw_t *)data)->model = MOUSE_MODEL_GENERIC;
1123		break;
1124
1125	case MOUSE_GETMODE:
1126		*(mousemode_t *)data = sc->mode;
1127		((mousemode_t *)data)->resolution =
1128		    MOUSE_RES_LOW - sc->mode.resolution;
1129		switch (sc->mode.level) {
1130		case 0:
1131			((mousemode_t *)data)->protocol = MOUSE_PROTO_PS2;
1132			((mousemode_t *)data)->packetsize =
1133			    MOUSE_PS2_PACKETSIZE;
1134			break;
1135		case 2:
1136			((mousemode_t *)data)->protocol = MOUSE_PROTO_PS2;
1137			((mousemode_t *)data)->packetsize =
1138			    MOUSE_PS2_PACKETSIZE + 1;
1139			break;
1140		}
1141		break;
1142
1143	case MOUSE_GETLEVEL:
1144		*(int *)data = sc->mode.level;
1145		break;
1146
1147	case MOUSE_SETLEVEL:
1148		if ((*(int *)data < 0) &&
1149		    (*(int *)data > 2)) {
1150			error = EINVAL;
1151			break;
1152		}
1153		sc->mode.level = *(int *)data ? 2 : 0;
1154		sc->zenabled = sc->mode.level ? 1 : 0;
1155		break;
1156
1157	default:
1158		error = ENOTTY;
1159		break;
1160	}
1161	cyapa_unlock(sc);
1162
1163	return (error);
1164}
1165
1166/*
1167 * MAJOR SUPPORT FUNCTIONS
1168 */
1169static void
1170cyapa_poll_thread(void *arg)
1171{
1172	struct cyapa_softc *sc;
1173	struct cyapa_regs regs;
1174	device_t bus;		/* iicbus */
1175	int error;
1176	int freq;
1177	int isidle;
1178	int pstate;
1179	int npstate;
1180	int last_reset;
1181
1182	sc = arg;
1183	freq = cyapa_norm_freq;
1184	isidle = 0;
1185	pstate = CMD_POWER_MODE_IDLE;
1186	last_reset = ticks;
1187
1188	bus = device_get_parent(sc->dev);
1189
1190	cyapa_lock(sc);
1191	sc->poll_thread_running = 1;
1192
1193	while (!sc->detaching) {
1194		cyapa_unlock(sc);
1195		error = iicbus_request_bus(bus, sc->dev, IIC_WAIT);
1196		if (error == 0) {
1197			error = cyapa_read_bytes(sc->dev, CMD_DEV_STATUS,
1198			    (void *)&regs, sizeof(regs));
1199			if (error == 0) {
1200				isidle = cyapa_raw_input(sc, &regs, freq);
1201			}
1202
1203			/*
1204			 * For some reason the device can crap-out.  If it
1205			 * drops back into bootstrap mode try to reinitialize
1206			 * it.
1207			 */
1208			if (cyapa_reset ||
1209			    ((regs.stat & CYAPA_STAT_RUNNING) == 0 &&
1210			     (unsigned)(ticks - last_reset) > TIME_TO_RESET)) {
1211				cyapa_reset = 0;
1212				last_reset = ticks;
1213				init_device(sc->dev, NULL, 2);
1214			}
1215			iicbus_release_bus(bus, sc->dev);
1216		}
1217		pause("cyapw", hz / freq);
1218		++sc->poll_ticks;
1219
1220		if (sc->count == 0) {
1221			freq = cyapa_idle_freq;
1222			npstate = CMD_POWER_MODE_IDLE;
1223		} else if (isidle) {
1224			freq = cyapa_slow_freq;
1225			npstate = CMD_POWER_MODE_IDLE;
1226		} else {
1227			freq = cyapa_norm_freq;
1228			npstate = CMD_POWER_MODE_FULL;
1229		}
1230		if (pstate != npstate) {
1231			pstate = npstate;
1232			cyapa_set_power_mode(sc, pstate);
1233			if (cyapa_debug) {
1234				switch(pstate) {
1235				case CMD_POWER_MODE_OFF:
1236					printf("cyapa: power off\n");
1237					break;
1238				case CMD_POWER_MODE_IDLE:
1239					printf("cyapa: power idle\n");
1240					break;
1241				case CMD_POWER_MODE_FULL:
1242					printf("cyapa: power full\n");
1243					break;
1244				}
1245			}
1246		}
1247
1248		cyapa_lock(sc);
1249	}
1250	sc->poll_thread_running = 0;
1251	cyapa_unlock(sc);
1252	kthread_exit();
1253}
1254
1255static int
1256cyapa_raw_input(struct cyapa_softc *sc, struct cyapa_regs *regs, int freq)
1257{
1258	int nfingers;
1259	int afingers;	/* actual fingers after culling */
1260	int i;
1261	int j;
1262	int k;
1263	int isidle;
1264	int thumbarea_begin;
1265	int seen_thumb;
1266	int x;
1267	int y;
1268	int z;
1269	int newfinger;
1270	int lessfingers;
1271	int click_x;
1272	int click_y;
1273	uint16_t but;	/* high bits used for simulated but4/but5 */
1274
1275	thumbarea_begin = sc->cap_resy -
1276	    ((sc->cap_resy *  cyapa_thumbarea_percent) / 100);
1277	click_x = click_y = 0;
1278
1279	/*
1280	 * If the device is not running the rest of the status
1281	 * means something else, set fingers to 0.
1282	 */
1283	if ((regs->stat & CYAPA_STAT_RUNNING) == 0) {
1284		regs->fngr = 0;
1285	}
1286
1287	/* Process fingers/movement */
1288	nfingers = CYAPA_FNGR_NUMFINGERS(regs->fngr);
1289	afingers = nfingers;
1290
1291	if (cyapa_debug) {
1292		printf("stat %02x buttons %c%c%c nfngrs=%d ",
1293		    regs->stat,
1294		    ((regs->fngr & CYAPA_FNGR_LEFT) ? 'L' : '-'),
1295		    ((regs->fngr & CYAPA_FNGR_MIDDLE) ? 'M' : '-'),
1296		    ((regs->fngr & CYAPA_FNGR_RIGHT) ? 'R' : '-'),
1297		    nfingers);
1298	}
1299
1300	seen_thumb = 0;
1301	for (i = 0; i < afingers; ) {
1302		if (cyapa_debug) {
1303			printf(" [x=%04d y=%04d p=%d i=%d]",
1304			    CYAPA_TOUCH_X(regs, i),
1305			    CYAPA_TOUCH_Y(regs, i),
1306			    CYAPA_TOUCH_P(regs, i),
1307			    regs->touch[i].id);
1308		}
1309		if ((CYAPA_TOUCH_Y(regs, i) > thumbarea_begin && seen_thumb) ||
1310		     CYAPA_TOUCH_P(regs, i) < cyapa_minpressure) {
1311			--afingers;
1312			if (i < afingers) {
1313			    regs->touch[i] = regs->touch[i+1];
1314			    continue;
1315			}
1316		} else {
1317			if (CYAPA_TOUCH_Y(regs, i) > thumbarea_begin)
1318			    seen_thumb = 1;
1319		}
1320		++i;
1321	}
1322	nfingers = afingers;
1323
1324	/* Tracking for local solutions */
1325	cyapa_lock(sc);
1326
1327	/*
1328	 * Track timing for finger-downs.  Used to detect false-3-finger
1329	 * button-down.
1330	 */
1331	switch(afingers) {
1332	case 0:
1333		break;
1334	case 1:
1335		if (sc->track_nfingers == 0)
1336			sc->finger1_ticks = sc->poll_ticks;
1337		break;
1338	case 2:
1339		if (sc->track_nfingers <= 0)
1340			sc->finger1_ticks = sc->poll_ticks;
1341		if (sc->track_nfingers <= 1)
1342			sc->finger2_ticks = sc->poll_ticks;
1343		break;
1344	case 3:
1345	default:
1346		if (sc->track_nfingers <= 0)
1347			sc->finger1_ticks = sc->poll_ticks;
1348		if (sc->track_nfingers <= 1)
1349			sc->finger2_ticks = sc->poll_ticks;
1350		if (sc->track_nfingers <= 2)
1351			sc->finger3_ticks = sc->poll_ticks;
1352		break;
1353	}
1354	newfinger = sc->track_nfingers < afingers;
1355	lessfingers = sc->track_nfingers > afingers;
1356	sc->track_nfingers = afingers;
1357
1358	/*
1359	 * Lookup and track finger indexes in the touch[] array.
1360	 */
1361	if (afingers == 0) {
1362		click_x = sc->track_x;
1363		click_y = sc->track_y;
1364		sc->track_x = -1;
1365		sc->track_y = -1;
1366		sc->track_z = -1;
1367		sc->fuzz_x = 0;
1368		sc->fuzz_y = 0;
1369		sc->fuzz_z = 0;
1370		sc->touch_x = -1;
1371		sc->touch_y = -1;
1372		sc->touch_z = -1;
1373		sc->track_id = -1;
1374		sc->track_but = 0;
1375		i = 0;
1376		j = 0;
1377		k = 0;
1378	} else {
1379		/*
1380		 * The id assigned on touch can move around in the array,
1381		 * find it.  If that finger is lifted up, assign some other
1382		 * finger for mouse tracking and reset track_x and track_y
1383		 * to avoid a mouse jump.
1384		 *
1385		 * If >= 2 fingers are down be sure not to assign i and
1386		 * j to the same index.
1387		 */
1388		for (i = 0; i < nfingers; ++i) {
1389			if (sc->track_id == regs->touch[i].id)
1390				break;
1391		}
1392		if (i == nfingers) {
1393			i = 0;
1394			sc->track_x = -1;
1395			sc->track_y = -1;
1396			sc->track_z = -1;
1397			while (CYAPA_TOUCH_Y(regs, i) >= thumbarea_begin &&
1398			    i < nfingers) ++i;
1399			if (i == nfingers) {
1400				i = 0;
1401			}
1402			sc->track_id = regs->touch[i].id;
1403		}
1404		else if ((sc->track_but ||
1405		     CYAPA_TOUCH_Y(regs, i) >= thumbarea_begin) &&
1406		    newfinger && afingers == 2) {
1407			j = regs->touch[0].id == sc->track_id ? 1 : 0;
1408			if (CYAPA_TOUCH_Y(regs, j) < thumbarea_begin) {
1409			    i = j;
1410			    sc->track_x = -1;
1411			    sc->track_y = -1;
1412			    sc->track_z = -1;
1413			    sc->track_id = regs->touch[i].id;
1414			}
1415		}
1416	}
1417
1418	/* Two finger scrolling - reset after timeout */
1419	if (sc->track_z != -1 && afingers != 2 &&
1420	    (sc->poll_ticks - sc->track_z_ticks) > cyapa_scroll_stick_ticks) {
1421		sc->track_z = -1;
1422		sc->track_z_ticks = 0;
1423	}
1424
1425	/* Initiate two finger scrolling */
1426	if (!(regs->fngr & CYAPA_FNGR_LEFT) &&
1427	    ((afingers && sc->track_z != -1) ||
1428	     (afingers == 2 && CYAPA_TOUCH_Y(regs, 0) < thumbarea_begin &&
1429	     CYAPA_TOUCH_Y(regs, 1) < thumbarea_begin))) {
1430		if (afingers == 2 && (sc->poll_ticks - sc->finger2_ticks)
1431		    > cyapa_scroll_wait_ticks) {
1432			z = (CYAPA_TOUCH_Y(regs, 0) +
1433			    CYAPA_TOUCH_Y(regs, 1)) >> 1;
1434			sc->delta_z += z / ZSCALE - sc->track_z;
1435			if (sc->track_z == -1) {
1436			    sc->delta_z = 0;
1437			}
1438			if (sc->touch_z == -1)
1439			    sc->touch_z = z;	/* not used atm */
1440			sc->track_z = z / ZSCALE;
1441			sc->track_z_ticks = sc->poll_ticks;
1442		}
1443	} else if (afingers) {
1444		/* Normal pad position reporting */
1445		x = CYAPA_TOUCH_X(regs, i);
1446		y = CYAPA_TOUCH_Y(regs, i);
1447		click_x = x;
1448		click_y = y;
1449		if (sc->track_x != -1 && sc->track_y < thumbarea_begin &&
1450		    (afingers > 1 || (sc->poll_ticks - sc->finger1_ticks)
1451		    >= cyapa_move_min_ticks || freq < cyapa_norm_freq)) {
1452			sc->delta_x += x - sc->track_x;
1453			sc->delta_y -= y - sc->track_y;
1454			if (sc->delta_x > sc->cap_resx)
1455				sc->delta_x = sc->cap_resx;
1456			if (sc->delta_x < -sc->cap_resx)
1457				sc->delta_x = -sc->cap_resx;
1458			if (sc->delta_y > sc->cap_resx)
1459				sc->delta_y = sc->cap_resy;
1460			if (sc->delta_y < -sc->cap_resy)
1461				sc->delta_y = -sc->cap_resy;
1462
1463			if (abs(sc->delta_y) > sc->cap_resy / 2 ||
1464			    abs(sc->delta_x) > sc->cap_resx / 2) {
1465				if (cyapa_debug)
1466					printf("Detected jump by %i %i\n",
1467					    sc->delta_x, sc->delta_y);
1468			    sc->delta_x = sc->delta_y = 0;
1469			}
1470		}
1471		if (sc->touch_x == -1) {
1472			sc->touch_x = x;
1473			sc->touch_y = y;
1474		}
1475		sc->track_x = x;
1476		sc->track_y = y;
1477	}
1478
1479	/* Select finger (L = 2/3x, M = 1/3u, R = 1/3d) */
1480	int is_tapclick = (cyapa_enable_tapclick && lessfingers &&
1481	    afingers == 0 && sc->poll_ticks - sc->finger1_ticks
1482	    >= cyapa_tapclick_min_ticks &&
1483	    sc->poll_ticks - sc->finger1_ticks < cyapa_tapclick_max_ticks);
1484
1485	if (regs->fngr & CYAPA_FNGR_LEFT || is_tapclick) {
1486		if (sc->track_but) {
1487			but = sc->track_but;
1488		} else if (afingers == 1) {
1489			if (click_x < sc->cap_resx * 2 / 3)
1490				but = CYAPA_FNGR_LEFT;
1491			else if (click_y < sc->cap_resy / 2)
1492				but = CYAPA_FNGR_MIDDLE;
1493			else
1494				but = CYAPA_FNGR_RIGHT;
1495		} else if (is_tapclick) {
1496			if (click_x < sc->cap_resx * 2 / 3 ||
1497			    cyapa_enable_tapclick < 2)
1498				but = CYAPA_FNGR_LEFT;
1499			else if (click_y < sc->cap_resy / 2 &&
1500			    cyapa_enable_tapclick > 2)
1501				but = CYAPA_FNGR_MIDDLE;
1502			else
1503				but = CYAPA_FNGR_RIGHT;
1504		} else {
1505			but = CYAPA_FNGR_LEFT;
1506		}
1507	} else {
1508		but = 0;
1509	}
1510
1511	/*
1512	 * Detect state change from last reported state and
1513	 * determine if we have gone idle.
1514	 */
1515	sc->track_but = but;
1516	if (sc->delta_x || sc->delta_y || sc->delta_z ||
1517	    sc->track_but != sc->reported_but) {
1518		sc->active_tick = ticks;
1519		if (sc->remote_mode == 0 && sc->reporting_mode)
1520			sc->data_signal = 1;
1521		isidle = 0;
1522	} else if ((unsigned)(ticks - sc->active_tick) >= TIME_TO_IDLE) {
1523		sc->active_tick = ticks - TIME_TO_IDLE; /* prevent overflow */
1524		isidle = 1;
1525	} else {
1526		isidle = 0;
1527	}
1528	cyapa_notify(sc);
1529	cyapa_unlock(sc);
1530
1531	if (cyapa_debug)
1532		printf("%i >> %i << %i\n", isidle, sc->track_id, sc->delta_y);
1533	return (isidle);
1534}
1535
1536static void
1537cyapa_set_power_mode(struct cyapa_softc *sc, int mode)
1538{
1539	uint8_t data;
1540	device_t bus;
1541	int error;
1542
1543	bus = device_get_parent(sc->dev);
1544	error = iicbus_request_bus(bus, sc->dev, IIC_WAIT);
1545	if (error == 0) {
1546		error = cyapa_read_bytes(sc->dev, CMD_POWER_MODE,
1547		    &data, 1);
1548		data = (data & ~0xFC) | mode;
1549		if (error == 0) {
1550			error = cyapa_write_bytes(sc->dev, CMD_POWER_MODE,
1551			    &data, 1);
1552		}
1553		iicbus_release_bus(bus, sc->dev);
1554	}
1555}
1556
1557/*
1558 * FIFO FUNCTIONS
1559 */
1560
1561/*
1562 * Returns non-zero if the fifo is empty
1563 */
1564static int
1565fifo_empty(struct cyapa_softc *sc, struct cyapa_fifo *fifo)
1566{
1567
1568	CYAPA_LOCK_ASSERT(sc);
1569
1570	return (fifo->rindex == fifo->windex);
1571}
1572
1573/*
1574 * Returns the number of characters available for reading from
1575 * the fifo without wrapping the fifo buffer.
1576 */
1577static size_t
1578fifo_ready(struct cyapa_softc *sc, struct cyapa_fifo *fifo)
1579{
1580	size_t n;
1581
1582	CYAPA_LOCK_ASSERT(sc);
1583
1584	n = CYAPA_BUFSIZE - (fifo->rindex & CYAPA_BUFMASK);
1585	if (n > (size_t)(fifo->windex - fifo->rindex))
1586		n = (size_t)(fifo->windex - fifo->rindex);
1587	return (n);
1588}
1589
1590/*
1591 * Returns a read pointer into the fifo and then bumps
1592 * rindex.  The FIFO must have at least 'n' characters in
1593 * it.  The value (n) can cause the index to wrap but users
1594 * of the buffer should never supply a value for (n) that wraps
1595 * the buffer.
1596 */
1597static char *
1598fifo_read(struct cyapa_softc *sc, struct cyapa_fifo *fifo, size_t n)
1599{
1600	char *ptr;
1601
1602	CYAPA_LOCK_ASSERT(sc);
1603	if (n > (CYAPA_BUFSIZE - (fifo->rindex & CYAPA_BUFMASK))) {
1604		printf("fifo_read: overflow\n");
1605		return (fifo->buf);
1606	}
1607	ptr = fifo->buf + (fifo->rindex & CYAPA_BUFMASK);
1608	fifo->rindex += n;
1609
1610	return (ptr);
1611}
1612
1613static uint8_t
1614fifo_read_char(struct cyapa_softc *sc, struct cyapa_fifo *fifo)
1615{
1616	uint8_t c;
1617
1618	CYAPA_LOCK_ASSERT(sc);
1619
1620	if (fifo->rindex == fifo->windex) {
1621		printf("fifo_read_char: overflow\n");
1622		c = 0;
1623	} else {
1624		c = fifo->buf[fifo->rindex & CYAPA_BUFMASK];
1625		++fifo->rindex;
1626	}
1627	return (c);
1628}
1629
1630
1631/*
1632 * Write a character to the FIFO.  The character will be discarded
1633 * if the FIFO is full.
1634 */
1635static void
1636fifo_write_char(struct cyapa_softc *sc, struct cyapa_fifo *fifo, uint8_t c)
1637{
1638
1639	CYAPA_LOCK_ASSERT(sc);
1640
1641	if (fifo->windex - fifo->rindex < CYAPA_BUFSIZE) {
1642		fifo->buf[fifo->windex & CYAPA_BUFMASK] = c;
1643		++fifo->windex;
1644	}
1645}
1646
1647/*
1648 * Return the amount of space available for writing without wrapping
1649 * the fifo.
1650 */
1651static size_t
1652fifo_space(struct cyapa_softc *sc, struct cyapa_fifo *fifo)
1653{
1654	size_t n;
1655
1656	CYAPA_LOCK_ASSERT(sc);
1657
1658	n = CYAPA_BUFSIZE - (fifo->windex & CYAPA_BUFMASK);
1659	if (n > (size_t)(CYAPA_BUFSIZE - (fifo->windex - fifo->rindex)))
1660		n = (size_t)(CYAPA_BUFSIZE - (fifo->windex - fifo->rindex));
1661	return (n);
1662}
1663
1664static char *
1665fifo_write(struct cyapa_softc *sc, struct cyapa_fifo *fifo, size_t n)
1666{
1667	char *ptr;
1668
1669	CYAPA_LOCK_ASSERT(sc);
1670
1671	ptr = fifo->buf + (fifo->windex & CYAPA_BUFMASK);
1672	fifo->windex += n;
1673
1674	return (ptr);
1675}
1676
1677static void
1678fifo_reset(struct cyapa_softc *sc, struct cyapa_fifo *fifo)
1679{
1680
1681	CYAPA_LOCK_ASSERT(sc);
1682
1683	fifo->rindex = 0;
1684	fifo->windex = 0;
1685}
1686
1687/*
1688 * Fuzz handling
1689 */
1690static int
1691cyapa_fuzz(int delta, int *fuzzp)
1692{
1693	int fuzz;
1694
1695	fuzz = *fuzzp;
1696	if (fuzz >= 0 && delta < 0) {
1697		++delta;
1698		--fuzz;
1699	} else if (fuzz <= 0 && delta > 0) {
1700		--delta;
1701		++fuzz;
1702	}
1703	*fuzzp = fuzz;
1704
1705	return (delta);
1706}
1707
1708DRIVER_MODULE(cyapa, iicbus, cyapa_driver, cyapa_devclass, NULL, NULL);
1709MODULE_DEPEND(cyapa, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
1710MODULE_VERSION(cyapa, 1);
1711