psm.c revision 200674
141016Sdfr/*-
241016Sdfr * Copyright (c) 1992, 1993 Erik Forsberg.
341016Sdfr * Copyright (c) 1996, 1997 Kazutaka YOKOTA.
441016Sdfr * All rights reserved.
541016Sdfr *
641016Sdfr * Redistribution and use in source and binary forms, with or without
741016Sdfr * modification, are permitted provided that the following conditions
841016Sdfr * are met:
941016Sdfr * 1. Redistributions of source code must retain the above copyright
1041016Sdfr *    notice, this list of conditions and the following disclaimer.
1141016Sdfr *
1241016Sdfr * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
1341016Sdfr * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1441016Sdfr * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
1541016Sdfr * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
1641016Sdfr * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1741016Sdfr * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1841016Sdfr * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
1941016Sdfr * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
2041016Sdfr * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2141016Sdfr * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2241016Sdfr */
2341016Sdfr/*
2441016Sdfr *  Ported to 386bsd Oct 17, 1992
2541016Sdfr *  Sandi Donno, Computer Science, University of Cape Town, South Africa
2641016Sdfr *  Please send bug reports to sandi@cs.uct.ac.za
2741016Sdfr *
2841016Sdfr *  Thanks are also due to Rick Macklem, rick@snowhite.cis.uoguelph.ca -
2941016Sdfr *  although I was only partially successful in getting the alpha release
3041016Sdfr *  of his "driver for the Logitech and ATI Inport Bus mice for use with
3141016Sdfr *  386bsd and the X386 port" to work with my Microsoft mouse, I nevertheless
3241016Sdfr *  found his code to be an invaluable reference when porting this driver
3341016Sdfr *  to 386bsd.
3441016Sdfr *
3541016Sdfr *  Further modifications for latest 386BSD+patchkit and port to NetBSD,
3641016Sdfr *  Andrew Herbert <andrew@werple.apana.org.au> - 8 June 1993
3741016Sdfr *
3841016Sdfr *  Cloned from the Microsoft Bus Mouse driver, also by Erik Forsberg, by
3941016Sdfr *  Andrew Herbert - 12 June 1993
4041016Sdfr *
4141016Sdfr *  Modified for PS/2 mouse by Charles Hannum <mycroft@ai.mit.edu>
4241016Sdfr *  - 13 June 1993
4341016Sdfr *
4441016Sdfr *  Modified for PS/2 AUX mouse by Shoji Yuen <yuen@nuie.nagoya-u.ac.jp>
4541016Sdfr *  - 24 October 1993
4641016Sdfr *
4741016Sdfr *  Hardware access routines and probe logic rewritten by
4841016Sdfr *  Kazutaka Yokota <yokota@zodiac.mech.utsunomiya-u.ac.jp>
4941016Sdfr *  - 3, 14, 22 October 1996.
5041016Sdfr *  - 12 November 1996. IOCTLs and rearranging `psmread', `psmioctl'...
5141016Sdfr *  - 14, 30 November 1996. Uses `kbdio.c'.
5241016Sdfr *  - 13 December 1996. Uses queuing version of `kbdio.c'.
53178019Sjkim *  - January/February 1997. Tweaked probe logic for
5441016Sdfr *    HiNote UltraII/Latitude/Armada laptops.
5541016Sdfr *  - 30 July 1997. Added APM support.
56178019Sjkim *  - 5 March 1997. Defined driver configuration flags (PSM_CONFIG_XXX).
5741016Sdfr *    Improved sync check logic.
5841016Sdfr *    Vendor specific support routines.
5941016Sdfr */
6041016Sdfr
61116181Sobrien#include <sys/cdefs.h>
62116181Sobrien__FBSDID("$FreeBSD: head/sys/dev/atkbdc/psm.c 200674 2009-12-18 17:46:57Z dumbbell $");
63116181Sobrien
64147271Smarius#include "opt_isa.h"
6541016Sdfr#include "opt_psm.h"
6641016Sdfr
6741016Sdfr#include <sys/param.h>
6841016Sdfr#include <sys/systm.h>
6941016Sdfr#include <sys/kernel.h>
7041016Sdfr#include <sys/module.h>
7141016Sdfr#include <sys/bus.h>
7241016Sdfr#include <sys/conf.h>
73189870Srnoland#include <sys/filio.h>
7441016Sdfr#include <sys/poll.h>
75189870Srnoland#include <sys/sigio.h>
76189870Srnoland#include <sys/signalvar.h>
7741016Sdfr#include <sys/syslog.h>
7845720Speter#include <machine/bus.h>
7941181Sdfr#include <sys/rman.h>
8070834Swollman#include <sys/selinfo.h>
81123442Salfred#include <sys/sysctl.h>
8284880Syokota#include <sys/time.h>
8341016Sdfr#include <sys/uio.h>
8441016Sdfr
85114216Skan#include <sys/limits.h>
8666860Sphk#include <sys/mouse.h>
8741181Sdfr#include <machine/resource.h>
8841016Sdfr
89147271Smarius#ifdef DEV_ISA
9041016Sdfr#include <isa/isavar.h>
91147271Smarius#endif
9241016Sdfr
93147271Smarius#include <dev/atkbdc/atkbdcreg.h>
94147271Smarius#include <dev/atkbdc/psm.h>
95147271Smarius
9641016Sdfr/*
9741016Sdfr * Driver specific options: the following options may be set by
9841016Sdfr * `options' statements in the kernel configuration file.
9941016Sdfr */
10041016Sdfr
10141016Sdfr/* debugging */
10241016Sdfr#ifndef PSM_DEBUG
103178019Sjkim#define	PSM_DEBUG	0	/*
104134405Sgibbs				 * logging: 0: none, 1: brief, 2: verbose
105134405Sgibbs				 *          3: sync errors, 4: all packets
106134405Sgibbs				 */
10741016Sdfr#endif
108178019Sjkim#define	VLOG(level, args)	do {	\
109178019Sjkim	if (verbose >= level)		\
110178019Sjkim		log args;		\
111134405Sgibbs} while (0)
11241016Sdfr
11384880Syokota#ifndef PSM_INPUT_TIMEOUT
114178019Sjkim#define	PSM_INPUT_TIMEOUT	2000000	/* 2 sec */
11584880Syokota#endif
11684880Syokota
117133297Sphilip#ifndef PSM_TAP_TIMEOUT
118178019Sjkim#define	PSM_TAP_TIMEOUT		125000
119133297Sphilip#endif
120133297Sphilip
121133297Sphilip#ifndef PSM_TAP_THRESHOLD
122178019Sjkim#define	PSM_TAP_THRESHOLD	25
123133297Sphilip#endif
124133297Sphilip
12541016Sdfr/* end of driver specific options */
12641016Sdfr
127178019Sjkim#define	PSMCPNP_DRIVER_NAME	"psmcpnp"
12883492Syokota
12941016Sdfr/* input queue */
130178019Sjkim#define	PSM_BUFSIZE		960
131178019Sjkim#define	PSM_SMALLBUFSIZE	240
13241016Sdfr
13341016Sdfr/* operation levels */
134178019Sjkim#define	PSM_LEVEL_BASE		0
135178019Sjkim#define	PSM_LEVEL_STANDARD	1
136178019Sjkim#define	PSM_LEVEL_NATIVE	2
137178019Sjkim#define	PSM_LEVEL_MIN		PSM_LEVEL_BASE
138178019Sjkim#define	PSM_LEVEL_MAX		PSM_LEVEL_NATIVE
13941016Sdfr
14048778Syokota/* Logitech PS2++ protocol */
141178019Sjkim#define	MOUSE_PS2PLUS_CHECKBITS(b)	\
142178019Sjkim    ((((b[2] & 0x03) << 2) | 0x02) == (b[1] & 0x0f))
143178019Sjkim#define	MOUSE_PS2PLUS_PACKET_TYPE(b)	\
144178019Sjkim    (((b[0] & 0x30) >> 2) | ((b[1] & 0x30) >> 4))
14548778Syokota
14641016Sdfr/* some macros */
147183397Sed#define	PSM_UNIT(dev)		(dev2unit(dev) >> 1)
148183397Sed#define	PSM_NBLOCKIO(dev)	(dev2unit(dev) & 1)
149178019Sjkim#define	PSM_MKMINOR(unit,block)	(((unit) << 1) | ((block) ? 0:1))
15041016Sdfr
15141016Sdfr/* ring buffer */
15241016Sdfrtypedef struct ringbuf {
153178019Sjkim	int		count;	/* # of valid elements in the buffer */
154178019Sjkim	int		head;	/* head pointer */
155178019Sjkim	int		tail;	/* tail poiner */
156178019Sjkim	u_char buf[PSM_BUFSIZE];
15741016Sdfr} ringbuf_t;
15841016Sdfr
159123442Salfred/* data buffer */
160123442Salfredtypedef struct packetbuf {
161178019Sjkim	u_char	ipacket[16];	/* interim input buffer */
162178019Sjkim	int	inputbytes;	/* # of bytes in the input buffer */
163123442Salfred} packetbuf_t;
164123442Salfred
165123442Salfred#ifndef PSM_PACKETQUEUE
166178019Sjkim#define	PSM_PACKETQUEUE	128
167123442Salfred#endif
168123442Salfred
169183888Sdumbbellenum {
170183888Sdumbbell	SYNAPTICS_SYSCTL_MIN_PRESSURE,
171183888Sdumbbell	SYNAPTICS_SYSCTL_MAX_PRESSURE,
172183888Sdumbbell	SYNAPTICS_SYSCTL_MAX_WIDTH,
173183888Sdumbbell	SYNAPTICS_SYSCTL_MARGIN_TOP,
174183888Sdumbbell	SYNAPTICS_SYSCTL_MARGIN_RIGHT,
175183888Sdumbbell	SYNAPTICS_SYSCTL_MARGIN_BOTTOM,
176183888Sdumbbell	SYNAPTICS_SYSCTL_MARGIN_LEFT,
177183888Sdumbbell	SYNAPTICS_SYSCTL_NA_TOP,
178183888Sdumbbell	SYNAPTICS_SYSCTL_NA_RIGHT,
179183888Sdumbbell	SYNAPTICS_SYSCTL_NA_BOTTOM,
180183888Sdumbbell	SYNAPTICS_SYSCTL_NA_LEFT,
181183888Sdumbbell	SYNAPTICS_SYSCTL_WINDOW_MIN,
182183888Sdumbbell	SYNAPTICS_SYSCTL_WINDOW_MAX,
183183888Sdumbbell	SYNAPTICS_SYSCTL_MULTIPLICATOR,
184183888Sdumbbell	SYNAPTICS_SYSCTL_WEIGHT_CURRENT,
185183888Sdumbbell	SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS,
186183888Sdumbbell	SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS_NA,
187183888Sdumbbell	SYNAPTICS_SYSCTL_WEIGHT_LEN_SQUARED,
188183888Sdumbbell	SYNAPTICS_SYSCTL_DIV_MIN,
189183888Sdumbbell	SYNAPTICS_SYSCTL_DIV_MAX,
190183888Sdumbbell	SYNAPTICS_SYSCTL_DIV_MAX_NA,
191183888Sdumbbell	SYNAPTICS_SYSCTL_DIV_LEN,
192183888Sdumbbell	SYNAPTICS_SYSCTL_TAP_MAX_DELTA,
193183888Sdumbbell	SYNAPTICS_SYSCTL_TAP_MIN_QUEUE,
194183888Sdumbbell	SYNAPTICS_SYSCTL_TAPHOLD_TIMEOUT,
195183888Sdumbbell	SYNAPTICS_SYSCTL_VSCROLL_HOR_AREA,
196183888Sdumbbell	SYNAPTICS_SYSCTL_VSCROLL_VER_AREA,
197183888Sdumbbell	SYNAPTICS_SYSCTL_VSCROLL_MIN_DELTA,
198183888Sdumbbell	SYNAPTICS_SYSCTL_VSCROLL_DIV_MIN,
199183888Sdumbbell	SYNAPTICS_SYSCTL_VSCROLL_DIV_MAX
200183888Sdumbbell};
201183888Sdumbbell
202139982Sphiliptypedef struct synapticsinfo {
203183888Sdumbbell	struct sysctl_ctx_list	 sysctl_ctx;
204178019Sjkim	struct sysctl_oid	*sysctl_tree;
205183888Sdumbbell	int			 directional_scrolls;
206183888Sdumbbell	int			 min_pressure;
207183888Sdumbbell	int			 max_pressure;
208183888Sdumbbell	int			 max_width;
209183888Sdumbbell	int			 margin_top;
210183888Sdumbbell	int			 margin_right;
211183888Sdumbbell	int			 margin_bottom;
212183888Sdumbbell	int			 margin_left;
213183888Sdumbbell	int			 na_top;
214183888Sdumbbell	int			 na_right;
215183888Sdumbbell	int			 na_bottom;
216183888Sdumbbell	int			 na_left;
217183888Sdumbbell	int			 window_min;
218183888Sdumbbell	int			 window_max;
219183888Sdumbbell	int			 multiplicator;
220183888Sdumbbell	int			 weight_current;
221183888Sdumbbell	int			 weight_previous;
222183888Sdumbbell	int			 weight_previous_na;
223183888Sdumbbell	int			 weight_len_squared;
224183888Sdumbbell	int			 div_min;
225183888Sdumbbell	int			 div_max;
226183888Sdumbbell	int			 div_max_na;
227183888Sdumbbell	int			 div_len;
228183888Sdumbbell	int			 tap_max_delta;
229183888Sdumbbell	int			 tap_min_queue;
230183888Sdumbbell	int			 taphold_timeout;
231183888Sdumbbell	int			 vscroll_ver_area;
232183888Sdumbbell	int			 vscroll_hor_area;
233183888Sdumbbell	int			 vscroll_min_delta;
234183888Sdumbbell	int			 vscroll_div_min;
235183888Sdumbbell	int			 vscroll_div_max;
236139982Sphilip} synapticsinfo_t;
237139982Sphilip
238183888Sdumbbelltypedef struct synapticspacket {
239183888Sdumbbell	int			x;
240183888Sdumbbell	int			y;
241183888Sdumbbell} synapticspacket_t;
242183888Sdumbbell
243183888Sdumbbell#define	SYNAPTICS_PACKETQUEUE 10
244183888Sdumbbell#define SYNAPTICS_QUEUE_CURSOR(x)					\
245183888Sdumbbell	(x + SYNAPTICS_PACKETQUEUE) % SYNAPTICS_PACKETQUEUE
246183888Sdumbbell
247183888Sdumbbelltypedef struct synapticsaction {
248183888Sdumbbell	synapticspacket_t	queue[SYNAPTICS_PACKETQUEUE];
249183888Sdumbbell	int			queue_len;
250183888Sdumbbell	int			queue_cursor;
251183888Sdumbbell	int			window_min;
252183888Sdumbbell	int			start_x;
253183888Sdumbbell	int			start_y;
254183888Sdumbbell	int			avg_dx;
255183888Sdumbbell	int			avg_dy;
256183888Sdumbbell	int			squelch_x;
257183888Sdumbbell	int			squelch_y;
258183888Sdumbbell	int			fingers_nb;
259183888Sdumbbell	int			tap_button;
260183888Sdumbbell	int			in_taphold;
261183888Sdumbbell	int			in_vscroll;
262183888Sdumbbell} synapticsaction_t;
263183888Sdumbbell
26441016Sdfr/* driver control block */
26541016Sdfrstruct psm_softc {		/* Driver status information */
266178019Sjkim	int		unit;
267178019Sjkim	struct selinfo	rsel;		/* Process selecting for Input */
268178019Sjkim	u_char		state;		/* Mouse driver state */
269178019Sjkim	int		config;		/* driver configuration flags */
270178019Sjkim	int		flags;		/* other flags */
271178019Sjkim	KBDC		kbdc;		/* handle to access kbd controller */
272178019Sjkim	struct resource	*intr;		/* IRQ resource */
273178019Sjkim	void		*ih;		/* interrupt handle */
274178019Sjkim	mousehw_t	hw;		/* hardware information */
275178019Sjkim	synapticshw_t	synhw;		/* Synaptics hardware information */
276178019Sjkim	synapticsinfo_t	syninfo;	/* Synaptics configuration */
277183888Sdumbbell	synapticsaction_t synaction;	/* Synaptics action context */
278178019Sjkim	mousemode_t	mode;		/* operation mode */
279178019Sjkim	mousemode_t	dflt_mode;	/* default operation mode */
280178019Sjkim	mousestatus_t	status;		/* accumulated mouse movement */
281178019Sjkim	ringbuf_t	queue;		/* mouse status queue */
282178019Sjkim	packetbuf_t	pqueue[PSM_PACKETQUEUE]; /* mouse data queue */
283178019Sjkim	int		pqueue_start;	/* start of data in queue */
284178019Sjkim	int		pqueue_end;	/* end of data in queue */
285178019Sjkim	int		button;		/* the latest button state */
286178019Sjkim	int		xold;		/* previous absolute X position */
287178019Sjkim	int		yold;		/* previous absolute Y position */
288178019Sjkim	int		xaverage;	/* average X position */
289178019Sjkim	int		yaverage;	/* average Y position */
290178019Sjkim	int		squelch; /* level to filter movement at low speed */
291178019Sjkim	int		zmax;	/* maximum pressure value for touchpads */
292178019Sjkim	int		syncerrors; /* # of bytes discarded to synchronize */
293178019Sjkim	int		pkterrors;  /* # of packets failed during quaranteen. */
294178019Sjkim	struct timeval	inputtimeout;
295178019Sjkim	struct timeval	lastsoftintr;	/* time of last soft interrupt */
296178019Sjkim	struct timeval	lastinputerr;	/* time last sync error happened */
297178019Sjkim	struct timeval	taptimeout;	/* tap timeout for touchpads */
298178019Sjkim	int		watchdog;	/* watchdog timer flag */
299178019Sjkim	struct callout_handle callout;	/* watchdog timer call out */
300178019Sjkim	struct callout_handle softcallout; /* buffer timer call out */
301178019Sjkim	struct cdev	*dev;
302178019Sjkim	struct cdev	*bdev;
303178019Sjkim	int		lasterr;
304178019Sjkim	int		cmdcount;
305189870Srnoland	struct sigio	*async;		/* Processes waiting for SIGIO */
30641016Sdfr};
307114293Smarkmstatic devclass_t psm_devclass;
308178019Sjkim#define	PSM_SOFTC(unit)	\
309178019Sjkim    ((struct psm_softc*)devclass_get_softc(psm_devclass, unit))
31041016Sdfr
31141016Sdfr/* driver state flags (state) */
312178019Sjkim#define	PSM_VALID		0x80
313178019Sjkim#define	PSM_OPEN		1	/* Device is open */
314178019Sjkim#define	PSM_ASLP		2	/* Waiting for mouse data */
315178019Sjkim#define	PSM_SOFTARMED		4	/* Software interrupt armed */
316178019Sjkim#define	PSM_NEED_SYNCBITS	8	/* Set syncbits using next data pkt */
31741016Sdfr
31841016Sdfr/* driver configuration flags (config) */
319178019Sjkim#define	PSM_CONFIG_RESOLUTION	0x000f	/* resolution */
320178019Sjkim#define	PSM_CONFIG_ACCEL	0x00f0  /* acceleration factor */
321178019Sjkim#define	PSM_CONFIG_NOCHECKSYNC	0x0100  /* disable sync. test */
322178019Sjkim#define	PSM_CONFIG_NOIDPROBE	0x0200  /* disable mouse model probe */
323178019Sjkim#define	PSM_CONFIG_NORESET	0x0400  /* don't reset the mouse */
324178019Sjkim#define	PSM_CONFIG_FORCETAP	0x0800  /* assume `tap' action exists */
325178019Sjkim#define	PSM_CONFIG_IGNPORTERROR	0x1000  /* ignore error in aux port test */
326178019Sjkim#define	PSM_CONFIG_HOOKRESUME	0x2000	/* hook the system resume event */
327178019Sjkim#define	PSM_CONFIG_INITAFTERSUSPEND 0x4000 /* init the device at the resume event */
328178019Sjkim#define	PSM_CONFIG_SYNCHACK	0x8000	/* enable `out-of-sync' hack */
32941016Sdfr
330178019Sjkim#define	PSM_CONFIG_FLAGS	\
331178019Sjkim    (PSM_CONFIG_RESOLUTION |	\
332178019Sjkim    PSM_CONFIG_ACCEL |		\
333178019Sjkim    PSM_CONFIG_NOCHECKSYNC |	\
334178019Sjkim    PSM_CONFIG_SYNCHACK |	\
335178019Sjkim    PSM_CONFIG_NOIDPROBE |	\
336178019Sjkim    PSM_CONFIG_NORESET |	\
337178019Sjkim    PSM_CONFIG_FORCETAP |	\
338178019Sjkim    PSM_CONFIG_IGNPORTERROR |	\
339178019Sjkim    PSM_CONFIG_HOOKRESUME |	\
340178019Sjkim    PSM_CONFIG_INITAFTERSUSPEND)
34141016Sdfr
34241016Sdfr/* other flags (flags) */
343178019Sjkim#define	PSM_FLAGS_FINGERDOWN	0x0001	/* VersaPad finger down */
34441016Sdfr
345135945Sphilip/* Tunables */
346200674Sdumbbellstatic int tap_enabled = -1;
347200674SdumbbellTUNABLE_INT("hw.psm.tap_enabled", &tap_enabled);
348200674Sdumbbell
349135945Sphilipstatic int synaptics_support = 0;
350135945SphilipTUNABLE_INT("hw.psm.synaptics_support", &synaptics_support);
351135945Sphilip
352139628Sphilipstatic int verbose = PSM_DEBUG;
353139628SphilipTUNABLE_INT("debug.psm.loglevel", &verbose);
354139628Sphilip
35541016Sdfr/* for backward compatibility */
356178019Sjkim#define	OLD_MOUSE_GETHWINFO	_IOR('M', 1, old_mousehw_t)
357178019Sjkim#define	OLD_MOUSE_GETMODE	_IOR('M', 2, old_mousemode_t)
358178019Sjkim#define	OLD_MOUSE_SETMODE	_IOW('M', 3, old_mousemode_t)
35941016Sdfr
36041016Sdfrtypedef struct old_mousehw {
361178019Sjkim	int	buttons;
362178019Sjkim	int	iftype;
363178019Sjkim	int	type;
364178019Sjkim	int	hwid;
36541016Sdfr} old_mousehw_t;
36641016Sdfr
36741016Sdfrtypedef struct old_mousemode {
368178019Sjkim	int	protocol;
369178019Sjkim	int	rate;
370178019Sjkim	int	resolution;
371178019Sjkim	int	accelfactor;
37241016Sdfr} old_mousemode_t;
37341016Sdfr
37441016Sdfr/* packet formatting function */
375178019Sjkimtypedef int	packetfunc_t(struct psm_softc *, u_char *, int *, int,
376178019Sjkim    mousestatus_t *);
37741016Sdfr
37841016Sdfr/* function prototypes */
379178019Sjkimstatic void	psmidentify(driver_t *, device_t);
380178019Sjkimstatic int	psmprobe(device_t);
381178019Sjkimstatic int	psmattach(device_t);
382178019Sjkimstatic int	psmdetach(device_t);
383178019Sjkimstatic int	psmresume(device_t);
38441016Sdfr
385178019Sjkimstatic d_open_t		psmopen;
386178019Sjkimstatic d_close_t	psmclose;
387178019Sjkimstatic d_read_t		psmread;
388178019Sjkimstatic d_write_t	psmwrite;
389178019Sjkimstatic d_ioctl_t	psmioctl;
390178019Sjkimstatic d_poll_t		psmpoll;
39141016Sdfr
392178019Sjkimstatic int	enable_aux_dev(KBDC);
393178019Sjkimstatic int	disable_aux_dev(KBDC);
394178019Sjkimstatic int	get_mouse_status(KBDC, int *, int, int);
395178019Sjkimstatic int	get_aux_id(KBDC);
396178019Sjkimstatic int	set_mouse_sampling_rate(KBDC, int);
397178019Sjkimstatic int	set_mouse_scaling(KBDC, int);
398178019Sjkimstatic int	set_mouse_resolution(KBDC, int);
399178019Sjkimstatic int	set_mouse_mode(KBDC);
400178019Sjkimstatic int	get_mouse_buttons(KBDC);
401178019Sjkimstatic int	is_a_mouse(int);
402178019Sjkimstatic void	recover_from_error(KBDC);
403178019Sjkimstatic int	restore_controller(KBDC, int);
404178019Sjkimstatic int	doinitialize(struct psm_softc *, mousemode_t *);
405178019Sjkimstatic int	doopen(struct psm_softc *, int);
406178019Sjkimstatic int	reinitialize(struct psm_softc *, int);
407178019Sjkimstatic char	*model_name(int);
408178019Sjkimstatic void	psmsoftintr(void *);
409178019Sjkimstatic void	psmintr(void *);
410178019Sjkimstatic void	psmtimeout(void *);
411178019Sjkimstatic int	timeelapsed(const struct timeval *, int, int,
412178019Sjkim		    const struct timeval *);
413178019Sjkimstatic void	dropqueue(struct psm_softc *);
414178019Sjkimstatic void	flushpackets(struct psm_softc *);
415178019Sjkimstatic void	proc_mmanplus(struct psm_softc *, packetbuf_t *,
416178019Sjkim		    mousestatus_t *, int *, int *, int *);
417178019Sjkimstatic int	proc_synaptics(struct psm_softc *, packetbuf_t *,
418178019Sjkim		    mousestatus_t *, int *, int *, int *);
419178019Sjkimstatic void	proc_versapad(struct psm_softc *, packetbuf_t *,
420178019Sjkim		    mousestatus_t *, int *, int *, int *);
421178019Sjkimstatic int	tame_mouse(struct psm_softc *, packetbuf_t *, mousestatus_t *,
422178019Sjkim		    u_char *);
42341016Sdfr
42441016Sdfr/* vendor specific features */
425178019Sjkimtypedef int	probefunc_t(struct psm_softc *);
42641016Sdfr
427178019Sjkimstatic int	mouse_id_proc1(KBDC, int, int, int *);
428178019Sjkimstatic int	mouse_ext_command(KBDC, int);
42941016Sdfr
430178019Sjkimstatic probefunc_t	enable_groller;
431178019Sjkimstatic probefunc_t	enable_gmouse;
432178019Sjkimstatic probefunc_t	enable_aglide;
433178019Sjkimstatic probefunc_t	enable_kmouse;
434178019Sjkimstatic probefunc_t	enable_msexplorer;
435178019Sjkimstatic probefunc_t	enable_msintelli;
436178019Sjkimstatic probefunc_t	enable_4dmouse;
437178019Sjkimstatic probefunc_t	enable_4dplus;
438178019Sjkimstatic probefunc_t	enable_mmanplus;
439178019Sjkimstatic probefunc_t	enable_synaptics;
440178019Sjkimstatic probefunc_t	enable_versapad;
441178019Sjkim
44241016Sdfrstatic struct {
443178019Sjkim	int		model;
444178019Sjkim	u_char		syncmask;
445178019Sjkim	int		packetsize;
446178019Sjkim	probefunc_t	*probefunc;
44741016Sdfr} vendortype[] = {
448178019Sjkim	/*
449178019Sjkim	 * WARNING: the order of probe is very important.  Don't mess it
450178019Sjkim	 * unless you know what you are doing.
451178019Sjkim	 */
452178019Sjkim	{ MOUSE_MODEL_NET,		/* Genius NetMouse */
453178019Sjkim	  0x08, MOUSE_PS2INTELLI_PACKETSIZE, enable_gmouse },
454178019Sjkim	{ MOUSE_MODEL_NETSCROLL,	/* Genius NetScroll */
455178019Sjkim	  0xc8, 6, enable_groller },
456178019Sjkim	{ MOUSE_MODEL_MOUSEMANPLUS,	/* Logitech MouseMan+ */
457178019Sjkim	  0x08, MOUSE_PS2_PACKETSIZE, enable_mmanplus },
458178019Sjkim	{ MOUSE_MODEL_EXPLORER,		/* Microsoft IntelliMouse Explorer */
459178019Sjkim	  0x08, MOUSE_PS2INTELLI_PACKETSIZE, enable_msexplorer },
460178019Sjkim	{ MOUSE_MODEL_4D,		/* A4 Tech 4D Mouse */
461178019Sjkim	  0x08, MOUSE_4D_PACKETSIZE, enable_4dmouse },
462178019Sjkim	{ MOUSE_MODEL_4DPLUS,		/* A4 Tech 4D+ Mouse */
463178019Sjkim	  0xc8, MOUSE_4DPLUS_PACKETSIZE, enable_4dplus },
464179474Sphilip	{ MOUSE_MODEL_SYNAPTICS,	/* Synaptics Touchpad */
465179474Sphilip	  0xc0, MOUSE_SYNAPTICS_PACKETSIZE, enable_synaptics },
466178019Sjkim	{ MOUSE_MODEL_INTELLI,		/* Microsoft IntelliMouse */
467178019Sjkim	  0x08, MOUSE_PS2INTELLI_PACKETSIZE, enable_msintelli },
468178019Sjkim	{ MOUSE_MODEL_GLIDEPOINT,	/* ALPS GlidePoint */
469178019Sjkim	  0xc0, MOUSE_PS2_PACKETSIZE, enable_aglide },
470178019Sjkim	{ MOUSE_MODEL_THINK,		/* Kensington ThinkingMouse */
471178019Sjkim	  0x80, MOUSE_PS2_PACKETSIZE, enable_kmouse },
472178019Sjkim	{ MOUSE_MODEL_VERSAPAD,		/* Interlink electronics VersaPad */
473178019Sjkim	  0xe8, MOUSE_PS2VERSA_PACKETSIZE, enable_versapad },
474178019Sjkim	{ MOUSE_MODEL_GENERIC,
475178019Sjkim	  0xc0, MOUSE_PS2_PACKETSIZE, NULL },
47641016Sdfr};
477178019Sjkim#define	GENERIC_MOUSE_ENTRY	\
478178019Sjkim    ((sizeof(vendortype) / sizeof(*vendortype)) - 1)
47941016Sdfr
48041016Sdfr/* device driver declarateion */
48141016Sdfrstatic device_method_t psm_methods[] = {
48241016Sdfr	/* Device interface */
48383147Syokota	DEVMETHOD(device_identify,	psmidentify),
48441016Sdfr	DEVMETHOD(device_probe,		psmprobe),
48541016Sdfr	DEVMETHOD(device_attach,	psmattach),
48658230Syokota	DEVMETHOD(device_detach,	psmdetach),
48754629Syokota	DEVMETHOD(device_resume,	psmresume),
48841016Sdfr
48941016Sdfr	{ 0, 0 }
49041016Sdfr};
49141016Sdfr
49241016Sdfrstatic driver_t psm_driver = {
493178019Sjkim	PSM_DRIVER_NAME,
494178019Sjkim	psm_methods,
495178019Sjkim	sizeof(struct psm_softc),
49641016Sdfr};
49741016Sdfr
49847625Sphkstatic struct cdevsw psm_cdevsw = {
499126080Sphk	.d_version =	D_VERSION,
500126080Sphk	.d_flags =	D_NEEDGIANT,
501111815Sphk	.d_open =	psmopen,
502111815Sphk	.d_close =	psmclose,
503111815Sphk	.d_read =	psmread,
504178017Sjkim	.d_write =	psmwrite,
505111815Sphk	.d_ioctl =	psmioctl,
506111815Sphk	.d_poll =	psmpoll,
507111815Sphk	.d_name =	PSM_DRIVER_NAME,
50841016Sdfr};
50941016Sdfr
51041016Sdfr/* device I/O routines */
51141016Sdfrstatic int
51241016Sdfrenable_aux_dev(KBDC kbdc)
51341016Sdfr{
514178019Sjkim	int res;
51541016Sdfr
516178019Sjkim	res = send_aux_command(kbdc, PSMC_ENABLE_DEV);
517178019Sjkim	VLOG(2, (LOG_DEBUG, "psm: ENABLE_DEV return code:%04x\n", res));
51841016Sdfr
519178019Sjkim	return (res == PSM_ACK);
52041016Sdfr}
52141016Sdfr
52241016Sdfrstatic int
52341016Sdfrdisable_aux_dev(KBDC kbdc)
52441016Sdfr{
525178019Sjkim	int res;
52641016Sdfr
527178019Sjkim	res = send_aux_command(kbdc, PSMC_DISABLE_DEV);
528178019Sjkim	VLOG(2, (LOG_DEBUG, "psm: DISABLE_DEV return code:%04x\n", res));
52941016Sdfr
530178019Sjkim	return (res == PSM_ACK);
53141016Sdfr}
53241016Sdfr
53341016Sdfrstatic int
53441016Sdfrget_mouse_status(KBDC kbdc, int *status, int flag, int len)
53541016Sdfr{
536178019Sjkim	int cmd;
537178019Sjkim	int res;
538178019Sjkim	int i;
53941016Sdfr
540178019Sjkim	switch (flag) {
541178019Sjkim	case 0:
542178019Sjkim	default:
543178019Sjkim		cmd = PSMC_SEND_DEV_STATUS;
544178019Sjkim		break;
545178019Sjkim	case 1:
546178019Sjkim		cmd = PSMC_SEND_DEV_DATA;
547178019Sjkim		break;
548178019Sjkim	}
549178019Sjkim	empty_aux_buffer(kbdc, 5);
550178019Sjkim	res = send_aux_command(kbdc, cmd);
551178019Sjkim	VLOG(2, (LOG_DEBUG, "psm: SEND_AUX_DEV_%s return code:%04x\n",
552178019Sjkim	    (flag == 1) ? "DATA" : "STATUS", res));
553178019Sjkim	if (res != PSM_ACK)
554178019Sjkim		return (0);
55541016Sdfr
556178019Sjkim	for (i = 0; i < len; ++i) {
557178019Sjkim		status[i] = read_aux_data(kbdc);
558178019Sjkim		if (status[i] < 0)
559178019Sjkim			break;
560178019Sjkim	}
56141016Sdfr
562178019Sjkim	VLOG(1, (LOG_DEBUG, "psm: %s %02x %02x %02x\n",
563178019Sjkim	    (flag == 1) ? "data" : "status", status[0], status[1], status[2]));
56441016Sdfr
565178019Sjkim	return (i);
56641016Sdfr}
56741016Sdfr
56841016Sdfrstatic int
56941016Sdfrget_aux_id(KBDC kbdc)
57041016Sdfr{
571178019Sjkim	int res;
572178019Sjkim	int id;
57341016Sdfr
574178019Sjkim	empty_aux_buffer(kbdc, 5);
575178019Sjkim	res = send_aux_command(kbdc, PSMC_SEND_DEV_ID);
576178019Sjkim	VLOG(2, (LOG_DEBUG, "psm: SEND_DEV_ID return code:%04x\n", res));
577178019Sjkim	if (res != PSM_ACK)
578178019Sjkim		return (-1);
57941016Sdfr
580178019Sjkim	/* 10ms delay */
581178019Sjkim	DELAY(10000);
58241016Sdfr
583178019Sjkim	id = read_aux_data(kbdc);
584178019Sjkim	VLOG(2, (LOG_DEBUG, "psm: device ID: %04x\n", id));
58541016Sdfr
586178019Sjkim	return (id);
58741016Sdfr}
58841016Sdfr
58941016Sdfrstatic int
59041016Sdfrset_mouse_sampling_rate(KBDC kbdc, int rate)
59141016Sdfr{
592178019Sjkim	int res;
59341016Sdfr
594178019Sjkim	res = send_aux_command_and_data(kbdc, PSMC_SET_SAMPLING_RATE, rate);
595178019Sjkim	VLOG(2, (LOG_DEBUG, "psm: SET_SAMPLING_RATE (%d) %04x\n", rate, res));
59641016Sdfr
597178019Sjkim	return ((res == PSM_ACK) ? rate : -1);
59841016Sdfr}
59941016Sdfr
60041016Sdfrstatic int
60141016Sdfrset_mouse_scaling(KBDC kbdc, int scale)
60241016Sdfr{
603178019Sjkim	int res;
60441016Sdfr
605178019Sjkim	switch (scale) {
606178019Sjkim	case 1:
607178019Sjkim	default:
608178019Sjkim		scale = PSMC_SET_SCALING11;
609178019Sjkim		break;
610178019Sjkim	case 2:
611178019Sjkim		scale = PSMC_SET_SCALING21;
612178019Sjkim		break;
613178019Sjkim	}
614178019Sjkim	res = send_aux_command(kbdc, scale);
615178019Sjkim	VLOG(2, (LOG_DEBUG, "psm: SET_SCALING%s return code:%04x\n",
616178019Sjkim	    (scale == PSMC_SET_SCALING21) ? "21" : "11", res));
61741016Sdfr
618178019Sjkim	return (res == PSM_ACK);
61941016Sdfr}
62041016Sdfr
62141016Sdfr/* `val' must be 0 through PSMD_MAX_RESOLUTION */
62241016Sdfrstatic int
62341016Sdfrset_mouse_resolution(KBDC kbdc, int val)
62441016Sdfr{
625178019Sjkim	int res;
62641016Sdfr
627178019Sjkim	res = send_aux_command_and_data(kbdc, PSMC_SET_RESOLUTION, val);
628178019Sjkim	VLOG(2, (LOG_DEBUG, "psm: SET_RESOLUTION (%d) %04x\n", val, res));
62941016Sdfr
630178019Sjkim	return ((res == PSM_ACK) ? val : -1);
63141016Sdfr}
63241016Sdfr
63341016Sdfr/*
63441016Sdfr * NOTE: once `set_mouse_mode()' is called, the mouse device must be
63541016Sdfr * re-enabled by calling `enable_aux_dev()'
63641016Sdfr */
63741016Sdfrstatic int
63841016Sdfrset_mouse_mode(KBDC kbdc)
63941016Sdfr{
640178019Sjkim	int res;
64141016Sdfr
642178019Sjkim	res = send_aux_command(kbdc, PSMC_SET_STREAM_MODE);
643178019Sjkim	VLOG(2, (LOG_DEBUG, "psm: SET_STREAM_MODE return code:%04x\n", res));
64441016Sdfr
645178019Sjkim	return (res == PSM_ACK);
64641016Sdfr}
64741016Sdfr
64841016Sdfrstatic int
64941016Sdfrget_mouse_buttons(KBDC kbdc)
65041016Sdfr{
651178019Sjkim	int c = 2;		/* assume two buttons by default */
652178019Sjkim	int status[3];
65341016Sdfr
654178019Sjkim	/*
655178019Sjkim	 * NOTE: a special sequence to obtain Logitech Mouse specific
656178019Sjkim	 * information: set resolution to 25 ppi, set scaling to 1:1, set
657178019Sjkim	 * scaling to 1:1, set scaling to 1:1. Then the second byte of the
658178019Sjkim	 * mouse status bytes is the number of available buttons.
659178019Sjkim	 * Some manufactures also support this sequence.
660178019Sjkim	 */
661178019Sjkim	if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW)
662178019Sjkim		return (c);
663178019Sjkim	if (set_mouse_scaling(kbdc, 1) && set_mouse_scaling(kbdc, 1) &&
664178019Sjkim	    set_mouse_scaling(kbdc, 1) &&
665178019Sjkim	    get_mouse_status(kbdc, status, 0, 3) >= 3 && status[1] != 0)
666178019Sjkim		return (status[1]);
667178019Sjkim	return (c);
66841016Sdfr}
66941016Sdfr
67041016Sdfr/* misc subroutines */
67141016Sdfr/*
67241016Sdfr * Someday, I will get the complete list of valid pointing devices and
67341016Sdfr * their IDs... XXX
67441016Sdfr */
67541016Sdfrstatic int
67641016Sdfris_a_mouse(int id)
67741016Sdfr{
67841016Sdfr#if 0
679178019Sjkim	static int valid_ids[] = {
680178019Sjkim		PSM_MOUSE_ID,		/* mouse */
681178019Sjkim		PSM_BALLPOINT_ID,	/* ballpoint device */
682178019Sjkim		PSM_INTELLI_ID,		/* Intellimouse */
683178019Sjkim		PSM_EXPLORER_ID,	/* Intellimouse Explorer */
684178019Sjkim		-1			/* end of table */
685178019Sjkim	};
686178019Sjkim	int i;
68741016Sdfr
688178019Sjkim	for (i = 0; valid_ids[i] >= 0; ++i)
689178019Sjkim	if (valid_ids[i] == id)
690178019Sjkim		return (TRUE);
691178019Sjkim	return (FALSE);
69241016Sdfr#else
693178019Sjkim	return (TRUE);
69441016Sdfr#endif
69541016Sdfr}
69641016Sdfr
69741016Sdfrstatic char *
69841016Sdfrmodel_name(int model)
69941016Sdfr{
700178019Sjkim	static struct {
701178019Sjkim		int	model_code;
702178019Sjkim		char	*model_name;
703178019Sjkim	} models[] = {
704178019Sjkim		{ MOUSE_MODEL_NETSCROLL,	"NetScroll" },
705178019Sjkim		{ MOUSE_MODEL_NET,		"NetMouse/NetScroll Optical" },
706178019Sjkim		{ MOUSE_MODEL_GLIDEPOINT,	"GlidePoint" },
707178019Sjkim		{ MOUSE_MODEL_THINK,		"ThinkingMouse" },
708178019Sjkim		{ MOUSE_MODEL_INTELLI,		"IntelliMouse" },
709178019Sjkim		{ MOUSE_MODEL_MOUSEMANPLUS,	"MouseMan+" },
710178019Sjkim		{ MOUSE_MODEL_VERSAPAD,		"VersaPad" },
711178019Sjkim		{ MOUSE_MODEL_EXPLORER,		"IntelliMouse Explorer" },
712178019Sjkim		{ MOUSE_MODEL_4D,		"4D Mouse" },
713178019Sjkim		{ MOUSE_MODEL_4DPLUS,		"4D+ Mouse" },
714178019Sjkim		{ MOUSE_MODEL_SYNAPTICS,	"Synaptics Touchpad" },
715178019Sjkim		{ MOUSE_MODEL_GENERIC,		"Generic PS/2 mouse" },
716178019Sjkim		{ MOUSE_MODEL_UNKNOWN,		"Unknown" },
717178019Sjkim	};
718178019Sjkim	int i;
71941016Sdfr
720178019Sjkim	for (i = 0; models[i].model_code != MOUSE_MODEL_UNKNOWN; ++i)
721178019Sjkim		if (models[i].model_code == model)
722178019Sjkim			break;
723178019Sjkim	return (models[i].model_name);
72441016Sdfr}
72541016Sdfr
72641016Sdfrstatic void
72741016Sdfrrecover_from_error(KBDC kbdc)
72841016Sdfr{
729178019Sjkim	/* discard anything left in the output buffer */
730178019Sjkim	empty_both_buffers(kbdc, 10);
73141016Sdfr
73241016Sdfr#if 0
733178019Sjkim	/*
734178019Sjkim	 * NOTE: KBDC_RESET_KBD may not restore the communication between the
735178019Sjkim	 * keyboard and the controller.
736178019Sjkim	 */
737178019Sjkim	reset_kbd(kbdc);
73841016Sdfr#else
739178019Sjkim	/*
740178019Sjkim	 * NOTE: somehow diagnostic and keyboard port test commands bring the
741178019Sjkim	 * keyboard back.
742178019Sjkim	 */
743178019Sjkim	if (!test_controller(kbdc))
744178019Sjkim		log(LOG_ERR, "psm: keyboard controller failed.\n");
745178019Sjkim	/* if there isn't a keyboard in the system, the following error is OK */
746178019Sjkim	if (test_kbd_port(kbdc) != 0)
747178019Sjkim		VLOG(1, (LOG_ERR, "psm: keyboard port failed.\n"));
74841016Sdfr#endif
74941016Sdfr}
75041016Sdfr
75141016Sdfrstatic int
75241016Sdfrrestore_controller(KBDC kbdc, int command_byte)
75341016Sdfr{
754178019Sjkim	empty_both_buffers(kbdc, 10);
75541016Sdfr
756178019Sjkim	if (!set_controller_command_byte(kbdc, 0xff, command_byte)) {
757178019Sjkim		log(LOG_ERR, "psm: failed to restore the keyboard controller "
758178019Sjkim		    "command byte.\n");
759178019Sjkim		empty_both_buffers(kbdc, 10);
760178019Sjkim		return (FALSE);
761178019Sjkim	} else {
762178019Sjkim		empty_both_buffers(kbdc, 10);
763178019Sjkim		return (TRUE);
764178019Sjkim	}
76541016Sdfr}
76641016Sdfr
767178019Sjkim/*
76841016Sdfr * Re-initialize the aux port and device. The aux port must be enabled
769178019Sjkim * and its interrupt must be disabled before calling this routine.
77041016Sdfr * The aux device will be disabled before returning.
77141016Sdfr * The keyboard controller must be locked via `kbdc_lock()' before
77241016Sdfr * calling this routine.
77341016Sdfr */
77441016Sdfrstatic int
77584880Syokotadoinitialize(struct psm_softc *sc, mousemode_t *mode)
77641016Sdfr{
777178019Sjkim	KBDC kbdc = sc->kbdc;
778178019Sjkim	int stat[3];
779178019Sjkim	int i;
78041016Sdfr
781178019Sjkim	switch((i = test_aux_port(kbdc))) {
782178019Sjkim	case 1:	/* ignore these errors */
783178019Sjkim	case 2:
784178019Sjkim	case 3:
785178019Sjkim	case PSM_ACK:
786178019Sjkim		if (verbose)
787178019Sjkim			log(LOG_DEBUG,
788178019Sjkim			    "psm%d: strange result for test aux port (%d).\n",
789178019Sjkim			    sc->unit, i);
790178019Sjkim		/* FALLTHROUGH */
791178019Sjkim	case 0:		/* no error */
792178019Sjkim		break;
793178019Sjkim	case -1:	/* time out */
794178019Sjkim	default:	/* error */
795178019Sjkim		recover_from_error(kbdc);
796178019Sjkim		if (sc->config & PSM_CONFIG_IGNPORTERROR)
797178019Sjkim			break;
798178019Sjkim		log(LOG_ERR, "psm%d: the aux port is not functioning (%d).\n",
799178019Sjkim		    sc->unit, i);
800178019Sjkim		return (FALSE);
801178019Sjkim	}
80241016Sdfr
803178019Sjkim	if (sc->config & PSM_CONFIG_NORESET) {
804178019Sjkim		/*
805178019Sjkim		 * Don't try to reset the pointing device.  It may possibly
806178019Sjkim		 * be left in the unknown state, though...
807178019Sjkim		 */
808178019Sjkim	} else {
809178019Sjkim		/*
810178019Sjkim		 * NOTE: some controllers appears to hang the `keyboard' when
811178019Sjkim		 * the aux port doesn't exist and `PSMC_RESET_DEV' is issued.
812178019Sjkim		 */
813178019Sjkim		if (!reset_aux_dev(kbdc)) {
814178019Sjkim			recover_from_error(kbdc);
815178019Sjkim			log(LOG_ERR, "psm%d: failed to reset the aux device.\n",
816178019Sjkim			    sc->unit);
817178019Sjkim			return (FALSE);
818178019Sjkim		}
819178019Sjkim	}
820178019Sjkim
821178019Sjkim	/*
822178019Sjkim	 * both the aux port and the aux device is functioning, see
823178019Sjkim	 * if the device can be enabled.
82445789Speter	 */
825178019Sjkim	if (!enable_aux_dev(kbdc) || !disable_aux_dev(kbdc)) {
826178019Sjkim		log(LOG_ERR, "psm%d: failed to enable the aux device.\n",
827178019Sjkim		    sc->unit);
828178019Sjkim		return (FALSE);
82945789Speter	}
830178019Sjkim	empty_both_buffers(kbdc, 10);	/* remove stray data if any */
83141016Sdfr
832178019Sjkim	if (sc->config & PSM_CONFIG_NOIDPROBE)
833178019Sjkim		i = GENERIC_MOUSE_ENTRY;
834178019Sjkim	else {
835178019Sjkim		/* FIXME: hardware ID, mouse buttons? */
83641016Sdfr
837178019Sjkim		/* other parameters */
838178019Sjkim		for (i = 0; vendortype[i].probefunc != NULL; ++i)
839178019Sjkim			if ((*vendortype[i].probefunc)(sc)) {
840178019Sjkim				if (verbose >= 2)
841178019Sjkim					log(LOG_ERR, "psm%d: found %s\n",
842178019Sjkim					    sc->unit,
843178019Sjkim					    model_name(vendortype[i].model));
844178019Sjkim				break;
845178019Sjkim			}
84641016Sdfr	}
84741016Sdfr
848178019Sjkim	sc->hw.model = vendortype[i].model;
849178019Sjkim	sc->mode.packetsize = vendortype[i].packetsize;
85041016Sdfr
851178019Sjkim	/* set mouse parameters */
852178019Sjkim	if (mode != (mousemode_t *)NULL) {
853178019Sjkim		if (mode->rate > 0)
854178019Sjkim			mode->rate = set_mouse_sampling_rate(kbdc, mode->rate);
855178019Sjkim		if (mode->resolution >= 0)
856178019Sjkim			mode->resolution =
857178019Sjkim			    set_mouse_resolution(kbdc, mode->resolution);
858178019Sjkim		set_mouse_scaling(kbdc, 1);
859178019Sjkim		set_mouse_mode(kbdc);
860178019Sjkim	}
86141016Sdfr
862178019Sjkim	/* Record sync on the next data packet we see. */
863178019Sjkim	sc->flags |= PSM_NEED_SYNCBITS;
86441016Sdfr
865178019Sjkim	/* just check the status of the mouse */
866178019Sjkim	if (get_mouse_status(kbdc, stat, 0, 3) < 3)
867178019Sjkim		log(LOG_DEBUG, "psm%d: failed to get status (doinitialize).\n",
868178019Sjkim		    sc->unit);
86941016Sdfr
870178019Sjkim	return (TRUE);
87141016Sdfr}
87241016Sdfr
87341016Sdfrstatic int
87484880Syokotadoopen(struct psm_softc *sc, int command_byte)
87541016Sdfr{
876178019Sjkim	int stat[3];
87741016Sdfr
878183888Sdumbbell	/*
879183888Sdumbbell	 * FIXME: Synaptics TouchPad seems to go back to Relative Mode with
880183888Sdumbbell	 * no obvious reason. Thus we check the current mode and restore the
881183888Sdumbbell	 * Absolute Mode if it was cleared.
882183888Sdumbbell	 *
883183888Sdumbbell	 * The previous hack at the end of psmprobe() wasn't efficient when
884183888Sdumbbell	 * moused(8) was restarted.
885183888Sdumbbell	 *
886183888Sdumbbell	 * A Reset (FF) or Set Defaults (F6) command would clear the
887183888Sdumbbell	 * Absolute Mode bit. But a verbose boot or debug.psm.loglevel=5
888183888Sdumbbell	 * doesn't show any evidence of such a command.
889183888Sdumbbell	 */
890183888Sdumbbell	if (sc->hw.model == MOUSE_MODEL_SYNAPTICS) {
891183888Sdumbbell		mouse_ext_command(sc->kbdc, 1);
892183888Sdumbbell		get_mouse_status(sc->kbdc, stat, 0, 3);
893183888Sdumbbell		if (stat[1] == 0x47 && stat[2] == 0x40) {
894183888Sdumbbell			/* Set the mode byte -- request wmode where
895183888Sdumbbell			 * available */
896183888Sdumbbell			if (sc->synhw.capExtended)
897183888Sdumbbell				mouse_ext_command(sc->kbdc, 0xc1);
898183888Sdumbbell			else
899183888Sdumbbell				mouse_ext_command(sc->kbdc, 0xc0);
900183888Sdumbbell			set_mouse_sampling_rate(sc->kbdc, 20);
901183888Sdumbbell			VLOG(5, (LOG_DEBUG, "psm%d: Synaptis Absolute Mode "
902183888Sdumbbell			    "hopefully restored\n",
903183888Sdumbbell			    sc->unit));
904183888Sdumbbell		}
905183888Sdumbbell	}
906183888Sdumbbell
907200674Sdumbbell	/*
908200674Sdumbbell	 * A user may want to disable tap and drag gestures on a Synaptics
909200674Sdumbbell	 * TouchPad when it operates in Relative Mode.
910200674Sdumbbell	 */
911200674Sdumbbell	if (sc->hw.model == MOUSE_MODEL_GENERIC) {
912200674Sdumbbell		if (tap_enabled > 0) {
913200674Sdumbbell			/*
914200674Sdumbbell			 * Enable tap & drag gestures. We use a Mode Byte
915200674Sdumbbell			 * and clear the DisGest bit (see ��2.5 of Synaptics
916200674Sdumbbell			 * TouchPad Interfacing Guide).
917200674Sdumbbell			 */
918200674Sdumbbell			VLOG(2, (LOG_DEBUG,
919200674Sdumbbell			    "psm%d: enable tap and drag gestures\n",
920200674Sdumbbell			    sc->unit));
921200674Sdumbbell			mouse_ext_command(sc->kbdc, 0x00);
922200674Sdumbbell			set_mouse_sampling_rate(sc->kbdc, 20);
923200674Sdumbbell		} else if (tap_enabled == 0) {
924200674Sdumbbell			/*
925200674Sdumbbell			 * Disable tap & drag gestures. We use a Mode Byte
926200674Sdumbbell			 * and set the DisGest bit (see ��2.5 of Synaptics
927200674Sdumbbell			 * TouchPad Interfacing Guide).
928200674Sdumbbell			 */
929200674Sdumbbell			VLOG(2, (LOG_DEBUG,
930200674Sdumbbell			    "psm%d: disable tap and drag gestures\n",
931200674Sdumbbell			    sc->unit));
932200674Sdumbbell			mouse_ext_command(sc->kbdc, 0x04);
933200674Sdumbbell			set_mouse_sampling_rate(sc->kbdc, 20);
934200674Sdumbbell		}
935200674Sdumbbell	}
936200674Sdumbbell
937178019Sjkim	/* enable the mouse device */
938178019Sjkim	if (!enable_aux_dev(sc->kbdc)) {
939178019Sjkim		/* MOUSE ERROR: failed to enable the mouse because:
940178019Sjkim		 * 1) the mouse is faulty,
941178019Sjkim		 * 2) the mouse has been removed(!?)
942178019Sjkim		 * In the latter case, the keyboard may have hung, and need
943178019Sjkim		 * recovery procedure...
944178019Sjkim		 */
945178019Sjkim		recover_from_error(sc->kbdc);
94641016Sdfr#if 0
947178019Sjkim		/* FIXME: we could reset the mouse here and try to enable
948178019Sjkim		 * it again. But it will take long time and it's not a good
949178019Sjkim		 * idea to disable the keyboard that long...
950178019Sjkim		 */
951178019Sjkim		if (!doinitialize(sc, &sc->mode) || !enable_aux_dev(sc->kbdc)) {
952178019Sjkim			recover_from_error(sc->kbdc);
95341016Sdfr#else
954178019Sjkim		{
95541016Sdfr#endif
956178019Sjkim			restore_controller(sc->kbdc, command_byte);
957178019Sjkim			/* mark this device is no longer available */
958178019Sjkim			sc->state &= ~PSM_VALID;
959178019Sjkim			log(LOG_ERR,
960178019Sjkim			    "psm%d: failed to enable the device (doopen).\n",
961178019Sjkim			sc->unit);
962178019Sjkim			return (EIO);
963178019Sjkim		}
96441016Sdfr	}
96541016Sdfr
966178019Sjkim	if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3)
967178019Sjkim		log(LOG_DEBUG, "psm%d: failed to get status (doopen).\n",
968178019Sjkim		    sc->unit);
96941016Sdfr
970178019Sjkim	/* enable the aux port and interrupt */
971178019Sjkim	if (!set_controller_command_byte(sc->kbdc,
97241016Sdfr	    kbdc_get_device_mask(sc->kbdc),
973178019Sjkim	    (command_byte & KBD_KBD_CONTROL_BITS) |
974178019Sjkim	    KBD_ENABLE_AUX_PORT | KBD_ENABLE_AUX_INT)) {
975178019Sjkim		/* CONTROLLER ERROR */
976178019Sjkim		disable_aux_dev(sc->kbdc);
977178019Sjkim		restore_controller(sc->kbdc, command_byte);
978178019Sjkim		log(LOG_ERR,
979178019Sjkim		    "psm%d: failed to enable the aux interrupt (doopen).\n",
980178019Sjkim		    sc->unit);
981178019Sjkim		return (EIO);
982178019Sjkim	}
98341016Sdfr
984178019Sjkim	/* start the watchdog timer */
985178019Sjkim	sc->watchdog = FALSE;
986178019Sjkim	sc->callout = timeout(psmtimeout, (void *)(uintptr_t)sc, hz*2);
98758230Syokota
988178019Sjkim	return (0);
98941016Sdfr}
99041016Sdfr
99184880Syokotastatic int
99284880Syokotareinitialize(struct psm_softc *sc, int doinit)
99384880Syokota{
994178019Sjkim	int err;
995178019Sjkim	int c;
996178019Sjkim	int s;
99784880Syokota
998178019Sjkim	/* don't let anybody mess with the aux device */
999178019Sjkim	if (!kbdc_lock(sc->kbdc, TRUE))
1000178019Sjkim		return (EIO);
1001178019Sjkim	s = spltty();
100284880Syokota
1003178019Sjkim	/* block our watchdog timer */
1004178019Sjkim	sc->watchdog = FALSE;
1005178019Sjkim	untimeout(psmtimeout, (void *)(uintptr_t)sc, sc->callout);
1006178019Sjkim	callout_handle_init(&sc->callout);
100784880Syokota
1008178019Sjkim	/* save the current controller command byte */
1009178019Sjkim	empty_both_buffers(sc->kbdc, 10);
1010178019Sjkim	c = get_controller_command_byte(sc->kbdc);
1011178019Sjkim	VLOG(2, (LOG_DEBUG,
1012178019Sjkim	    "psm%d: current command byte: %04x (reinitialize).\n",
1013178019Sjkim	    sc->unit, c));
101484880Syokota
1015178019Sjkim	/* enable the aux port but disable the aux interrupt and the keyboard */
1016178019Sjkim	if ((c == -1) || !set_controller_command_byte(sc->kbdc,
101784880Syokota	    kbdc_get_device_mask(sc->kbdc),
1018178019Sjkim	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT |
1019178019Sjkim	    KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
1020178019Sjkim		/* CONTROLLER ERROR */
1021178019Sjkim		splx(s);
1022178019Sjkim		kbdc_lock(sc->kbdc, FALSE);
1023178019Sjkim		log(LOG_ERR,
1024178019Sjkim		    "psm%d: unable to set the command byte (reinitialize).\n",
1025178019Sjkim		    sc->unit);
1026178019Sjkim		return (EIO);
1027178019Sjkim	}
102884880Syokota
1029178019Sjkim	/* flush any data */
1030178019Sjkim	if (sc->state & PSM_VALID) {
1031178019Sjkim		/* this may fail; but never mind... */
1032178019Sjkim		disable_aux_dev(sc->kbdc);
1033178019Sjkim		empty_aux_buffer(sc->kbdc, 10);
1034178019Sjkim	}
1035178019Sjkim	flushpackets(sc);
1036178019Sjkim	sc->syncerrors = 0;
1037178019Sjkim	sc->pkterrors = 0;
1038178019Sjkim	memset(&sc->lastinputerr, 0, sizeof(sc->lastinputerr));
103984880Syokota
1040178019Sjkim	/* try to detect the aux device; are you still there? */
1041178019Sjkim	err = 0;
1042178019Sjkim	if (doinit) {
1043178019Sjkim		if (doinitialize(sc, &sc->mode)) {
1044178019Sjkim			/* yes */
1045178019Sjkim			sc->state |= PSM_VALID;
1046178019Sjkim		} else {
1047178019Sjkim			/* the device has gone! */
1048178019Sjkim			restore_controller(sc->kbdc, c);
1049178019Sjkim			sc->state &= ~PSM_VALID;
1050178019Sjkim			log(LOG_ERR,
1051178019Sjkim			    "psm%d: the aux device has gone! (reinitialize).\n",
1052178019Sjkim			    sc->unit);
1053178019Sjkim			err = ENXIO;
1054178019Sjkim		}
105584880Syokota	}
1056178019Sjkim	splx(s);
105784880Syokota
1058178019Sjkim	/* restore the driver state */
1059178019Sjkim	if ((sc->state & PSM_OPEN) && (err == 0)) {
1060178019Sjkim		/* enable the aux device and the port again */
1061178019Sjkim		err = doopen(sc, c);
1062178019Sjkim		if (err != 0)
1063178019Sjkim			log(LOG_ERR, "psm%d: failed to enable the device "
1064178019Sjkim			    "(reinitialize).\n", sc->unit);
1065178019Sjkim	} else {
1066178019Sjkim		/* restore the keyboard port and disable the aux port */
1067178019Sjkim		if (!set_controller_command_byte(sc->kbdc,
1068178019Sjkim		    kbdc_get_device_mask(sc->kbdc),
1069178019Sjkim		    (c & KBD_KBD_CONTROL_BITS) |
1070178019Sjkim		    KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
1071178019Sjkim			/* CONTROLLER ERROR */
1072178019Sjkim			log(LOG_ERR, "psm%d: failed to disable the aux port "
1073178019Sjkim			    "(reinitialize).\n", sc->unit);
1074178019Sjkim			err = EIO;
1075178019Sjkim		}
107684880Syokota	}
107784880Syokota
1078178019Sjkim	kbdc_lock(sc->kbdc, FALSE);
1079178019Sjkim	return (err);
108084880Syokota}
108184880Syokota
108241016Sdfr/* psm driver entry points */
108341016Sdfr
108483147Syokotastatic void
108583147Syokotapsmidentify(driver_t *driver, device_t parent)
108683147Syokota{
1087178019Sjkim	device_t psmc;
1088178019Sjkim	device_t psm;
1089178019Sjkim	u_long irq;
1090178019Sjkim	int unit;
109183147Syokota
1092178019Sjkim	unit = device_get_unit(parent);
109383931Syokota
1094178019Sjkim	/* always add at least one child */
1095178019Sjkim	psm = BUS_ADD_CHILD(parent, KBDC_RID_AUX, driver->name, unit);
1096178019Sjkim	if (psm == NULL)
1097178019Sjkim		return;
109883931Syokota
1099178019Sjkim	irq = bus_get_resource_start(psm, SYS_RES_IRQ, KBDC_RID_AUX);
1100178019Sjkim	if (irq > 0)
1101178019Sjkim		return;
110283931Syokota
1103178019Sjkim	/*
1104178019Sjkim	 * If the PS/2 mouse device has already been reported by ACPI or
1105178019Sjkim	 * PnP BIOS, obtain the IRQ resource from it.
1106178019Sjkim	 * (See psmcpnp_attach() below.)
1107178019Sjkim	 */
1108178019Sjkim	psmc = device_find_child(device_get_parent(parent),
1109178019Sjkim	    PSMCPNP_DRIVER_NAME, unit);
1110178019Sjkim	if (psmc == NULL)
1111178019Sjkim		return;
1112178019Sjkim	irq = bus_get_resource_start(psmc, SYS_RES_IRQ, 0);
1113178019Sjkim	if (irq <= 0)
1114178019Sjkim		return;
1115178019Sjkim	bus_set_resource(psm, SYS_RES_IRQ, KBDC_RID_AUX, irq, 1);
111683147Syokota}
111783147Syokota
1118178019Sjkim#define	endprobe(v)	do {			\
1119178019Sjkim	if (bootverbose)			\
1120178019Sjkim		--verbose;			\
1121178019Sjkim	kbdc_set_device_mask(sc->kbdc, mask);	\
1122178019Sjkim	kbdc_lock(sc->kbdc, FALSE);		\
1123178019Sjkim	return (v);				\
1124178019Sjkim} while (0)
112541016Sdfr
112641016Sdfrstatic int
112741016Sdfrpsmprobe(device_t dev)
112841016Sdfr{
1129178019Sjkim	int unit = device_get_unit(dev);
1130178019Sjkim	struct psm_softc *sc = device_get_softc(dev);
1131178019Sjkim	int stat[3];
1132178019Sjkim	int command_byte;
1133178019Sjkim	int mask;
1134178019Sjkim	int rid;
1135178019Sjkim	int i;
113641016Sdfr
113741016Sdfr#if 0
1138178019Sjkim	kbdc_debug(TRUE);
113941016Sdfr#endif
114058230Syokota
1141178019Sjkim	/* see if IRQ is available */
1142178019Sjkim	rid = KBDC_RID_AUX;
1143178019Sjkim	sc->intr = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
1144178019Sjkim	    RF_SHAREABLE | RF_ACTIVE);
1145178019Sjkim	if (sc->intr == NULL) {
1146178019Sjkim		if (bootverbose)
1147178019Sjkim			device_printf(dev, "unable to allocate IRQ\n");
1148178019Sjkim		return (ENXIO);
1149178019Sjkim	}
1150178019Sjkim	bus_release_resource(dev, SYS_RES_IRQ, rid, sc->intr);
115158230Syokota
1152178019Sjkim	sc->unit = unit;
1153178019Sjkim	sc->kbdc = atkbdc_open(device_get_unit(device_get_parent(dev)));
1154178019Sjkim	sc->config = device_get_flags(dev) & PSM_CONFIG_FLAGS;
1155178019Sjkim	/* XXX: for backward compatibility */
115658230Syokota#if defined(PSM_HOOKRESUME) || defined(PSM_HOOKAPM)
1157178019Sjkim	sc->config |=
115858230Syokota#ifdef PSM_RESETAFTERSUSPEND
115958230Syokota	PSM_CONFIG_HOOKRESUME | PSM_CONFIG_INITAFTERSUSPEND;
116058230Syokota#else
116158230Syokota	PSM_CONFIG_HOOKRESUME;
116258230Syokota#endif
116358230Syokota#endif /* PSM_HOOKRESUME | PSM_HOOKAPM */
1164178019Sjkim	sc->flags = 0;
1165178019Sjkim	if (bootverbose)
1166178019Sjkim		++verbose;
116741016Sdfr
1168178019Sjkim	device_set_desc(dev, "PS/2 Mouse");
116943105Sdfr
1170178019Sjkim	if (!kbdc_lock(sc->kbdc, TRUE)) {
1171178019Sjkim		printf("psm%d: unable to lock the controller.\n", unit);
1172178019Sjkim		if (bootverbose)
1173178019Sjkim			--verbose;
1174178019Sjkim		return (ENXIO);
1175178019Sjkim	}
117641016Sdfr
1177178019Sjkim	/*
1178178019Sjkim	 * NOTE: two bits in the command byte controls the operation of the
1179178019Sjkim	 * aux port (mouse port): the aux port disable bit (bit 5) and the aux
1180178019Sjkim	 * port interrupt (IRQ 12) enable bit (bit 2).
1181178019Sjkim	 */
118241016Sdfr
1183178019Sjkim	/* discard anything left after the keyboard initialization */
1184178019Sjkim	empty_both_buffers(sc->kbdc, 10);
118541016Sdfr
1186178019Sjkim	/* save the current command byte; it will be used later */
1187178019Sjkim	mask = kbdc_get_device_mask(sc->kbdc) & ~KBD_AUX_CONTROL_BITS;
1188178019Sjkim	command_byte = get_controller_command_byte(sc->kbdc);
1189178019Sjkim	if (verbose)
1190178019Sjkim		printf("psm%d: current command byte:%04x\n", unit,
1191178019Sjkim		    command_byte);
1192178019Sjkim	if (command_byte == -1) {
1193178019Sjkim		/* CONTROLLER ERROR */
1194178019Sjkim		printf("psm%d: unable to get the current command byte value.\n",
1195178019Sjkim			unit);
1196178019Sjkim		endprobe(ENXIO);
1197178019Sjkim	}
119841016Sdfr
1199178019Sjkim	/*
1200178019Sjkim	 * disable the keyboard port while probing the aux port, which must be
1201178019Sjkim	 * enabled during this routine
1202178019Sjkim	 */
1203178019Sjkim	if (!set_controller_command_byte(sc->kbdc,
120441016Sdfr	    KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS,
1205178019Sjkim	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT |
1206178019Sjkim	    KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
1207178019Sjkim		/*
1208178019Sjkim		 * this is CONTROLLER ERROR; I don't know how to recover
1209178019Sjkim		 * from this error...
1210178019Sjkim		 */
1211178019Sjkim		restore_controller(sc->kbdc, command_byte);
1212178019Sjkim		printf("psm%d: unable to set the command byte.\n", unit);
1213178019Sjkim		endprobe(ENXIO);
1214178019Sjkim	}
1215178019Sjkim	write_controller_command(sc->kbdc, KBDC_ENABLE_AUX_PORT);
121641016Sdfr
121745789Speter	/*
1218178019Sjkim	 * NOTE: `test_aux_port()' is designed to return with zero if the aux
1219178019Sjkim	 * port exists and is functioning. However, some controllers appears
1220178019Sjkim	 * to respond with zero even when the aux port doesn't exist. (It may
1221178019Sjkim	 * be that this is only the case when the controller DOES have the aux
1222178019Sjkim	 * port but the port is not wired on the motherboard.) The keyboard
1223178019Sjkim	 * controllers without the port, such as the original AT, are
1224178019Sjkim	 * supporsed to return with an error code or simply time out. In any
1225178019Sjkim	 * case, we have to continue probing the port even when the controller
1226178019Sjkim	 * passes this test.
1227117302Smikeh	 *
1228178019Sjkim	 * XXX: some controllers erroneously return the error code 1, 2 or 3
1229178019Sjkim	 * when it has the perfectly functional aux port. We have to ignore
1230178019Sjkim	 * this error code. Even if the controller HAS error with the aux
1231178019Sjkim	 * port, it will be detected later...
1232178019Sjkim	 * XXX: another incompatible controller returns PSM_ACK (0xfa)...
123345789Speter	 */
1234178019Sjkim	switch ((i = test_aux_port(sc->kbdc))) {
1235178019Sjkim	case 1:		/* ignore these errors */
1236178019Sjkim	case 2:
1237178019Sjkim	case 3:
1238178019Sjkim	case PSM_ACK:
1239178019Sjkim		if (verbose)
1240178019Sjkim			printf("psm%d: strange result for test aux port "
1241178019Sjkim			    "(%d).\n", unit, i);
1242178019Sjkim		/* FALLTHROUGH */
1243178019Sjkim	case 0:		/* no error */
1244178019Sjkim		break;
1245178019Sjkim	case -1:	/* time out */
1246178019Sjkim	default:	/* error */
1247178019Sjkim		recover_from_error(sc->kbdc);
1248178019Sjkim		if (sc->config & PSM_CONFIG_IGNPORTERROR)
1249178019Sjkim			break;
1250178019Sjkim		restore_controller(sc->kbdc, command_byte);
1251178019Sjkim		if (verbose)
1252178019Sjkim			printf("psm%d: the aux port is not functioning (%d).\n",
1253178019Sjkim			    unit, i);
1254178019Sjkim		endprobe(ENXIO);
125545789Speter	}
125645789Speter
1257178019Sjkim	if (sc->config & PSM_CONFIG_NORESET) {
1258178019Sjkim		/*
1259178019Sjkim		 * Don't try to reset the pointing device.  It may possibly be
1260178019Sjkim		 * left in the unknown state, though...
1261178019Sjkim		 */
1262178019Sjkim	} else {
1263178019Sjkim		/*
1264178019Sjkim		 * NOTE: some controllers appears to hang the `keyboard' when
1265178019Sjkim		 * the aux port doesn't exist and `PSMC_RESET_DEV' is issued.
1266178019Sjkim		 *
1267178019Sjkim		 * Attempt to reset the controller twice -- this helps
1268178019Sjkim		 * pierce through some KVM switches. The second reset
1269178019Sjkim		 * is non-fatal.
1270178019Sjkim		 */
1271178019Sjkim		if (!reset_aux_dev(sc->kbdc)) {
1272178019Sjkim			recover_from_error(sc->kbdc);
1273178019Sjkim			restore_controller(sc->kbdc, command_byte);
1274178019Sjkim			if (verbose)
1275178019Sjkim				printf("psm%d: failed to reset the aux "
1276178019Sjkim				    "device.\n", unit);
1277178019Sjkim			endprobe(ENXIO);
1278178019Sjkim		} else if (!reset_aux_dev(sc->kbdc)) {
1279178019Sjkim			recover_from_error(sc->kbdc);
1280178019Sjkim			if (verbose >= 2)
1281178019Sjkim				printf("psm%d: failed to reset the aux device "
1282178019Sjkim				    "(2).\n", unit);
1283178019Sjkim		}
1284178019Sjkim	}
128541016Sdfr
1286178019Sjkim	/*
1287178019Sjkim	 * both the aux port and the aux device is functioning, see if the
1288178019Sjkim	 * device can be enabled. NOTE: when enabled, the device will start
1289178019Sjkim	 * sending data; we shall immediately disable the device once we know
1290178019Sjkim	 * the device can be enabled.
1291178019Sjkim	 */
1292178019Sjkim	if (!enable_aux_dev(sc->kbdc) || !disable_aux_dev(sc->kbdc)) {
1293178019Sjkim		/* MOUSE ERROR */
1294178019Sjkim		recover_from_error(sc->kbdc);
1295178019Sjkim		restore_controller(sc->kbdc, command_byte);
1296178019Sjkim		if (verbose)
1297178019Sjkim			printf("psm%d: failed to enable the aux device.\n",
1298178019Sjkim			    unit);
1299178019Sjkim		endprobe(ENXIO);
1300178019Sjkim	}
130141016Sdfr
1302178019Sjkim	/* save the default values after reset */
1303178019Sjkim	if (get_mouse_status(sc->kbdc, stat, 0, 3) >= 3) {
1304178019Sjkim		sc->dflt_mode.rate = sc->mode.rate = stat[2];
1305178019Sjkim		sc->dflt_mode.resolution = sc->mode.resolution = stat[1];
1306178019Sjkim	} else {
1307178019Sjkim		sc->dflt_mode.rate = sc->mode.rate = -1;
1308178019Sjkim		sc->dflt_mode.resolution = sc->mode.resolution = -1;
1309178019Sjkim	}
131041016Sdfr
1311178019Sjkim	/* hardware information */
1312178019Sjkim	sc->hw.iftype = MOUSE_IF_PS2;
131341016Sdfr
1314178019Sjkim	/* verify the device is a mouse */
1315178019Sjkim	sc->hw.hwid = get_aux_id(sc->kbdc);
1316178019Sjkim	if (!is_a_mouse(sc->hw.hwid)) {
1317178019Sjkim		restore_controller(sc->kbdc, command_byte);
1318178019Sjkim		if (verbose)
1319178019Sjkim			printf("psm%d: unknown device type (%d).\n", unit,
1320178019Sjkim			    sc->hw.hwid);
1321178019Sjkim		endprobe(ENXIO);
1322178019Sjkim	}
1323178019Sjkim	switch (sc->hw.hwid) {
1324178019Sjkim	case PSM_BALLPOINT_ID:
1325178019Sjkim		sc->hw.type = MOUSE_TRACKBALL;
132645789Speter		break;
1327178019Sjkim	case PSM_MOUSE_ID:
1328178019Sjkim	case PSM_INTELLI_ID:
1329178019Sjkim	case PSM_EXPLORER_ID:
1330178019Sjkim	case PSM_4DMOUSE_ID:
1331178019Sjkim	case PSM_4DPLUS_ID:
1332178019Sjkim		sc->hw.type = MOUSE_MOUSE;
1333178019Sjkim		break;
1334178019Sjkim	default:
1335178019Sjkim		sc->hw.type = MOUSE_UNKNOWN;
1336178019Sjkim		break;
133741016Sdfr	}
133841016Sdfr
1339178019Sjkim	if (sc->config & PSM_CONFIG_NOIDPROBE) {
1340178019Sjkim		sc->hw.buttons = 2;
1341178019Sjkim		i = GENERIC_MOUSE_ENTRY;
1342178019Sjkim	} else {
1343178019Sjkim		/* # of buttons */
1344178019Sjkim		sc->hw.buttons = get_mouse_buttons(sc->kbdc);
134541016Sdfr
1346178019Sjkim		/* other parameters */
1347178019Sjkim		for (i = 0; vendortype[i].probefunc != NULL; ++i)
1348178019Sjkim			if ((*vendortype[i].probefunc)(sc)) {
1349178019Sjkim				if (verbose >= 2)
1350178019Sjkim					printf("psm%d: found %s\n", unit,
1351178019Sjkim					    model_name(vendortype[i].model));
1352178019Sjkim				break;
1353178019Sjkim			}
1354178019Sjkim	}
135541016Sdfr
1356178019Sjkim	sc->hw.model = vendortype[i].model;
1357178019Sjkim
1358178019Sjkim	sc->dflt_mode.level = PSM_LEVEL_BASE;
1359178019Sjkim	sc->dflt_mode.packetsize = MOUSE_PS2_PACKETSIZE;
1360178019Sjkim	sc->dflt_mode.accelfactor = (sc->config & PSM_CONFIG_ACCEL) >> 4;
1361178019Sjkim	if (sc->config & PSM_CONFIG_NOCHECKSYNC)
1362178019Sjkim		sc->dflt_mode.syncmask[0] = 0;
1363178019Sjkim	else
1364178019Sjkim		sc->dflt_mode.syncmask[0] = vendortype[i].syncmask;
1365178019Sjkim	if (sc->config & PSM_CONFIG_FORCETAP)
1366178019Sjkim		sc->dflt_mode.syncmask[0] &= ~MOUSE_PS2_TAP;
1367178019Sjkim	sc->dflt_mode.syncmask[1] = 0;	/* syncbits */
1368178019Sjkim	sc->mode = sc->dflt_mode;
1369178019Sjkim	sc->mode.packetsize = vendortype[i].packetsize;
1370178019Sjkim
1371178019Sjkim	/* set mouse parameters */
137248773Syokota#if 0
1373178019Sjkim	/*
1374178019Sjkim	 * A version of Logitech FirstMouse+ won't report wheel movement,
1375178019Sjkim	 * if SET_DEFAULTS is sent...  Don't use this command.
1376178019Sjkim	 * This fix was found by Takashi Nishida.
1377178019Sjkim	 */
1378178019Sjkim	i = send_aux_command(sc->kbdc, PSMC_SET_DEFAULTS);
1379178019Sjkim	if (verbose >= 2)
1380178019Sjkim		printf("psm%d: SET_DEFAULTS return code:%04x\n", unit, i);
138148773Syokota#endif
1382178019Sjkim	if (sc->config & PSM_CONFIG_RESOLUTION)
1383178019Sjkim		sc->mode.resolution =
1384178019Sjkim		    set_mouse_resolution(sc->kbdc,
1385178019Sjkim		    (sc->config & PSM_CONFIG_RESOLUTION) - 1);
1386178019Sjkim	else if (sc->mode.resolution >= 0)
1387178019Sjkim		sc->mode.resolution =
1388178019Sjkim		    set_mouse_resolution(sc->kbdc, sc->dflt_mode.resolution);
1389178019Sjkim	if (sc->mode.rate > 0)
1390178019Sjkim		sc->mode.rate =
1391178019Sjkim		    set_mouse_sampling_rate(sc->kbdc, sc->dflt_mode.rate);
1392178019Sjkim	set_mouse_scaling(sc->kbdc, 1);
139341016Sdfr
1394178019Sjkim	/* Record sync on the next data packet we see. */
1395178019Sjkim	sc->flags |= PSM_NEED_SYNCBITS;
139641016Sdfr
1397178019Sjkim	/* just check the status of the mouse */
1398178019Sjkim	/*
1399178019Sjkim	 * NOTE: XXX there are some arcane controller/mouse combinations out
1400178019Sjkim	 * there, which hung the controller unless there is data transmission
1401178019Sjkim	 * after ACK from the mouse.
140241016Sdfr	 */
1403178019Sjkim	if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3)
1404178019Sjkim		printf("psm%d: failed to get status.\n", unit);
1405178019Sjkim	else {
1406178019Sjkim		/*
1407178019Sjkim		 * When in its native mode, some mice operate with different
1408178019Sjkim		 * default parameters than in the PS/2 compatible mode.
1409178019Sjkim		 */
1410178019Sjkim		sc->dflt_mode.rate = sc->mode.rate = stat[2];
1411178019Sjkim		sc->dflt_mode.resolution = sc->mode.resolution = stat[1];
1412178019Sjkim	}
141341016Sdfr
1414178019Sjkim	/* disable the aux port for now... */
1415178019Sjkim	if (!set_controller_command_byte(sc->kbdc,
141641016Sdfr	    KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS,
1417178019Sjkim	    (command_byte & KBD_KBD_CONTROL_BITS) |
1418178019Sjkim	    KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
1419178019Sjkim		/*
1420178019Sjkim		 * this is CONTROLLER ERROR; I don't know the proper way to
1421178019Sjkim		 * recover from this error...
1422178019Sjkim		 */
1423178019Sjkim		restore_controller(sc->kbdc, command_byte);
1424178019Sjkim		printf("psm%d: unable to set the command byte.\n", unit);
1425178019Sjkim		endprobe(ENXIO);
1426178019Sjkim	}
1427178019Sjkim
1428178019Sjkim	/* done */
1429178019Sjkim	kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS);
1430178019Sjkim	kbdc_lock(sc->kbdc, FALSE);
1431178019Sjkim	return (0);
143241016Sdfr}
143341016Sdfr
143441016Sdfrstatic int
143541016Sdfrpsmattach(device_t dev)
143641016Sdfr{
1437178019Sjkim	int unit = device_get_unit(dev);
1438178019Sjkim	struct psm_softc *sc = device_get_softc(dev);
1439178019Sjkim	int error;
1440178019Sjkim	int rid;
144141016Sdfr
1442178019Sjkim	/* Setup initial state */
1443178019Sjkim	sc->state = PSM_VALID;
1444178019Sjkim	callout_handle_init(&sc->callout);
144541016Sdfr
1446178019Sjkim	/* Setup our interrupt handler */
1447178019Sjkim	rid = KBDC_RID_AUX;
1448178019Sjkim	sc->intr = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
1449178019Sjkim	    RF_SHAREABLE | RF_ACTIVE);
1450178019Sjkim	if (sc->intr == NULL)
1451178019Sjkim		return (ENXIO);
1452178019Sjkim	error = bus_setup_intr(dev, sc->intr, INTR_TYPE_TTY, NULL, psmintr, sc,
1453178019Sjkim	    &sc->ih);
1454178019Sjkim	if (error) {
1455178019Sjkim		bus_release_resource(dev, SYS_RES_IRQ, rid, sc->intr);
1456178019Sjkim		return (error);
1457178019Sjkim	}
145858230Syokota
1459178019Sjkim	/* Done */
1460178019Sjkim	sc->dev = make_dev(&psm_cdevsw, PSM_MKMINOR(unit, FALSE), 0, 0, 0666,
1461178019Sjkim	    "psm%d", unit);
1462178019Sjkim	sc->bdev = make_dev(&psm_cdevsw, PSM_MKMINOR(unit, TRUE), 0, 0, 0666,
1463178019Sjkim	    "bpsm%d", unit);
146441016Sdfr
1465178019Sjkim	if (!verbose)
1466178019Sjkim		printf("psm%d: model %s, device ID %d\n",
1467178019Sjkim		    unit, model_name(sc->hw.model), sc->hw.hwid & 0x00ff);
1468178019Sjkim	else {
1469178019Sjkim		printf("psm%d: model %s, device ID %d-%02x, %d buttons\n",
1470178019Sjkim		    unit, model_name(sc->hw.model), sc->hw.hwid & 0x00ff,
1471178019Sjkim		    sc->hw.hwid >> 8, sc->hw.buttons);
1472178019Sjkim		printf("psm%d: config:%08x, flags:%08x, packet size:%d\n",
1473178019Sjkim		    unit, sc->config, sc->flags, sc->mode.packetsize);
1474178019Sjkim		printf("psm%d: syncmask:%02x, syncbits:%02x\n",
1475178019Sjkim		    unit, sc->mode.syncmask[0], sc->mode.syncmask[1]);
1476178019Sjkim	}
147741016Sdfr
1478178019Sjkim	if (bootverbose)
1479178019Sjkim		--verbose;
148041016Sdfr
1481178019Sjkim	return (0);
148241016Sdfr}
148341016Sdfr
148441016Sdfrstatic int
148558230Syokotapsmdetach(device_t dev)
148658230Syokota{
1487178019Sjkim	struct psm_softc *sc;
1488178019Sjkim	int rid;
148958230Syokota
1490178019Sjkim	sc = device_get_softc(dev);
1491178019Sjkim	if (sc->state & PSM_OPEN)
1492178019Sjkim		return (EBUSY);
149358230Syokota
1494178019Sjkim	rid = KBDC_RID_AUX;
1495178019Sjkim	bus_teardown_intr(dev, sc->intr, sc->ih);
1496178019Sjkim	bus_release_resource(dev, SYS_RES_IRQ, rid, sc->intr);
149758230Syokota
1498178019Sjkim	destroy_dev(sc->dev);
1499178019Sjkim	destroy_dev(sc->bdev);
150058230Syokota
1501178019Sjkim	return (0);
150258230Syokota}
150358230Syokota
150458230Syokotastatic int
1505130585Sphkpsmopen(struct cdev *dev, int flag, int fmt, struct thread *td)
150641016Sdfr{
1507178019Sjkim	int unit = PSM_UNIT(dev);
1508178019Sjkim	struct psm_softc *sc;
1509178019Sjkim	int command_byte;
1510178019Sjkim	int err;
1511178019Sjkim	int s;
151241016Sdfr
1513178019Sjkim	/* Get device data */
1514178019Sjkim	sc = PSM_SOFTC(unit);
1515178019Sjkim	if ((sc == NULL) || (sc->state & PSM_VALID) == 0) {
1516178019Sjkim		/* the device is no longer valid/functioning */
1517178019Sjkim		return (ENXIO);
1518178019Sjkim	}
151941016Sdfr
1520178019Sjkim	/* Disallow multiple opens */
1521178019Sjkim	if (sc->state & PSM_OPEN)
1522178019Sjkim		return (EBUSY);
152341016Sdfr
1524178019Sjkim	device_busy(devclass_get_device(psm_devclass, unit));
152541016Sdfr
1526178019Sjkim	/* Initialize state */
1527178019Sjkim	sc->mode.level = sc->dflt_mode.level;
1528178019Sjkim	sc->mode.protocol = sc->dflt_mode.protocol;
1529178019Sjkim	sc->watchdog = FALSE;
1530189870Srnoland	sc->async = NULL;
153141016Sdfr
1532178019Sjkim	/* flush the event queue */
1533178019Sjkim	sc->queue.count = 0;
1534178019Sjkim	sc->queue.head = 0;
1535178019Sjkim	sc->queue.tail = 0;
1536178019Sjkim	sc->status.flags = 0;
1537178019Sjkim	sc->status.button = 0;
1538178019Sjkim	sc->status.obutton = 0;
1539178019Sjkim	sc->status.dx = 0;
1540178019Sjkim	sc->status.dy = 0;
1541178019Sjkim	sc->status.dz = 0;
1542178019Sjkim	sc->button = 0;
1543178019Sjkim	sc->pqueue_start = 0;
1544178019Sjkim	sc->pqueue_end = 0;
154541016Sdfr
1546178019Sjkim	/* empty input buffer */
1547178019Sjkim	flushpackets(sc);
1548178019Sjkim	sc->syncerrors = 0;
1549178019Sjkim	sc->pkterrors = 0;
155041016Sdfr
1551178019Sjkim	/* don't let timeout routines in the keyboard driver to poll the kbdc */
1552178019Sjkim	if (!kbdc_lock(sc->kbdc, TRUE))
1553178019Sjkim		return (EIO);
155441016Sdfr
1555178019Sjkim	/* save the current controller command byte */
1556178019Sjkim	s = spltty();
1557178019Sjkim	command_byte = get_controller_command_byte(sc->kbdc);
155841016Sdfr
1559178019Sjkim	/* enable the aux port and temporalily disable the keyboard */
1560178019Sjkim	if (command_byte == -1 || !set_controller_command_byte(sc->kbdc,
156141016Sdfr	    kbdc_get_device_mask(sc->kbdc),
1562178019Sjkim	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT |
1563178019Sjkim	    KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
1564178019Sjkim		/* CONTROLLER ERROR; do you know how to get out of this? */
1565178019Sjkim		kbdc_lock(sc->kbdc, FALSE);
1566178019Sjkim		splx(s);
1567178019Sjkim		log(LOG_ERR,
1568178019Sjkim		    "psm%d: unable to set the command byte (psmopen).\n", unit);
1569178019Sjkim		return (EIO);
1570178019Sjkim	}
1571178019Sjkim	/*
1572178019Sjkim	 * Now that the keyboard controller is told not to generate
1573178019Sjkim	 * the keyboard and mouse interrupts, call `splx()' to allow
1574178019Sjkim	 * the other tty interrupts. The clock interrupt may also occur,
1575178019Sjkim	 * but timeout routines will be blocked by the poll flag set
1576178019Sjkim	 * via `kbdc_lock()'
1577178019Sjkim	 */
157841016Sdfr	splx(s);
157941016Sdfr
1580178019Sjkim	/* enable the mouse device */
1581178019Sjkim	err = doopen(sc, command_byte);
1582178019Sjkim
1583178019Sjkim	/* done */
1584178019Sjkim	if (err == 0)
1585178019Sjkim		sc->state |= PSM_OPEN;
1586178019Sjkim	kbdc_lock(sc->kbdc, FALSE);
1587178019Sjkim	return (err);
158841016Sdfr}
158941016Sdfr
159041016Sdfrstatic int
1591130585Sphkpsmclose(struct cdev *dev, int flag, int fmt, struct thread *td)
159241016Sdfr{
1593178019Sjkim	int unit = PSM_UNIT(dev);
1594178019Sjkim	struct psm_softc *sc = PSM_SOFTC(unit);
1595178019Sjkim	int stat[3];
1596178019Sjkim	int command_byte;
1597178019Sjkim	int s;
159841016Sdfr
1599178019Sjkim	/* don't let timeout routines in the keyboard driver to poll the kbdc */
1600178019Sjkim	if (!kbdc_lock(sc->kbdc, TRUE))
1601178019Sjkim		return (EIO);
160241016Sdfr
1603178019Sjkim	/* save the current controller command byte */
1604178019Sjkim	s = spltty();
1605178019Sjkim	command_byte = get_controller_command_byte(sc->kbdc);
1606178019Sjkim	if (command_byte == -1) {
1607178019Sjkim		kbdc_lock(sc->kbdc, FALSE);
1608178019Sjkim		splx(s);
1609178019Sjkim		return (EIO);
1610178019Sjkim	}
161141016Sdfr
1612178019Sjkim	/* disable the aux interrupt and temporalily disable the keyboard */
1613178019Sjkim	if (!set_controller_command_byte(sc->kbdc,
161441016Sdfr	    kbdc_get_device_mask(sc->kbdc),
1615178019Sjkim	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT |
1616178019Sjkim	    KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
1617178019Sjkim		log(LOG_ERR,
1618178019Sjkim		    "psm%d: failed to disable the aux int (psmclose).\n", unit);
1619178019Sjkim		/* CONTROLLER ERROR;
1620178019Sjkim		 * NOTE: we shall force our way through. Because the only
1621178019Sjkim		 * ill effect we shall see is that we may not be able
1622178019Sjkim		 * to read ACK from the mouse, and it doesn't matter much
1623178019Sjkim		 * so long as the mouse will accept the DISABLE command.
1624178019Sjkim		 */
1625178019Sjkim	}
1626178019Sjkim	splx(s);
162741016Sdfr
1628178019Sjkim	/* stop the watchdog timer */
1629178019Sjkim	untimeout(psmtimeout, (void *)(uintptr_t)sc, sc->callout);
1630178019Sjkim	callout_handle_init(&sc->callout);
163158230Syokota
1632178019Sjkim	/* remove anything left in the output buffer */
1633178019Sjkim	empty_aux_buffer(sc->kbdc, 10);
163441016Sdfr
1635178019Sjkim	/* disable the aux device, port and interrupt */
1636178019Sjkim	if (sc->state & PSM_VALID) {
1637178019Sjkim		if (!disable_aux_dev(sc->kbdc)) {
1638178019Sjkim			/* MOUSE ERROR;
1639178019Sjkim			 * NOTE: we don't return (error) and continue,
1640178019Sjkim			 * pretending we have successfully disabled the device.
1641178019Sjkim			 * It's OK because the interrupt routine will discard
1642178019Sjkim			 * any data from the mouse hereafter.
1643178019Sjkim			 */
1644178019Sjkim			log(LOG_ERR,
1645178019Sjkim			    "psm%d: failed to disable the device (psmclose).\n",
1646178019Sjkim			    unit);
1647178019Sjkim		}
164841016Sdfr
1649178019Sjkim		if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3)
1650178019Sjkim			log(LOG_DEBUG,
1651178019Sjkim			    "psm%d: failed to get status (psmclose).\n", unit);
1652178019Sjkim	}
165341016Sdfr
1654178019Sjkim	if (!set_controller_command_byte(sc->kbdc,
165541016Sdfr	    kbdc_get_device_mask(sc->kbdc),
1656178019Sjkim	    (command_byte & KBD_KBD_CONTROL_BITS) |
1657178019Sjkim	    KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
1658178019Sjkim		/*
1659178019Sjkim		 * CONTROLLER ERROR;
1660178019Sjkim		 * we shall ignore this error; see the above comment.
1661178019Sjkim		 */
1662178019Sjkim		log(LOG_ERR,
1663178019Sjkim		    "psm%d: failed to disable the aux port (psmclose).\n",
1664178019Sjkim		    unit);
1665178019Sjkim	}
166641016Sdfr
1667178019Sjkim	/* remove anything left in the output buffer */
1668178019Sjkim	empty_aux_buffer(sc->kbdc, 10);
166941016Sdfr
1670189870Srnoland	/* clean up and sigio requests */
1671189870Srnoland	if (sc->async != NULL) {
1672189870Srnoland		funsetown(&sc->async);
1673189870Srnoland		sc->async = NULL;
1674189870Srnoland	}
1675189870Srnoland
1676178019Sjkim	/* close is almost always successful */
1677178019Sjkim	sc->state &= ~PSM_OPEN;
1678178019Sjkim	kbdc_lock(sc->kbdc, FALSE);
1679178019Sjkim	device_unbusy(devclass_get_device(psm_devclass, unit));
1680178019Sjkim	return (0);
168141016Sdfr}
168241016Sdfr
168341016Sdfrstatic int
1684178019Sjkimtame_mouse(struct psm_softc *sc, packetbuf_t *pb, mousestatus_t *status,
1685178019Sjkim    u_char *buf)
168641016Sdfr{
1687178019Sjkim	static u_char butmapps2[8] = {
1688178019Sjkim		0,
1689178019Sjkim		MOUSE_PS2_BUTTON1DOWN,
1690178019Sjkim		MOUSE_PS2_BUTTON2DOWN,
1691178019Sjkim		MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN,
1692178019Sjkim		MOUSE_PS2_BUTTON3DOWN,
1693178019Sjkim		MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON3DOWN,
1694178019Sjkim		MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN,
1695178019Sjkim		MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN |
1696178019Sjkim		    MOUSE_PS2_BUTTON3DOWN,
1697178019Sjkim	};
1698178019Sjkim	static u_char butmapmsc[8] = {
1699178019Sjkim		MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP |
1700178019Sjkim		    MOUSE_MSC_BUTTON3UP,
1701178019Sjkim		MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
1702178019Sjkim		MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP,
1703178019Sjkim		MOUSE_MSC_BUTTON3UP,
1704178019Sjkim		MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP,
1705178019Sjkim		MOUSE_MSC_BUTTON2UP,
1706178019Sjkim		MOUSE_MSC_BUTTON1UP,
1707178019Sjkim		0,
1708178019Sjkim	};
1709178019Sjkim	int mapped;
1710178019Sjkim	int i;
171141016Sdfr
1712178019Sjkim	if (sc->mode.level == PSM_LEVEL_BASE) {
1713178019Sjkim		mapped = status->button & ~MOUSE_BUTTON4DOWN;
1714178019Sjkim		if (status->button & MOUSE_BUTTON4DOWN)
1715178019Sjkim			mapped |= MOUSE_BUTTON1DOWN;
1716178019Sjkim		status->button = mapped;
1717178019Sjkim		buf[0] = MOUSE_PS2_SYNC | butmapps2[mapped & MOUSE_STDBUTTONS];
1718178019Sjkim		i = imax(imin(status->dx, 255), -256);
1719178019Sjkim		if (i < 0)
1720178019Sjkim			buf[0] |= MOUSE_PS2_XNEG;
1721178019Sjkim		buf[1] = i;
1722178019Sjkim		i = imax(imin(status->dy, 255), -256);
1723178019Sjkim		if (i < 0)
1724178019Sjkim			buf[0] |= MOUSE_PS2_YNEG;
1725178019Sjkim		buf[2] = i;
1726178019Sjkim		return (MOUSE_PS2_PACKETSIZE);
1727178019Sjkim	} else if (sc->mode.level == PSM_LEVEL_STANDARD) {
1728178019Sjkim		buf[0] = MOUSE_MSC_SYNC |
1729178019Sjkim		    butmapmsc[status->button & MOUSE_STDBUTTONS];
1730178019Sjkim		i = imax(imin(status->dx, 255), -256);
1731178019Sjkim		buf[1] = i >> 1;
1732178019Sjkim		buf[3] = i - buf[1];
1733178019Sjkim		i = imax(imin(status->dy, 255), -256);
1734178019Sjkim		buf[2] = i >> 1;
1735178019Sjkim		buf[4] = i - buf[2];
1736178019Sjkim		i = imax(imin(status->dz, 127), -128);
1737178019Sjkim		buf[5] = (i >> 1) & 0x7f;
1738178019Sjkim		buf[6] = (i - (i >> 1)) & 0x7f;
1739178019Sjkim		buf[7] = (~status->button >> 3) & 0x7f;
1740178019Sjkim		return (MOUSE_SYS_PACKETSIZE);
1741178019Sjkim	}
1742178019Sjkim	return (pb->inputbytes);
174341016Sdfr}
174441016Sdfr
174541016Sdfrstatic int
1746130585Sphkpsmread(struct cdev *dev, struct uio *uio, int flag)
174741016Sdfr{
1748178019Sjkim	register struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev));
1749178019Sjkim	u_char buf[PSM_SMALLBUFSIZE];
1750178019Sjkim	int error = 0;
1751178019Sjkim	int s;
1752178019Sjkim	int l;
175341016Sdfr
1754178019Sjkim	if ((sc->state & PSM_VALID) == 0)
1755178019Sjkim		return (EIO);
175641016Sdfr
1757178019Sjkim	/* block until mouse activity occured */
1758178019Sjkim	s = spltty();
1759178019Sjkim	while (sc->queue.count <= 0) {
1760178019Sjkim		if (PSM_NBLOCKIO(dev)) {
1761178019Sjkim			splx(s);
1762178019Sjkim			return (EWOULDBLOCK);
1763178019Sjkim		}
1764178019Sjkim		sc->state |= PSM_ASLP;
1765178019Sjkim		error = tsleep(sc, PZERO | PCATCH, "psmrea", 0);
1766178019Sjkim		sc->state &= ~PSM_ASLP;
1767178019Sjkim		if (error) {
1768178019Sjkim			splx(s);
1769178019Sjkim			return (error);
1770178019Sjkim		} else if ((sc->state & PSM_VALID) == 0) {
1771178019Sjkim			/* the device disappeared! */
1772178019Sjkim			splx(s);
1773178019Sjkim			return (EIO);
1774178019Sjkim		}
177541016Sdfr	}
1776178019Sjkim	splx(s);
177741016Sdfr
1778178019Sjkim	/* copy data to the user land */
1779178019Sjkim	while ((sc->queue.count > 0) && (uio->uio_resid > 0)) {
1780178019Sjkim		s = spltty();
1781178019Sjkim		l = imin(sc->queue.count, uio->uio_resid);
1782178019Sjkim		if (l > sizeof(buf))
1783178019Sjkim			l = sizeof(buf);
1784178019Sjkim		if (l > sizeof(sc->queue.buf) - sc->queue.head) {
1785178019Sjkim			bcopy(&sc->queue.buf[sc->queue.head], &buf[0],
1786178019Sjkim			    sizeof(sc->queue.buf) - sc->queue.head);
1787178019Sjkim			bcopy(&sc->queue.buf[0],
1788178019Sjkim			    &buf[sizeof(sc->queue.buf) - sc->queue.head],
1789178019Sjkim			    l - (sizeof(sc->queue.buf) - sc->queue.head));
1790178019Sjkim		} else
1791178019Sjkim			bcopy(&sc->queue.buf[sc->queue.head], &buf[0], l);
1792178019Sjkim		sc->queue.count -= l;
1793178019Sjkim		sc->queue.head = (sc->queue.head + l) % sizeof(sc->queue.buf);
1794178019Sjkim		splx(s);
1795178019Sjkim		error = uiomove(buf, l, uio);
1796178019Sjkim		if (error)
1797178019Sjkim			break;
179841016Sdfr	}
179941016Sdfr
1800178019Sjkim	return (error);
180141016Sdfr}
180241016Sdfr
180341016Sdfrstatic int
180441016Sdfrblock_mouse_data(struct psm_softc *sc, int *c)
180541016Sdfr{
1806178019Sjkim	int s;
180741016Sdfr
1808178019Sjkim	if (!kbdc_lock(sc->kbdc, TRUE))
1809178019Sjkim		return (EIO);
181041016Sdfr
1811178019Sjkim	s = spltty();
1812178019Sjkim	*c = get_controller_command_byte(sc->kbdc);
1813178019Sjkim	if ((*c == -1) || !set_controller_command_byte(sc->kbdc,
181441016Sdfr	    kbdc_get_device_mask(sc->kbdc),
1815178019Sjkim	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT |
1816178019Sjkim	    KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
1817178019Sjkim		/* this is CONTROLLER ERROR */
1818178019Sjkim		splx(s);
1819178019Sjkim		kbdc_lock(sc->kbdc, FALSE);
1820178019Sjkim		return (EIO);
1821178019Sjkim	}
1822178019Sjkim
1823178019Sjkim	/*
1824178019Sjkim	 * The device may be in the middle of status data transmission.
1825178019Sjkim	 * The transmission will be interrupted, thus, incomplete status
1826178019Sjkim	 * data must be discarded. Although the aux interrupt is disabled
1827178019Sjkim	 * at the keyboard controller level, at most one aux interrupt
1828178019Sjkim	 * may have already been pending and a data byte is in the
1829178019Sjkim	 * output buffer; throw it away. Note that the second argument
1830178019Sjkim	 * to `empty_aux_buffer()' is zero, so that the call will just
1831178019Sjkim	 * flush the internal queue.
1832178019Sjkim	 * `psmintr()' will be invoked after `splx()' if an interrupt is
1833178019Sjkim	 * pending; it will see no data and returns immediately.
1834178019Sjkim	 */
1835178019Sjkim	empty_aux_buffer(sc->kbdc, 0);		/* flush the queue */
1836178019Sjkim	read_aux_data_no_wait(sc->kbdc);	/* throw away data if any */
1837178019Sjkim	flushpackets(sc);
183841016Sdfr	splx(s);
183941016Sdfr
1840178019Sjkim	return (0);
184141016Sdfr}
184241016Sdfr
1843123442Salfredstatic void
1844123442Salfreddropqueue(struct psm_softc *sc)
1845123442Salfred{
1846123442Salfred
1847178019Sjkim	sc->queue.count = 0;
1848178019Sjkim	sc->queue.head = 0;
1849178019Sjkim	sc->queue.tail = 0;
1850123442Salfred	if ((sc->state & PSM_SOFTARMED) != 0) {
1851123442Salfred		sc->state &= ~PSM_SOFTARMED;
1852123442Salfred		untimeout(psmsoftintr, (void *)(uintptr_t)sc, sc->softcallout);
1853123442Salfred	}
1854123442Salfred	sc->pqueue_start = sc->pqueue_end;
1855123442Salfred}
1856123442Salfred
1857123442Salfredstatic void
1858123442Salfredflushpackets(struct psm_softc *sc)
1859123442Salfred{
1860123442Salfred
1861123442Salfred	dropqueue(sc);
1862123442Salfred	bzero(&sc->pqueue, sizeof(sc->pqueue));
1863123442Salfred}
1864123442Salfred
186541016Sdfrstatic int
186641016Sdfrunblock_mouse_data(struct psm_softc *sc, int c)
186741016Sdfr{
1868178019Sjkim	int error = 0;
186941016Sdfr
1870178019Sjkim	/*
1871178019Sjkim	 * We may have seen a part of status data during `set_mouse_XXX()'.
1872178019Sjkim	 * they have been queued; flush it.
1873178019Sjkim	 */
1874178019Sjkim	empty_aux_buffer(sc->kbdc, 0);
187541016Sdfr
1876178019Sjkim	/* restore ports and interrupt */
1877178019Sjkim	if (!set_controller_command_byte(sc->kbdc,
1878178019Sjkim	    kbdc_get_device_mask(sc->kbdc),
187941016Sdfr	    c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) {
1880178019Sjkim		/*
1881178019Sjkim		 * CONTROLLER ERROR; this is serious, we may have
1882178019Sjkim		 * been left with the inaccessible keyboard and
1883178019Sjkim		 * the disabled mouse interrupt.
1884178019Sjkim		 */
1885178019Sjkim		error = EIO;
1886178019Sjkim	}
188741016Sdfr
1888178019Sjkim	kbdc_lock(sc->kbdc, FALSE);
1889178019Sjkim	return (error);
189041016Sdfr}
189141016Sdfr
189241016Sdfrstatic int
1893178017Sjkimpsmwrite(struct cdev *dev, struct uio *uio, int flag)
1894178017Sjkim{
1895178019Sjkim	register struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev));
1896178019Sjkim	u_char buf[PSM_SMALLBUFSIZE];
1897178019Sjkim	int error = 0, i, l;
1898178017Sjkim
1899178019Sjkim	if ((sc->state & PSM_VALID) == 0)
1900178019Sjkim		return (EIO);
1901178017Sjkim
1902178019Sjkim	if (sc->mode.level < PSM_LEVEL_NATIVE)
1903178019Sjkim		return (ENODEV);
1904178017Sjkim
1905178019Sjkim	/* copy data from the user land */
1906178019Sjkim	while (uio->uio_resid > 0) {
1907178019Sjkim		l = imin(PSM_SMALLBUFSIZE, uio->uio_resid);
1908178019Sjkim		error = uiomove(buf, l, uio);
1909178019Sjkim		if (error)
1910178019Sjkim			break;
1911178019Sjkim		for (i = 0; i < l; i++) {
1912178019Sjkim			VLOG(4, (LOG_DEBUG, "psm: cmd 0x%x\n", buf[i]));
1913178019Sjkim			if (!write_aux_command(sc->kbdc, buf[i])) {
1914178019Sjkim				VLOG(2, (LOG_DEBUG,
1915178019Sjkim				    "psm: cmd 0x%x failed.\n", buf[i]));
1916178019Sjkim				return (reinitialize(sc, FALSE));
1917178019Sjkim			}
1918178019Sjkim		}
1919178017Sjkim	}
1920178017Sjkim
1921178019Sjkim	return (error);
1922178017Sjkim}
1923178017Sjkim
1924178017Sjkimstatic int
1925178019Sjkimpsmioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
1926178019Sjkim    struct thread *td)
192741016Sdfr{
1928178019Sjkim	struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev));
1929178019Sjkim	mousemode_t mode;
1930178019Sjkim	mousestatus_t status;
193141016Sdfr#if (defined(MOUSE_GETVARS))
1932178019Sjkim	mousevar_t *var;
193341016Sdfr#endif
1934178019Sjkim	mousedata_t *data;
1935178019Sjkim	int stat[3];
1936178019Sjkim	int command_byte;
1937178019Sjkim	int error = 0;
1938178019Sjkim	int s;
193941016Sdfr
1940178019Sjkim	/* Perform IOCTL command */
1941178019Sjkim	switch (cmd) {
194241016Sdfr
1943178019Sjkim	case OLD_MOUSE_GETHWINFO:
1944178019Sjkim		s = spltty();
1945178019Sjkim		((old_mousehw_t *)addr)->buttons = sc->hw.buttons;
1946178019Sjkim		((old_mousehw_t *)addr)->iftype = sc->hw.iftype;
1947178019Sjkim		((old_mousehw_t *)addr)->type = sc->hw.type;
1948178019Sjkim		((old_mousehw_t *)addr)->hwid = sc->hw.hwid & 0x00ff;
1949178019Sjkim		splx(s);
1950178019Sjkim		break;
195141016Sdfr
1952178019Sjkim	case MOUSE_GETHWINFO:
1953178019Sjkim		s = spltty();
1954178019Sjkim		*(mousehw_t *)addr = sc->hw;
1955178019Sjkim		if (sc->mode.level == PSM_LEVEL_BASE)
1956178019Sjkim			((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC;
1957178019Sjkim		splx(s);
1958178019Sjkim		break;
195941016Sdfr
1960178019Sjkim	case MOUSE_SYN_GETHWINFO:
1961178019Sjkim		s = spltty();
1962178019Sjkim		if (synaptics_support && sc->hw.model == MOUSE_MODEL_SYNAPTICS)
1963178019Sjkim			*(synapticshw_t *)addr = sc->synhw;
1964178019Sjkim		else
1965178019Sjkim			error = EINVAL;
1966178019Sjkim		splx(s);
1967178019Sjkim		break;
1968132865Snjl
1969178019Sjkim	case OLD_MOUSE_GETMODE:
1970178019Sjkim		s = spltty();
1971178019Sjkim		switch (sc->mode.level) {
1972178019Sjkim		case PSM_LEVEL_BASE:
1973178019Sjkim			((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
1974178019Sjkim			break;
1975178019Sjkim		case PSM_LEVEL_STANDARD:
1976178019Sjkim			((old_mousemode_t *)addr)->protocol =
1977178019Sjkim			    MOUSE_PROTO_SYSMOUSE;
1978178019Sjkim			break;
1979178019Sjkim		case PSM_LEVEL_NATIVE:
1980178019Sjkim			((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
1981178019Sjkim			break;
1982178019Sjkim		}
1983178019Sjkim		((old_mousemode_t *)addr)->rate = sc->mode.rate;
1984178019Sjkim		((old_mousemode_t *)addr)->resolution = sc->mode.resolution;
1985178019Sjkim		((old_mousemode_t *)addr)->accelfactor = sc->mode.accelfactor;
1986178019Sjkim		splx(s);
1987178019Sjkim		break;
198841016Sdfr
1989178019Sjkim	case MOUSE_GETMODE:
1990178019Sjkim		s = spltty();
1991178019Sjkim		*(mousemode_t *)addr = sc->mode;
1992178019Sjkim		if ((sc->flags & PSM_NEED_SYNCBITS) != 0) {
1993178019Sjkim			((mousemode_t *)addr)->syncmask[0] = 0;
1994178019Sjkim			((mousemode_t *)addr)->syncmask[1] = 0;
1995178019Sjkim		}
1996178019Sjkim		((mousemode_t *)addr)->resolution =
1997178019Sjkim			MOUSE_RES_LOW - sc->mode.resolution;
1998178019Sjkim		switch (sc->mode.level) {
1999178019Sjkim		case PSM_LEVEL_BASE:
2000178019Sjkim			((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
2001178019Sjkim			((mousemode_t *)addr)->packetsize =
2002178019Sjkim			    MOUSE_PS2_PACKETSIZE;
2003178019Sjkim			break;
2004178019Sjkim		case PSM_LEVEL_STANDARD:
2005178019Sjkim			((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE;
2006178019Sjkim			((mousemode_t *)addr)->packetsize =
2007178019Sjkim			    MOUSE_SYS_PACKETSIZE;
2008178019Sjkim			((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK;
2009178019Sjkim			((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC;
2010178019Sjkim			break;
2011178019Sjkim		case PSM_LEVEL_NATIVE:
2012178019Sjkim			/* FIXME: this isn't quite correct... XXX */
2013178019Sjkim			((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
2014178019Sjkim			break;
2015178019Sjkim		}
2016178019Sjkim		splx(s);
2017178019Sjkim		break;
201841016Sdfr
2019178019Sjkim	case OLD_MOUSE_SETMODE:
2020178019Sjkim	case MOUSE_SETMODE:
2021178019Sjkim		if (cmd == OLD_MOUSE_SETMODE) {
2022178019Sjkim			mode.rate = ((old_mousemode_t *)addr)->rate;
2023178019Sjkim			/*
2024178019Sjkim			 * resolution  old I/F   new I/F
2025178019Sjkim			 * default        0         0
2026178019Sjkim			 * low            1        -2
2027178019Sjkim			 * medium low     2        -3
2028178019Sjkim			 * medium high    3        -4
2029178019Sjkim			 * high           4        -5
2030178019Sjkim			 */
2031178019Sjkim			if (((old_mousemode_t *)addr)->resolution > 0)
2032178019Sjkim				mode.resolution =
2033178019Sjkim				    -((old_mousemode_t *)addr)->resolution - 1;
2034178019Sjkim			else
2035178019Sjkim				mode.resolution = 0;
2036178019Sjkim			mode.accelfactor =
2037178019Sjkim			    ((old_mousemode_t *)addr)->accelfactor;
2038178019Sjkim			mode.level = -1;
2039178019Sjkim		} else
2040178019Sjkim			mode = *(mousemode_t *)addr;
204141016Sdfr
2042178019Sjkim		/* adjust and validate parameters. */
2043178019Sjkim		if (mode.rate > UCHAR_MAX)
2044178019Sjkim			return (EINVAL);
2045178019Sjkim		if (mode.rate == 0)
2046178019Sjkim			mode.rate = sc->dflt_mode.rate;
2047178019Sjkim		else if (mode.rate == -1)
2048178019Sjkim			/* don't change the current setting */
2049178019Sjkim			;
2050178019Sjkim		else if (mode.rate < 0)
2051178019Sjkim			return (EINVAL);
2052178019Sjkim		if (mode.resolution >= UCHAR_MAX)
2053178019Sjkim			return (EINVAL);
2054178019Sjkim		if (mode.resolution >= 200)
2055178019Sjkim			mode.resolution = MOUSE_RES_HIGH;
2056178019Sjkim		else if (mode.resolution >= 100)
2057178019Sjkim			mode.resolution = MOUSE_RES_MEDIUMHIGH;
2058178019Sjkim		else if (mode.resolution >= 50)
2059178019Sjkim			mode.resolution = MOUSE_RES_MEDIUMLOW;
2060178019Sjkim		else if (mode.resolution > 0)
2061178019Sjkim			mode.resolution = MOUSE_RES_LOW;
2062178019Sjkim		if (mode.resolution == MOUSE_RES_DEFAULT)
2063178019Sjkim			mode.resolution = sc->dflt_mode.resolution;
2064178019Sjkim		else if (mode.resolution == -1)
2065178019Sjkim			/* don't change the current setting */
2066178019Sjkim			;
2067178019Sjkim		else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */
2068178019Sjkim			mode.resolution = MOUSE_RES_LOW - mode.resolution;
2069178019Sjkim		if (mode.level == -1)
2070178019Sjkim			/* don't change the current setting */
2071178019Sjkim			mode.level = sc->mode.level;
2072178019Sjkim		else if ((mode.level < PSM_LEVEL_MIN) ||
2073178019Sjkim		    (mode.level > PSM_LEVEL_MAX))
2074178019Sjkim			return (EINVAL);
2075178019Sjkim		if (mode.accelfactor == -1)
2076178019Sjkim			/* don't change the current setting */
2077178019Sjkim			mode.accelfactor = sc->mode.accelfactor;
2078178019Sjkim		else if (mode.accelfactor < 0)
2079178019Sjkim			return (EINVAL);
208041016Sdfr
2081178019Sjkim		/* don't allow anybody to poll the keyboard controller */
2082178019Sjkim		error = block_mouse_data(sc, &command_byte);
2083178019Sjkim		if (error)
2084178019Sjkim			return (error);
208541016Sdfr
2086178019Sjkim		/* set mouse parameters */
2087178019Sjkim		if (mode.rate > 0)
2088178019Sjkim			mode.rate = set_mouse_sampling_rate(sc->kbdc,
2089178019Sjkim			    mode.rate);
2090178019Sjkim		if (mode.resolution >= 0)
2091178019Sjkim			mode.resolution =
2092178019Sjkim			    set_mouse_resolution(sc->kbdc, mode.resolution);
2093178019Sjkim		set_mouse_scaling(sc->kbdc, 1);
2094178019Sjkim		get_mouse_status(sc->kbdc, stat, 0, 3);
209541016Sdfr
2096178019Sjkim		s = spltty();
2097178019Sjkim		sc->mode.rate = mode.rate;
2098178019Sjkim		sc->mode.resolution = mode.resolution;
2099178019Sjkim		sc->mode.accelfactor = mode.accelfactor;
2100178019Sjkim		sc->mode.level = mode.level;
2101178019Sjkim		splx(s);
210241016Sdfr
2103178019Sjkim		unblock_mouse_data(sc, command_byte);
2104178019Sjkim		break;
210541016Sdfr
2106178019Sjkim	case MOUSE_GETLEVEL:
2107178019Sjkim		*(int *)addr = sc->mode.level;
2108178019Sjkim		break;
210941016Sdfr
2110178019Sjkim	case MOUSE_SETLEVEL:
2111178019Sjkim		if ((*(int *)addr < PSM_LEVEL_MIN) ||
2112178019Sjkim		    (*(int *)addr > PSM_LEVEL_MAX))
2113178019Sjkim			return (EINVAL);
2114178019Sjkim		sc->mode.level = *(int *)addr;
2115178019Sjkim		break;
211641016Sdfr
2117178019Sjkim	case MOUSE_GETSTATUS:
2118178019Sjkim		s = spltty();
2119178019Sjkim		status = sc->status;
2120178019Sjkim		sc->status.flags = 0;
2121178019Sjkim		sc->status.obutton = sc->status.button;
2122178019Sjkim		sc->status.button = 0;
2123178019Sjkim		sc->status.dx = 0;
2124178019Sjkim		sc->status.dy = 0;
2125178019Sjkim		sc->status.dz = 0;
2126178019Sjkim		splx(s);
2127178019Sjkim		*(mousestatus_t *)addr = status;
2128178019Sjkim		break;
212941016Sdfr
213041016Sdfr#if (defined(MOUSE_GETVARS))
2131178019Sjkim	case MOUSE_GETVARS:
2132178019Sjkim		var = (mousevar_t *)addr;
2133178019Sjkim		bzero(var, sizeof(*var));
2134178019Sjkim		s = spltty();
2135178019Sjkim		var->var[0] = MOUSE_VARS_PS2_SIG;
2136178019Sjkim		var->var[1] = sc->config;
2137178019Sjkim		var->var[2] = sc->flags;
2138178019Sjkim		splx(s);
2139178019Sjkim		break;
214041016Sdfr
2141178019Sjkim	case MOUSE_SETVARS:
2142178019Sjkim		return (ENODEV);
214341016Sdfr#endif /* MOUSE_GETVARS */
214441016Sdfr
2145178019Sjkim	case MOUSE_READSTATE:
2146178019Sjkim	case MOUSE_READDATA:
2147178019Sjkim		data = (mousedata_t *)addr;
2148178019Sjkim		if (data->len > sizeof(data->buf)/sizeof(data->buf[0]))
2149178019Sjkim			return (EINVAL);
215041016Sdfr
2151178019Sjkim		error = block_mouse_data(sc, &command_byte);
2152178019Sjkim		if (error)
2153178019Sjkim			return (error);
2154178019Sjkim		if ((data->len = get_mouse_status(sc->kbdc, data->buf,
2155178019Sjkim		    (cmd == MOUSE_READDATA) ? 1 : 0, data->len)) <= 0)
2156178019Sjkim			error = EIO;
2157178019Sjkim		unblock_mouse_data(sc, command_byte);
2158178019Sjkim		break;
215941016Sdfr
216041016Sdfr#if (defined(MOUSE_SETRESOLUTION))
2161178019Sjkim	case MOUSE_SETRESOLUTION:
2162178019Sjkim		mode.resolution = *(int *)addr;
2163178019Sjkim		if (mode.resolution >= UCHAR_MAX)
2164178019Sjkim			return (EINVAL);
2165178019Sjkim		else if (mode.resolution >= 200)
2166178019Sjkim			mode.resolution = MOUSE_RES_HIGH;
2167178019Sjkim		else if (mode.resolution >= 100)
2168178019Sjkim			mode.resolution = MOUSE_RES_MEDIUMHIGH;
2169178019Sjkim		else if (mode.resolution >= 50)
2170178019Sjkim			mode.resolution = MOUSE_RES_MEDIUMLOW;
2171178019Sjkim		else if (mode.resolution > 0)
2172178019Sjkim			mode.resolution = MOUSE_RES_LOW;
2173178019Sjkim		if (mode.resolution == MOUSE_RES_DEFAULT)
2174178019Sjkim			mode.resolution = sc->dflt_mode.resolution;
2175178019Sjkim		else if (mode.resolution == -1)
2176178019Sjkim			mode.resolution = sc->mode.resolution;
2177178019Sjkim		else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */
2178178019Sjkim			mode.resolution = MOUSE_RES_LOW - mode.resolution;
217941016Sdfr
2180178019Sjkim		error = block_mouse_data(sc, &command_byte);
2181178019Sjkim		if (error)
2182178019Sjkim			return (error);
2183178019Sjkim		sc->mode.resolution =
2184178019Sjkim		    set_mouse_resolution(sc->kbdc, mode.resolution);
2185178019Sjkim		if (sc->mode.resolution != mode.resolution)
2186178019Sjkim			error = EIO;
2187178019Sjkim		unblock_mouse_data(sc, command_byte);
2188178019Sjkim		break;
218941016Sdfr#endif /* MOUSE_SETRESOLUTION */
219041016Sdfr
219141016Sdfr#if (defined(MOUSE_SETRATE))
2192178019Sjkim	case MOUSE_SETRATE:
2193178019Sjkim		mode.rate = *(int *)addr;
2194178019Sjkim		if (mode.rate > UCHAR_MAX)
2195178019Sjkim			return (EINVAL);
2196178019Sjkim		if (mode.rate == 0)
2197178019Sjkim			mode.rate = sc->dflt_mode.rate;
2198178019Sjkim		else if (mode.rate < 0)
2199178019Sjkim			mode.rate = sc->mode.rate;
220041016Sdfr
2201178019Sjkim		error = block_mouse_data(sc, &command_byte);
2202178019Sjkim		if (error)
2203178019Sjkim			return (error);
2204178019Sjkim		sc->mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate);
2205178019Sjkim		if (sc->mode.rate != mode.rate)
2206178019Sjkim			error = EIO;
2207178019Sjkim		unblock_mouse_data(sc, command_byte);
2208178019Sjkim		break;
220941016Sdfr#endif /* MOUSE_SETRATE */
221041016Sdfr
221141016Sdfr#if (defined(MOUSE_SETSCALING))
2212178019Sjkim	case MOUSE_SETSCALING:
2213178019Sjkim		if ((*(int *)addr <= 0) || (*(int *)addr > 2))
2214178019Sjkim			return (EINVAL);
221541016Sdfr
2216178019Sjkim		error = block_mouse_data(sc, &command_byte);
2217178019Sjkim		if (error)
2218178019Sjkim			return (error);
2219178019Sjkim		if (!set_mouse_scaling(sc->kbdc, *(int *)addr))
2220178019Sjkim			error = EIO;
2221178019Sjkim		unblock_mouse_data(sc, command_byte);
2222178019Sjkim		break;
222341016Sdfr#endif /* MOUSE_SETSCALING */
222441016Sdfr
222541016Sdfr#if (defined(MOUSE_GETHWID))
2226178019Sjkim	case MOUSE_GETHWID:
2227178019Sjkim		error = block_mouse_data(sc, &command_byte);
2228178019Sjkim		if (error)
2229178019Sjkim			return (error);
2230178019Sjkim		sc->hw.hwid &= ~0x00ff;
2231178019Sjkim		sc->hw.hwid |= get_aux_id(sc->kbdc);
2232178019Sjkim		*(int *)addr = sc->hw.hwid & 0x00ff;
2233178019Sjkim		unblock_mouse_data(sc, command_byte);
2234178019Sjkim		break;
223541016Sdfr#endif /* MOUSE_GETHWID */
223641016Sdfr
2237189870Srnoland	case FIONBIO:
2238189870Srnoland	case FIOASYNC:
2239189870Srnoland		break;
2240189870Srnoland	case FIOSETOWN:
2241189870Srnoland		error = fsetown(*(int *)addr, &sc->async);
2242189870Srnoland		break;
2243189870Srnoland	case FIOGETOWN:
2244189870Srnoland		*(int *) addr = fgetown(&sc->async);
2245189870Srnoland		break;
2246178019Sjkim	default:
2247178019Sjkim		return (ENOTTY);
2248178019Sjkim	}
224941016Sdfr
2250178019Sjkim	return (error);
225141016Sdfr}
225241016Sdfr
225341016Sdfrstatic void
225458230Syokotapsmtimeout(void *arg)
225558230Syokota{
2256178019Sjkim	struct psm_softc *sc;
2257178019Sjkim	int s;
225858230Syokota
2259178019Sjkim	sc = (struct psm_softc *)arg;
2260178019Sjkim	s = spltty();
2261178019Sjkim	if (sc->watchdog && kbdc_lock(sc->kbdc, TRUE)) {
2262178019Sjkim		VLOG(4, (LOG_DEBUG, "psm%d: lost interrupt?\n", sc->unit));
2263178019Sjkim		psmintr(sc);
2264178019Sjkim		kbdc_lock(sc->kbdc, FALSE);
2265178019Sjkim	}
2266178019Sjkim	sc->watchdog = TRUE;
2267178019Sjkim	splx(s);
2268178019Sjkim	sc->callout = timeout(psmtimeout, (void *)(uintptr_t)sc, hz);
226958230Syokota}
227058230Syokota
2271139628Sphilip/* Add all sysctls under the debug.psm and hw.psm nodes */
2272139628SphilipSYSCTL_NODE(_debug, OID_AUTO, psm, CTLFLAG_RD, 0, "ps/2 mouse");
2273139628SphilipSYSCTL_NODE(_hw, OID_AUTO, psm, CTLFLAG_RD, 0, "ps/2 mouse");
2274123442Salfred
2275180818StrhodesSYSCTL_INT(_debug_psm, OID_AUTO, loglevel, CTLFLAG_RW, &verbose, 0,
2276180818Strhodes    "Verbosity level");
2277123442Salfred
2278139628Sphilipstatic int psmhz = 20;
2279180818StrhodesSYSCTL_INT(_debug_psm, OID_AUTO, hz, CTLFLAG_RW, &psmhz, 0,
2280180818Strhodes    "Frequency of the softcallout (in hz)");
2281123442Salfredstatic int psmerrsecs = 2;
2282180818StrhodesSYSCTL_INT(_debug_psm, OID_AUTO, errsecs, CTLFLAG_RW, &psmerrsecs, 0,
2283180818Strhodes    "Number of seconds during which packets will dropped after a sync error");
2284123442Salfredstatic int psmerrusecs = 0;
2285180818StrhodesSYSCTL_INT(_debug_psm, OID_AUTO, errusecs, CTLFLAG_RW, &psmerrusecs, 0,
2286180818Strhodes    "Microseconds to add to psmerrsecs");
2287123442Salfredstatic int psmsecs = 0;
2288180818StrhodesSYSCTL_INT(_debug_psm, OID_AUTO, secs, CTLFLAG_RW, &psmsecs, 0,
2289180818Strhodes    "Max number of seconds between soft interrupts");
2290123442Salfredstatic int psmusecs = 500000;
2291180818StrhodesSYSCTL_INT(_debug_psm, OID_AUTO, usecs, CTLFLAG_RW, &psmusecs, 0,
2292180818Strhodes    "Microseconds to add to psmsecs");
2293139628Sphilipstatic int pkterrthresh = 2;
2294180818StrhodesSYSCTL_INT(_debug_psm, OID_AUTO, pkterrthresh, CTLFLAG_RW, &pkterrthresh, 0,
2295180818Strhodes    "Number of error packets allowed before reinitializing the mouse");
2296123442Salfred
2297200674SdumbbellSYSCTL_INT(_hw_psm, OID_AUTO, tap_enabled, CTLFLAG_RW, &tap_enabled, 0,
2298200674Sdumbbell    "Enable tap and drag gestures");
2299139628Sphilipstatic int tap_threshold = PSM_TAP_THRESHOLD;
2300180818StrhodesSYSCTL_INT(_hw_psm, OID_AUTO, tap_threshold, CTLFLAG_RW, &tap_threshold, 0,
2301180818Strhodes    "Button tap threshold");
2302139628Sphilipstatic int tap_timeout = PSM_TAP_TIMEOUT;
2303180818StrhodesSYSCTL_INT(_hw_psm, OID_AUTO, tap_timeout, CTLFLAG_RW, &tap_timeout, 0,
2304180818Strhodes    "Tap timeout for touchpads");
2305134405Sgibbs
230658230Syokotastatic void
230741016Sdfrpsmintr(void *arg)
230841016Sdfr{
2309178019Sjkim	struct psm_softc *sc = arg;
2310178019Sjkim	struct timeval now;
2311178019Sjkim	int c;
2312178019Sjkim	packetbuf_t *pb;
231341016Sdfr
2314123442Salfred
2315178019Sjkim	/* read until there is nothing to read */
2316178019Sjkim	while((c = read_aux_data_no_wait(sc->kbdc)) != -1) {
2317178019Sjkim		pb = &sc->pqueue[sc->pqueue_end];
231884880Syokota
2319178019Sjkim		/* discard the byte if the device is not open */
2320178019Sjkim		if ((sc->state & PSM_OPEN) == 0)
2321178019Sjkim			continue;
232241016Sdfr
2323178019Sjkim		getmicrouptime(&now);
2324178019Sjkim		if ((pb->inputbytes > 0) &&
2325178019Sjkim		    timevalcmp(&now, &sc->inputtimeout, >)) {
2326178019Sjkim			VLOG(3, (LOG_DEBUG, "psmintr: delay too long; "
2327178019Sjkim			    "resetting byte count\n"));
2328178019Sjkim			pb->inputbytes = 0;
2329178019Sjkim			sc->syncerrors = 0;
2330178019Sjkim			sc->pkterrors = 0;
2331178019Sjkim		}
2332178019Sjkim		sc->inputtimeout.tv_sec = PSM_INPUT_TIMEOUT / 1000000;
2333178019Sjkim		sc->inputtimeout.tv_usec = PSM_INPUT_TIMEOUT % 1000000;
2334178019Sjkim		timevaladd(&sc->inputtimeout, &now);
233541016Sdfr
2336178019Sjkim		pb->ipacket[pb->inputbytes++] = c;
2337178017Sjkim
2338178019Sjkim		if (sc->mode.level == PSM_LEVEL_NATIVE) {
2339178019Sjkim			VLOG(4, (LOG_DEBUG, "psmintr: %02x\n", pb->ipacket[0]));
2340178019Sjkim			sc->syncerrors = 0;
2341178019Sjkim			sc->pkterrors = 0;
2342178019Sjkim			goto next;
2343178019Sjkim		} else {
2344178019Sjkim			if (pb->inputbytes < sc->mode.packetsize)
2345178019Sjkim				continue;
234641016Sdfr
2347178019Sjkim			VLOG(4, (LOG_DEBUG,
2348178019Sjkim			    "psmintr: %02x %02x %02x %02x %02x %02x\n",
2349178019Sjkim			    pb->ipacket[0], pb->ipacket[1], pb->ipacket[2],
2350178019Sjkim			    pb->ipacket[3], pb->ipacket[4], pb->ipacket[5]));
2351178019Sjkim		}
2352134405Sgibbs
2353178019Sjkim		c = pb->ipacket[0];
2354178019Sjkim
2355178019Sjkim		if ((sc->flags & PSM_NEED_SYNCBITS) != 0) {
2356178019Sjkim			sc->mode.syncmask[1] = (c & sc->mode.syncmask[0]);
2357178019Sjkim			sc->flags &= ~PSM_NEED_SYNCBITS;
2358178019Sjkim			VLOG(2, (LOG_DEBUG,
2359178019Sjkim			    "psmintr: Sync bytes now %04x,%04x\n",
2360178019Sjkim			    sc->mode.syncmask[0], sc->mode.syncmask[0]));
2361178019Sjkim		} else if ((c & sc->mode.syncmask[0]) != sc->mode.syncmask[1]) {
2362178019Sjkim			VLOG(3, (LOG_DEBUG, "psmintr: out of sync "
2363178019Sjkim			    "(%04x != %04x) %d cmds since last error.\n",
2364178019Sjkim			    c & sc->mode.syncmask[0], sc->mode.syncmask[1],
2365178019Sjkim			    sc->cmdcount - sc->lasterr));
2366178019Sjkim			sc->lasterr = sc->cmdcount;
2367178019Sjkim			/*
2368178019Sjkim			 * The sync byte test is a weak measure of packet
2369178019Sjkim			 * validity.  Conservatively discard any input yet
2370178019Sjkim			 * to be seen by userland when we detect a sync
2371178019Sjkim			 * error since there is a good chance some of
2372178019Sjkim			 * the queued packets have undetected errors.
2373178019Sjkim			 */
2374178019Sjkim			dropqueue(sc);
2375178019Sjkim			if (sc->syncerrors == 0)
2376178019Sjkim				sc->pkterrors++;
2377178019Sjkim			++sc->syncerrors;
2378178019Sjkim			sc->lastinputerr = now;
2379178019Sjkim			if (sc->syncerrors >= sc->mode.packetsize * 2 ||
2380178019Sjkim			    sc->pkterrors >= pkterrthresh) {
2381178019Sjkim				/*
2382178019Sjkim				 * If we've failed to find a single sync byte
2383178019Sjkim				 * in 2 packets worth of data, or we've seen
2384178019Sjkim				 * persistent packet errors during the
2385178019Sjkim				 * validation period, reinitialize the mouse
2386178019Sjkim				 * in hopes of returning it to the expected
2387178019Sjkim				 * mode.
2388178019Sjkim				 */
2389178019Sjkim				VLOG(3, (LOG_DEBUG,
2390178019Sjkim				    "psmintr: reset the mouse.\n"));
2391178019Sjkim				reinitialize(sc, TRUE);
2392178019Sjkim			} else if (sc->syncerrors == sc->mode.packetsize) {
2393178019Sjkim				/*
2394178019Sjkim				 * Try a soft reset after searching for a sync
2395178019Sjkim				 * byte through a packet length of bytes.
2396178019Sjkim				 */
2397178019Sjkim				VLOG(3, (LOG_DEBUG,
2398178019Sjkim				    "psmintr: re-enable the mouse.\n"));
2399178019Sjkim				pb->inputbytes = 0;
2400178019Sjkim				disable_aux_dev(sc->kbdc);
2401178019Sjkim				enable_aux_dev(sc->kbdc);
2402178019Sjkim			} else {
2403178019Sjkim				VLOG(3, (LOG_DEBUG,
2404178019Sjkim				    "psmintr: discard a byte (%d)\n",
2405178019Sjkim				    sc->syncerrors));
2406178019Sjkim				pb->inputbytes--;
2407178019Sjkim				bcopy(&pb->ipacket[1], &pb->ipacket[0],
2408178019Sjkim				    pb->inputbytes);
2409178019Sjkim			}
2410178019Sjkim			continue;
2411178019Sjkim		}
2412178019Sjkim
2413134405Sgibbs		/*
2414178019Sjkim		 * We have what appears to be a valid packet.
2415178019Sjkim		 * Reset the error counters.
2416134405Sgibbs		 */
2417178019Sjkim		sc->syncerrors = 0;
2418134405Sgibbs
2419134405Sgibbs		/*
2420178019Sjkim		 * Drop even good packets if they occur within a timeout
2421178019Sjkim		 * period of a sync error.  This allows the detection of
2422178019Sjkim		 * a change in the mouse's packet mode without exposing
2423178019Sjkim		 * erratic mouse behavior to the user.  Some KVMs forget
2424178019Sjkim		 * enhanced mouse modes during switch events.
2425134405Sgibbs		 */
2426178019Sjkim		if (!timeelapsed(&sc->lastinputerr, psmerrsecs, psmerrusecs,
2427178019Sjkim		    &now)) {
2428178019Sjkim			pb->inputbytes = 0;
2429178019Sjkim			continue;
2430178019Sjkim		}
2431134405Sgibbs
2432178019Sjkim		/*
2433178019Sjkim		 * Now that we're out of the validation period, reset
2434178019Sjkim		 * the packet error count.
2435178019Sjkim		 */
2436178019Sjkim		sc->pkterrors = 0;
2437134405Sgibbs
2438178019Sjkim		sc->cmdcount++;
2439178019Sjkimnext:
2440178019Sjkim		if (++sc->pqueue_end >= PSM_PACKETQUEUE)
2441178019Sjkim			sc->pqueue_end = 0;
2442178019Sjkim		/*
2443178019Sjkim		 * If we've filled the queue then call the softintr ourselves,
2444178019Sjkim		 * otherwise schedule the interrupt for later.
2445178019Sjkim		 */
2446178019Sjkim		if (!timeelapsed(&sc->lastsoftintr, psmsecs, psmusecs, &now) ||
2447178019Sjkim		    (sc->pqueue_end == sc->pqueue_start)) {
2448178019Sjkim			if ((sc->state & PSM_SOFTARMED) != 0) {
2449178019Sjkim				sc->state &= ~PSM_SOFTARMED;
2450178019Sjkim				untimeout(psmsoftintr, arg, sc->softcallout);
2451178019Sjkim			}
2452178019Sjkim			psmsoftintr(arg);
2453178019Sjkim		} else if ((sc->state & PSM_SOFTARMED) == 0) {
2454178019Sjkim			sc->state |= PSM_SOFTARMED;
2455178019Sjkim			sc->softcallout = timeout(psmsoftintr, arg,
2456178019Sjkim			    psmhz < 1 ? 1 : (hz/psmhz));
2457123442Salfred		}
2458123442Salfred	}
2459123442Salfred}
2460123442Salfred
2461123442Salfredstatic void
2462178019Sjkimproc_mmanplus(struct psm_softc *sc, packetbuf_t *pb, mousestatus_t *ms,
2463178019Sjkim    int *x, int *y, int *z)
2464123442Salfred{
2465123442Salfred
2466178019Sjkim	/*
2467178019Sjkim	 * PS2++ protocl packet
2468178019Sjkim	 *
2469178019Sjkim	 *          b7 b6 b5 b4 b3 b2 b1 b0
2470178019Sjkim	 * byte 1:  *  1  p3 p2 1  *  *  *
2471178019Sjkim	 * byte 2:  c1 c2 p1 p0 d1 d0 1  0
2472178019Sjkim	 *
2473178019Sjkim	 * p3-p0: packet type
2474178019Sjkim	 * c1, c2: c1 & c2 == 1, if p2 == 0
2475178019Sjkim	 *         c1 & c2 == 0, if p2 == 1
2476178019Sjkim	 *
2477178019Sjkim	 * packet type: 0 (device type)
2478178019Sjkim	 * See comments in enable_mmanplus() below.
2479178019Sjkim	 *
2480178019Sjkim	 * packet type: 1 (wheel data)
2481178019Sjkim	 *
2482178019Sjkim	 *          b7 b6 b5 b4 b3 b2 b1 b0
2483178019Sjkim	 * byte 3:  h  *  B5 B4 s  d2 d1 d0
2484178019Sjkim	 *
2485178019Sjkim	 * h: 1, if horizontal roller data
2486178019Sjkim	 *    0, if vertical roller data
2487178019Sjkim	 * B4, B5: button 4 and 5
2488178019Sjkim	 * s: sign bit
2489178019Sjkim	 * d2-d0: roller data
2490178019Sjkim	 *
2491178019Sjkim	 * packet type: 2 (reserved)
249241016Sdfr	 */
2493178019Sjkim	if (((pb->ipacket[0] & MOUSE_PS2PLUS_SYNCMASK) == MOUSE_PS2PLUS_SYNC) &&
2494178019Sjkim	    (abs(*x) > 191) && MOUSE_PS2PLUS_CHECKBITS(pb->ipacket)) {
2495178019Sjkim		/*
2496178019Sjkim		 * the extended data packet encodes button
2497178019Sjkim		 * and wheel events
2498178019Sjkim		 */
2499123442Salfred		switch (MOUSE_PS2PLUS_PACKET_TYPE(pb->ipacket)) {
250048778Syokota		case 1:
2501178019Sjkim			/* wheel data packet */
2502178019Sjkim			*x = *y = 0;
2503178019Sjkim			if (pb->ipacket[2] & 0x80) {
2504178019Sjkim				/* XXX horizontal roller count - ignore it */
2505178019Sjkim				;
2506178019Sjkim			} else {
2507178019Sjkim				/* vertical roller count */
2508178019Sjkim				*z = (pb->ipacket[2] & MOUSE_PS2PLUS_ZNEG) ?
2509178019Sjkim				    (pb->ipacket[2] & 0x0f) - 16 :
2510178019Sjkim				    (pb->ipacket[2] & 0x0f);
2511178019Sjkim			}
2512178019Sjkim			ms->button |= (pb->ipacket[2] &
2513178019Sjkim			    MOUSE_PS2PLUS_BUTTON4DOWN) ?
2514178019Sjkim			    MOUSE_BUTTON4DOWN : 0;
2515178019Sjkim			ms->button |= (pb->ipacket[2] &
2516178019Sjkim			    MOUSE_PS2PLUS_BUTTON5DOWN) ?
2517178019Sjkim			    MOUSE_BUTTON5DOWN : 0;
2518178019Sjkim			break;
251948778Syokota		case 2:
2520178019Sjkim			/*
2521178019Sjkim			 * this packet type is reserved by
2522178019Sjkim			 * Logitech...
2523178019Sjkim			 */
2524178019Sjkim			/*
2525178019Sjkim			 * IBM ScrollPoint Mouse uses this
2526178019Sjkim			 * packet type to encode both vertical
2527178019Sjkim			 * and horizontal scroll movement.
2528178019Sjkim			 */
2529178019Sjkim			*x = *y = 0;
2530178019Sjkim			/* horizontal count */
2531178019Sjkim			if (pb->ipacket[2] & 0x0f)
2532178019Sjkim				*z = (pb->ipacket[2] & MOUSE_SPOINT_WNEG) ?
2533178019Sjkim				    -2 : 2;
2534178019Sjkim			/* vertical count */
2535178019Sjkim			if (pb->ipacket[2] & 0xf0)
2536178019Sjkim				*z = (pb->ipacket[2] & MOUSE_SPOINT_ZNEG) ?
2537178019Sjkim				    -1 : 1;
2538178019Sjkim			break;
253948778Syokota		case 0:
2540178019Sjkim			/* device type packet - shouldn't happen */
2541178019Sjkim			/* FALLTHROUGH */
254248778Syokota		default:
2543178019Sjkim			*x = *y = 0;
2544178019Sjkim			ms->button = ms->obutton;
2545178019Sjkim			VLOG(1, (LOG_DEBUG, "psmintr: unknown PS2++ packet "
2546178019Sjkim			    "type %d: 0x%02x 0x%02x 0x%02x\n",
2547178019Sjkim			    MOUSE_PS2PLUS_PACKET_TYPE(pb->ipacket),
2548178019Sjkim			    pb->ipacket[0], pb->ipacket[1], pb->ipacket[2]));
2549178019Sjkim			break;
255048778Syokota		}
2551178019Sjkim	} else {
255241016Sdfr		/* preserve button states */
2553178019Sjkim		ms->button |= ms->obutton & MOUSE_EXTBUTTONS;
2554178019Sjkim	}
2555178019Sjkim}
255641016Sdfr
2557178019Sjkimstatic int
2558178019Sjkimproc_synaptics(struct psm_softc *sc, packetbuf_t *pb, mousestatus_t *ms,
2559178019Sjkim    int *x, int *y, int *z)
2560178019Sjkim{
2561178019Sjkim	static int touchpad_buttons;
2562178019Sjkim	static int guest_buttons;
2563183888Sdumbbell	int w, x0, y0;
256441016Sdfr
2565178019Sjkim	/* TouchPad PS/2 absolute mode message format
2566178019Sjkim	 *
2567178019Sjkim	 *  Bits:        7   6   5   4   3   2   1   0 (LSB)
2568178019Sjkim	 *  ------------------------------------------------
2569178019Sjkim	 *  ipacket[0]:  1   0  W3  W2   0  W1   R   L
2570178019Sjkim	 *  ipacket[1]: Yb  Ya  Y9  Y8  Xb  Xa  X9  X8
2571178019Sjkim	 *  ipacket[2]: Z7  Z6  Z5  Z4  Z3  Z2  Z1  Z0
2572178019Sjkim	 *  ipacket[3]:  1   1  Yc  Xc   0  W0   D   U
2573178019Sjkim	 *  ipacket[4]: X7  X6  X5  X4  X3  X2  X1  X0
2574178019Sjkim	 *  ipacket[5]: Y7  Y6  Y5  Y4  Y3  Y2  Y1  Y0
2575178019Sjkim	 *
2576178019Sjkim	 * Legend:
2577178019Sjkim	 *  L: left physical mouse button
2578178019Sjkim	 *  R: right physical mouse button
2579178019Sjkim	 *  D: down button
2580178019Sjkim	 *  U: up button
2581178019Sjkim	 *  W: "wrist" value
2582178019Sjkim	 *  X: x position
2583183888Sdumbbell	 *  Y: y position
2584178019Sjkim	 *  Z: pressure
2585178019Sjkim	 *
2586178019Sjkim	 * Absolute reportable limits:    0 - 6143.
2587178019Sjkim	 * Typical bezel limits:       1472 - 5472.
2588178019Sjkim	 * Typical edge marings:       1632 - 5312.
2589178019Sjkim	 *
2590178019Sjkim	 * w = 3 Passthrough Packet
2591178019Sjkim	 *
2592178019Sjkim	 * Byte 2,5,6 == Byte 1,2,3 of "Guest"
2593178019Sjkim	 */
259441016Sdfr
2595178019Sjkim	if (!synaptics_support)
2596178019Sjkim		return (0);
259741016Sdfr
2598178019Sjkim	/* Sanity check for out of sync packets. */
2599178019Sjkim	if ((pb->ipacket[0] & 0xc8) != 0x80 ||
2600178019Sjkim	    (pb->ipacket[3] & 0xc8) != 0xc0)
2601178019Sjkim		return (-1);
260249965Syokota
2603183888Sdumbbell	*x = *y = 0;
260458230Syokota
2605183888Sdumbbell	/*
2606183888Sdumbbell	 * Pressure value.
2607183888Sdumbbell	 * Interpretation:
2608183888Sdumbbell	 *   z = 0      No finger contact
2609183888Sdumbbell	 *   z = 10     Finger hovering near the pad
2610183888Sdumbbell	 *   z = 30     Very light finger contact
2611183888Sdumbbell	 *   z = 80     Normal finger contact
2612183888Sdumbbell	 *   z = 110    Very heavy finger contact
2613183888Sdumbbell	 *   z = 200    Finger lying flat on pad surface
2614183888Sdumbbell	 *   z = 255    Maximum reportable Z
2615183888Sdumbbell	 */
2616178019Sjkim	*z = pb->ipacket[2];
261758230Syokota
2618183888Sdumbbell	/*
2619183888Sdumbbell	 * Finger width value
2620183888Sdumbbell	 * Interpretation:
2621183888Sdumbbell	 *   w = 0      Two finger on the pad (capMultiFinger needed)
2622183888Sdumbbell	 *   w = 1      Three or more fingers (capMultiFinger needed)
2623183888Sdumbbell	 *   w = 2      Pen (instead of finger) (capPen needed)
2624183888Sdumbbell	 *   w = 3      Reserved (passthrough?)
2625183888Sdumbbell	 *   w = 4-7    Finger of normal width (capPalmDetect needed)
2626183888Sdumbbell	 *   w = 8-14   Very wide finger or palm (capPalmDetect needed)
2627183888Sdumbbell	 *   w = 15     Maximum reportable width (capPalmDetect needed)
2628183888Sdumbbell	 */
2629183888Sdumbbell	/* XXX Is checking capExtended enough? */
2630178019Sjkim	if (sc->synhw.capExtended)
2631133301Sphilip		w = ((pb->ipacket[0] & 0x30) >> 2) |
2632133301Sphilip		    ((pb->ipacket[0] & 0x04) >> 1) |
2633133301Sphilip		    ((pb->ipacket[3] & 0x04) >> 2);
2634178019Sjkim	else {
2635183888Sdumbbell		/* Assume a finger of regular width. */
2636133301Sphilip		w = 4;
2637178019Sjkim	}
2638133301Sphilip
2639178019Sjkim	/* Handle packets from the guest device */
2640183888Sdumbbell	/* XXX Documentation? */
2641178019Sjkim	if (w == 3 && sc->synhw.capPassthrough) {
2642178019Sjkim		*x = ((pb->ipacket[1] & 0x10) ?
2643133868Sphilip		    pb->ipacket[4] - 256 : pb->ipacket[4]);
2644178019Sjkim		*y = ((pb->ipacket[1] & 0x20) ?
2645133868Sphilip		    pb->ipacket[5] - 256 : pb->ipacket[5]);
2646178019Sjkim		*z = 0;
2647133868Sphilip
2648133868Sphilip		guest_buttons = 0;
2649133868Sphilip		if (pb->ipacket[1] & 0x01)
2650178019Sjkim			guest_buttons |= MOUSE_BUTTON1DOWN;
2651133868Sphilip		if (pb->ipacket[1] & 0x04)
2652178019Sjkim			guest_buttons |= MOUSE_BUTTON2DOWN;
2653133868Sphilip		if (pb->ipacket[1] & 0x02)
2654178019Sjkim			guest_buttons |= MOUSE_BUTTON3DOWN;
2655133868Sphilip
2656178019Sjkim		ms->button = touchpad_buttons | guest_buttons;
2657183888Sdumbbell		goto SYNAPTICS_END;
2658178019Sjkim	}
2659133868Sphilip
2660178019Sjkim	/* Button presses */
2661178019Sjkim	touchpad_buttons = 0;
2662178019Sjkim	if (pb->ipacket[0] & 0x01)
2663178019Sjkim		touchpad_buttons |= MOUSE_BUTTON1DOWN;
2664178019Sjkim	if (pb->ipacket[0] & 0x02)
2665178019Sjkim		touchpad_buttons |= MOUSE_BUTTON3DOWN;
2666132865Snjl
2667178019Sjkim	if (sc->synhw.capExtended && sc->synhw.capFourButtons) {
2668133296Sphilip		if ((pb->ipacket[3] & 0x01) && (pb->ipacket[0] & 0x01) == 0)
2669178019Sjkim			touchpad_buttons |= MOUSE_BUTTON4DOWN;
2670133296Sphilip		if ((pb->ipacket[3] & 0x02) && (pb->ipacket[0] & 0x02) == 0)
2671178019Sjkim			touchpad_buttons |= MOUSE_BUTTON5DOWN;
2672178019Sjkim	}
2673132865Snjl
2674178019Sjkim	/*
2675178019Sjkim	 * In newer pads - bit 0x02 in the third byte of
2676178019Sjkim	 * the packet indicates that we have an extended
2677178019Sjkim	 * button press.
2678178019Sjkim	 */
2679183888Sdumbbell	/* XXX Documentation? */
2680178019Sjkim	if (pb->ipacket[3] & 0x02) {
2681178019Sjkim		/*
2682178019Sjkim		 * if directional_scrolls is not 1, we treat any of
2683178019Sjkim		 * the scrolling directions as middle-click.
2684178019Sjkim		 */
2685139982Sphilip		if (sc->syninfo.directional_scrolls) {
2686178019Sjkim			if (pb->ipacket[4] & 0x01)
2687178019Sjkim				touchpad_buttons |= MOUSE_BUTTON4DOWN;
2688178019Sjkim			if (pb->ipacket[5] & 0x01)
2689178019Sjkim				touchpad_buttons |= MOUSE_BUTTON5DOWN;
2690178019Sjkim			if (pb->ipacket[4] & 0x02)
2691178019Sjkim				touchpad_buttons |= MOUSE_BUTTON6DOWN;
2692178019Sjkim			if (pb->ipacket[5] & 0x02)
2693178019Sjkim				touchpad_buttons |= MOUSE_BUTTON7DOWN;
2694139982Sphilip		} else {
2695183888Sdumbbell			if ((pb->ipacket[4] & 0x0F) ||
2696183888Sdumbbell			    (pb->ipacket[5] & 0x0F))
2697178019Sjkim				touchpad_buttons |= MOUSE_BUTTON2DOWN;
2698139982Sphilip		}
2699178019Sjkim	}
2700139982Sphilip
2701178019Sjkim	ms->button = touchpad_buttons | guest_buttons;
2702139982Sphilip
2703183888Sdumbbell	/* Check pressure to detect a real wanted action on the
2704183888Sdumbbell	 * touchpad. */
2705183888Sdumbbell	if (*z >= sc->syninfo.min_pressure) {
2706183888Sdumbbell		synapticsaction_t *synaction;
2707183888Sdumbbell		int cursor, peer, window;
2708183888Sdumbbell		int dx, dy, dxp, dyp;
2709183888Sdumbbell		int max_width, max_pressure;
2710183888Sdumbbell		int margin_top, margin_right, margin_bottom, margin_left;
2711183888Sdumbbell		int na_top, na_right, na_bottom, na_left;
2712183888Sdumbbell		int window_min, window_max;
2713183888Sdumbbell		int multiplicator;
2714183888Sdumbbell		int weight_current, weight_previous, weight_len_squared;
2715183888Sdumbbell		int div_min, div_max, div_len;
2716183888Sdumbbell		int vscroll_hor_area, vscroll_ver_area;
2717183888Sdumbbell
2718183888Sdumbbell		int len, weight_prev_x, weight_prev_y;
2719183888Sdumbbell		int div_max_x, div_max_y, div_x, div_y;
2720183888Sdumbbell
2721183888Sdumbbell		/* Read sysctl. */
2722183888Sdumbbell		/* XXX Verify values? */
2723183888Sdumbbell		max_width = sc->syninfo.max_width;
2724183888Sdumbbell		max_pressure = sc->syninfo.max_pressure;
2725183888Sdumbbell		margin_top = sc->syninfo.margin_top;
2726183888Sdumbbell		margin_right = sc->syninfo.margin_right;
2727183888Sdumbbell		margin_bottom = sc->syninfo.margin_bottom;
2728183888Sdumbbell		margin_left = sc->syninfo.margin_left;
2729183888Sdumbbell		na_top = sc->syninfo.na_top;
2730183888Sdumbbell		na_right = sc->syninfo.na_right;
2731183888Sdumbbell		na_bottom = sc->syninfo.na_bottom;
2732183888Sdumbbell		na_left = sc->syninfo.na_left;
2733183888Sdumbbell		window_min = sc->syninfo.window_min;
2734183888Sdumbbell		window_max = sc->syninfo.window_max;
2735183888Sdumbbell		multiplicator = sc->syninfo.multiplicator;
2736183888Sdumbbell		weight_current = sc->syninfo.weight_current;
2737183888Sdumbbell		weight_previous = sc->syninfo.weight_previous;
2738183888Sdumbbell		weight_len_squared = sc->syninfo.weight_len_squared;
2739183888Sdumbbell		div_min = sc->syninfo.div_min;
2740183888Sdumbbell		div_max = sc->syninfo.div_max;
2741183888Sdumbbell		div_len = sc->syninfo.div_len;
2742183888Sdumbbell		vscroll_hor_area = sc->syninfo.vscroll_hor_area;
2743183888Sdumbbell		vscroll_ver_area = sc->syninfo.vscroll_ver_area;
2744183888Sdumbbell
2745183888Sdumbbell		/* Palm detection. */
2746183888Sdumbbell		if (!(
2747183888Sdumbbell		    (sc->synhw.capMultiFinger && (w == 0 || w == 1)) ||
2748183888Sdumbbell		    (sc->synhw.capPalmDetect && w >= 4 && w <= max_width) ||
2749183888Sdumbbell		    (!sc->synhw.capPalmDetect && *z <= max_pressure) ||
2750183888Sdumbbell		    (sc->synhw.capPen && w == 2))) {
2751183888Sdumbbell			/*
2752183888Sdumbbell			 * We consider the packet irrelevant for the current
2753183888Sdumbbell			 * action when:
2754183888Sdumbbell			 *  - the width isn't comprised in:
2755183888Sdumbbell			 *    [4; max_width]
2756183888Sdumbbell			 *  - the pressure isn't comprised in:
2757183888Sdumbbell			 *    [min_pressure; max_pressure]
2758183888Sdumbbell			 *  - pen aren't supported but w is 2
2759183888Sdumbbell			 *
2760183888Sdumbbell			 *  Note that this doesn't terminate the current action.
2761183888Sdumbbell			 */
2762183888Sdumbbell			VLOG(2, (LOG_DEBUG,
2763183888Sdumbbell			    "synaptics: palm detected! (%d)\n", w));
2764183888Sdumbbell			goto SYNAPTICS_END;
2765183888Sdumbbell		}
2766183888Sdumbbell
2767183888Sdumbbell		/* Read current absolute position. */
2768132865Snjl		x0 = ((pb->ipacket[3] & 0x10) << 8) |
2769183888Sdumbbell		    ((pb->ipacket[1] & 0x0f) << 8) |
2770183888Sdumbbell		    pb->ipacket[4];
2771132865Snjl		y0 = ((pb->ipacket[3] & 0x20) << 7) |
2772183888Sdumbbell		    ((pb->ipacket[1] & 0xf0) << 4) |
2773183888Sdumbbell		    pb->ipacket[5];
2774132865Snjl
2775183888Sdumbbell		synaction = &(sc->synaction);
2776139982Sphilip
2777183888Sdumbbell		/*
2778183888Sdumbbell		 * If the action is just beginning, init the structure and
2779183888Sdumbbell		 * compute tap timeout.
2780183888Sdumbbell		 */
2781183888Sdumbbell		if (!(sc->flags & PSM_FLAGS_FINGERDOWN)) {
2782183888Sdumbbell			VLOG(3, (LOG_DEBUG, "synaptics: ----\n"));
2783183888Sdumbbell
2784183888Sdumbbell			/* Store the first point of this action. */
2785183888Sdumbbell			synaction->start_x = x0;
2786183888Sdumbbell			synaction->start_y = y0;
2787183888Sdumbbell			dx = dy = 0;
2788183888Sdumbbell
2789183888Sdumbbell			/* Initialize queue. */
2790183888Sdumbbell			synaction->queue_cursor = SYNAPTICS_PACKETQUEUE;
2791183888Sdumbbell			synaction->queue_len = 0;
2792183888Sdumbbell			synaction->window_min = window_min;
2793183888Sdumbbell
2794183888Sdumbbell			/* Reset average. */
2795183888Sdumbbell			synaction->avg_dx = 0;
2796183888Sdumbbell			synaction->avg_dy = 0;
2797183888Sdumbbell
2798183888Sdumbbell			/* Reset squelch. */
2799183888Sdumbbell			synaction->squelch_x = 0;
2800183888Sdumbbell			synaction->squelch_y = 0;
2801183888Sdumbbell
2802183888Sdumbbell			/* Reset pressure peak. */
2803183888Sdumbbell			sc->zmax = 0;
2804183888Sdumbbell
2805183888Sdumbbell			/* Reset fingers count. */
2806183888Sdumbbell			synaction->fingers_nb = 0;
2807183888Sdumbbell
2808183888Sdumbbell			/* Reset virtual scrolling state. */
2809183888Sdumbbell			synaction->in_vscroll = 0;
2810183888Sdumbbell
2811183888Sdumbbell			/* Compute tap timeout. */
2812183888Sdumbbell			sc->taptimeout.tv_sec  = tap_timeout / 1000000;
2813183888Sdumbbell			sc->taptimeout.tv_usec = tap_timeout % 1000000;
2814183888Sdumbbell			timevaladd(&sc->taptimeout, &sc->lastsoftintr);
2815183888Sdumbbell
2816183888Sdumbbell			sc->flags |= PSM_FLAGS_FINGERDOWN;
2817183888Sdumbbell		} else {
2818183888Sdumbbell			/* Calculate the current delta. */
2819183888Sdumbbell			cursor = synaction->queue_cursor;
2820183888Sdumbbell			dx = x0 - synaction->queue[cursor].x;
2821183888Sdumbbell			dy = y0 - synaction->queue[cursor].y;
2822183888Sdumbbell		}
2823183888Sdumbbell
2824183888Sdumbbell		/* If in tap-hold, add the recorded button. */
2825183888Sdumbbell		if (synaction->in_taphold)
2826183888Sdumbbell			ms->button |= synaction->tap_button;
2827183888Sdumbbell
2828183888Sdumbbell		/*
2829183888Sdumbbell		 * From now on, we can use the SYNAPTICS_END label to skip
2830183888Sdumbbell		 * the current packet.
2831183888Sdumbbell		 */
2832183888Sdumbbell
2833183888Sdumbbell		/*
2834183888Sdumbbell		 * Limit the coordinates to the specified margins because
2835183888Sdumbbell		 * this area isn't very reliable.
2836183888Sdumbbell		 */
2837183888Sdumbbell		if (x0 <= margin_left)
2838183888Sdumbbell			x0 = margin_left;
2839183888Sdumbbell		else if (x0 >= 6143 - margin_right)
2840183888Sdumbbell			x0 = 6143 - margin_right;
2841183888Sdumbbell		if (y0 <= margin_bottom)
2842183888Sdumbbell			y0 = margin_bottom;
2843183888Sdumbbell		else if (y0 >= 6143 - margin_top)
2844183888Sdumbbell			y0 = 6143 - margin_top;
2845183888Sdumbbell
2846183888Sdumbbell		VLOG(3, (LOG_DEBUG, "synaptics: ipacket: [%d, %d], %d, %d\n",
2847183888Sdumbbell		    x0, y0, *z, w));
2848183888Sdumbbell
2849183888Sdumbbell		/* Queue this new packet. */
2850183888Sdumbbell		cursor = SYNAPTICS_QUEUE_CURSOR(synaction->queue_cursor - 1);
2851183888Sdumbbell		synaction->queue[cursor].x = x0;
2852183888Sdumbbell		synaction->queue[cursor].y = y0;
2853183888Sdumbbell		synaction->queue_cursor = cursor;
2854183888Sdumbbell		if (synaction->queue_len < SYNAPTICS_PACKETQUEUE)
2855183888Sdumbbell			synaction->queue_len++;
2856183888Sdumbbell		VLOG(5, (LOG_DEBUG,
2857183888Sdumbbell		    "synaptics: cursor[%d]: x=%d, y=%d, dx=%d, dy=%d\n",
2858183888Sdumbbell		    cursor, x0, y0, dx, dy));
2859183888Sdumbbell
2860183888Sdumbbell		/*
2861183888Sdumbbell		 * For tap, we keep the maximum number of fingers and the
2862183888Sdumbbell		 * pressure peak. Also with multiple fingers, we increase
2863183888Sdumbbell		 * the minimum window.
2864183888Sdumbbell		 */
2865183888Sdumbbell		switch (w) {
2866183888Sdumbbell		case 1: /* Three or more fingers. */
2867183888Sdumbbell			synaction->fingers_nb = imax(3, synaction->fingers_nb);
2868183888Sdumbbell			synaction->window_min = window_max;
2869183888Sdumbbell			break;
2870183888Sdumbbell		case 0: /* Two fingers. */
2871183888Sdumbbell			synaction->fingers_nb = imax(2, synaction->fingers_nb);
2872183888Sdumbbell			synaction->window_min = window_max;
2873183888Sdumbbell			break;
2874183888Sdumbbell		default: /* One finger or undetectable. */
2875183888Sdumbbell			synaction->fingers_nb = imax(1, synaction->fingers_nb);
2876183888Sdumbbell		}
2877183888Sdumbbell		sc->zmax = imax(*z, sc->zmax);
2878183888Sdumbbell
2879183888Sdumbbell		/* Do we have enough packets to consider this a movement? */
2880183888Sdumbbell		if (synaction->queue_len < synaction->window_min)
2881183888Sdumbbell			goto SYNAPTICS_END;
2882183888Sdumbbell
2883183888Sdumbbell		/* Is a scrolling action occuring? */
2884183888Sdumbbell		if (!synaction->in_taphold && !synaction->in_vscroll) {
2885178019Sjkim			/*
2886183888Sdumbbell			 * A scrolling action must not conflict with a tap
2887183888Sdumbbell			 * action. Here are the conditions to consider a
2888183888Sdumbbell			 * scrolling action:
2889183888Sdumbbell			 *  - the action in a configurable area
2890183888Sdumbbell			 *  - one of the following:
2891183888Sdumbbell			 *     . the distance between the last packet and the
2892183888Sdumbbell			 *       first should be above a configurable minimum
2893183888Sdumbbell			 *     . tap timed out
2894178019Sjkim			 */
2895183888Sdumbbell			dxp = abs(synaction->queue[synaction->queue_cursor].x -
2896183888Sdumbbell			    synaction->start_x);
2897183888Sdumbbell			dyp = abs(synaction->queue[synaction->queue_cursor].y -
2898183888Sdumbbell			    synaction->start_y);
2899139982Sphilip
2900183888Sdumbbell			if (timevalcmp(&sc->lastsoftintr, &sc->taptimeout, >) ||
2901183888Sdumbbell			    dxp >= sc->syninfo.vscroll_min_delta ||
2902183888Sdumbbell			    dyp >= sc->syninfo.vscroll_min_delta) {
2903183888Sdumbbell				/* Check for horizontal scrolling. */
2904183888Sdumbbell				if ((vscroll_hor_area > 0 &&
2905183888Sdumbbell				    synaction->start_y <= vscroll_hor_area) ||
2906183888Sdumbbell				    (vscroll_hor_area < 0 &&
2907183888Sdumbbell				     synaction->start_y >=
2908183888Sdumbbell				     6143 + vscroll_hor_area))
2909183888Sdumbbell					synaction->in_vscroll += 2;
2910139982Sphilip
2911183888Sdumbbell				/* Check for vertical scrolling. */
2912183888Sdumbbell				if ((vscroll_ver_area > 0 &&
2913183888Sdumbbell				    synaction->start_x <= vscroll_ver_area) ||
2914183888Sdumbbell				    (vscroll_ver_area < 0 &&
2915183888Sdumbbell				     synaction->start_x >=
2916183888Sdumbbell				     6143 + vscroll_ver_area))
2917183888Sdumbbell					synaction->in_vscroll += 1;
2918139982Sphilip
2919183888Sdumbbell				/* Avoid conflicts if area overlaps. */
2920183888Sdumbbell				if (synaction->in_vscroll == 3)
2921183888Sdumbbell					synaction->in_vscroll =
2922183888Sdumbbell					    (dxp > dyp) ? 2 : 1;
2923183888Sdumbbell			}
2924183888Sdumbbell			VLOG(5, (LOG_DEBUG,
2925183888Sdumbbell			    "synaptics: virtual scrolling: %s "
2926183888Sdumbbell			    "(direction=%d, dxp=%d, dyp=%d)\n",
2927183888Sdumbbell			    synaction->in_vscroll ? "YES" : "NO",
2928183888Sdumbbell			    synaction->in_vscroll, dxp, dyp));
2929183888Sdumbbell		}
2930139982Sphilip
2931183888Sdumbbell		weight_prev_x = weight_prev_y = weight_previous;
2932183888Sdumbbell		div_max_x = div_max_y = div_max;
2933183888Sdumbbell
2934183888Sdumbbell		if (synaction->in_vscroll) {
2935183888Sdumbbell			/* Dividers are different with virtual scrolling. */
2936183888Sdumbbell			div_min = sc->syninfo.vscroll_div_min;
2937183888Sdumbbell			div_max_x = div_max_y = sc->syninfo.vscroll_div_max;
2938183888Sdumbbell		} else {
2939178019Sjkim			/*
2940183888Sdumbbell			 * There's a lot of noise in coordinates when
2941183888Sdumbbell			 * the finger is on the touchpad's borders. When
2942183888Sdumbbell			 * using this area, we apply a special weight and
2943183888Sdumbbell			 * div.
2944178019Sjkim			 */
2945183888Sdumbbell			if (x0 <= na_left || x0 >= 6143 - na_right) {
2946183888Sdumbbell				weight_prev_x = sc->syninfo.weight_previous_na;
2947183888Sdumbbell				div_max_x = sc->syninfo.div_max_na;
2948183888Sdumbbell			}
2949139982Sphilip
2950183888Sdumbbell			if (y0 <= na_bottom || y0 >= 6143 - na_top) {
2951183888Sdumbbell				weight_prev_y = sc->syninfo.weight_previous_na;
2952183888Sdumbbell				div_max_y = sc->syninfo.div_max_na;
2953183888Sdumbbell			}
2954183888Sdumbbell		}
2955139982Sphilip
2956183888Sdumbbell		/*
2957183888Sdumbbell		 * Calculate weights for the average operands and
2958183888Sdumbbell		 * the divisor. Both depend on the distance between
2959183888Sdumbbell		 * the current packet and a previous one (based on the
2960183888Sdumbbell		 * window width).
2961183888Sdumbbell		 */
2962183888Sdumbbell		window = imin(synaction->queue_len, window_max);
2963183888Sdumbbell		peer = SYNAPTICS_QUEUE_CURSOR(cursor + window - 1);
2964183888Sdumbbell		dxp = abs(x0 - synaction->queue[peer].x) + 1;
2965183888Sdumbbell		dyp = abs(y0 - synaction->queue[peer].y) + 1;
2966183888Sdumbbell		len = (dxp * dxp) + (dyp * dyp);
2967183888Sdumbbell		weight_prev_x = imin(weight_prev_x,
2968183888Sdumbbell		    weight_len_squared * weight_prev_x / len);
2969183888Sdumbbell		weight_prev_y = imin(weight_prev_y,
2970183888Sdumbbell		    weight_len_squared * weight_prev_y / len);
2971183888Sdumbbell
2972183888Sdumbbell		len = (dxp + dyp) / 2;
2973183888Sdumbbell		div_x = div_len * div_max_x / len;
2974183888Sdumbbell		div_x = imin(div_max_x, div_x);
2975183888Sdumbbell		div_x = imax(div_min, div_x);
2976183888Sdumbbell		div_y = div_len * div_max_y / len;
2977183888Sdumbbell		div_y = imin(div_max_y, div_y);
2978183888Sdumbbell		div_y = imax(div_min, div_y);
2979183888Sdumbbell
2980183888Sdumbbell		VLOG(3, (LOG_DEBUG,
2981183888Sdumbbell		    "synaptics: peer=%d, len=%d, weight=%d/%d, div=%d/%d\n",
2982183888Sdumbbell		    peer, len, weight_prev_x, weight_prev_y, div_x, div_y));
2983183888Sdumbbell
2984183888Sdumbbell		/* Compute averages. */
2985183888Sdumbbell		synaction->avg_dx =
2986183888Sdumbbell		    (weight_current * dx * multiplicator +
2987183888Sdumbbell		     weight_prev_x * synaction->avg_dx) /
2988183888Sdumbbell		    (weight_current + weight_prev_x);
2989183888Sdumbbell
2990183888Sdumbbell		synaction->avg_dy =
2991183888Sdumbbell		    (weight_current * dy * multiplicator +
2992183888Sdumbbell		     weight_prev_y * synaction->avg_dy) /
2993183888Sdumbbell		    (weight_current + weight_prev_y);
2994183888Sdumbbell
2995183888Sdumbbell		VLOG(5, (LOG_DEBUG,
2996183888Sdumbbell		    "synaptics: avg_dx~=%d, avg_dy~=%d\n",
2997183888Sdumbbell		    synaction->avg_dx / multiplicator,
2998183888Sdumbbell		    synaction->avg_dy / multiplicator));
2999183888Sdumbbell
3000183888Sdumbbell		/* Use these averages to calculate x & y. */
3001183888Sdumbbell		synaction->squelch_x += synaction->avg_dx;
3002183888Sdumbbell		*x = synaction->squelch_x / (div_x * multiplicator);
3003183888Sdumbbell		synaction->squelch_x = synaction->squelch_x %
3004183888Sdumbbell		    (div_x * multiplicator);
3005183888Sdumbbell
3006183888Sdumbbell		synaction->squelch_y += synaction->avg_dy;
3007183888Sdumbbell		*y = synaction->squelch_y / (div_y * multiplicator);
3008183888Sdumbbell		synaction->squelch_y = synaction->squelch_y %
3009183888Sdumbbell		    (div_y * multiplicator);
3010183888Sdumbbell
3011183888Sdumbbell		if (synaction->in_vscroll) {
3012183888Sdumbbell			switch(synaction->in_vscroll) {
3013183888Sdumbbell			case 1: /* Vertical scrolling. */
3014183888Sdumbbell				if (*y != 0)
3015183888Sdumbbell					ms->button |= (*y > 0) ?
3016183888Sdumbbell					    MOUSE_BUTTON4DOWN :
3017183888Sdumbbell					    MOUSE_BUTTON5DOWN;
3018183888Sdumbbell				break;
3019183888Sdumbbell			case 2: /* Horizontal scrolling. */
3020183888Sdumbbell				if (*x != 0)
3021183888Sdumbbell					ms->button |= (*x > 0) ?
3022183888Sdumbbell					    MOUSE_BUTTON7DOWN :
3023183888Sdumbbell					    MOUSE_BUTTON6DOWN;
3024183888Sdumbbell				break;
3025183888Sdumbbell			}
3026183888Sdumbbell
3027183888Sdumbbell			/* The pointer is not moved. */
3028183888Sdumbbell			*x = *y = 0;
3029183888Sdumbbell		} else {
3030183888Sdumbbell			VLOG(3, (LOG_DEBUG, "synaptics: [%d, %d] -> [%d, %d]\n",
3031183888Sdumbbell			    dx, dy, *x, *y));
3032183888Sdumbbell		}
3033183888Sdumbbell	} else if (sc->flags & PSM_FLAGS_FINGERDOWN) {
3034183888Sdumbbell		/*
3035183888Sdumbbell		 * An action is currently taking place but the pressure
3036183888Sdumbbell		 * dropped under the minimum, putting an end to it.
3037183888Sdumbbell		 */
3038183888Sdumbbell		synapticsaction_t *synaction;
3039183888Sdumbbell		int taphold_timeout, dx, dy, tap_max_delta;
3040183888Sdumbbell
3041183888Sdumbbell		synaction = &(sc->synaction);
3042183888Sdumbbell		dx = abs(synaction->queue[synaction->queue_cursor].x -
3043183888Sdumbbell		    synaction->start_x);
3044183888Sdumbbell		dy = abs(synaction->queue[synaction->queue_cursor].y -
3045183888Sdumbbell		    synaction->start_y);
3046183888Sdumbbell
3047183888Sdumbbell		/* Max delta is disabled for multi-fingers tap. */
3048183888Sdumbbell		if (synaction->fingers_nb > 1)
3049183888Sdumbbell			tap_max_delta = imax(dx, dy);
3050183888Sdumbbell		else
3051183888Sdumbbell			tap_max_delta = sc->syninfo.tap_max_delta;
3052183888Sdumbbell
3053183888Sdumbbell		sc->flags &= ~PSM_FLAGS_FINGERDOWN;
3054183888Sdumbbell
3055183888Sdumbbell		/* Check for tap. */
3056183888Sdumbbell		VLOG(3, (LOG_DEBUG,
3057183888Sdumbbell		    "synaptics: zmax=%d, dx=%d, dy=%d, "
3058183888Sdumbbell		    "delta=%d, fingers=%d, queue=%d\n",
3059183888Sdumbbell		    sc->zmax, dx, dy, tap_max_delta, synaction->fingers_nb,
3060183888Sdumbbell		    synaction->queue_len));
3061183888Sdumbbell		if (!synaction->in_vscroll && sc->zmax >= tap_threshold &&
3062183888Sdumbbell		    timevalcmp(&sc->lastsoftintr, &sc->taptimeout, <=) &&
3063183888Sdumbbell		    dx <= tap_max_delta && dy <= tap_max_delta &&
3064183888Sdumbbell		    synaction->queue_len >= sc->syninfo.tap_min_queue) {
3065178019Sjkim			/*
3066183888Sdumbbell			 * We have a tap if:
3067183888Sdumbbell			 *   - the maximum pressure went over tap_threshold
3068183888Sdumbbell			 *   - the action ended before tap_timeout
3069183888Sdumbbell			 *
3070183888Sdumbbell			 * To handle tap-hold, we must delay any button push to
3071183888Sdumbbell			 * the next action.
3072178019Sjkim			 */
3073183888Sdumbbell			if (synaction->in_taphold) {
3074183888Sdumbbell				/*
3075183888Sdumbbell				 * This is the second and last tap of a
3076183888Sdumbbell				 * double tap action, not a tap-hold.
3077183888Sdumbbell				 */
3078183888Sdumbbell				synaction->in_taphold = 0;
3079139982Sphilip
3080183888Sdumbbell				/*
3081183888Sdumbbell				 * For double-tap to work:
3082183888Sdumbbell				 *   - no button press is emitted (to
3083183888Sdumbbell				 *     simulate a button release)
3084183888Sdumbbell				 *   - PSM_FLAGS_FINGERDOWN is set to
3085183888Sdumbbell				 *     force the next packet to emit a
3086183888Sdumbbell				 *     button press)
3087183888Sdumbbell				 */
3088183888Sdumbbell				VLOG(2, (LOG_DEBUG,
3089183888Sdumbbell				    "synaptics: button RELEASE: %d\n",
3090183888Sdumbbell				    synaction->tap_button));
3091183888Sdumbbell				sc->flags |= PSM_FLAGS_FINGERDOWN;
3092178019Sjkim			} else {
3093178019Sjkim				/*
3094183888Sdumbbell				 * This is the first tap: we set the
3095183888Sdumbbell				 * tap-hold state and notify the button
3096183888Sdumbbell				 * down event.
3097178019Sjkim				 */
3098183888Sdumbbell				synaction->in_taphold = 1;
3099183888Sdumbbell				taphold_timeout = sc->syninfo.taphold_timeout;
3100183888Sdumbbell				sc->taptimeout.tv_sec  = taphold_timeout /
3101183888Sdumbbell				    1000000;
3102183888Sdumbbell				sc->taptimeout.tv_usec = taphold_timeout %
3103183888Sdumbbell				    1000000;
3104183888Sdumbbell				timevaladd(&sc->taptimeout, &sc->lastsoftintr);
3105139982Sphilip
3106183888Sdumbbell				switch (synaction->fingers_nb) {
3107183888Sdumbbell				case 3:
3108183888Sdumbbell					synaction->tap_button =
3109183888Sdumbbell					    MOUSE_BUTTON2DOWN;
3110183888Sdumbbell					break;
3111183888Sdumbbell				case 2:
3112183888Sdumbbell					synaction->tap_button =
3113183888Sdumbbell					    MOUSE_BUTTON3DOWN;
3114183888Sdumbbell					break;
3115183888Sdumbbell				default:
3116183888Sdumbbell					synaction->tap_button =
3117183888Sdumbbell					    MOUSE_BUTTON1DOWN;
3118183888Sdumbbell				}
3119183888Sdumbbell				VLOG(2, (LOG_DEBUG,
3120183888Sdumbbell				    "synaptics: button PRESS: %d\n",
3121183888Sdumbbell				    synaction->tap_button));
3122183888Sdumbbell				ms->button |= synaction->tap_button;
3123178019Sjkim			}
3124183888Sdumbbell		} else {
3125183888Sdumbbell			/*
3126183888Sdumbbell			 * Not enough pressure or timeout: reset
3127183888Sdumbbell			 * tap-hold state.
3128183888Sdumbbell			 */
3129183888Sdumbbell			if (synaction->in_taphold) {
3130183888Sdumbbell				VLOG(2, (LOG_DEBUG,
3131183888Sdumbbell				    "synaptics: button RELEASE: %d\n",
3132183888Sdumbbell				    synaction->tap_button));
3133183888Sdumbbell				synaction->in_taphold = 0;
3134183888Sdumbbell			} else {
3135183888Sdumbbell				VLOG(2, (LOG_DEBUG,
3136183888Sdumbbell				    "synaptics: not a tap-hold\n"));
3137183888Sdumbbell			}
3138183888Sdumbbell		}
3139183888Sdumbbell	} else if (!(sc->flags & PSM_FLAGS_FINGERDOWN) &&
3140183888Sdumbbell	    sc->synaction.in_taphold) {
3141178019Sjkim		/*
3142183888Sdumbbell		 * For a tap-hold to work, the button must remain down at
3143183888Sdumbbell		 * least until timeout (where the in_taphold flags will be
3144183888Sdumbbell		 * cleared) or during the next action.
3145139982Sphilip		 */
3146183888Sdumbbell		if (timevalcmp(&sc->lastsoftintr, &sc->taptimeout, <=)) {
3147183888Sdumbbell			ms->button |= sc->synaction.tap_button;
3148183888Sdumbbell		} else {
3149183888Sdumbbell			VLOG(2, (LOG_DEBUG,
3150183888Sdumbbell			    "synaptics: button RELEASE: %d\n",
3151183888Sdumbbell			    sc->synaction.tap_button));
3152183888Sdumbbell			sc->synaction.in_taphold = 0;
3153133297Sphilip		}
3154178019Sjkim	}
3155133296Sphilip
3156183888SdumbbellSYNAPTICS_END:
3157183888Sdumbbell	/*
3158183888Sdumbbell	 * Use the extra buttons as a scrollwheel
3159183888Sdumbbell	 *
3160183888Sdumbbell	 * XXX X.Org uses the Z axis for vertical wheel only,
3161183888Sdumbbell	 * whereas moused(8) understands special values to differ
3162183888Sdumbbell	 * vertical and horizontal wheels.
3163183888Sdumbbell	 *
3164183888Sdumbbell	 * xf86-input-mouse needs therefore a small patch to
3165183888Sdumbbell	 * understand these special values. Without it, the
3166183888Sdumbbell	 * horizontal wheel acts as a vertical wheel in X.Org.
3167183888Sdumbbell	 *
3168183888Sdumbbell	 * That's why the horizontal wheel is disabled by
3169183888Sdumbbell	 * default for now.
3170183888Sdumbbell	 */
3171183888Sdumbbell	if (ms->button & MOUSE_BUTTON4DOWN) {
3172178019Sjkim		*z = -1;
3173183888Sdumbbell		ms->button &= ~MOUSE_BUTTON4DOWN;
3174183888Sdumbbell	} else if (ms->button & MOUSE_BUTTON5DOWN) {
3175178019Sjkim		*z = 1;
3176183888Sdumbbell		ms->button &= ~MOUSE_BUTTON5DOWN;
3177183888Sdumbbell	} else if (ms->button & MOUSE_BUTTON6DOWN) {
3178183888Sdumbbell		*z = -2;
3179183888Sdumbbell		ms->button &= ~MOUSE_BUTTON6DOWN;
3180183888Sdumbbell	} else if (ms->button & MOUSE_BUTTON7DOWN) {
3181183888Sdumbbell		*z = 2;
3182183888Sdumbbell		ms->button &= ~MOUSE_BUTTON7DOWN;
3183183888Sdumbbell	} else
3184178019Sjkim		*z = 0;
3185178019Sjkim
3186178019Sjkim	return (0);
3187178019Sjkim}
3188178019Sjkim
3189178019Sjkimstatic void
3190178019Sjkimproc_versapad(struct psm_softc *sc, packetbuf_t *pb, mousestatus_t *ms,
3191178019Sjkim    int *x, int *y, int *z)
3192178019Sjkim{
3193178019Sjkim	static int butmap_versapad[8] = {
3194178019Sjkim		0,
3195178019Sjkim		MOUSE_BUTTON3DOWN,
3196178019Sjkim		0,
3197178019Sjkim		MOUSE_BUTTON3DOWN,
3198178019Sjkim		MOUSE_BUTTON1DOWN,
3199178019Sjkim		MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
3200178019Sjkim		MOUSE_BUTTON1DOWN,
3201178019Sjkim		MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN
3202178019Sjkim	};
3203178019Sjkim	int c, x0, y0;
3204178019Sjkim
3205178019Sjkim	/* VersaPad PS/2 absolute mode message format
3206178019Sjkim	 *
3207178019Sjkim	 * [packet1]     7   6   5   4   3   2   1   0(LSB)
3208178019Sjkim	 *  ipacket[0]:  1   1   0   A   1   L   T   R
3209178019Sjkim	 *  ipacket[1]: H7  H6  H5  H4  H3  H2  H1  H0
3210178019Sjkim	 *  ipacket[2]: V7  V6  V5  V4  V3  V2  V1  V0
3211178019Sjkim	 *  ipacket[3]:  1   1   1   A   1   L   T   R
3212178019Sjkim	 *  ipacket[4]:V11 V10  V9  V8 H11 H10  H9  H8
3213178019Sjkim	 *  ipacket[5]:  0  P6  P5  P4  P3  P2  P1  P0
3214178019Sjkim	 *
3215178019Sjkim	 * [note]
3216178019Sjkim	 *  R: right physical mouse button (1=on)
3217178019Sjkim	 *  T: touch pad virtual button (1=tapping)
3218178019Sjkim	 *  L: left physical mouse button (1=on)
3219178019Sjkim	 *  A: position data is valid (1=valid)
3220178019Sjkim	 *  H: horizontal data (12bit signed integer. H11 is sign bit.)
3221178019Sjkim	 *  V: vertical data (12bit signed integer. V11 is sign bit.)
3222178019Sjkim	 *  P: pressure data
3223178019Sjkim	 *
3224178019Sjkim	 * Tapping is mapped to MOUSE_BUTTON4.
3225178019Sjkim	 */
3226178019Sjkim	c = pb->ipacket[0];
3227178019Sjkim	*x = *y = 0;
3228178019Sjkim	ms->button = butmap_versapad[c & MOUSE_PS2VERSA_BUTTONS];
3229178019Sjkim	ms->button |= (c & MOUSE_PS2VERSA_TAP) ? MOUSE_BUTTON4DOWN : 0;
3230178019Sjkim	if (c & MOUSE_PS2VERSA_IN_USE) {
3231178019Sjkim		x0 = pb->ipacket[1] | (((pb->ipacket[4]) & 0x0f) << 8);
3232178019Sjkim		y0 = pb->ipacket[2] | (((pb->ipacket[4]) & 0xf0) << 4);
3233178019Sjkim		if (x0 & 0x800)
3234178019Sjkim			x0 -= 0x1000;
3235178019Sjkim		if (y0 & 0x800)
3236178019Sjkim			y0 -= 0x1000;
3237178019Sjkim		if (sc->flags & PSM_FLAGS_FINGERDOWN) {
3238178019Sjkim			*x = sc->xold - x0;
3239178019Sjkim			*y = y0 - sc->yold;
3240178019Sjkim			if (*x < 0)	/* XXX */
3241178019Sjkim				++*x;
3242178019Sjkim			else if (*x)
3243178019Sjkim				--*x;
3244178019Sjkim			if (*y < 0)
3245178019Sjkim				++*y;
3246178019Sjkim			else if (*y)
3247178019Sjkim				--*y;
3248178019Sjkim		} else
3249178019Sjkim			sc->flags |= PSM_FLAGS_FINGERDOWN;
3250178019Sjkim		sc->xold = x0;
3251178019Sjkim		sc->yold = y0;
3252178019Sjkim	} else
3253178019Sjkim		sc->flags &= ~PSM_FLAGS_FINGERDOWN;
3254178019Sjkim}
3255178019Sjkim
3256178019Sjkimstatic void
3257178019Sjkimpsmsoftintr(void *arg)
3258178019Sjkim{
3259178019Sjkim	/*
3260178019Sjkim	 * the table to turn PS/2 mouse button bits (MOUSE_PS2_BUTTON?DOWN)
3261178019Sjkim	 * into `mousestatus' button bits (MOUSE_BUTTON?DOWN).
3262178019Sjkim	 */
3263178019Sjkim	static int butmap[8] = {
3264178019Sjkim		0,
3265178019Sjkim		MOUSE_BUTTON1DOWN,
3266178019Sjkim		MOUSE_BUTTON3DOWN,
3267178019Sjkim		MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
3268178019Sjkim		MOUSE_BUTTON2DOWN,
3269178019Sjkim		MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
3270178019Sjkim		MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
3271178019Sjkim		MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
3272178019Sjkim	};
3273178019Sjkim	register struct psm_softc *sc = arg;
3274178019Sjkim	mousestatus_t ms;
3275178019Sjkim	packetbuf_t *pb;
3276178019Sjkim	int x, y, z, c, l, s;
3277178019Sjkim
3278178019Sjkim	getmicrouptime(&sc->lastsoftintr);
3279178019Sjkim
3280178019Sjkim	s = spltty();
3281178019Sjkim
3282178019Sjkim	do {
3283178019Sjkim		pb = &sc->pqueue[sc->pqueue_start];
3284178019Sjkim
3285178019Sjkim		if (sc->mode.level == PSM_LEVEL_NATIVE)
3286178019Sjkim			goto next_native;
3287178019Sjkim
3288178019Sjkim		c = pb->ipacket[0];
3289178019Sjkim		/*
3290178019Sjkim		 * A kludge for Kensington device!
3291178019Sjkim		 * The MSB of the horizontal count appears to be stored in
3292178019Sjkim		 * a strange place.
3293178019Sjkim		 */
3294178019Sjkim		if (sc->hw.model == MOUSE_MODEL_THINK)
3295178019Sjkim			pb->ipacket[1] |= (c & MOUSE_PS2_XOVERFLOW) ? 0x80 : 0;
3296178019Sjkim
3297178019Sjkim		/* ignore the overflow bits... */
3298178019Sjkim		x = (c & MOUSE_PS2_XNEG) ?
3299178019Sjkim		    pb->ipacket[1] - 256 : pb->ipacket[1];
3300178019Sjkim		y = (c & MOUSE_PS2_YNEG) ?
3301178019Sjkim		    pb->ipacket[2] - 256 : pb->ipacket[2];
3302133296Sphilip		z = 0;
3303178019Sjkim		ms.obutton = sc->button;	  /* previous button state */
3304178019Sjkim		ms.button = butmap[c & MOUSE_PS2_BUTTONS];
3305178019Sjkim		/* `tapping' action */
3306178019Sjkim		if (sc->config & PSM_CONFIG_FORCETAP)
3307178019Sjkim			ms.button |= ((c & MOUSE_PS2_TAP)) ?
3308178019Sjkim			    0 : MOUSE_BUTTON4DOWN;
3309133296Sphilip
3310178019Sjkim		switch (sc->hw.model) {
3311132865Snjl
3312178019Sjkim		case MOUSE_MODEL_EXPLORER:
3313178019Sjkim			/*
3314178019Sjkim			 *          b7 b6 b5 b4 b3 b2 b1 b0
3315178019Sjkim			 * byte 1:  oy ox sy sx 1  M  R  L
3316178019Sjkim			 * byte 2:  x  x  x  x  x  x  x  x
3317178019Sjkim			 * byte 3:  y  y  y  y  y  y  y  y
3318178019Sjkim			 * byte 4:  *  *  S2 S1 s  d2 d1 d0
3319178019Sjkim			 *
3320178019Sjkim			 * L, M, R, S1, S2: left, middle, right and side buttons
3321178019Sjkim			 * s: wheel data sign bit
3322178019Sjkim			 * d2-d0: wheel data
3323178019Sjkim			 */
3324178019Sjkim			z = (pb->ipacket[3] & MOUSE_EXPLORER_ZNEG) ?
3325178019Sjkim			    (pb->ipacket[3] & 0x0f) - 16 :
3326178019Sjkim			    (pb->ipacket[3] & 0x0f);
3327178019Sjkim			ms.button |=
3328178019Sjkim			    (pb->ipacket[3] & MOUSE_EXPLORER_BUTTON4DOWN) ?
3329178019Sjkim			    MOUSE_BUTTON4DOWN : 0;
3330178019Sjkim			ms.button |=
3331178019Sjkim			    (pb->ipacket[3] & MOUSE_EXPLORER_BUTTON5DOWN) ?
3332178019Sjkim			    MOUSE_BUTTON5DOWN : 0;
3333178019Sjkim			break;
3334178019Sjkim
3335178019Sjkim		case MOUSE_MODEL_INTELLI:
3336178019Sjkim		case MOUSE_MODEL_NET:
3337178019Sjkim			/* wheel data is in the fourth byte */
3338178019Sjkim			z = (char)pb->ipacket[3];
3339178019Sjkim			/*
3340178019Sjkim			 * XXX some mice may send 7 when there is no Z movement?			 */
3341178019Sjkim			if ((z >= 7) || (z <= -7))
3342178019Sjkim				z = 0;
3343178019Sjkim			/* some compatible mice have additional buttons */
3344178019Sjkim			ms.button |= (c & MOUSE_PS2INTELLI_BUTTON4DOWN) ?
3345178019Sjkim			    MOUSE_BUTTON4DOWN : 0;
3346178019Sjkim			ms.button |= (c & MOUSE_PS2INTELLI_BUTTON5DOWN) ?
3347178019Sjkim			    MOUSE_BUTTON5DOWN : 0;
3348178019Sjkim			break;
3349178019Sjkim
3350178019Sjkim		case MOUSE_MODEL_MOUSEMANPLUS:
3351178019Sjkim			proc_mmanplus(sc, pb, &ms, &x, &y, &z);
3352178019Sjkim			break;
3353178019Sjkim
3354178019Sjkim		case MOUSE_MODEL_GLIDEPOINT:
3355178019Sjkim			/* `tapping' action */
3356178019Sjkim			ms.button |= ((c & MOUSE_PS2_TAP)) ? 0 :
3357178019Sjkim			    MOUSE_BUTTON4DOWN;
3358178019Sjkim			break;
3359178019Sjkim
3360178019Sjkim		case MOUSE_MODEL_NETSCROLL:
3361178019Sjkim			/*
3362178019Sjkim			 * three addtional bytes encode buttons and
3363178019Sjkim			 * wheel events
3364178019Sjkim			 */
3365178019Sjkim			ms.button |= (pb->ipacket[3] & MOUSE_PS2_BUTTON3DOWN) ?
3366178019Sjkim			    MOUSE_BUTTON4DOWN : 0;
3367178019Sjkim			ms.button |= (pb->ipacket[3] & MOUSE_PS2_BUTTON1DOWN) ?
3368178019Sjkim			    MOUSE_BUTTON5DOWN : 0;
3369178019Sjkim			z = (pb->ipacket[3] & MOUSE_PS2_XNEG) ?
3370178019Sjkim			    pb->ipacket[4] - 256 : pb->ipacket[4];
3371178019Sjkim			break;
3372178019Sjkim
3373178019Sjkim		case MOUSE_MODEL_THINK:
3374178019Sjkim			/* the fourth button state in the first byte */
3375178019Sjkim			ms.button |= (c & MOUSE_PS2_TAP) ?
3376178019Sjkim			    MOUSE_BUTTON4DOWN : 0;
3377178019Sjkim			break;
3378178019Sjkim
3379178019Sjkim		case MOUSE_MODEL_VERSAPAD:
3380178019Sjkim			proc_versapad(sc, pb, &ms, &x, &y, &z);
3381178019Sjkim			c = ((x < 0) ? MOUSE_PS2_XNEG : 0) |
3382178019Sjkim			    ((y < 0) ? MOUSE_PS2_YNEG : 0);
3383178019Sjkim			break;
3384178019Sjkim
3385178019Sjkim		case MOUSE_MODEL_4D:
3386178019Sjkim			/*
3387178019Sjkim			 *          b7 b6 b5 b4 b3 b2 b1 b0
3388178019Sjkim			 * byte 1:  s2 d2 s1 d1 1  M  R  L
3389178019Sjkim			 * byte 2:  sx x  x  x  x  x  x  x
3390178019Sjkim			 * byte 3:  sy y  y  y  y  y  y  y
3391178019Sjkim			 *
3392178019Sjkim			 * s1: wheel 1 direction
3393178019Sjkim			 * d1: wheel 1 data
3394178019Sjkim			 * s2: wheel 2 direction
3395178019Sjkim			 * d2: wheel 2 data
3396178019Sjkim			 */
3397178019Sjkim			x = (pb->ipacket[1] & 0x80) ?
3398178019Sjkim			    pb->ipacket[1] - 256 : pb->ipacket[1];
3399178019Sjkim			y = (pb->ipacket[2] & 0x80) ?
3400178019Sjkim			    pb->ipacket[2] - 256 : pb->ipacket[2];
3401178019Sjkim			switch (c & MOUSE_4D_WHEELBITS) {
3402178019Sjkim			case 0x10:
3403178019Sjkim				z = 1;
3404178019Sjkim				break;
3405178019Sjkim			case 0x30:
3406178019Sjkim				z = -1;
3407178019Sjkim				break;
3408178019Sjkim			case 0x40:	/* XXX 2nd wheel turning right */
3409178019Sjkim				z = 2;
3410178019Sjkim				break;
3411178019Sjkim			case 0xc0:	/* XXX 2nd wheel turning left */
3412178019Sjkim				z = -2;
3413178019Sjkim				break;
3414178019Sjkim			}
3415178019Sjkim			break;
3416178019Sjkim
3417178019Sjkim		case MOUSE_MODEL_4DPLUS:
3418178019Sjkim			if ((x < 16 - 256) && (y < 16 - 256)) {
3419178019Sjkim				/*
3420178019Sjkim				 *          b7 b6 b5 b4 b3 b2 b1 b0
3421178019Sjkim				 * byte 1:  0  0  1  1  1  M  R  L
3422178019Sjkim				 * byte 2:  0  0  0  0  1  0  0  0
3423178019Sjkim				 * byte 3:  0  0  0  0  S  s  d1 d0
3424178019Sjkim				 *
3425178019Sjkim				 * L, M, R, S: left, middle, right,
3426178019Sjkim				 *             and side buttons
3427178019Sjkim				 * s: wheel data sign bit
3428178019Sjkim				 * d1-d0: wheel data
3429178019Sjkim				 */
3430178019Sjkim				x = y = 0;
3431178019Sjkim				if (pb->ipacket[2] & MOUSE_4DPLUS_BUTTON4DOWN)
3432178019Sjkim					ms.button |= MOUSE_BUTTON4DOWN;
3433178019Sjkim				z = (pb->ipacket[2] & MOUSE_4DPLUS_ZNEG) ?
3434178019Sjkim				    ((pb->ipacket[2] & 0x07) - 8) :
3435178019Sjkim				    (pb->ipacket[2] & 0x07) ;
3436178019Sjkim			} else {
3437178019Sjkim				/* preserve previous button states */
3438178019Sjkim				ms.button |= ms.obutton & MOUSE_EXTBUTTONS;
3439178019Sjkim			}
3440178019Sjkim			break;
3441178019Sjkim
3442178019Sjkim		case MOUSE_MODEL_SYNAPTICS:
3443178019Sjkim			if (proc_synaptics(sc, pb, &ms, &x, &y, &z) != 0)
3444178019Sjkim				goto next;
3445178019Sjkim			break;
3446178019Sjkim
3447178019Sjkim		case MOUSE_MODEL_GENERIC:
3448178019Sjkim		default:
3449178019Sjkim			break;
3450178019Sjkim		}
3451178019Sjkim
3452178019Sjkim	/* scale values */
3453178019Sjkim	if (sc->mode.accelfactor >= 1) {
3454178019Sjkim		if (x != 0) {
3455178019Sjkim			x = x * x / sc->mode.accelfactor;
3456178019Sjkim			if (x == 0)
3457178019Sjkim				x = 1;
3458178019Sjkim			if (c & MOUSE_PS2_XNEG)
3459178019Sjkim				x = -x;
3460178019Sjkim		}
3461178019Sjkim		if (y != 0) {
3462178019Sjkim			y = y * y / sc->mode.accelfactor;
3463178019Sjkim			if (y == 0)
3464178019Sjkim				y = 1;
3465178019Sjkim			if (c & MOUSE_PS2_YNEG)
3466178019Sjkim				y = -y;
3467178019Sjkim		}
346841016Sdfr	}
346941016Sdfr
3470178019Sjkim	ms.dx = x;
3471178019Sjkim	ms.dy = y;
3472178019Sjkim	ms.dz = z;
3473178019Sjkim	ms.flags = ((x || y || z) ? MOUSE_POSCHANGED : 0) |
3474178019Sjkim	    (ms.obutton ^ ms.button);
347541016Sdfr
3476178017Sjkim	pb->inputbytes = tame_mouse(sc, pb, &ms, pb->ipacket);
347741016Sdfr
3478178019Sjkim	sc->status.flags |= ms.flags;
3479178019Sjkim	sc->status.dx += ms.dx;
3480178019Sjkim	sc->status.dy += ms.dy;
3481178019Sjkim	sc->status.dz += ms.dz;
3482178019Sjkim	sc->status.button = ms.button;
3483178019Sjkim	sc->button = ms.button;
348441016Sdfr
3485178019Sjkimnext_native:
348658230Syokota	sc->watchdog = FALSE;
348758230Syokota
3488178019Sjkim	/* queue data */
3489178019Sjkim	if (sc->queue.count + pb->inputbytes < sizeof(sc->queue.buf)) {
3490178019Sjkim		l = imin(pb->inputbytes,
3491178019Sjkim		    sizeof(sc->queue.buf) - sc->queue.tail);
3492178019Sjkim		bcopy(&pb->ipacket[0], &sc->queue.buf[sc->queue.tail], l);
3493178019Sjkim		if (pb->inputbytes > l)
3494178019Sjkim			bcopy(&pb->ipacket[l], &sc->queue.buf[0],
3495178019Sjkim			    pb->inputbytes - l);
3496178019Sjkim		sc->queue.tail = (sc->queue.tail + pb->inputbytes) %
3497178019Sjkim		    sizeof(sc->queue.buf);
3498178019Sjkim		sc->queue.count += pb->inputbytes;
349941016Sdfr	}
3500178019Sjkim	pb->inputbytes = 0;
350141016Sdfr
3502178019Sjkimnext:
3503123442Salfred	if (++sc->pqueue_start >= PSM_PACKETQUEUE)
3504123442Salfred		sc->pqueue_start = 0;
3505178019Sjkim	} while (sc->pqueue_start != sc->pqueue_end);
3506178019Sjkim
3507178019Sjkim	if (sc->state & PSM_ASLP) {
3508178019Sjkim		sc->state &= ~PSM_ASLP;
3509178019Sjkim		wakeup(sc);
3510178019Sjkim	}
3511178019Sjkim	selwakeuppri(&sc->rsel, PZERO);
3512189870Srnoland	if (sc->async != NULL) {
3513189870Srnoland		pgsigio(&sc->async, SIGIO, 0);
3514189870Srnoland	}
3515178019Sjkim	sc->state &= ~PSM_SOFTARMED;
3516178019Sjkim	splx(s);
351741016Sdfr}
351841016Sdfr
351941016Sdfrstatic int
3520130585Sphkpsmpoll(struct cdev *dev, int events, struct thread *td)
352141016Sdfr{
3522178019Sjkim	struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev));
3523178019Sjkim	int s;
3524178019Sjkim	int revents = 0;
352541016Sdfr
3526178019Sjkim	/* Return true if a mouse event available */
3527178019Sjkim	s = spltty();
3528178019Sjkim	if (events & (POLLIN | POLLRDNORM)) {
3529178019Sjkim		if (sc->queue.count > 0)
3530178019Sjkim			revents |= events & (POLLIN | POLLRDNORM);
3531178019Sjkim		else
3532178019Sjkim			selrecord(td, &sc->rsel);
3533178019Sjkim	}
3534178019Sjkim	splx(s);
353541016Sdfr
3536178019Sjkim	return (revents);
353741016Sdfr}
353841016Sdfr
353941016Sdfr/* vendor/model specific routines */
354041016Sdfr
354141016Sdfrstatic int mouse_id_proc1(KBDC kbdc, int res, int scale, int *status)
354241016Sdfr{
3543178019Sjkim	if (set_mouse_resolution(kbdc, res) != res)
3544178019Sjkim		return (FALSE);
3545178019Sjkim	if (set_mouse_scaling(kbdc, scale) &&
3546178019Sjkim	    set_mouse_scaling(kbdc, scale) &&
3547178019Sjkim	    set_mouse_scaling(kbdc, scale) &&
3548178019Sjkim	    (get_mouse_status(kbdc, status, 0, 3) >= 3))
3549178019Sjkim		return (TRUE);
3550178019Sjkim	return (FALSE);
355141016Sdfr}
355241016Sdfr
3553178019Sjkimstatic int
355469438Syokotamouse_ext_command(KBDC kbdc, int command)
355569438Syokota{
3556178019Sjkim	int c;
355769438Syokota
3558178019Sjkim	c = (command >> 6) & 0x03;
3559178019Sjkim	if (set_mouse_resolution(kbdc, c) != c)
3560178019Sjkim		return (FALSE);
3561178019Sjkim	c = (command >> 4) & 0x03;
3562178019Sjkim	if (set_mouse_resolution(kbdc, c) != c)
3563178019Sjkim		return (FALSE);
3564178019Sjkim	c = (command >> 2) & 0x03;
3565178019Sjkim	if (set_mouse_resolution(kbdc, c) != c)
3566178019Sjkim		return (FALSE);
3567178019Sjkim	c = (command >> 0) & 0x03;
3568178019Sjkim	if (set_mouse_resolution(kbdc, c) != c)
3569178019Sjkim		return (FALSE);
3570178019Sjkim	return (TRUE);
357169438Syokota}
357269438Syokota
3573153072Sru#ifdef notyet
357441016Sdfr/* Logitech MouseMan Cordless II */
357541016Sdfrstatic int
357641016Sdfrenable_lcordless(struct psm_softc *sc)
357741016Sdfr{
3578178019Sjkim	int status[3];
3579178019Sjkim	int ch;
358041016Sdfr
3581178019Sjkim	if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 2, status))
3582178019Sjkim		return (FALSE);
3583178019Sjkim	if (status[1] == PSMD_RES_HIGH)
3584178019Sjkim		return (FALSE);
3585178019Sjkim	ch = (status[0] & 0x07) - 1;	/* channel # */
3586178019Sjkim	if ((ch <= 0) || (ch > 4))
3587178019Sjkim		return (FALSE);
3588178019Sjkim	/*
3589178019Sjkim	 * status[1]: always one?
3590178019Sjkim	 * status[2]: battery status? (0-100)
3591178019Sjkim	 */
3592178019Sjkim	return (TRUE);
359341016Sdfr}
359441016Sdfr#endif /* notyet */
359541016Sdfr
359658230Syokota/* Genius NetScroll Mouse, MouseSystems SmartScroll Mouse */
359741016Sdfrstatic int
359841016Sdfrenable_groller(struct psm_softc *sc)
359941016Sdfr{
3600178019Sjkim	int status[3];
360141016Sdfr
3602178019Sjkim	/*
3603178019Sjkim	 * The special sequence to enable the fourth button and the
3604178019Sjkim	 * roller. Immediately after this sequence check status bytes.
3605178019Sjkim	 * if the mouse is NetScroll, the second and the third bytes are
3606178019Sjkim	 * '3' and 'D'.
3607178019Sjkim	 */
360841016Sdfr
3609178019Sjkim	/*
3610178019Sjkim	 * If the mouse is an ordinary PS/2 mouse, the status bytes should
3611178019Sjkim	 * look like the following.
3612178019Sjkim	 *
3613178019Sjkim	 * byte 1 bit 7 always 0
3614178019Sjkim	 *        bit 6 stream mode (0)
3615178019Sjkim	 *        bit 5 disabled (0)
3616178019Sjkim	 *        bit 4 1:1 scaling (0)
3617178019Sjkim	 *        bit 3 always 0
3618178019Sjkim	 *        bit 0-2 button status
3619178019Sjkim	 * byte 2 resolution (PSMD_RES_HIGH)
3620178019Sjkim	 * byte 3 report rate (?)
3621178019Sjkim	 */
362241016Sdfr
3623178019Sjkim	if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status))
3624178019Sjkim		return (FALSE);
3625178019Sjkim	if ((status[1] != '3') || (status[2] != 'D'))
3626178019Sjkim		return (FALSE);
3627178019Sjkim	/* FIXME: SmartScroll Mouse has 5 buttons! XXX */
3628178019Sjkim	sc->hw.buttons = 4;
3629178019Sjkim	return (TRUE);
363041016Sdfr}
363141016Sdfr
363258230Syokota/* Genius NetMouse/NetMouse Pro, ASCII Mie Mouse, NetScroll Optical */
363341016Sdfrstatic int
363441016Sdfrenable_gmouse(struct psm_softc *sc)
363541016Sdfr{
3636178019Sjkim	int status[3];
363741016Sdfr
3638178019Sjkim	/*
3639178019Sjkim	 * The special sequence to enable the middle, "rubber" button.
3640178019Sjkim	 * Immediately after this sequence check status bytes.
3641178019Sjkim	 * if the mouse is NetMouse, NetMouse Pro, or ASCII MIE Mouse,
3642178019Sjkim	 * the second and the third bytes are '3' and 'U'.
3643178019Sjkim	 * NOTE: NetMouse reports that it has three buttons although it has
3644178019Sjkim	 * two buttons and a rubber button. NetMouse Pro and MIE Mouse
3645178019Sjkim	 * say they have three buttons too and they do have a button on the
3646178019Sjkim	 * side...
3647178019Sjkim	 */
3648178019Sjkim	if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status))
3649178019Sjkim		return (FALSE);
3650178019Sjkim	if ((status[1] != '3') || (status[2] != 'U'))
3651178019Sjkim		return (FALSE);
3652178019Sjkim	return (TRUE);
365341016Sdfr}
365441016Sdfr
365541016Sdfr/* ALPS GlidePoint */
365641016Sdfrstatic int
365741016Sdfrenable_aglide(struct psm_softc *sc)
365841016Sdfr{
3659178019Sjkim	int status[3];
366041016Sdfr
3661178019Sjkim	/*
3662178019Sjkim	 * The special sequence to obtain ALPS GlidePoint specific
3663178019Sjkim	 * information. Immediately after this sequence, status bytes will
3664178019Sjkim	 * contain something interesting.
3665178019Sjkim	 * NOTE: ALPS produces several models of GlidePoint. Some of those
3666178019Sjkim	 * do not respond to this sequence, thus, cannot be detected this way.
3667178019Sjkim	 */
3668178019Sjkim	if (set_mouse_sampling_rate(sc->kbdc, 100) != 100)
3669178019Sjkim		return (FALSE);
3670178019Sjkim	if (!mouse_id_proc1(sc->kbdc, PSMD_RES_LOW, 2, status))
3671178019Sjkim		return (FALSE);
3672178019Sjkim	if ((status[1] == PSMD_RES_LOW) || (status[2] == 100))
3673178019Sjkim		return (FALSE);
3674178019Sjkim	return (TRUE);
367541016Sdfr}
367641016Sdfr
367741016Sdfr/* Kensington ThinkingMouse/Trackball */
367841016Sdfrstatic int
367941016Sdfrenable_kmouse(struct psm_softc *sc)
368041016Sdfr{
3681178019Sjkim	static u_char rate[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 };
3682178019Sjkim	KBDC kbdc = sc->kbdc;
3683178019Sjkim	int status[3];
3684178019Sjkim	int id1;
3685178019Sjkim	int id2;
3686178019Sjkim	int i;
368741016Sdfr
3688178019Sjkim	id1 = get_aux_id(kbdc);
3689178019Sjkim	if (set_mouse_sampling_rate(kbdc, 10) != 10)
3690178019Sjkim		return (FALSE);
3691178019Sjkim	/*
3692178019Sjkim	 * The device is now in the native mode? It returns a different
3693178019Sjkim	 * ID value...
3694178019Sjkim	 */
3695178019Sjkim	id2 = get_aux_id(kbdc);
3696178019Sjkim	if ((id1 == id2) || (id2 != 2))
3697178019Sjkim		return (FALSE);
369841016Sdfr
3699178019Sjkim	if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW)
3700178019Sjkim		return (FALSE);
370141016Sdfr#if PSM_DEBUG >= 2
3702178019Sjkim	/* at this point, resolution is LOW, sampling rate is 10/sec */
3703178019Sjkim	if (get_mouse_status(kbdc, status, 0, 3) < 3)
3704178019Sjkim		return (FALSE);
370541016Sdfr#endif
370641016Sdfr
3707178019Sjkim	/*
3708178019Sjkim	 * The special sequence to enable the third and fourth buttons.
3709178019Sjkim	 * Otherwise they behave like the first and second buttons.
3710178019Sjkim	 */
3711178019Sjkim	for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i)
3712178019Sjkim		if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i])
3713178019Sjkim			return (FALSE);
371441016Sdfr
3715178019Sjkim	/*
3716178019Sjkim	 * At this point, the device is using default resolution and
3717178019Sjkim	 * sampling rate for the native mode.
3718178019Sjkim	 */
3719178019Sjkim	if (get_mouse_status(kbdc, status, 0, 3) < 3)
3720178019Sjkim		return (FALSE);
3721178019Sjkim	if ((status[1] == PSMD_RES_LOW) || (status[2] == rate[i - 1]))
3722178019Sjkim		return (FALSE);
372341016Sdfr
3724178019Sjkim	/* the device appears be enabled by this sequence, diable it for now */
3725178019Sjkim	disable_aux_dev(kbdc);
3726178019Sjkim	empty_aux_buffer(kbdc, 5);
372741016Sdfr
3728178019Sjkim	return (TRUE);
372941016Sdfr}
373041016Sdfr
373158230Syokota/* Logitech MouseMan+/FirstMouse+, IBM ScrollPoint Mouse */
373241016Sdfrstatic int
373341016Sdfrenable_mmanplus(struct psm_softc *sc)
373441016Sdfr{
3735178019Sjkim	KBDC kbdc = sc->kbdc;
3736178019Sjkim	int data[3];
373741016Sdfr
3738178019Sjkim	/* the special sequence to enable the fourth button and the roller. */
3739178019Sjkim	/*
3740178019Sjkim	 * NOTE: for ScrollPoint to respond correctly, the SET_RESOLUTION
3741178019Sjkim	 * must be called exactly three times since the last RESET command
3742178019Sjkim	 * before this sequence. XXX
3743178019Sjkim	 */
3744178019Sjkim	if (!set_mouse_scaling(kbdc, 1))
3745178019Sjkim		return (FALSE);
3746178019Sjkim	if (!mouse_ext_command(kbdc, 0x39) || !mouse_ext_command(kbdc, 0xdb))
3747178019Sjkim		return (FALSE);
3748178019Sjkim	if (get_mouse_status(kbdc, data, 1, 3) < 3)
3749178019Sjkim		return (FALSE);
375041016Sdfr
3751178019Sjkim	/*
3752178019Sjkim	 * PS2++ protocl, packet type 0
3753178019Sjkim	 *
3754178019Sjkim	 *          b7 b6 b5 b4 b3 b2 b1 b0
3755178019Sjkim	 * byte 1:  *  1  p3 p2 1  *  *  *
3756178019Sjkim	 * byte 2:  1  1  p1 p0 m1 m0 1  0
3757178019Sjkim	 * byte 3:  m7 m6 m5 m4 m3 m2 m1 m0
3758178019Sjkim	 *
3759178019Sjkim	 * p3-p0: packet type: 0
3760178019Sjkim	 * m7-m0: model ID: MouseMan+:0x50,
3761178019Sjkim	 *		    FirstMouse+:0x51,
3762178019Sjkim	 *		    ScrollPoint:0x58...
3763178019Sjkim	 */
3764178019Sjkim	/* check constant bits */
3765178019Sjkim	if ((data[0] & MOUSE_PS2PLUS_SYNCMASK) != MOUSE_PS2PLUS_SYNC)
3766178019Sjkim		return (FALSE);
3767178019Sjkim	if ((data[1] & 0xc3) != 0xc2)
3768178019Sjkim		return (FALSE);
3769178019Sjkim	/* check d3-d0 in byte 2 */
3770178019Sjkim	if (!MOUSE_PS2PLUS_CHECKBITS(data))
3771178019Sjkim		return (FALSE);
3772178019Sjkim	/* check p3-p0 */
3773178019Sjkim	if (MOUSE_PS2PLUS_PACKET_TYPE(data) != 0)
3774178019Sjkim		return (FALSE);
377541016Sdfr
3776178019Sjkim	sc->hw.hwid &= 0x00ff;
3777178019Sjkim	sc->hw.hwid |= data[2] << 8;	/* save model ID */
377848778Syokota
3779178019Sjkim	/*
3780178019Sjkim	 * MouseMan+ (or FirstMouse+) is now in its native mode, in which
3781178019Sjkim	 * the wheel and the fourth button events are encoded in the
3782178019Sjkim	 * special data packet. The mouse may be put in the IntelliMouse mode
3783178019Sjkim	 * if it is initialized by the IntelliMouse's method.
3784178019Sjkim	 */
3785178019Sjkim	return (TRUE);
378641016Sdfr}
378741016Sdfr
378858230Syokota/* MS IntelliMouse Explorer */
378958230Syokotastatic int
379058230Syokotaenable_msexplorer(struct psm_softc *sc)
379158230Syokota{
3792178019Sjkim	static u_char rate0[] = { 200, 100, 80, };
3793178019Sjkim	static u_char rate1[] = { 200, 200, 80, };
3794178019Sjkim	KBDC kbdc = sc->kbdc;
3795178019Sjkim	int id;
3796178019Sjkim	int i;
379758230Syokota
3798178019Sjkim	/*
3799178019Sjkim	 * This is needed for at least A4Tech X-7xx mice - they do not go
3800178019Sjkim	 * straight to Explorer mode, but need to be set to Intelli mode
3801178019Sjkim	 * first.
3802178019Sjkim	 */
3803178019Sjkim	enable_msintelli(sc);
3804176554Srink
3805178019Sjkim	/* the special sequence to enable the extra buttons and the roller. */
3806178019Sjkim	for (i = 0; i < sizeof(rate1)/sizeof(rate1[0]); ++i)
3807178019Sjkim		if (set_mouse_sampling_rate(kbdc, rate1[i]) != rate1[i])
3808178019Sjkim			return (FALSE);
3809178019Sjkim	/* the device will give the genuine ID only after the above sequence */
3810178019Sjkim	id = get_aux_id(kbdc);
3811178019Sjkim	if (id != PSM_EXPLORER_ID)
3812178019Sjkim		return (FALSE);
381369438Syokota
3814178019Sjkim	sc->hw.hwid = id;
3815178019Sjkim	sc->hw.buttons = 5;		/* IntelliMouse Explorer XXX */
381669438Syokota
3817178019Sjkim	/*
3818178019Sjkim	 * XXX: this is a kludge to fool some KVM switch products
3819178019Sjkim	 * which think they are clever enough to know the 4-byte IntelliMouse
3820178019Sjkim	 * protocol, and assume any other protocols use 3-byte packets.
3821178019Sjkim	 * They don't convey 4-byte data packets from the IntelliMouse Explorer
3822178019Sjkim	 * correctly to the host computer because of this!
3823178019Sjkim	 * The following sequence is actually IntelliMouse's "wake up"
3824178019Sjkim	 * sequence; it will make the KVM think the mouse is IntelliMouse
3825178019Sjkim	 * when it is in fact IntelliMouse Explorer.
3826178019Sjkim	 */
3827178019Sjkim	for (i = 0; i < sizeof(rate0)/sizeof(rate0[0]); ++i)
3828178019Sjkim		if (set_mouse_sampling_rate(kbdc, rate0[i]) != rate0[i])
3829178019Sjkim			break;
3830178019Sjkim	id = get_aux_id(kbdc);
383158923Syokota
3832178019Sjkim	return (TRUE);
383358230Syokota}
383458230Syokota
383541016Sdfr/* MS IntelliMouse */
383641016Sdfrstatic int
383741016Sdfrenable_msintelli(struct psm_softc *sc)
383841016Sdfr{
3839178019Sjkim	/*
3840178019Sjkim	 * Logitech MouseMan+ and FirstMouse+ will also respond to this
3841178019Sjkim	 * probe routine and act like IntelliMouse.
3842178019Sjkim	 */
384341016Sdfr
3844178019Sjkim	static u_char rate[] = { 200, 100, 80, };
3845178019Sjkim	KBDC kbdc = sc->kbdc;
3846178019Sjkim	int id;
3847178019Sjkim	int i;
384841016Sdfr
3849178019Sjkim	/* the special sequence to enable the third button and the roller. */
3850178019Sjkim	for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i)
3851178019Sjkim		if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i])
3852178019Sjkim			return (FALSE);
3853178019Sjkim	/* the device will give the genuine ID only after the above sequence */
3854178019Sjkim	id = get_aux_id(kbdc);
3855178019Sjkim	if (id != PSM_INTELLI_ID)
3856178019Sjkim		return (FALSE);
385741016Sdfr
3858178019Sjkim	sc->hw.hwid = id;
3859178019Sjkim	sc->hw.buttons = 3;
386041016Sdfr
3861178019Sjkim	return (TRUE);
386241016Sdfr}
386341016Sdfr
386458230Syokota/* A4 Tech 4D Mouse */
386558230Syokotastatic int
386658230Syokotaenable_4dmouse(struct psm_softc *sc)
386758230Syokota{
3868178019Sjkim	/*
3869178019Sjkim	 * Newer wheel mice from A4 Tech may use the 4D+ protocol.
3870178019Sjkim	 */
387158230Syokota
3872178019Sjkim	static u_char rate[] = { 200, 100, 80, 60, 40, 20 };
3873178019Sjkim	KBDC kbdc = sc->kbdc;
3874178019Sjkim	int id;
3875178019Sjkim	int i;
387658230Syokota
3877178019Sjkim	for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i)
3878178019Sjkim		if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i])
3879178019Sjkim			return (FALSE);
3880178019Sjkim	id = get_aux_id(kbdc);
3881178019Sjkim	/*
3882178019Sjkim	 * WinEasy 4D, 4 Way Scroll 4D: 6
3883178019Sjkim	 * Cable-Free 4D: 8 (4DPLUS)
3884178019Sjkim	 * WinBest 4D+, 4 Way Scroll 4D+: 8 (4DPLUS)
3885178019Sjkim	 */
3886178019Sjkim	if (id != PSM_4DMOUSE_ID)
3887178019Sjkim		return (FALSE);
388858230Syokota
3889178019Sjkim	sc->hw.hwid = id;
3890178019Sjkim	sc->hw.buttons = 3;		/* XXX some 4D mice have 4? */
389158230Syokota
3892178019Sjkim	return (TRUE);
389358230Syokota}
389458230Syokota
389558230Syokota/* A4 Tech 4D+ Mouse */
389658230Syokotastatic int
389758230Syokotaenable_4dplus(struct psm_softc *sc)
389858230Syokota{
3899178019Sjkim	/*
3900178019Sjkim	 * Newer wheel mice from A4 Tech seem to use this protocol.
3901178019Sjkim	 * Older models are recognized as either 4D Mouse or IntelliMouse.
3902178019Sjkim	 */
3903178019Sjkim	KBDC kbdc = sc->kbdc;
3904178019Sjkim	int id;
390558230Syokota
3906178019Sjkim	/*
3907178019Sjkim	 * enable_4dmouse() already issued the following ID sequence...
3908178019Sjkim	static u_char rate[] = { 200, 100, 80, 60, 40, 20 };
3909178019Sjkim	int i;
391058230Syokota
3911178019Sjkim	for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i)
3912178019Sjkim		if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i])
3913178019Sjkim			return (FALSE);
3914178019Sjkim	*/
391558230Syokota
3916178019Sjkim	id = get_aux_id(kbdc);
3917178019Sjkim	switch (id) {
3918178019Sjkim	case PSM_4DPLUS_ID:
3919178019Sjkim		sc->hw.buttons = 4;
3920178019Sjkim		break;
3921178019Sjkim	case PSM_4DPLUS_RFSW35_ID:
3922178019Sjkim		sc->hw.buttons = 3;
3923178019Sjkim		break;
3924178019Sjkim	default:
3925178019Sjkim		return (FALSE);
3926178019Sjkim	}
392758230Syokota
3928178019Sjkim	sc->hw.hwid = id;
392958230Syokota
3930178019Sjkim	return (TRUE);
393158230Syokota}
393258230Syokota
3933132865Snjl/* Synaptics Touchpad */
3934132865Snjlstatic int
3935183888Sdumbbellsynaptics_sysctl(SYSCTL_HANDLER_ARGS)
3936132865Snjl{
3937183888Sdumbbell	int error, arg;
3938132865Snjl
3939183888Sdumbbell	/* Read the current value. */
3940183888Sdumbbell	arg = *(int *)oidp->oid_arg1;
3941183888Sdumbbell	error = sysctl_handle_int(oidp, &arg, 0, req);
3942135945Sphilip
3943183888Sdumbbell	/* Sanity check. */
3944183888Sdumbbell	if (error || !req->newptr)
3945183888Sdumbbell		return (error);
3946183888Sdumbbell
3947183888Sdumbbell	/*
3948183888Sdumbbell	 * Check that the new value is in the concerned node's range
3949183888Sdumbbell	 * of values.
3950183888Sdumbbell	 */
3951183888Sdumbbell	switch (oidp->oid_arg2) {
3952183888Sdumbbell	case SYNAPTICS_SYSCTL_MIN_PRESSURE:
3953183888Sdumbbell	case SYNAPTICS_SYSCTL_MAX_PRESSURE:
3954183888Sdumbbell		if (arg < 0 || arg > 255)
3955183888Sdumbbell			return (EINVAL);
3956183888Sdumbbell		break;
3957183888Sdumbbell	case SYNAPTICS_SYSCTL_MAX_WIDTH:
3958183888Sdumbbell		if (arg < 4 || arg > 15)
3959183888Sdumbbell			return (EINVAL);
3960183888Sdumbbell		break;
3961183888Sdumbbell	case SYNAPTICS_SYSCTL_MARGIN_TOP:
3962183888Sdumbbell	case SYNAPTICS_SYSCTL_MARGIN_RIGHT:
3963183888Sdumbbell	case SYNAPTICS_SYSCTL_MARGIN_BOTTOM:
3964183888Sdumbbell	case SYNAPTICS_SYSCTL_MARGIN_LEFT:
3965183888Sdumbbell	case SYNAPTICS_SYSCTL_NA_TOP:
3966183888Sdumbbell	case SYNAPTICS_SYSCTL_NA_RIGHT:
3967183888Sdumbbell	case SYNAPTICS_SYSCTL_NA_BOTTOM:
3968183888Sdumbbell	case SYNAPTICS_SYSCTL_NA_LEFT:
3969183888Sdumbbell		if (arg < 0 || arg > 6143)
3970183888Sdumbbell			return (EINVAL);
3971183888Sdumbbell		break;
3972183888Sdumbbell	case SYNAPTICS_SYSCTL_WINDOW_MIN:
3973183888Sdumbbell	case SYNAPTICS_SYSCTL_WINDOW_MAX:
3974183888Sdumbbell	case SYNAPTICS_SYSCTL_TAP_MIN_QUEUE:
3975183888Sdumbbell		if (arg < 1 || arg > SYNAPTICS_PACKETQUEUE)
3976183888Sdumbbell			return (EINVAL);
3977183888Sdumbbell		break;
3978183888Sdumbbell	case SYNAPTICS_SYSCTL_MULTIPLICATOR:
3979183888Sdumbbell	case SYNAPTICS_SYSCTL_WEIGHT_CURRENT:
3980183888Sdumbbell	case SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS:
3981183888Sdumbbell	case SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS_NA:
3982183888Sdumbbell	case SYNAPTICS_SYSCTL_WEIGHT_LEN_SQUARED:
3983183888Sdumbbell	case SYNAPTICS_SYSCTL_DIV_MIN:
3984183888Sdumbbell	case SYNAPTICS_SYSCTL_DIV_MAX:
3985183888Sdumbbell	case SYNAPTICS_SYSCTL_DIV_MAX_NA:
3986183888Sdumbbell	case SYNAPTICS_SYSCTL_DIV_LEN:
3987183888Sdumbbell	case SYNAPTICS_SYSCTL_VSCROLL_DIV_MIN:
3988183888Sdumbbell	case SYNAPTICS_SYSCTL_VSCROLL_DIV_MAX:
3989183888Sdumbbell		if (arg < 1)
3990183888Sdumbbell			return (EINVAL);
3991183888Sdumbbell		break;
3992183888Sdumbbell	case SYNAPTICS_SYSCTL_TAP_MAX_DELTA:
3993183888Sdumbbell	case SYNAPTICS_SYSCTL_TAPHOLD_TIMEOUT:
3994183888Sdumbbell	case SYNAPTICS_SYSCTL_VSCROLL_MIN_DELTA:
3995183888Sdumbbell		if (arg < 0)
3996183888Sdumbbell			return (EINVAL);
3997183888Sdumbbell		break;
3998183888Sdumbbell	case SYNAPTICS_SYSCTL_VSCROLL_HOR_AREA:
3999183888Sdumbbell	case SYNAPTICS_SYSCTL_VSCROLL_VER_AREA:
4000183888Sdumbbell		if (arg < -6143 || arg > 6143)
4001183888Sdumbbell			return (EINVAL);
4002183888Sdumbbell		break;
4003183888Sdumbbell	default:
4004183888Sdumbbell		return (EINVAL);
4005183888Sdumbbell	}
4006183888Sdumbbell
4007183888Sdumbbell	/* Update. */
4008183888Sdumbbell	*(int *)oidp->oid_arg1 = arg;
4009183888Sdumbbell
4010183888Sdumbbell	return (error);
4011183888Sdumbbell}
4012183888Sdumbbell
4013183888Sdumbbellstatic void
4014183888Sdumbbellsynaptics_sysctl_create_tree(struct psm_softc *sc)
4015183888Sdumbbell{
4016183888Sdumbbell
4017186218Sdumbbell	if (sc->syninfo.sysctl_tree != NULL)
4018186218Sdumbbell		return;
4019186218Sdumbbell
4020178019Sjkim	/* Attach extra synaptics sysctl nodes under hw.psm.synaptics */
4021178019Sjkim	sysctl_ctx_init(&sc->syninfo.sysctl_ctx);
4022178019Sjkim	sc->syninfo.sysctl_tree = SYSCTL_ADD_NODE(&sc->syninfo.sysctl_ctx,
4023178019Sjkim	    SYSCTL_STATIC_CHILDREN(_hw_psm), OID_AUTO, "synaptics", CTLFLAG_RD,
4024178019Sjkim	    0, "Synaptics TouchPad");
4025139982Sphilip
4026183888Sdumbbell	/* hw.psm.synaptics.directional_scrolls. */
4027178019Sjkim	sc->syninfo.directional_scrolls = 1;
4028178019Sjkim	SYSCTL_ADD_INT(&sc->syninfo.sysctl_ctx,
4029178019Sjkim	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4030183888Sdumbbell	    "directional_scrolls", CTLFLAG_RW|CTLFLAG_ANYBODY,
4031178019Sjkim	    &sc->syninfo.directional_scrolls, 0,
4032183888Sdumbbell	    "Enable hardware scrolling pad (if non-zero) or register it as "
4033183888Sdumbbell	    "a middle-click (if 0)");
4034139982Sphilip
4035183888Sdumbbell	/* hw.psm.synaptics.min_pressure. */
4036183888Sdumbbell	sc->syninfo.min_pressure = 16;
4037183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4038178019Sjkim	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4039183888Sdumbbell	    "min_pressure", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4040183888Sdumbbell	    &sc->syninfo.min_pressure, SYNAPTICS_SYSCTL_MIN_PRESSURE,
4041183888Sdumbbell	    synaptics_sysctl, "I",
4042183888Sdumbbell	    "Minimum pressure required to start an action");
4043139982Sphilip
4044183888Sdumbbell	/* hw.psm.synaptics.max_pressure. */
4045183888Sdumbbell	sc->syninfo.max_pressure = 220;
4046183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4047178019Sjkim	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4048183888Sdumbbell	    "max_pressure", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4049183888Sdumbbell	    &sc->syninfo.max_pressure, SYNAPTICS_SYSCTL_MAX_PRESSURE,
4050183888Sdumbbell	    synaptics_sysctl, "I",
4051183888Sdumbbell	    "Maximum pressure to detect palm");
4052139982Sphilip
4053183888Sdumbbell	/* hw.psm.synaptics.max_width. */
4054183888Sdumbbell	sc->syninfo.max_width = 10;
4055183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4056178019Sjkim	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4057183888Sdumbbell	    "max_width", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4058183888Sdumbbell	    &sc->syninfo.max_width, SYNAPTICS_SYSCTL_MAX_WIDTH,
4059183888Sdumbbell	    synaptics_sysctl, "I",
4060183888Sdumbbell	    "Maximum finger width to detect palm");
4061132865Snjl
4062183888Sdumbbell	/* hw.psm.synaptics.top_margin. */
4063183888Sdumbbell	sc->syninfo.margin_top = 200;
4064183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4065183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4066183888Sdumbbell	    "margin_top", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4067183888Sdumbbell	    &sc->syninfo.margin_top, SYNAPTICS_SYSCTL_MARGIN_TOP,
4068183888Sdumbbell	    synaptics_sysctl, "I",
4069183888Sdumbbell	    "Top margin");
4070183888Sdumbbell
4071183888Sdumbbell	/* hw.psm.synaptics.right_margin. */
4072183888Sdumbbell	sc->syninfo.margin_right = 200;
4073183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4074183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4075183888Sdumbbell	    "margin_right", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4076183888Sdumbbell	    &sc->syninfo.margin_right, SYNAPTICS_SYSCTL_MARGIN_RIGHT,
4077183888Sdumbbell	    synaptics_sysctl, "I",
4078183888Sdumbbell	    "Right margin");
4079183888Sdumbbell
4080183888Sdumbbell	/* hw.psm.synaptics.bottom_margin. */
4081183888Sdumbbell	sc->syninfo.margin_bottom = 200;
4082183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4083183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4084183888Sdumbbell	    "margin_bottom", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4085183888Sdumbbell	    &sc->syninfo.margin_bottom, SYNAPTICS_SYSCTL_MARGIN_BOTTOM,
4086183888Sdumbbell	    synaptics_sysctl, "I",
4087183888Sdumbbell	    "Bottom margin");
4088183888Sdumbbell
4089183888Sdumbbell	/* hw.psm.synaptics.left_margin. */
4090183888Sdumbbell	sc->syninfo.margin_left = 200;
4091183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4092183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4093183888Sdumbbell	    "margin_left", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4094183888Sdumbbell	    &sc->syninfo.margin_left, SYNAPTICS_SYSCTL_MARGIN_LEFT,
4095183888Sdumbbell	    synaptics_sysctl, "I",
4096183888Sdumbbell	    "Left margin");
4097183888Sdumbbell
4098183888Sdumbbell	/* hw.psm.synaptics.na_top. */
4099183888Sdumbbell	sc->syninfo.na_top = 1783;
4100183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4101183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4102183888Sdumbbell	    "na_top", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4103183888Sdumbbell	    &sc->syninfo.na_top, SYNAPTICS_SYSCTL_NA_TOP,
4104183888Sdumbbell	    synaptics_sysctl, "I",
4105183888Sdumbbell	    "Top noisy area, where weight_previous_na is used instead "
4106183888Sdumbbell	    "of weight_previous");
4107183888Sdumbbell
4108183888Sdumbbell	/* hw.psm.synaptics.na_right. */
4109183888Sdumbbell	sc->syninfo.na_right = 563;
4110183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4111183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4112183888Sdumbbell	    "na_right", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4113183888Sdumbbell	    &sc->syninfo.na_right, SYNAPTICS_SYSCTL_NA_RIGHT,
4114183888Sdumbbell	    synaptics_sysctl, "I",
4115183888Sdumbbell	    "Right noisy area, where weight_previous_na is used instead "
4116183888Sdumbbell	    "of weight_previous");
4117183888Sdumbbell
4118183888Sdumbbell	/* hw.psm.synaptics.na_bottom. */
4119183888Sdumbbell	sc->syninfo.na_bottom = 1408;
4120183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4121183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4122183888Sdumbbell	    "na_bottom", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4123183888Sdumbbell	    &sc->syninfo.na_bottom, SYNAPTICS_SYSCTL_NA_BOTTOM,
4124183888Sdumbbell	    synaptics_sysctl, "I",
4125183888Sdumbbell	    "Bottom noisy area, where weight_previous_na is used instead "
4126183888Sdumbbell	    "of weight_previous");
4127183888Sdumbbell
4128183888Sdumbbell	/* hw.psm.synaptics.na_left. */
4129183888Sdumbbell	sc->syninfo.na_left = 1600;
4130183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4131183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4132183888Sdumbbell	    "na_left", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4133183888Sdumbbell	    &sc->syninfo.na_left, SYNAPTICS_SYSCTL_NA_LEFT,
4134183888Sdumbbell	    synaptics_sysctl, "I",
4135183888Sdumbbell	    "Left noisy area, where weight_previous_na is used instead "
4136183888Sdumbbell	    "of weight_previous");
4137183888Sdumbbell
4138183888Sdumbbell	/* hw.psm.synaptics.window_min. */
4139183888Sdumbbell	sc->syninfo.window_min = 4;
4140183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4141183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4142183888Sdumbbell	    "window_min", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4143183888Sdumbbell	    &sc->syninfo.window_min, SYNAPTICS_SYSCTL_WINDOW_MIN,
4144183888Sdumbbell	    synaptics_sysctl, "I",
4145183888Sdumbbell	    "Minimum window size to start an action");
4146183888Sdumbbell
4147183888Sdumbbell	/* hw.psm.synaptics.window_max. */
4148183888Sdumbbell	sc->syninfo.window_max = 10;
4149183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4150183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4151183888Sdumbbell	    "window_max", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4152183888Sdumbbell	    &sc->syninfo.window_max, SYNAPTICS_SYSCTL_WINDOW_MAX,
4153183888Sdumbbell	    synaptics_sysctl, "I",
4154183888Sdumbbell	    "Maximum window size");
4155183888Sdumbbell
4156183888Sdumbbell	/* hw.psm.synaptics.multiplicator. */
4157183888Sdumbbell	sc->syninfo.multiplicator = 10000;
4158183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4159183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4160183888Sdumbbell	    "multiplicator", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4161183888Sdumbbell	    &sc->syninfo.multiplicator, SYNAPTICS_SYSCTL_MULTIPLICATOR,
4162183888Sdumbbell	    synaptics_sysctl, "I",
4163183888Sdumbbell	    "Multiplicator to increase precision in averages and divisions");
4164183888Sdumbbell
4165183888Sdumbbell	/* hw.psm.synaptics.weight_current. */
4166183888Sdumbbell	sc->syninfo.weight_current = 3;
4167183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4168183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4169183888Sdumbbell	    "weight_current", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4170183888Sdumbbell	    &sc->syninfo.weight_current, SYNAPTICS_SYSCTL_WEIGHT_CURRENT,
4171183888Sdumbbell	    synaptics_sysctl, "I",
4172183888Sdumbbell	    "Weight of the current movement in the new average");
4173183888Sdumbbell
4174183888Sdumbbell	/* hw.psm.synaptics.weight_previous. */
4175183888Sdumbbell	sc->syninfo.weight_previous = 6;
4176183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4177183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4178183888Sdumbbell	    "weight_previous", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4179183888Sdumbbell	    &sc->syninfo.weight_previous, SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS,
4180183888Sdumbbell	    synaptics_sysctl, "I",
4181183888Sdumbbell	    "Weight of the previous average");
4182183888Sdumbbell
4183183888Sdumbbell	/* hw.psm.synaptics.weight_previous_na. */
4184183888Sdumbbell	sc->syninfo.weight_previous_na = 20;
4185183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4186183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4187183888Sdumbbell	    "weight_previous_na", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4188183888Sdumbbell	    &sc->syninfo.weight_previous_na,
4189183888Sdumbbell	    SYNAPTICS_SYSCTL_WEIGHT_PREVIOUS_NA,
4190183888Sdumbbell	    synaptics_sysctl, "I",
4191183888Sdumbbell	    "Weight of the previous average (inside the noisy area)");
4192183888Sdumbbell
4193183888Sdumbbell	/* hw.psm.synaptics.weight_len_squared. */
4194183888Sdumbbell	sc->syninfo.weight_len_squared = 2000;
4195183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4196183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4197183888Sdumbbell	    "weight_len_squared", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4198183888Sdumbbell	    &sc->syninfo.weight_len_squared,
4199183888Sdumbbell	    SYNAPTICS_SYSCTL_WEIGHT_LEN_SQUARED,
4200183888Sdumbbell	    synaptics_sysctl, "I",
4201183888Sdumbbell	    "Length (squared) of segments where weight_previous "
4202183888Sdumbbell	    "starts to decrease");
4203183888Sdumbbell
4204183888Sdumbbell	/* hw.psm.synaptics.div_min. */
4205183888Sdumbbell	sc->syninfo.div_min = 9;
4206183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4207183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4208183888Sdumbbell	    "div_min", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4209183888Sdumbbell	    &sc->syninfo.div_min, SYNAPTICS_SYSCTL_DIV_MIN,
4210183888Sdumbbell	    synaptics_sysctl, "I",
4211183888Sdumbbell	    "Divisor for fast movements");
4212183888Sdumbbell
4213183888Sdumbbell	/* hw.psm.synaptics.div_max. */
4214183888Sdumbbell	sc->syninfo.div_max = 17;
4215183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4216183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4217183888Sdumbbell	    "div_max", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4218183888Sdumbbell	    &sc->syninfo.div_max, SYNAPTICS_SYSCTL_DIV_MAX,
4219183888Sdumbbell	    synaptics_sysctl, "I",
4220183888Sdumbbell	    "Divisor for slow movements");
4221183888Sdumbbell
4222183888Sdumbbell	/* hw.psm.synaptics.div_max_na. */
4223183888Sdumbbell	sc->syninfo.div_max_na = 30;
4224183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4225183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4226183888Sdumbbell	    "div_max_na", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4227183888Sdumbbell	    &sc->syninfo.div_max_na, SYNAPTICS_SYSCTL_DIV_MAX_NA,
4228183888Sdumbbell	    synaptics_sysctl, "I",
4229183888Sdumbbell	    "Divisor with slow movements (inside the noisy area)");
4230183888Sdumbbell
4231183888Sdumbbell	/* hw.psm.synaptics.div_len. */
4232183888Sdumbbell	sc->syninfo.div_len = 100;
4233183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4234183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4235183888Sdumbbell	    "div_len", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4236183888Sdumbbell	    &sc->syninfo.div_len, SYNAPTICS_SYSCTL_DIV_LEN,
4237183888Sdumbbell	    synaptics_sysctl, "I",
4238183888Sdumbbell	    "Length of segments where div_max starts to decrease");
4239183888Sdumbbell
4240183888Sdumbbell	/* hw.psm.synaptics.tap_max_delta. */
4241183888Sdumbbell	sc->syninfo.tap_max_delta = 80;
4242183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4243183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4244183888Sdumbbell	    "tap_max_delta", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4245183888Sdumbbell	    &sc->syninfo.tap_max_delta, SYNAPTICS_SYSCTL_TAP_MAX_DELTA,
4246183888Sdumbbell	    synaptics_sysctl, "I",
4247183888Sdumbbell	    "Length of segments above which a tap is ignored");
4248183888Sdumbbell
4249183888Sdumbbell	/* hw.psm.synaptics.tap_min_queue. */
4250183888Sdumbbell	sc->syninfo.tap_min_queue = 2;
4251183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4252183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4253183888Sdumbbell	    "tap_min_queue", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4254183888Sdumbbell	    &sc->syninfo.tap_min_queue, SYNAPTICS_SYSCTL_TAP_MIN_QUEUE,
4255183888Sdumbbell	    synaptics_sysctl, "I",
4256183888Sdumbbell	    "Number of packets required to consider a tap");
4257183888Sdumbbell
4258183888Sdumbbell	/* hw.psm.synaptics.taphold_timeout. */
4259183888Sdumbbell	sc->synaction.in_taphold = 0;
4260183888Sdumbbell	sc->syninfo.taphold_timeout = tap_timeout;
4261183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4262183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4263183888Sdumbbell	    "taphold_timeout", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4264183888Sdumbbell	    &sc->syninfo.taphold_timeout, SYNAPTICS_SYSCTL_TAPHOLD_TIMEOUT,
4265183888Sdumbbell	    synaptics_sysctl, "I",
4266183888Sdumbbell	    "Maximum elapsed time between two taps to consider a tap-hold "
4267183888Sdumbbell	    "action");
4268183888Sdumbbell
4269183888Sdumbbell	/* hw.psm.synaptics.vscroll_hor_area. */
4270183888Sdumbbell	sc->syninfo.vscroll_hor_area = 0; /* 1300 */
4271183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4272183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4273183888Sdumbbell	    "vscroll_hor_area", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4274183888Sdumbbell	    &sc->syninfo.vscroll_hor_area, SYNAPTICS_SYSCTL_VSCROLL_HOR_AREA,
4275183888Sdumbbell	    synaptics_sysctl, "I",
4276183888Sdumbbell	    "Area reserved for horizontal virtual scrolling");
4277183888Sdumbbell
4278183888Sdumbbell	/* hw.psm.synaptics.vscroll_ver_area. */
4279183888Sdumbbell	sc->syninfo.vscroll_ver_area = -600;
4280183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4281183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4282183888Sdumbbell	    "vscroll_ver_area", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4283183888Sdumbbell	    &sc->syninfo.vscroll_ver_area, SYNAPTICS_SYSCTL_VSCROLL_VER_AREA,
4284183888Sdumbbell	    synaptics_sysctl, "I",
4285183888Sdumbbell	    "Area reserved for vertical virtual scrolling");
4286183888Sdumbbell
4287183888Sdumbbell	/* hw.psm.synaptics.vscroll_min_delta. */
4288183888Sdumbbell	sc->syninfo.vscroll_min_delta = 50;
4289183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4290183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4291183888Sdumbbell	    "vscroll_min_delta", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4292183888Sdumbbell	    &sc->syninfo.vscroll_min_delta,
4293183888Sdumbbell	    SYNAPTICS_SYSCTL_VSCROLL_MIN_DELTA,
4294183888Sdumbbell	    synaptics_sysctl, "I",
4295183888Sdumbbell	    "Minimum movement to consider virtual scrolling");
4296183888Sdumbbell
4297183888Sdumbbell	/* hw.psm.synaptics.vscroll_div_min. */
4298183888Sdumbbell	sc->syninfo.vscroll_div_min = 100;
4299183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4300183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4301183888Sdumbbell	    "vscroll_div_min", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4302183888Sdumbbell	    &sc->syninfo.vscroll_div_min, SYNAPTICS_SYSCTL_VSCROLL_DIV_MIN,
4303183888Sdumbbell	    synaptics_sysctl, "I",
4304183888Sdumbbell	    "Divisor for fast scrolling");
4305183888Sdumbbell
4306183888Sdumbbell	/* hw.psm.synaptics.vscroll_div_min. */
4307183888Sdumbbell	sc->syninfo.vscroll_div_max = 150;
4308183888Sdumbbell	SYSCTL_ADD_PROC(&sc->syninfo.sysctl_ctx,
4309183888Sdumbbell	    SYSCTL_CHILDREN(sc->syninfo.sysctl_tree), OID_AUTO,
4310183888Sdumbbell	    "vscroll_div_max", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
4311183888Sdumbbell	    &sc->syninfo.vscroll_div_max, SYNAPTICS_SYSCTL_VSCROLL_DIV_MAX,
4312183888Sdumbbell	    synaptics_sysctl, "I",
4313183888Sdumbbell	    "Divisor for slow scrolling");
4314183888Sdumbbell}
4315183888Sdumbbell
4316183888Sdumbbellstatic int
4317183888Sdumbbellenable_synaptics(struct psm_softc *sc)
4318183888Sdumbbell{
4319183888Sdumbbell	int status[3];
4320183888Sdumbbell	KBDC kbdc;
4321183888Sdumbbell
4322183888Sdumbbell	if (!synaptics_support)
4323183888Sdumbbell		return (FALSE);
4324183888Sdumbbell
4325178019Sjkim	kbdc = sc->kbdc;
4326183888Sdumbbell	VLOG(3, (LOG_DEBUG, "synaptics: BEGIN init\n"));
4327178019Sjkim	sc->hw.buttons = 3;
4328178019Sjkim	sc->squelch = 0;
4329132865Snjl
4330186175Sdumbbell	/*
4331186175Sdumbbell	 * Just to be on the safe side: this avoids troubles with
4332186175Sdumbbell	 * following mouse_ext_command() when the previous command
4333186175Sdumbbell	 * was PSMC_SET_RESOLUTION. Set Scaling has no effect on
4334186175Sdumbbell	 * Synaptics Touchpad behaviour.
4335186175Sdumbbell	 */
4336178019Sjkim	set_mouse_scaling(kbdc, 1);
4337133295Sphilip
4338186175Sdumbbell	/* Identify the Touchpad version. */
4339178019Sjkim	if (mouse_ext_command(kbdc, 0) == 0)
4340178019Sjkim		return (FALSE);
4341178019Sjkim	if (get_mouse_status(kbdc, status, 0, 3) != 3)
4342178019Sjkim		return (FALSE);
4343178019Sjkim	if (status[1] != 0x47)
4344178019Sjkim		return (FALSE);
4345133295Sphilip
4346178019Sjkim	sc->synhw.infoMinor = status[0];
4347178019Sjkim	sc->synhw.infoMajor = status[2] & 0x0f;
4348132865Snjl
4349178019Sjkim	if (verbose >= 2)
4350178019Sjkim		printf("Synaptics Touchpad v%d.%d\n", sc->synhw.infoMajor,
4351178019Sjkim		    sc->synhw.infoMinor);
4352132865Snjl
4353178019Sjkim	if (sc->synhw.infoMajor < 4) {
4354178019Sjkim		printf("  Unsupported (pre-v4) Touchpad detected\n");
4355178019Sjkim		return (FALSE);
4356178019Sjkim	}
4357133295Sphilip
4358186175Sdumbbell	/* Get the Touchpad model information. */
4359178019Sjkim	if (mouse_ext_command(kbdc, 3) == 0)
4360178019Sjkim		return (FALSE);
4361178019Sjkim	if (get_mouse_status(kbdc, status, 0, 3) != 3)
4362178019Sjkim		return (FALSE);
4363178019Sjkim	if ((status[1] & 0x01) != 0) {
4364178019Sjkim		printf("  Failed to read model information\n");
4365178019Sjkim		return (FALSE);
4366178019Sjkim	}
4367132865Snjl
4368178019Sjkim	sc->synhw.infoRot180   = (status[0] & 0x80) >> 7;
4369178019Sjkim	sc->synhw.infoPortrait = (status[0] & 0x40) >> 6;
4370178019Sjkim	sc->synhw.infoSensor   =  status[0] & 0x3f;
4371178019Sjkim	sc->synhw.infoHardware = (status[1] & 0xfe) >> 1;
4372178019Sjkim	sc->synhw.infoNewAbs   = (status[2] & 0x80) >> 7;
4373178019Sjkim	sc->synhw.capPen       = (status[2] & 0x40) >> 6;
4374178019Sjkim	sc->synhw.infoSimplC   = (status[2] & 0x20) >> 5;
4375178019Sjkim	sc->synhw.infoGeometry =  status[2] & 0x0f;
4376132865Snjl
4377133295Sphilip	if (verbose >= 2) {
4378178019Sjkim		printf("  Model information:\n");
4379178019Sjkim		printf("   infoRot180: %d\n", sc->synhw.infoRot180);
4380178019Sjkim		printf("   infoPortrait: %d\n", sc->synhw.infoPortrait);
4381178019Sjkim		printf("   infoSensor: %d\n", sc->synhw.infoSensor);
4382178019Sjkim		printf("   infoHardware: %d\n", sc->synhw.infoHardware);
4383178019Sjkim		printf("   infoNewAbs: %d\n", sc->synhw.infoNewAbs);
4384178019Sjkim		printf("   capPen: %d\n", sc->synhw.capPen);
4385178019Sjkim		printf("   infoSimplC: %d\n", sc->synhw.infoSimplC);
4386178019Sjkim		printf("   infoGeometry: %d\n", sc->synhw.infoGeometry);
4387133295Sphilip	}
4388139982Sphilip
4389186175Sdumbbell	/* Read the extended capability bits. */
4390178019Sjkim	if (mouse_ext_command(kbdc, 2) == 0)
4391178019Sjkim		return (FALSE);
4392178019Sjkim	if (get_mouse_status(kbdc, status, 0, 3) != 3)
4393178019Sjkim		return (FALSE);
4394178019Sjkim	if (status[1] != 0x47) {
4395178019Sjkim		printf("  Failed to read extended capability bits\n");
4396178019Sjkim		return (FALSE);
4397178019Sjkim	}
4398178019Sjkim
4399186175Sdumbbell	/* Set the different capabilities when they exist. */
4400178019Sjkim	if ((status[0] & 0x80) >> 7) {
4401178019Sjkim		sc->synhw.capExtended    = (status[0] & 0x80) >> 7;
4402178019Sjkim		sc->synhw.capPassthrough = (status[2] & 0x80) >> 7;
4403178019Sjkim		sc->synhw.capSleep       = (status[2] & 0x10) >> 4;
4404178019Sjkim		sc->synhw.capFourButtons = (status[2] & 0x08) >> 3;
4405178019Sjkim		sc->synhw.capMultiFinger = (status[2] & 0x02) >> 1;
4406178019Sjkim		sc->synhw.capPalmDetect  = (status[2] & 0x01);
4407178019Sjkim
4408178019Sjkim		if (verbose >= 2) {
4409178019Sjkim			printf("  Extended capabilities:\n");
4410178019Sjkim			printf("   capExtended: %d\n", sc->synhw.capExtended);
4411178019Sjkim			printf("   capPassthrough: %d\n",
4412178019Sjkim			    sc->synhw.capPassthrough);
4413178019Sjkim			printf("   capSleep: %d\n", sc->synhw.capSleep);
4414178019Sjkim			printf("   capFourButtons: %d\n",
4415178019Sjkim			    sc->synhw.capFourButtons);
4416178019Sjkim			printf("   capMultiFinger: %d\n",
4417178019Sjkim			    sc->synhw.capMultiFinger);
4418178019Sjkim			printf("   capPalmDetect: %d\n",
4419178019Sjkim			    sc->synhw.capPalmDetect);
4420178019Sjkim		}
4421178019Sjkim
4422178019Sjkim		/*
4423186175Sdumbbell		 * If we have bits set in status[0] & 0x70, then we can load
4424186175Sdumbbell		 * more information about buttons using query 0x09.
4425178019Sjkim		 */
4426178019Sjkim		if (status[0] & 0x70) {
4427178019Sjkim			if (mouse_ext_command(kbdc, 0x09) == 0)
4428178019Sjkim				return (FALSE);
4429178019Sjkim			if (get_mouse_status(kbdc, status, 0, 3) != 3)
4430178019Sjkim				return (FALSE);
4431178019Sjkim			sc->hw.buttons = ((status[1] & 0xf0) >> 4) + 3;
4432178019Sjkim			if (verbose >= 2)
4433178019Sjkim				printf("  Additional Buttons: %d\n",
4434178019Sjkim				    sc->hw.buttons -3);
4435178019Sjkim		}
4436178019Sjkim	} else {
4437178019Sjkim		sc->synhw.capExtended = 0;
4438178019Sjkim
4439178019Sjkim		if (verbose >= 2)
4440178019Sjkim			printf("  No extended capabilities\n");
4441178019Sjkim	}
4442178019Sjkim
4443139982Sphilip	/*
4444186175Sdumbbell	 * Read the mode byte.
4445178019Sjkim	 *
4446178019Sjkim	 * XXX: Note the Synaptics documentation also defines the first
4447178019Sjkim	 * byte of the response to this query to be a constant 0x3b, this
4448178019Sjkim	 * does not appear to be true for Touchpads with guest devices.
4449139982Sphilip	 */
4450178019Sjkim	if (mouse_ext_command(kbdc, 1) == 0)
4451139982Sphilip		return (FALSE);
4452178019Sjkim	if (get_mouse_status(kbdc, status, 0, 3) != 3)
4453139982Sphilip		return (FALSE);
4454178019Sjkim	if (status[1] != 0x47) {
4455178019Sjkim		printf("  Failed to read mode byte\n");
4456178019Sjkim		return (FALSE);
4457139982Sphilip	}
4458139982Sphilip
4459186175Sdumbbell	/* Set the mode byte; request wmode where available. */
4460178019Sjkim	if (sc->synhw.capExtended)
4461178019Sjkim		mouse_ext_command(kbdc, 0xc1);
4462178019Sjkim	else
4463178019Sjkim		mouse_ext_command(kbdc, 0xc0);
4464133295Sphilip
4465186175Sdumbbell	/* "Commit" the Set Mode Byte command sent above. */
4466178019Sjkim	set_mouse_sampling_rate(kbdc, 20);
4467132865Snjl
4468178019Sjkim	/*
4469178019Sjkim	 * Report the correct number of buttons
4470178019Sjkim	 *
4471178019Sjkim	 * XXX: I'm not sure this is used anywhere.
4472178019Sjkim	 */
4473178019Sjkim	if (sc->synhw.capExtended && sc->synhw.capFourButtons)
4474178019Sjkim		sc->hw.buttons = 4;
4475132865Snjl
4476183888Sdumbbell	VLOG(3, (LOG_DEBUG, "synaptics: END init (%d buttons)\n",
4477183888Sdumbbell	    sc->hw.buttons));
4478183888Sdumbbell
4479183888Sdumbbell	/* Create sysctl tree. */
4480183888Sdumbbell	synaptics_sysctl_create_tree(sc);
4481183888Sdumbbell
4482186218Sdumbbell	/*
4483186218Sdumbbell	 * The touchpad will have to be reinitialized after
4484186218Sdumbbell	 * suspend/resume.
4485186218Sdumbbell	 */
4486186218Sdumbbell	sc->config |= PSM_CONFIG_HOOKRESUME | PSM_CONFIG_INITAFTERSUSPEND;
4487186218Sdumbbell
4488178019Sjkim	return (TRUE);
4489132865Snjl}
4490133295Sphilip
449149965Syokota/* Interlink electronics VersaPad */
449249965Syokotastatic int
449349965Syokotaenable_versapad(struct psm_softc *sc)
449449965Syokota{
4495178019Sjkim	KBDC kbdc = sc->kbdc;
4496178019Sjkim	int data[3];
449749965Syokota
4498178019Sjkim	set_mouse_resolution(kbdc, PSMD_RES_MEDIUM_HIGH); /* set res. 2 */
4499178019Sjkim	set_mouse_sampling_rate(kbdc, 100);		/* set rate 100 */
4500178019Sjkim	set_mouse_scaling(kbdc, 1);			/* set scale 1:1 */
4501178019Sjkim	set_mouse_scaling(kbdc, 1);			/* set scale 1:1 */
4502178019Sjkim	set_mouse_scaling(kbdc, 1);			/* set scale 1:1 */
4503178019Sjkim	set_mouse_scaling(kbdc, 1);			/* set scale 1:1 */
4504178019Sjkim	if (get_mouse_status(kbdc, data, 0, 3) < 3)	/* get status */
4505178019Sjkim		return (FALSE);
4506178019Sjkim	if (data[2] != 0xa || data[1] != 0 )	/* rate == 0xa && res. == 0 */
4507178019Sjkim		return (FALSE);
4508178019Sjkim	set_mouse_scaling(kbdc, 1);			/* set scale 1:1 */
450949965Syokota
4510178019Sjkim	sc->config |= PSM_CONFIG_HOOKRESUME | PSM_CONFIG_INITAFTERSUSPEND;
451158230Syokota
4512178019Sjkim	return (TRUE);				/* PS/2 absolute mode */
451349965Syokota}
451449965Syokota
4515147271Smarius/*
4516147271Smarius * Return true if 'now' is earlier than (start + (secs.usecs)).
4517147271Smarius * Now may be NULL and the function will fetch the current time from
4518147271Smarius * getmicrouptime(), or a cached 'now' can be passed in.
4519147271Smarius * All values should be numbers derived from getmicrouptime().
4520147271Smarius */
452141016Sdfrstatic int
4522147271Smariustimeelapsed(start, secs, usecs, now)
4523147271Smarius	const struct timeval *start, *now;
4524147271Smarius	int secs, usecs;
4525147271Smarius{
4526147271Smarius	struct timeval snow, tv;
4527147271Smarius
4528147271Smarius	/* if there is no 'now' passed in, the get it as a convience. */
4529147271Smarius	if (now == NULL) {
4530147271Smarius		getmicrouptime(&snow);
4531147271Smarius		now = &snow;
4532147271Smarius	}
4533178019Sjkim
4534147271Smarius	tv.tv_sec = secs;
4535147271Smarius	tv.tv_usec = usecs;
4536147271Smarius	timevaladd(&tv, start);
4537147271Smarius	return (timevalcmp(&tv, now, <));
4538147271Smarius}
4539147271Smarius
4540147271Smariusstatic int
454154629Syokotapsmresume(device_t dev)
454241016Sdfr{
4543178019Sjkim	struct psm_softc *sc = device_get_softc(dev);
4544178019Sjkim	int unit = device_get_unit(dev);
4545178019Sjkim	int err;
454641016Sdfr
4547178019Sjkim	VLOG(2, (LOG_NOTICE, "psm%d: system resume hook called.\n", unit));
454841016Sdfr
4549178019Sjkim	if (!(sc->config & PSM_CONFIG_HOOKRESUME))
4550178019Sjkim		return (0);
455158230Syokota
4552178019Sjkim	err = reinitialize(sc, sc->config & PSM_CONFIG_INITAFTERSUSPEND);
455341016Sdfr
4554178019Sjkim	if ((sc->state & PSM_ASLP) && !(sc->state & PSM_VALID)) {
4555178019Sjkim		/*
4556178019Sjkim		 * Release the blocked process; it must be notified that
4557178019Sjkim		 * the device cannot be accessed anymore.
4558178019Sjkim		 */
4559178019Sjkim		sc->state &= ~PSM_ASLP;
4560178019Sjkim		wakeup(sc);
4561178019Sjkim	}
456241016Sdfr
4563178019Sjkim	VLOG(2, (LOG_DEBUG, "psm%d: system resume hook exiting.\n", unit));
456441016Sdfr
4565178019Sjkim	return (err);
456641016Sdfr}
456741016Sdfr
456852997SpeterDRIVER_MODULE(psm, atkbdc, psm_driver, psm_devclass, 0, 0);
456983147Syokota
4570147271Smarius#ifdef DEV_ISA
4571147271Smarius
457283147Syokota/*
457383147Syokota * This sucks up assignments from PNPBIOS and ACPI.
457483147Syokota */
457583147Syokota
457683931Syokota/*
457783931Syokota * When the PS/2 mouse device is reported by ACPI or PnP BIOS, it may
457883931Syokota * appear BEFORE the AT keyboard controller.  As the PS/2 mouse device
457983931Syokota * can be probed and attached only after the AT keyboard controller is
458083931Syokota * attached, we shall quietly reserve the IRQ resource for later use.
458183931Syokota * If the PS/2 mouse device is reported to us AFTER the keyboard controller,
458283931Syokota * copy the IRQ resource to the PS/2 mouse device instance hanging
458383931Syokota * under the keyboard controller, then probe and attach it.
458483931Syokota */
458583147Syokota
458683147Syokotastatic	devclass_t			psmcpnp_devclass;
458783147Syokota
458883147Syokotastatic	device_probe_t			psmcpnp_probe;
458983147Syokotastatic	device_attach_t			psmcpnp_attach;
459083147Syokota
459183147Syokotastatic device_method_t psmcpnp_methods[] = {
459283147Syokota	DEVMETHOD(device_probe,		psmcpnp_probe),
459383147Syokota	DEVMETHOD(device_attach,	psmcpnp_attach),
4594178019Sjkim
459583147Syokota	{ 0, 0 }
459683147Syokota};
459783147Syokota
459883147Syokotastatic driver_t psmcpnp_driver = {
459983147Syokota	PSMCPNP_DRIVER_NAME,
460083147Syokota	psmcpnp_methods,
460183147Syokota	1,			/* no softc */
460283147Syokota};
460383147Syokota
460483147Syokotastatic struct isa_pnp_id psmcpnp_ids[] = {
460588188Ssheldonh	{ 0x030fd041, "PS/2 mouse port" },		/* PNP0F03 */
4606156730Stakawata	{ 0x0e0fd041, "PS/2 mouse port" },		/* PNP0F0E */
4607156730Stakawata	{ 0x120fd041, "PS/2 mouse port" },		/* PNP0F12 */
460883147Syokota	{ 0x130fd041, "PS/2 mouse port" },		/* PNP0F13 */
460983147Syokota	{ 0x1303d041, "PS/2 port" },			/* PNP0313, XXX */
4610117117Smikeh	{ 0x02002e4f, "Dell PS/2 mouse port" },		/* Lat. X200, Dell */
4611156730Stakawata	{ 0x0002a906, "ALPS Glide Point" },		/* ALPS Glide Point */
461283492Syokota	{ 0x80374d24, "IBM PS/2 mouse port" },		/* IBM3780, ThinkPad */
461384407Stakawata	{ 0x81374d24, "IBM PS/2 mouse port" },		/* IBM3781, ThinkPad */
4614109679Shsu	{ 0x0190d94d, "SONY VAIO PS/2 mouse port"},     /* SNY9001, Vaio */
4615109679Shsu	{ 0x0290d94d, "SONY VAIO PS/2 mouse port"},	/* SNY9002, Vaio */
4616109710Smarcel	{ 0x0390d94d, "SONY VAIO PS/2 mouse port"},	/* SNY9003, Vaio */
4617109679Shsu	{ 0x0490d94d, "SONY VAIO PS/2 mouse port"},     /* SNY9004, Vaio */
461883147Syokota	{ 0 }
461983147Syokota};
462083147Syokota
462183147Syokotastatic int
462283147Syokotacreate_a_copy(device_t atkbdc, device_t me)
462383147Syokota{
462483147Syokota	device_t psm;
462583147Syokota	u_long irq;
462683147Syokota
462783931Syokota	/* find the PS/2 mouse device instance under the keyboard controller */
462883931Syokota	psm = device_find_child(atkbdc, PSM_DRIVER_NAME,
4629178019Sjkim	    device_get_unit(atkbdc));
463083147Syokota	if (psm == NULL)
4631178019Sjkim		return (ENXIO);
463283931Syokota	if (device_get_state(psm) != DS_NOTPRESENT)
4633178019Sjkim		return (0);
463483147Syokota
463583931Syokota	/* move our resource to the found device */
463683931Syokota	irq = bus_get_resource_start(me, SYS_RES_IRQ, 0);
463783147Syokota	bus_set_resource(psm, SYS_RES_IRQ, KBDC_RID_AUX, irq, 1);
463883147Syokota
463983147Syokota	/* ...then probe and attach it */
4640178019Sjkim	return (device_probe_and_attach(psm));
464183147Syokota}
464283147Syokota
464383147Syokotastatic int
464483147Syokotapsmcpnp_probe(device_t dev)
464583147Syokota{
464683931Syokota	struct resource *res;
464783931Syokota	u_long irq;
464883931Syokota	int rid;
464983147Syokota
465083147Syokota	if (ISA_PNP_PROBE(device_get_parent(dev), dev, psmcpnp_ids))
4651178019Sjkim		return (ENXIO);
465283147Syokota
465383931Syokota	/*
465483931Syokota	 * The PnP BIOS and ACPI are supposed to assign an IRQ (12)
465583931Syokota	 * to the PS/2 mouse device node. But, some buggy PnP BIOS
465683931Syokota	 * declares the PS/2 mouse device node without an IRQ resource!
465783931Syokota	 * If this happens, we shall refer to device hints.
465883931Syokota	 * If we still don't find it there, use a hardcoded value... XXX
465983931Syokota	 */
466083931Syokota	rid = 0;
466183931Syokota	irq = bus_get_resource_start(dev, SYS_RES_IRQ, rid);
466283931Syokota	if (irq <= 0) {
466383931Syokota		if (resource_long_value(PSM_DRIVER_NAME,
4664178019Sjkim		    device_get_unit(dev),"irq", &irq) != 0)
466583931Syokota			irq = 12;	/* XXX */
466683931Syokota		device_printf(dev, "irq resource info is missing; "
4667178019Sjkim		    "assuming irq %ld\n", irq);
466883931Syokota		bus_set_resource(dev, SYS_RES_IRQ, rid, irq, 1);
466983931Syokota	}
4670178019Sjkim	res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE);
467183931Syokota	bus_release_resource(dev, SYS_RES_IRQ, rid, res);
467283147Syokota
467383147Syokota	/* keep quiet */
467483147Syokota	if (!bootverbose)
467583147Syokota		device_quiet(dev);
467683492Syokota
467783931Syokota	return ((res == NULL) ? ENXIO : 0);
467883147Syokota}
467983147Syokota
468083147Syokotastatic int
468183147Syokotapsmcpnp_attach(device_t dev)
468283147Syokota{
468383492Syokota	device_t atkbdc;
468483931Syokota	int rid;
468583147Syokota
468683931Syokota	/* find the keyboard controller, which may be on acpi* or isa* bus */
468783931Syokota	atkbdc = devclass_get_device(devclass_find(ATKBDC_DRIVER_NAME),
4688178019Sjkim	    device_get_unit(dev));
4689178019Sjkim	if ((atkbdc != NULL) && (device_get_state(atkbdc) == DS_ATTACHED))
469083492Syokota		create_a_copy(atkbdc, dev);
4691178019Sjkim	else {
469283931Syokota		/*
469383931Syokota		 * If we don't have the AT keyboard controller yet,
469483931Syokota		 * just reserve the IRQ for later use...
469583931Syokota		 * (See psmidentify() above.)
469683931Syokota		 */
469783931Syokota		rid = 0;
4698127135Snjl		bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE);
469983931Syokota	}
470083492Syokota
4701178019Sjkim	return (0);
470283147Syokota}
470383147Syokota
470483147SyokotaDRIVER_MODULE(psmcpnp, isa, psmcpnp_driver, psmcpnp_devclass, 0, 0);
470583147SyokotaDRIVER_MODULE(psmcpnp, acpi, psmcpnp_driver, psmcpnp_devclass, 0, 0);
4706147271Smarius
4707147271Smarius#endif /* DEV_ISA */
4708