psm.c revision 135945
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'.
5341016Sdfr *  - January/February 1997. Tweaked probe logic for
5441016Sdfr *    HiNote UltraII/Latitude/Armada laptops.
5541016Sdfr *  - 30 July 1997. Added APM support.
5641016Sdfr *  - 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 135945 2004-09-29 23:49:57Z philip $");
63116181Sobrien
6441016Sdfr#include "opt_psm.h"
6541016Sdfr
6641016Sdfr#include <sys/param.h>
6741016Sdfr#include <sys/systm.h>
6841016Sdfr#include <sys/kernel.h>
6941016Sdfr#include <sys/module.h>
7041016Sdfr#include <sys/bus.h>
7141016Sdfr#include <sys/conf.h>
7241016Sdfr#include <sys/poll.h>
7341016Sdfr#include <sys/syslog.h>
7445720Speter#include <machine/bus.h>
7541181Sdfr#include <sys/rman.h>
7670834Swollman#include <sys/selinfo.h>
77123442Salfred#include <sys/sysctl.h>
7884880Syokota#include <sys/time.h>
7941016Sdfr#include <sys/uio.h>
8041016Sdfr
81114216Skan#include <sys/limits.h>
8266860Sphk#include <sys/mouse.h>
8341181Sdfr#include <machine/resource.h>
8441016Sdfr
8541016Sdfr#include <isa/isavar.h>
8643105Sdfr#include <dev/kbd/atkbdcreg.h>
8741016Sdfr
8841016Sdfr/*
8941016Sdfr * Driver specific options: the following options may be set by
9041016Sdfr * `options' statements in the kernel configuration file.
9141016Sdfr */
9241016Sdfr
9341016Sdfr/* debugging */
9441016Sdfr#ifndef PSM_DEBUG
95134405Sgibbs#define PSM_DEBUG	0	/*
96134405Sgibbs				 * logging: 0: none, 1: brief, 2: verbose
97134405Sgibbs				 *          3: sync errors, 4: all packets
98134405Sgibbs				 */
9941016Sdfr#endif
100134405Sgibbs#define VLOG(level, args) 	\
101134405Sgibbsdo {				\
102134405Sgibbs	if (verbose >= level)	\
103134405Sgibbs		log args;	\
104134405Sgibbs} while (0)
10541016Sdfr
10684880Syokota#ifndef PSM_INPUT_TIMEOUT
10784880Syokota#define PSM_INPUT_TIMEOUT	2000000	/* 2 sec */
10884880Syokota#endif
10984880Syokota
110133297Sphilip#ifndef PSM_TAP_TIMEOUT
111133297Sphilip#define PSM_TAP_TIMEOUT		125000
112133297Sphilip#endif
113133297Sphilip
114133297Sphilip#ifndef PSM_TAP_THRESHOLD
115133297Sphilip#define PSM_TAP_THRESHOLD	25
116133297Sphilip#endif
117133297Sphilip
11841016Sdfr/* end of driver specific options */
11941016Sdfr
12083492Syokota#define PSM_DRIVER_NAME		"psm"
12183931Syokota#define PSMCPNP_DRIVER_NAME	"psmcpnp"
12283492Syokota
12341016Sdfr/* input queue */
12441016Sdfr#define PSM_BUFSIZE		960
12541016Sdfr#define PSM_SMALLBUFSIZE	240
12641016Sdfr
12741016Sdfr/* operation levels */
12841016Sdfr#define PSM_LEVEL_BASE		0
12941016Sdfr#define PSM_LEVEL_STANDARD	1
13041016Sdfr#define PSM_LEVEL_NATIVE	2
13141016Sdfr#define PSM_LEVEL_MIN		PSM_LEVEL_BASE
13241016Sdfr#define PSM_LEVEL_MAX		PSM_LEVEL_NATIVE
13341016Sdfr
13448778Syokota/* Logitech PS2++ protocol */
13548778Syokota#define MOUSE_PS2PLUS_CHECKBITS(b)	\
13648778Syokota				((((b[2] & 0x03) << 2) | 0x02) == (b[1] & 0x0f))
13748778Syokota#define MOUSE_PS2PLUS_PACKET_TYPE(b)	\
13848778Syokota				(((b[0] & 0x30) >> 2) | ((b[1] & 0x30) >> 4))
13948778Syokota
14041016Sdfr/* some macros */
14141016Sdfr#define PSM_UNIT(dev)		(minor(dev) >> 1)
14241016Sdfr#define PSM_NBLOCKIO(dev)	(minor(dev) & 1)
14341016Sdfr#define PSM_MKMINOR(unit,block)	(((unit) << 1) | ((block) ? 0:1))
14441016Sdfr
14541016Sdfr/* ring buffer */
14641016Sdfrtypedef struct ringbuf {
14741016Sdfr    int           count;	/* # of valid elements in the buffer */
14841016Sdfr    int           head;		/* head pointer */
14941016Sdfr    int           tail;		/* tail poiner */
15041016Sdfr    unsigned char buf[PSM_BUFSIZE];
15141016Sdfr} ringbuf_t;
15241016Sdfr
153123442Salfred/* data buffer */
154123442Salfredtypedef struct packetbuf {
155123442Salfred    unsigned char ipacket[16];	/* interim input buffer */
156123442Salfred    int           inputbytes;	/* # of bytes in the input buffer */
157123442Salfred} packetbuf_t;
158123442Salfred
159123442Salfred#ifndef PSM_PACKETQUEUE
160123442Salfred#define PSM_PACKETQUEUE	128
161123442Salfred#endif
162123442Salfred
16341016Sdfr/* driver control block */
16441016Sdfrstruct psm_softc {		/* Driver status information */
16584880Syokota    int		  unit;
16641016Sdfr    struct selinfo rsel;	/* Process selecting for Input */
16741016Sdfr    unsigned char state;	/* Mouse driver state */
16841016Sdfr    int           config;	/* driver configuration flags */
16941016Sdfr    int           flags;	/* other flags */
17041016Sdfr    KBDC          kbdc;		/* handle to access the keyboard controller */
17158230Syokota    struct resource *intr;	/* IRQ resource */
17258230Syokota    void	  *ih;		/* interrupt handle */
17341016Sdfr    mousehw_t     hw;		/* hardware information */
174132865Snjl    synapticshw_t synhw;	/* Synaptics-specific hardware information */
17541016Sdfr    mousemode_t   mode;		/* operation mode */
17641016Sdfr    mousemode_t   dflt_mode;	/* default operation mode */
17741016Sdfr    mousestatus_t status;	/* accumulated mouse movement */
17841016Sdfr    ringbuf_t     queue;	/* mouse status queue */
179123442Salfred    packetbuf_t   pqueue[PSM_PACKETQUEUE];	/* mouse data queue */
180123442Salfred    int           pqueue_start; /* start of data in queue */
181123442Salfred    int           pqueue_end;   /* end of data in queue */
18241016Sdfr    int           button;	/* the latest button state */
18349965Syokota    int		  xold;	/* previous absolute X position */
18449965Syokota    int		  yold;	/* previous absolute Y position */
185133297Sphilip    int		  zmax;	/* maximum pressure value for touchpads */
186134405Sgibbs    int		  syncerrors;	/* # of bytes discarded searching for sync */
187134405Sgibbs    int		  pkterrors;	/* # of packets failed during quaranteen. */
18884880Syokota    struct timeval inputtimeout;
189123442Salfred    struct timeval lastsoftintr;	/* time of last soft interrupt */
190123442Salfred    struct timeval lastinputerr;	/* time last sync error happened */
191133297Sphilip    struct timeval taptimeout;		/* tap timeout for touchpads */
19258230Syokota    int		  watchdog;	/* watchdog timer flag */
19358230Syokota    struct callout_handle callout;	/* watchdog timer call out */
194123442Salfred    struct callout_handle softcallout;	/* buffer timer call out */
195130585Sphk    struct cdev *dev;
196130585Sphk    struct cdev *bdev;
197123442Salfred    int           lasterr;
198123442Salfred    int           cmdcount;
19941016Sdfr};
200114293Smarkmstatic devclass_t psm_devclass;
20141016Sdfr#define PSM_SOFTC(unit)	((struct psm_softc*)devclass_get_softc(psm_devclass, unit))
20241016Sdfr
20341016Sdfr/* driver state flags (state) */
20441016Sdfr#define PSM_VALID		0x80
20541016Sdfr#define PSM_OPEN		1	/* Device is open */
20641016Sdfr#define PSM_ASLP		2	/* Waiting for mouse data */
207123442Salfred#define PSM_SOFTARMED		4	/* Software interrupt armed */
208133918Sgibbs#define PSM_NEED_SYNCBITS	8	/* Set syncbits using next data pkt */
20941016Sdfr
21041016Sdfr/* driver configuration flags (config) */
21141016Sdfr#define PSM_CONFIG_RESOLUTION	0x000f	/* resolution */
21241016Sdfr#define PSM_CONFIG_ACCEL	0x00f0  /* acceleration factor */
21341016Sdfr#define PSM_CONFIG_NOCHECKSYNC	0x0100  /* disable sync. test */
21445789Speter#define PSM_CONFIG_NOIDPROBE	0x0200  /* disable mouse model probe */
21545789Speter#define PSM_CONFIG_NORESET	0x0400  /* don't reset the mouse */
21645789Speter#define PSM_CONFIG_FORCETAP	0x0800  /* assume `tap' action exists */
21745789Speter#define PSM_CONFIG_IGNPORTERROR	0x1000  /* ignore error in aux port test */
21858230Syokota#define PSM_CONFIG_HOOKRESUME	0x2000	/* hook the system resume event */
21958230Syokota#define PSM_CONFIG_INITAFTERSUSPEND 0x4000 /* init the device at the resume event */
22069439Syokota#define PSM_CONFIG_SYNCHACK	0x8000 /* enable `out-of-sync' hack */
22141016Sdfr
22241016Sdfr#define PSM_CONFIG_FLAGS	(PSM_CONFIG_RESOLUTION 		\
22341016Sdfr				    | PSM_CONFIG_ACCEL		\
22445789Speter				    | PSM_CONFIG_NOCHECKSYNC	\
22569439Syokota				    | PSM_CONFIG_SYNCHACK	\
22645789Speter				    | PSM_CONFIG_NOIDPROBE	\
22745789Speter				    | PSM_CONFIG_NORESET	\
22845789Speter				    | PSM_CONFIG_FORCETAP	\
22958230Syokota				    | PSM_CONFIG_IGNPORTERROR	\
23058230Syokota				    | PSM_CONFIG_HOOKRESUME	\
23158230Syokota				    | PSM_CONFIG_INITAFTERSUSPEND)
23241016Sdfr
23341016Sdfr/* other flags (flags) */
23449965Syokota#define PSM_FLAGS_FINGERDOWN	0x0001 /* VersaPad finger down */
23541016Sdfr
236135945Sphilip/* Tunables */
237135945Sphilipstatic int synaptics_support = 0;
238135945SphilipTUNABLE_INT("hw.psm.synaptics_support", &synaptics_support);
239135945Sphilip
24041016Sdfr/* for backward compatibility */
24141016Sdfr#define OLD_MOUSE_GETHWINFO	_IOR('M', 1, old_mousehw_t)
24241016Sdfr#define OLD_MOUSE_GETMODE	_IOR('M', 2, old_mousemode_t)
24341016Sdfr#define OLD_MOUSE_SETMODE	_IOW('M', 3, old_mousemode_t)
24441016Sdfr
24541016Sdfrtypedef struct old_mousehw {
24641016Sdfr    int buttons;
24741016Sdfr    int iftype;
24841016Sdfr    int type;
24941016Sdfr    int hwid;
25041016Sdfr} old_mousehw_t;
25141016Sdfr
25241016Sdfrtypedef struct old_mousemode {
25341016Sdfr    int protocol;
25441016Sdfr    int rate;
25541016Sdfr    int resolution;
25641016Sdfr    int accelfactor;
25741016Sdfr} old_mousemode_t;
25841016Sdfr
25941016Sdfr/* packet formatting function */
26092756Salfredtypedef int packetfunc_t(struct psm_softc *, unsigned char *,
26192756Salfred			      int *, int, mousestatus_t *);
26241016Sdfr
26341016Sdfr/* function prototypes */
26492756Salfredstatic void psmidentify(driver_t *, device_t);
26592756Salfredstatic int psmprobe(device_t);
26692756Salfredstatic int psmattach(device_t);
26792756Salfredstatic int psmdetach(device_t);
26892756Salfredstatic int psmresume(device_t);
26941016Sdfr
27041016Sdfrstatic d_open_t psmopen;
27141016Sdfrstatic d_close_t psmclose;
27241016Sdfrstatic d_read_t psmread;
27341016Sdfrstatic d_ioctl_t psmioctl;
27441016Sdfrstatic d_poll_t psmpoll;
27541016Sdfr
27692756Salfredstatic int enable_aux_dev(KBDC);
27792756Salfredstatic int disable_aux_dev(KBDC);
27892756Salfredstatic int get_mouse_status(KBDC, int *, int, int);
27992756Salfredstatic int get_aux_id(KBDC);
28092756Salfredstatic int set_mouse_sampling_rate(KBDC, int);
28192756Salfredstatic int set_mouse_scaling(KBDC, int);
28292756Salfredstatic int set_mouse_resolution(KBDC, int);
28392756Salfredstatic int set_mouse_mode(KBDC);
28492756Salfredstatic int get_mouse_buttons(KBDC);
28592756Salfredstatic int is_a_mouse(int);
28692756Salfredstatic void recover_from_error(KBDC);
28792756Salfredstatic int restore_controller(KBDC, int);
28892756Salfredstatic int doinitialize(struct psm_softc *, mousemode_t *);
28992756Salfredstatic int doopen(struct psm_softc *, int);
29092756Salfredstatic int reinitialize(struct psm_softc *, int);
29192756Salfredstatic char *model_name(int);
292123442Salfredstatic void psmsoftintr(void *);
29392756Salfredstatic void psmintr(void *);
29492756Salfredstatic void psmtimeout(void *);
295123442Salfredstatic int timeelapsed(const struct timeval *,
296123442Salfred    int, int, const struct timeval *);
297123442Salfredstatic void dropqueue(struct psm_softc *);
298123442Salfredstatic void flushpackets(struct psm_softc *);
29941016Sdfr
30041016Sdfr/* vendor specific features */
30192756Salfredtypedef int probefunc_t(struct psm_softc *);
30241016Sdfr
30392756Salfredstatic int mouse_id_proc1(KBDC, int, int, int *);
30492756Salfredstatic int mouse_ext_command(KBDC, int);
30541016Sdfrstatic probefunc_t enable_groller;
30641016Sdfrstatic probefunc_t enable_gmouse;
30741016Sdfrstatic probefunc_t enable_aglide;
30841016Sdfrstatic probefunc_t enable_kmouse;
30958230Syokotastatic probefunc_t enable_msexplorer;
31041016Sdfrstatic probefunc_t enable_msintelli;
31158230Syokotastatic probefunc_t enable_4dmouse;
31258230Syokotastatic probefunc_t enable_4dplus;
31341016Sdfrstatic probefunc_t enable_mmanplus;
314132865Snjlstatic probefunc_t enable_synaptics;
31549965Syokotastatic probefunc_t enable_versapad;
316123442Salfredstatic int tame_mouse(struct psm_softc *, packetbuf_t *, mousestatus_t *, unsigned char *);
31741016Sdfr
31841016Sdfrstatic struct {
31941016Sdfr    int                 model;
32041016Sdfr    unsigned char	syncmask;
32141016Sdfr    int 		packetsize;
32241016Sdfr    probefunc_t 	*probefunc;
32341016Sdfr} vendortype[] = {
32458230Syokota    /*
32558230Syokota     * WARNING: the order of probe is very important.  Don't mess it
32658230Syokota     * unless you know what you are doing.
32758230Syokota     */
32841016Sdfr    { MOUSE_MODEL_NET,			/* Genius NetMouse */
32958230Syokota      0x08, MOUSE_PS2INTELLI_PACKETSIZE, enable_gmouse, },
33041016Sdfr    { MOUSE_MODEL_NETSCROLL,		/* Genius NetScroll */
33141016Sdfr      0xc8, 6, enable_groller, },
33258230Syokota    { MOUSE_MODEL_MOUSEMANPLUS,		/* Logitech MouseMan+ */
33358230Syokota      0x08, MOUSE_PS2_PACKETSIZE, enable_mmanplus, },
33458230Syokota    { MOUSE_MODEL_EXPLORER,		/* Microsoft IntelliMouse Explorer */
33558230Syokota      0x08, MOUSE_PS2INTELLI_PACKETSIZE, enable_msexplorer, },
33658230Syokota    { MOUSE_MODEL_4D,			/* A4 Tech 4D Mouse */
33758230Syokota      0x08, MOUSE_4D_PACKETSIZE, enable_4dmouse, },
33858230Syokota    { MOUSE_MODEL_4DPLUS,		/* A4 Tech 4D+ Mouse */
33958230Syokota      0xc8, MOUSE_4DPLUS_PACKETSIZE, enable_4dplus, },
34058230Syokota    { MOUSE_MODEL_INTELLI,		/* Microsoft IntelliMouse */
34158230Syokota      0x08, MOUSE_PS2INTELLI_PACKETSIZE, enable_msintelli, },
34241016Sdfr    { MOUSE_MODEL_GLIDEPOINT,		/* ALPS GlidePoint */
34341016Sdfr      0xc0, MOUSE_PS2_PACKETSIZE, enable_aglide, },
34441016Sdfr    { MOUSE_MODEL_THINK,		/* Kensignton ThinkingMouse */
34541016Sdfr      0x80, MOUSE_PS2_PACKETSIZE, enable_kmouse, },
34649965Syokota    { MOUSE_MODEL_VERSAPAD,		/* Interlink electronics VersaPad */
34749965Syokota      0xe8, MOUSE_PS2VERSA_PACKETSIZE, enable_versapad, },
348132865Snjl    { MOUSE_MODEL_SYNAPTICS,		/* Synaptics Touchpad */
349132865Snjl      0xc0, MOUSE_SYNAPTICS_PACKETSIZE, enable_synaptics, },
35041016Sdfr    { MOUSE_MODEL_GENERIC,
35141016Sdfr      0xc0, MOUSE_PS2_PACKETSIZE, NULL, },
35241016Sdfr};
35363951Syokota#define GENERIC_MOUSE_ENTRY	((sizeof(vendortype) / sizeof(*vendortype)) - 1)
35441016Sdfr
35541016Sdfr/* device driver declarateion */
35641016Sdfrstatic device_method_t psm_methods[] = {
35741016Sdfr	/* Device interface */
35883147Syokota	DEVMETHOD(device_identify,	psmidentify),
35941016Sdfr	DEVMETHOD(device_probe,		psmprobe),
36041016Sdfr	DEVMETHOD(device_attach,	psmattach),
36158230Syokota	DEVMETHOD(device_detach,	psmdetach),
36254629Syokota	DEVMETHOD(device_resume,	psmresume),
36341016Sdfr
36441016Sdfr	{ 0, 0 }
36541016Sdfr};
36641016Sdfr
36741016Sdfrstatic driver_t psm_driver = {
36883492Syokota    PSM_DRIVER_NAME,
36941016Sdfr    psm_methods,
37041016Sdfr    sizeof(struct psm_softc),
37141016Sdfr};
37241016Sdfr
37341016Sdfr
37447625Sphkstatic struct cdevsw psm_cdevsw = {
375126080Sphk	.d_version =	D_VERSION,
376126080Sphk	.d_flags =	D_NEEDGIANT,
377111815Sphk	.d_open =	psmopen,
378111815Sphk	.d_close =	psmclose,
379111815Sphk	.d_read =	psmread,
380111815Sphk	.d_ioctl =	psmioctl,
381111815Sphk	.d_poll =	psmpoll,
382111815Sphk	.d_name =	PSM_DRIVER_NAME,
38341016Sdfr};
38441016Sdfr
38541016Sdfr/* debug message level */
38641016Sdfrstatic int verbose = PSM_DEBUG;
38741016Sdfr
38841016Sdfr/* device I/O routines */
38941016Sdfrstatic int
39041016Sdfrenable_aux_dev(KBDC kbdc)
39141016Sdfr{
39241016Sdfr    int res;
39341016Sdfr
39441016Sdfr    res = send_aux_command(kbdc, PSMC_ENABLE_DEV);
395134405Sgibbs    VLOG(2, (LOG_DEBUG, "psm: ENABLE_DEV return code:%04x\n", res));
39641016Sdfr
39741016Sdfr    return (res == PSM_ACK);
39841016Sdfr}
39941016Sdfr
40041016Sdfrstatic int
40141016Sdfrdisable_aux_dev(KBDC kbdc)
40241016Sdfr{
40341016Sdfr    int res;
40441016Sdfr
40541016Sdfr    res = send_aux_command(kbdc, PSMC_DISABLE_DEV);
406134405Sgibbs    VLOG(2, (LOG_DEBUG, "psm: DISABLE_DEV return code:%04x\n", res));
40741016Sdfr
40841016Sdfr    return (res == PSM_ACK);
40941016Sdfr}
41041016Sdfr
41141016Sdfrstatic int
41241016Sdfrget_mouse_status(KBDC kbdc, int *status, int flag, int len)
41341016Sdfr{
41441016Sdfr    int cmd;
41541016Sdfr    int res;
41641016Sdfr    int i;
41741016Sdfr
41841016Sdfr    switch (flag) {
41941016Sdfr    case 0:
42041016Sdfr    default:
42141016Sdfr	cmd = PSMC_SEND_DEV_STATUS;
42241016Sdfr	break;
42341016Sdfr    case 1:
42441016Sdfr	cmd = PSMC_SEND_DEV_DATA;
42541016Sdfr	break;
42641016Sdfr    }
42741016Sdfr    empty_aux_buffer(kbdc, 5);
42841016Sdfr    res = send_aux_command(kbdc, cmd);
429134405Sgibbs    VLOG(2, (LOG_DEBUG, "psm: SEND_AUX_DEV_%s return code:%04x\n",
430134405Sgibbs	 (flag == 1) ? "DATA" : "STATUS", res));
43141016Sdfr    if (res != PSM_ACK)
43241016Sdfr        return 0;
43341016Sdfr
43441016Sdfr    for (i = 0; i < len; ++i) {
43541016Sdfr        status[i] = read_aux_data(kbdc);
43641016Sdfr	if (status[i] < 0)
43741016Sdfr	    break;
43841016Sdfr    }
43941016Sdfr
440134405Sgibbs    VLOG(1, (LOG_DEBUG, "psm: %s %02x %02x %02x\n",
441134405Sgibbs         (flag == 1) ? "data" : "status", status[0], status[1], status[2]));
44241016Sdfr
44341016Sdfr    return i;
44441016Sdfr}
44541016Sdfr
44641016Sdfrstatic int
44741016Sdfrget_aux_id(KBDC kbdc)
44841016Sdfr{
44941016Sdfr    int res;
45041016Sdfr    int id;
45141016Sdfr
45241016Sdfr    empty_aux_buffer(kbdc, 5);
45341016Sdfr    res = send_aux_command(kbdc, PSMC_SEND_DEV_ID);
454134405Sgibbs    VLOG(2, (LOG_DEBUG, "psm: SEND_DEV_ID return code:%04x\n", res));
45541016Sdfr    if (res != PSM_ACK)
45641016Sdfr	return (-1);
45741016Sdfr
45841016Sdfr    /* 10ms delay */
45941016Sdfr    DELAY(10000);
46041016Sdfr
46141016Sdfr    id = read_aux_data(kbdc);
462134405Sgibbs    VLOG(2, (LOG_DEBUG, "psm: device ID: %04x\n", id));
46341016Sdfr
46441016Sdfr    return id;
46541016Sdfr}
46641016Sdfr
46741016Sdfrstatic int
46841016Sdfrset_mouse_sampling_rate(KBDC kbdc, int rate)
46941016Sdfr{
47041016Sdfr    int res;
47141016Sdfr
47241016Sdfr    res = send_aux_command_and_data(kbdc, PSMC_SET_SAMPLING_RATE, rate);
473134405Sgibbs    VLOG(2, (LOG_DEBUG, "psm: SET_SAMPLING_RATE (%d) %04x\n", rate, res));
47441016Sdfr
47541016Sdfr    return ((res == PSM_ACK) ? rate : -1);
47641016Sdfr}
47741016Sdfr
47841016Sdfrstatic int
47941016Sdfrset_mouse_scaling(KBDC kbdc, int scale)
48041016Sdfr{
48141016Sdfr    int res;
48241016Sdfr
48341016Sdfr    switch (scale) {
48441016Sdfr    case 1:
48541016Sdfr    default:
48641016Sdfr	scale = PSMC_SET_SCALING11;
48741016Sdfr	break;
48841016Sdfr    case 2:
48941016Sdfr	scale = PSMC_SET_SCALING21;
49041016Sdfr	break;
49141016Sdfr    }
49241016Sdfr    res = send_aux_command(kbdc, scale);
493134405Sgibbs    VLOG(2, (LOG_DEBUG, "psm: SET_SCALING%s return code:%04x\n",
494134405Sgibbs	 (scale == PSMC_SET_SCALING21) ? "21" : "11", res));
49541016Sdfr
49641016Sdfr    return (res == PSM_ACK);
49741016Sdfr}
49841016Sdfr
49941016Sdfr/* `val' must be 0 through PSMD_MAX_RESOLUTION */
50041016Sdfrstatic int
50141016Sdfrset_mouse_resolution(KBDC kbdc, int val)
50241016Sdfr{
50341016Sdfr    int res;
50441016Sdfr
50541016Sdfr    res = send_aux_command_and_data(kbdc, PSMC_SET_RESOLUTION, val);
506134405Sgibbs    VLOG(2, (LOG_DEBUG, "psm: SET_RESOLUTION (%d) %04x\n", val, res));
50741016Sdfr
50841016Sdfr    return ((res == PSM_ACK) ? val : -1);
50941016Sdfr}
51041016Sdfr
51141016Sdfr/*
51241016Sdfr * NOTE: once `set_mouse_mode()' is called, the mouse device must be
51341016Sdfr * re-enabled by calling `enable_aux_dev()'
51441016Sdfr */
51541016Sdfrstatic int
51641016Sdfrset_mouse_mode(KBDC kbdc)
51741016Sdfr{
51841016Sdfr    int res;
51941016Sdfr
52041016Sdfr    res = send_aux_command(kbdc, PSMC_SET_STREAM_MODE);
521134405Sgibbs    VLOG(2, (LOG_DEBUG, "psm: SET_STREAM_MODE return code:%04x\n", res));
52241016Sdfr
52341016Sdfr    return (res == PSM_ACK);
52441016Sdfr}
52541016Sdfr
52641016Sdfrstatic int
52741016Sdfrget_mouse_buttons(KBDC kbdc)
52841016Sdfr{
52941016Sdfr    int c = 2;		/* assume two buttons by default */
53041016Sdfr    int status[3];
53141016Sdfr
53241016Sdfr    /*
53341016Sdfr     * NOTE: a special sequence to obtain Logitech Mouse specific
53441016Sdfr     * information: set resolution to 25 ppi, set scaling to 1:1, set
53541016Sdfr     * scaling to 1:1, set scaling to 1:1. Then the second byte of the
53641016Sdfr     * mouse status bytes is the number of available buttons.
53741016Sdfr     * Some manufactures also support this sequence.
53841016Sdfr     */
53941016Sdfr    if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW)
54041016Sdfr        return c;
54141016Sdfr    if (set_mouse_scaling(kbdc, 1) && set_mouse_scaling(kbdc, 1)
54241016Sdfr        && set_mouse_scaling(kbdc, 1)
54341016Sdfr	&& (get_mouse_status(kbdc, status, 0, 3) >= 3)) {
54441016Sdfr        if (status[1] != 0)
54541016Sdfr            return status[1];
54641016Sdfr    }
54741016Sdfr    return c;
54841016Sdfr}
54941016Sdfr
55041016Sdfr/* misc subroutines */
55141016Sdfr/*
55241016Sdfr * Someday, I will get the complete list of valid pointing devices and
55341016Sdfr * their IDs... XXX
55441016Sdfr */
55541016Sdfrstatic int
55641016Sdfris_a_mouse(int id)
55741016Sdfr{
55841016Sdfr#if 0
55941016Sdfr    static int valid_ids[] = {
56041016Sdfr        PSM_MOUSE_ID,		/* mouse */
56141016Sdfr        PSM_BALLPOINT_ID,	/* ballpoint device */
56241016Sdfr        PSM_INTELLI_ID,		/* Intellimouse */
56358230Syokota        PSM_EXPLORER_ID,	/* Intellimouse Explorer */
56441016Sdfr        -1			/* end of table */
56541016Sdfr    };
56641016Sdfr    int i;
56741016Sdfr
56841016Sdfr    for (i = 0; valid_ids[i] >= 0; ++i)
56941016Sdfr        if (valid_ids[i] == id)
57041016Sdfr            return TRUE;
57141016Sdfr    return FALSE;
57241016Sdfr#else
57341016Sdfr    return TRUE;
57441016Sdfr#endif
57541016Sdfr}
57641016Sdfr
57741016Sdfrstatic char *
57841016Sdfrmodel_name(int model)
57941016Sdfr{
58041016Sdfr    static struct {
58141016Sdfr	int model_code;
58241016Sdfr	char *model_name;
58341016Sdfr    } models[] = {
58458230Syokota        { MOUSE_MODEL_NETSCROLL,	"NetScroll" },
58558230Syokota        { MOUSE_MODEL_NET,		"NetMouse/NetScroll Optical" },
58641016Sdfr        { MOUSE_MODEL_GLIDEPOINT,	"GlidePoint" },
58741016Sdfr        { MOUSE_MODEL_THINK,		"ThinkingMouse" },
58841016Sdfr        { MOUSE_MODEL_INTELLI,		"IntelliMouse" },
58941016Sdfr        { MOUSE_MODEL_MOUSEMANPLUS,	"MouseMan+" },
59049965Syokota        { MOUSE_MODEL_VERSAPAD,		"VersaPad" },
59158230Syokota        { MOUSE_MODEL_EXPLORER,		"IntelliMouse Explorer" },
59258230Syokota        { MOUSE_MODEL_4D,		"4D Mouse" },
59358230Syokota        { MOUSE_MODEL_4DPLUS,		"4D+ Mouse" },
594132865Snjl        { MOUSE_MODEL_SYNAPTICS,	"Synaptics Touchpad" },
59541016Sdfr        { MOUSE_MODEL_GENERIC,		"Generic PS/2 mouse" },
59641016Sdfr        { MOUSE_MODEL_UNKNOWN,		NULL },
59741016Sdfr    };
59841016Sdfr    int i;
59941016Sdfr
60041016Sdfr    for (i = 0; models[i].model_code != MOUSE_MODEL_UNKNOWN; ++i) {
60141016Sdfr	if (models[i].model_code == model)
60241016Sdfr	    return models[i].model_name;
60341016Sdfr    }
60441016Sdfr    return "Unknown";
60541016Sdfr}
60641016Sdfr
60741016Sdfrstatic void
60841016Sdfrrecover_from_error(KBDC kbdc)
60941016Sdfr{
61041016Sdfr    /* discard anything left in the output buffer */
61141016Sdfr    empty_both_buffers(kbdc, 10);
61241016Sdfr
61341016Sdfr#if 0
61441016Sdfr    /*
61541016Sdfr     * NOTE: KBDC_RESET_KBD may not restore the communication between the
61641016Sdfr     * keyboard and the controller.
61741016Sdfr     */
61841016Sdfr    reset_kbd(kbdc);
61941016Sdfr#else
62041016Sdfr    /*
62141016Sdfr     * NOTE: somehow diagnostic and keyboard port test commands bring the
62241016Sdfr     * keyboard back.
62341016Sdfr     */
62441016Sdfr    if (!test_controller(kbdc))
62541016Sdfr        log(LOG_ERR, "psm: keyboard controller failed.\n");
62641016Sdfr    /* if there isn't a keyboard in the system, the following error is OK */
627134405Sgibbs    if (test_kbd_port(kbdc) != 0)
628134405Sgibbs	VLOG(1, (LOG_ERR, "psm: keyboard port failed.\n"));
62941016Sdfr#endif
63041016Sdfr}
63141016Sdfr
63241016Sdfrstatic int
63341016Sdfrrestore_controller(KBDC kbdc, int command_byte)
63441016Sdfr{
63541016Sdfr    empty_both_buffers(kbdc, 10);
63641016Sdfr
63741016Sdfr    if (!set_controller_command_byte(kbdc, 0xff, command_byte)) {
63841016Sdfr	log(LOG_ERR, "psm: failed to restore the keyboard controller "
63941016Sdfr		     "command byte.\n");
64058230Syokota	empty_both_buffers(kbdc, 10);
64141016Sdfr	return FALSE;
64241016Sdfr    } else {
64358230Syokota	empty_both_buffers(kbdc, 10);
64441016Sdfr	return TRUE;
64541016Sdfr    }
64641016Sdfr}
64741016Sdfr
64841016Sdfr/*
64941016Sdfr * Re-initialize the aux port and device. The aux port must be enabled
65041016Sdfr * and its interrupt must be disabled before calling this routine.
65141016Sdfr * The aux device will be disabled before returning.
65241016Sdfr * The keyboard controller must be locked via `kbdc_lock()' before
65341016Sdfr * calling this routine.
65441016Sdfr */
65541016Sdfrstatic int
65684880Syokotadoinitialize(struct psm_softc *sc, mousemode_t *mode)
65741016Sdfr{
65841016Sdfr    KBDC kbdc = sc->kbdc;
65941016Sdfr    int stat[3];
66041016Sdfr    int i;
66141016Sdfr
66241016Sdfr    switch((i = test_aux_port(kbdc))) {
663132270Smux    case 1:	/* ignore these errors */
664132270Smux    case 2:
665132270Smux    case 3:
66641016Sdfr    case PSM_ACK:
66741016Sdfr	if (verbose)
66841016Sdfr	    log(LOG_DEBUG, "psm%d: strange result for test aux port (%d).\n",
66984880Syokota	        sc->unit, i);
670102412Scharnier	/* FALLTHROUGH */
67141016Sdfr    case 0:	/* no error */
67241016Sdfr    	break;
67341016Sdfr    case -1: 	/* time out */
67441016Sdfr    default: 	/* error */
67541016Sdfr    	recover_from_error(kbdc);
67645789Speter	if (sc->config & PSM_CONFIG_IGNPORTERROR)
67745789Speter	    break;
67841016Sdfr    	log(LOG_ERR, "psm%d: the aux port is not functioning (%d).\n",
67984880Syokota    	    sc->unit, i);
68041016Sdfr    	return FALSE;
68141016Sdfr    }
68241016Sdfr
68345789Speter    if (sc->config & PSM_CONFIG_NORESET) {
68445789Speter	/*
68545789Speter	 * Don't try to reset the pointing device.  It may possibly be
68645789Speter	 * left in the unknown state, though...
68745789Speter	 */
68845789Speter    } else {
68945789Speter	/*
69045789Speter	 * NOTE: some controllers appears to hang the `keyboard' when
69145789Speter	 * the aux port doesn't exist and `PSMC_RESET_DEV' is issued.
69245789Speter	 */
69345789Speter	if (!reset_aux_dev(kbdc)) {
69445789Speter            recover_from_error(kbdc);
69584880Syokota            log(LOG_ERR, "psm%d: failed to reset the aux device.\n", sc->unit);
69645789Speter            return FALSE;
69745789Speter	}
69841016Sdfr    }
69941016Sdfr
70041016Sdfr    /*
70141016Sdfr     * both the aux port and the aux device is functioning, see
70241016Sdfr     * if the device can be enabled.
70341016Sdfr     */
70441016Sdfr    if (!enable_aux_dev(kbdc) || !disable_aux_dev(kbdc)) {
70584880Syokota        log(LOG_ERR, "psm%d: failed to enable the aux device.\n", sc->unit);
70641016Sdfr        return FALSE;
70741016Sdfr    }
70841016Sdfr    empty_both_buffers(kbdc, 10);	/* remove stray data if any */
70941016Sdfr
71045789Speter    if (sc->config & PSM_CONFIG_NOIDPROBE) {
71145789Speter	i = GENERIC_MOUSE_ENTRY;
71245789Speter    } else {
71345789Speter	/* FIXME: hardware ID, mouse buttons? */
71441016Sdfr
71545789Speter	/* other parameters */
71645789Speter	for (i = 0; vendortype[i].probefunc != NULL; ++i) {
71745789Speter	    if ((*vendortype[i].probefunc)(sc)) {
71845789Speter		if (verbose >= 2)
71945789Speter		    log(LOG_ERR, "psm%d: found %s\n",
72084880Syokota			sc->unit, model_name(vendortype[i].model));
72145789Speter		break;
72245789Speter	    }
72341016Sdfr	}
72441016Sdfr    }
72541016Sdfr
72641016Sdfr    sc->hw.model = vendortype[i].model;
72741016Sdfr    sc->mode.packetsize = vendortype[i].packetsize;
72841016Sdfr
72941016Sdfr    /* set mouse parameters */
73041016Sdfr    if (mode != (mousemode_t *)NULL) {
73141016Sdfr	if (mode->rate > 0)
73241016Sdfr            mode->rate = set_mouse_sampling_rate(kbdc, mode->rate);
73341016Sdfr	if (mode->resolution >= 0)
73441016Sdfr            mode->resolution = set_mouse_resolution(kbdc, mode->resolution);
73541016Sdfr        set_mouse_scaling(kbdc, 1);
73641016Sdfr        set_mouse_mode(kbdc);
73741016Sdfr    }
73841016Sdfr
739133918Sgibbs    /* Record sync on the next data packet we see. */
740133918Sgibbs    sc->flags |= PSM_NEED_SYNCBITS;
74141016Sdfr
74241016Sdfr    /* just check the status of the mouse */
74341016Sdfr    if (get_mouse_status(kbdc, stat, 0, 3) < 3)
74484880Syokota        log(LOG_DEBUG, "psm%d: failed to get status (doinitialize).\n",
74584880Syokota	    sc->unit);
74641016Sdfr
74741016Sdfr    return TRUE;
74841016Sdfr}
74941016Sdfr
75041016Sdfrstatic int
75184880Syokotadoopen(struct psm_softc *sc, int command_byte)
75241016Sdfr{
75341016Sdfr    int stat[3];
75441016Sdfr
75541016Sdfr    /* enable the mouse device */
75641016Sdfr    if (!enable_aux_dev(sc->kbdc)) {
75741016Sdfr	/* MOUSE ERROR: failed to enable the mouse because:
75841016Sdfr	 * 1) the mouse is faulty,
75941016Sdfr	 * 2) the mouse has been removed(!?)
76041016Sdfr	 * In the latter case, the keyboard may have hung, and need
76141016Sdfr	 * recovery procedure...
76241016Sdfr	 */
76341016Sdfr	recover_from_error(sc->kbdc);
76441016Sdfr#if 0
76541016Sdfr	/* FIXME: we could reset the mouse here and try to enable
76641016Sdfr	 * it again. But it will take long time and it's not a good
76741016Sdfr	 * idea to disable the keyboard that long...
76841016Sdfr	 */
76984880Syokota	if (!doinitialize(sc, &sc->mode) || !enable_aux_dev(sc->kbdc)) {
77041016Sdfr	    recover_from_error(sc->kbdc);
77141016Sdfr#else
77241016Sdfr        {
77341016Sdfr#endif
77441016Sdfr            restore_controller(sc->kbdc, command_byte);
77541016Sdfr	    /* mark this device is no longer available */
77641016Sdfr	    sc->state &= ~PSM_VALID;
77741016Sdfr	    log(LOG_ERR, "psm%d: failed to enable the device (doopen).\n",
77884880Syokota		sc->unit);
77941016Sdfr	    return (EIO);
78041016Sdfr	}
78141016Sdfr    }
78241016Sdfr
78341016Sdfr    if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3)
78484880Syokota        log(LOG_DEBUG, "psm%d: failed to get status (doopen).\n", sc->unit);
78541016Sdfr
78641016Sdfr    /* enable the aux port and interrupt */
78741016Sdfr    if (!set_controller_command_byte(sc->kbdc,
78841016Sdfr	    kbdc_get_device_mask(sc->kbdc),
78941016Sdfr	    (command_byte & KBD_KBD_CONTROL_BITS)
79041016Sdfr		| KBD_ENABLE_AUX_PORT | KBD_ENABLE_AUX_INT)) {
79141016Sdfr	/* CONTROLLER ERROR */
79241016Sdfr	disable_aux_dev(sc->kbdc);
79341016Sdfr        restore_controller(sc->kbdc, command_byte);
79441016Sdfr	log(LOG_ERR, "psm%d: failed to enable the aux interrupt (doopen).\n",
79584880Syokota	    sc->unit);
79641016Sdfr	return (EIO);
79741016Sdfr    }
79841016Sdfr
79958230Syokota    /* start the watchdog timer */
80058230Syokota    sc->watchdog = FALSE;
80184880Syokota    sc->callout = timeout(psmtimeout, (void *)(uintptr_t)sc, hz*2);
80258230Syokota
80341016Sdfr    return (0);
80441016Sdfr}
80541016Sdfr
80684880Syokotastatic int
80784880Syokotareinitialize(struct psm_softc *sc, int doinit)
80884880Syokota{
80984880Syokota    int err;
81084880Syokota    int c;
81184880Syokota    int s;
81284880Syokota
81384880Syokota    /* don't let anybody mess with the aux device */
81484880Syokota    if (!kbdc_lock(sc->kbdc, TRUE))
81584880Syokota	return (EIO);
81684880Syokota    s = spltty();
81784880Syokota
81884880Syokota    /* block our watchdog timer */
81984880Syokota    sc->watchdog = FALSE;
82084880Syokota    untimeout(psmtimeout, (void *)(uintptr_t)sc, sc->callout);
82184880Syokota    callout_handle_init(&sc->callout);
82284880Syokota
82384880Syokota    /* save the current controller command byte */
82484880Syokota    empty_both_buffers(sc->kbdc, 10);
82584880Syokota    c = get_controller_command_byte(sc->kbdc);
826134405Sgibbs    VLOG(2, (LOG_DEBUG, "psm%d: current command byte: %04x (reinitialize).\n",
827134405Sgibbs	 sc->unit, c));
82884880Syokota
82984880Syokota    /* enable the aux port but disable the aux interrupt and the keyboard */
83084880Syokota    if ((c == -1) || !set_controller_command_byte(sc->kbdc,
83184880Syokota	    kbdc_get_device_mask(sc->kbdc),
83284880Syokota  	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
83384880Syokota	        | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
83484880Syokota        /* CONTROLLER ERROR */
83584880Syokota	splx(s);
83684880Syokota        kbdc_lock(sc->kbdc, FALSE);
83784880Syokota	log(LOG_ERR, "psm%d: unable to set the command byte (reinitialize).\n",
83884880Syokota	    sc->unit);
83984880Syokota	return (EIO);
84084880Syokota    }
84184880Syokota
84284880Syokota    /* flush any data */
84384880Syokota    if (sc->state & PSM_VALID) {
84484880Syokota	disable_aux_dev(sc->kbdc);	/* this may fail; but never mind... */
84584880Syokota	empty_aux_buffer(sc->kbdc, 10);
84684880Syokota    }
847123442Salfred    flushpackets(sc);
84884880Syokota    sc->syncerrors = 0;
849134405Sgibbs    sc->pkterrors = 0;
850134405Sgibbs    memset(&sc->lastinputerr, 0, sizeof(sc->lastinputerr));
85184880Syokota
85284880Syokota    /* try to detect the aux device; are you still there? */
85384880Syokota    err = 0;
85484880Syokota    if (doinit) {
85584880Syokota	if (doinitialize(sc, &sc->mode)) {
85684880Syokota	    /* yes */
85784880Syokota	    sc->state |= PSM_VALID;
85884880Syokota	} else {
85984880Syokota	    /* the device has gone! */
86084880Syokota	    restore_controller(sc->kbdc, c);
86184880Syokota	    sc->state &= ~PSM_VALID;
86284880Syokota	    log(LOG_ERR, "psm%d: the aux device has gone! (reinitialize).\n",
86384880Syokota		sc->unit);
86484880Syokota	    err = ENXIO;
86584880Syokota	}
86684880Syokota    }
86784880Syokota    splx(s);
86884880Syokota
86984880Syokota    /* restore the driver state */
87084880Syokota    if ((sc->state & PSM_OPEN) && (err == 0)) {
87184880Syokota        /* enable the aux device and the port again */
87284880Syokota	err = doopen(sc, c);
87384880Syokota	if (err != 0)
87484880Syokota	    log(LOG_ERR, "psm%d: failed to enable the device (reinitialize).\n",
87584880Syokota		sc->unit);
87684880Syokota    } else {
87784880Syokota        /* restore the keyboard port and disable the aux port */
87884880Syokota        if (!set_controller_command_byte(sc->kbdc,
87984880Syokota                kbdc_get_device_mask(sc->kbdc),
88084880Syokota                (c & KBD_KBD_CONTROL_BITS)
88184880Syokota                    | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
88284880Syokota            /* CONTROLLER ERROR */
883133918Sgibbs            log(LOG_ERR,
884133918Sgibbs                "psm%d: failed to disable the aux port (reinitialize).\n",
88584880Syokota                sc->unit);
88684880Syokota            err = EIO;
88784880Syokota	}
88884880Syokota    }
88984880Syokota
89084880Syokota    kbdc_lock(sc->kbdc, FALSE);
89184880Syokota    return (err);
89284880Syokota}
89384880Syokota
89441016Sdfr/* psm driver entry points */
89541016Sdfr
89683147Syokotastatic void
89783147Syokotapsmidentify(driver_t *driver, device_t parent)
89883147Syokota{
89983931Syokota    device_t psmc;
90083931Syokota    device_t psm;
90183931Syokota    u_long irq;
90283931Syokota    int unit;
90383147Syokota
90483931Syokota    unit = device_get_unit(parent);
90583931Syokota
90683147Syokota    /* always add at least one child */
90783931Syokota    psm = BUS_ADD_CHILD(parent, KBDC_RID_AUX, driver->name, unit);
90883931Syokota    if (psm == NULL)
90983931Syokota	return;
91083931Syokota
91183931Syokota    irq = bus_get_resource_start(psm, SYS_RES_IRQ, KBDC_RID_AUX);
91283931Syokota    if (irq > 0)
91383931Syokota	return;
91483931Syokota
91583931Syokota    /*
91683931Syokota     * If the PS/2 mouse device has already been reported by ACPI or
91783931Syokota     * PnP BIOS, obtain the IRQ resource from it.
91883931Syokota     * (See psmcpnp_attach() below.)
91983931Syokota     */
92083931Syokota    psmc = device_find_child(device_get_parent(parent),
92183931Syokota			     PSMCPNP_DRIVER_NAME, unit);
92283931Syokota    if (psmc == NULL)
92383931Syokota	return;
92483931Syokota    irq = bus_get_resource_start(psmc, SYS_RES_IRQ, 0);
92583931Syokota    if (irq <= 0)
92683931Syokota	return;
92783931Syokota    bus_set_resource(psm, SYS_RES_IRQ, KBDC_RID_AUX, irq, 1);
92883147Syokota}
92983147Syokota
930115569Sphk#define endprobe(v)	do {   if (bootverbose)				\
93141016Sdfr				--verbose;   				\
93241016Sdfr                            kbdc_set_device_mask(sc->kbdc, mask);	\
93341016Sdfr			    kbdc_lock(sc->kbdc, FALSE);			\
93441016Sdfr			    return (v);	     				\
935115569Sphk			} while (0)
93641016Sdfr
93741016Sdfrstatic int
93841016Sdfrpsmprobe(device_t dev)
93941016Sdfr{
94041016Sdfr    int unit = device_get_unit(dev);
94141016Sdfr    struct psm_softc *sc = device_get_softc(dev);
94241016Sdfr    int stat[3];
94341016Sdfr    int command_byte;
94441016Sdfr    int mask;
94558230Syokota    int rid;
94641016Sdfr    int i;
94741016Sdfr
94841016Sdfr#if 0
94941016Sdfr    kbdc_debug(TRUE);
95041016Sdfr#endif
95158230Syokota
95283147Syokota    /* see if IRQ is available */
95383147Syokota    rid = KBDC_RID_AUX;
954127135Snjl    sc->intr = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
955127135Snjl				      RF_SHAREABLE | RF_ACTIVE);
95683147Syokota    if (sc->intr == NULL) {
95783147Syokota	if (bootverbose)
95883147Syokota            device_printf(dev, "unable to allocate IRQ\n");
95983147Syokota        return (ENXIO);
96083147Syokota    }
96183147Syokota    bus_release_resource(dev, SYS_RES_IRQ, rid, sc->intr);
96258230Syokota
96384880Syokota    sc->unit = unit;
96458230Syokota    sc->kbdc = atkbdc_open(device_get_unit(device_get_parent(dev)));
96583147Syokota    sc->config = device_get_flags(dev) & PSM_CONFIG_FLAGS;
96658230Syokota    /* XXX: for backward compatibility */
96758230Syokota#if defined(PSM_HOOKRESUME) || defined(PSM_HOOKAPM)
96858230Syokota    sc->config |=
96958230Syokota#ifdef PSM_RESETAFTERSUSPEND
97058230Syokota	PSM_CONFIG_HOOKRESUME | PSM_CONFIG_INITAFTERSUSPEND;
97158230Syokota#else
97258230Syokota	PSM_CONFIG_HOOKRESUME;
97358230Syokota#endif
97458230Syokota#endif /* PSM_HOOKRESUME | PSM_HOOKAPM */
97541016Sdfr    sc->flags = 0;
97641016Sdfr    if (bootverbose)
97741016Sdfr        ++verbose;
97841016Sdfr
97943105Sdfr    device_set_desc(dev, "PS/2 Mouse");
98043105Sdfr
98141016Sdfr    if (!kbdc_lock(sc->kbdc, TRUE)) {
98241016Sdfr        printf("psm%d: unable to lock the controller.\n", unit);
98341016Sdfr        if (bootverbose)
98441016Sdfr            --verbose;
98541016Sdfr	return (ENXIO);
98641016Sdfr    }
98741016Sdfr
98841016Sdfr    /*
98941016Sdfr     * NOTE: two bits in the command byte controls the operation of the
99041016Sdfr     * aux port (mouse port): the aux port disable bit (bit 5) and the aux
99141016Sdfr     * port interrupt (IRQ 12) enable bit (bit 2).
99241016Sdfr     */
99341016Sdfr
99441016Sdfr    /* discard anything left after the keyboard initialization */
99541016Sdfr    empty_both_buffers(sc->kbdc, 10);
99641016Sdfr
99741016Sdfr    /* save the current command byte; it will be used later */
99841016Sdfr    mask = kbdc_get_device_mask(sc->kbdc) & ~KBD_AUX_CONTROL_BITS;
99941016Sdfr    command_byte = get_controller_command_byte(sc->kbdc);
100041016Sdfr    if (verbose)
100141016Sdfr        printf("psm%d: current command byte:%04x\n", unit, command_byte);
100241016Sdfr    if (command_byte == -1) {
100341016Sdfr        /* CONTROLLER ERROR */
100441016Sdfr        printf("psm%d: unable to get the current command byte value.\n",
100541016Sdfr            unit);
100641016Sdfr        endprobe(ENXIO);
100741016Sdfr    }
100841016Sdfr
100941016Sdfr    /*
101041016Sdfr     * disable the keyboard port while probing the aux port, which must be
101141016Sdfr     * enabled during this routine
101241016Sdfr     */
101341016Sdfr    if (!set_controller_command_byte(sc->kbdc,
101441016Sdfr	    KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS,
101541016Sdfr  	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
101641016Sdfr                | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
101741016Sdfr        /*
101841016Sdfr	 * this is CONTROLLER ERROR; I don't know how to recover
101941016Sdfr         * from this error...
102041016Sdfr	 */
102141016Sdfr        restore_controller(sc->kbdc, command_byte);
102241016Sdfr        printf("psm%d: unable to set the command byte.\n", unit);
102341016Sdfr        endprobe(ENXIO);
102441016Sdfr    }
102545789Speter    write_controller_command(sc->kbdc, KBDC_ENABLE_AUX_PORT);
102641016Sdfr
102741016Sdfr    /*
102841016Sdfr     * NOTE: `test_aux_port()' is designed to return with zero if the aux
102941016Sdfr     * port exists and is functioning. However, some controllers appears
103041016Sdfr     * to respond with zero even when the aux port doesn't exist. (It may
103141016Sdfr     * be that this is only the case when the controller DOES have the aux
103241016Sdfr     * port but the port is not wired on the motherboard.) The keyboard
103341016Sdfr     * controllers without the port, such as the original AT, are
103441016Sdfr     * supporsed to return with an error code or simply time out. In any
103541016Sdfr     * case, we have to continue probing the port even when the controller
103641016Sdfr     * passes this test.
103741016Sdfr     *
1038132270Smux     * XXX: some controllers erroneously return the error code 1, 2 or 3
1039132270Smux     * when it has the perfectly functional aux port. We have to ignore
1040132270Smux     * this error code. Even if the controller HAS error with the aux
1041132270Smux     * port, it will be detected later...
104241016Sdfr     * XXX: another incompatible controller returns PSM_ACK (0xfa)...
104341016Sdfr     */
104441016Sdfr    switch ((i = test_aux_port(sc->kbdc))) {
1045132270Smux    case 1:	   /* ignore these errors */
1046132270Smux    case 2:
1047132270Smux    case 3:
104841016Sdfr    case PSM_ACK:
104941016Sdfr        if (verbose)
105041016Sdfr	    printf("psm%d: strange result for test aux port (%d).\n",
105141016Sdfr	        unit, i);
1052102412Scharnier	/* FALLTHROUGH */
105341016Sdfr    case 0:        /* no error */
105441016Sdfr        break;
105541016Sdfr    case -1:        /* time out */
105641016Sdfr    default:        /* error */
105741016Sdfr        recover_from_error(sc->kbdc);
105845789Speter	if (sc->config & PSM_CONFIG_IGNPORTERROR)
105945789Speter	    break;
106041016Sdfr        restore_controller(sc->kbdc, command_byte);
106141016Sdfr        if (verbose)
106241016Sdfr            printf("psm%d: the aux port is not functioning (%d).\n",
106341016Sdfr                unit, i);
106441016Sdfr        endprobe(ENXIO);
106541016Sdfr    }
106641016Sdfr
106745789Speter    if (sc->config & PSM_CONFIG_NORESET) {
106845789Speter	/*
106945789Speter	 * Don't try to reset the pointing device.  It may possibly be
107045789Speter	 * left in the unknown state, though...
107145789Speter	 */
107245789Speter    } else {
107345789Speter	/*
107445789Speter	 * NOTE: some controllers appears to hang the `keyboard' when the aux
107545789Speter	 * port doesn't exist and `PSMC_RESET_DEV' is issued.
1076117302Smikeh	 *
1077117302Smikeh	 * Attempt to reset the controller twice -- this helps
1078117302Smikeh	 * pierce through some KVM switches. The second reset
1079117302Smikeh	 * is non-fatal.
108045789Speter	 */
108145789Speter	if (!reset_aux_dev(sc->kbdc)) {
108245789Speter            recover_from_error(sc->kbdc);
108345789Speter            restore_controller(sc->kbdc, command_byte);
108445789Speter            if (verbose)
108545789Speter        	printf("psm%d: failed to reset the aux device.\n", unit);
108645789Speter            endprobe(ENXIO);
1087117302Smikeh	} else if (!reset_aux_dev(sc->kbdc)) {
1088117302Smikeh	    recover_from_error(sc->kbdc);
1089117302Smikeh	    if (verbose >= 2)
1090117302Smikeh        	printf("psm%d: failed to reset the aux device (2).\n",
1091117302Smikeh        	    unit);
109245789Speter	}
109341016Sdfr    }
109445789Speter
109541016Sdfr    /*
109641016Sdfr     * both the aux port and the aux device is functioning, see if the
109741016Sdfr     * device can be enabled. NOTE: when enabled, the device will start
109841016Sdfr     * sending data; we shall immediately disable the device once we know
109941016Sdfr     * the device can be enabled.
110041016Sdfr     */
110141016Sdfr    if (!enable_aux_dev(sc->kbdc) || !disable_aux_dev(sc->kbdc)) {
110245789Speter        /* MOUSE ERROR */
110345789Speter	recover_from_error(sc->kbdc);
110445789Speter	restore_controller(sc->kbdc, command_byte);
110545789Speter	if (verbose)
110645789Speter	    printf("psm%d: failed to enable the aux device.\n", unit);
110741016Sdfr        endprobe(ENXIO);
110841016Sdfr    }
110941016Sdfr
111041016Sdfr    /* save the default values after reset */
111141016Sdfr    if (get_mouse_status(sc->kbdc, stat, 0, 3) >= 3) {
111241016Sdfr	sc->dflt_mode.rate = sc->mode.rate = stat[2];
111341016Sdfr	sc->dflt_mode.resolution = sc->mode.resolution = stat[1];
111441016Sdfr    } else {
111541016Sdfr	sc->dflt_mode.rate = sc->mode.rate = -1;
111641016Sdfr	sc->dflt_mode.resolution = sc->mode.resolution = -1;
111741016Sdfr    }
111841016Sdfr
111941016Sdfr    /* hardware information */
112041016Sdfr    sc->hw.iftype = MOUSE_IF_PS2;
112141016Sdfr
112241016Sdfr    /* verify the device is a mouse */
112341016Sdfr    sc->hw.hwid = get_aux_id(sc->kbdc);
112441016Sdfr    if (!is_a_mouse(sc->hw.hwid)) {
112541016Sdfr        restore_controller(sc->kbdc, command_byte);
112641016Sdfr        if (verbose)
112741016Sdfr            printf("psm%d: unknown device type (%d).\n", unit, sc->hw.hwid);
112841016Sdfr        endprobe(ENXIO);
112941016Sdfr    }
113041016Sdfr    switch (sc->hw.hwid) {
113141016Sdfr    case PSM_BALLPOINT_ID:
113241016Sdfr        sc->hw.type = MOUSE_TRACKBALL;
113341016Sdfr        break;
113441016Sdfr    case PSM_MOUSE_ID:
113541016Sdfr    case PSM_INTELLI_ID:
113658230Syokota    case PSM_EXPLORER_ID:
113758230Syokota    case PSM_4DMOUSE_ID:
113858230Syokota    case PSM_4DPLUS_ID:
113941016Sdfr        sc->hw.type = MOUSE_MOUSE;
114041016Sdfr        break;
114141016Sdfr    default:
114241016Sdfr        sc->hw.type = MOUSE_UNKNOWN;
114341016Sdfr        break;
114441016Sdfr    }
114541016Sdfr
114645789Speter    if (sc->config & PSM_CONFIG_NOIDPROBE) {
114745789Speter	sc->hw.buttons = 2;
114845789Speter	i = GENERIC_MOUSE_ENTRY;
114945789Speter    } else {
115045789Speter	/* # of buttons */
115145789Speter	sc->hw.buttons = get_mouse_buttons(sc->kbdc);
115241016Sdfr
115345789Speter	/* other parameters */
115445789Speter	for (i = 0; vendortype[i].probefunc != NULL; ++i) {
115545789Speter	    if ((*vendortype[i].probefunc)(sc)) {
115645789Speter		if (verbose >= 2)
115745789Speter		    printf("psm%d: found %s\n",
115845789Speter			   unit, model_name(vendortype[i].model));
115945789Speter		break;
116045789Speter	    }
116141016Sdfr	}
116241016Sdfr    }
116341016Sdfr
116441016Sdfr    sc->hw.model = vendortype[i].model;
116541016Sdfr
116641016Sdfr    sc->dflt_mode.level = PSM_LEVEL_BASE;
116741016Sdfr    sc->dflt_mode.packetsize = MOUSE_PS2_PACKETSIZE;
116841016Sdfr    sc->dflt_mode.accelfactor = (sc->config & PSM_CONFIG_ACCEL) >> 4;
116941016Sdfr    if (sc->config & PSM_CONFIG_NOCHECKSYNC)
117041016Sdfr        sc->dflt_mode.syncmask[0] = 0;
117141016Sdfr    else
117241016Sdfr        sc->dflt_mode.syncmask[0] = vendortype[i].syncmask;
117345789Speter    if (sc->config & PSM_CONFIG_FORCETAP)
1174134405Sgibbs        sc->dflt_mode.syncmask[0] &= ~MOUSE_PS2_TAP;
117541016Sdfr    sc->dflt_mode.syncmask[1] = 0;	/* syncbits */
117641016Sdfr    sc->mode = sc->dflt_mode;
117741016Sdfr    sc->mode.packetsize = vendortype[i].packetsize;
117841016Sdfr
117941016Sdfr    /* set mouse parameters */
118048773Syokota#if 0
118148773Syokota    /*
118248773Syokota     * A version of Logitech FirstMouse+ won't report wheel movement,
118348773Syokota     * if SET_DEFAULTS is sent...  Don't use this command.
118448773Syokota     * This fix was found by Takashi Nishida.
118548773Syokota     */
118641016Sdfr    i = send_aux_command(sc->kbdc, PSMC_SET_DEFAULTS);
118741016Sdfr    if (verbose >= 2)
118841016Sdfr	printf("psm%d: SET_DEFAULTS return code:%04x\n", unit, i);
118948773Syokota#endif
119041016Sdfr    if (sc->config & PSM_CONFIG_RESOLUTION) {
119141016Sdfr        sc->mode.resolution
119241016Sdfr	    = set_mouse_resolution(sc->kbdc,
119348773Syokota				   (sc->config & PSM_CONFIG_RESOLUTION) - 1);
119448773Syokota    } else if (sc->mode.resolution >= 0) {
119548773Syokota	sc->mode.resolution
119648773Syokota	    = set_mouse_resolution(sc->kbdc, sc->dflt_mode.resolution);
119741016Sdfr    }
119848773Syokota    if (sc->mode.rate > 0) {
119948773Syokota	sc->mode.rate = set_mouse_sampling_rate(sc->kbdc, sc->dflt_mode.rate);
120048773Syokota    }
120148773Syokota    set_mouse_scaling(sc->kbdc, 1);
120241016Sdfr
1203133918Sgibbs    /* Record sync on the next data packet we see. */
1204133918Sgibbs    sc->flags |= PSM_NEED_SYNCBITS;
120541016Sdfr
120641016Sdfr    /* just check the status of the mouse */
120741016Sdfr    /*
120841016Sdfr     * NOTE: XXX there are some arcane controller/mouse combinations out
120941016Sdfr     * there, which hung the controller unless there is data transmission
121041016Sdfr     * after ACK from the mouse.
121141016Sdfr     */
121241016Sdfr    if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) {
121341016Sdfr        printf("psm%d: failed to get status.\n", unit);
121441016Sdfr    } else {
121541016Sdfr	/*
121641016Sdfr	 * When in its native mode, some mice operate with different
121741016Sdfr	 * default parameters than in the PS/2 compatible mode.
121841016Sdfr	 */
121941016Sdfr        sc->dflt_mode.rate = sc->mode.rate = stat[2];
122041016Sdfr        sc->dflt_mode.resolution = sc->mode.resolution = stat[1];
122141016Sdfr     }
122241016Sdfr
122341016Sdfr    /* disable the aux port for now... */
122441016Sdfr    if (!set_controller_command_byte(sc->kbdc,
122541016Sdfr	    KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS,
122641016Sdfr            (command_byte & KBD_KBD_CONTROL_BITS)
122741016Sdfr                | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
122841016Sdfr        /*
122941016Sdfr	 * this is CONTROLLER ERROR; I don't know the proper way to
123041016Sdfr         * recover from this error...
123141016Sdfr	 */
123241016Sdfr        restore_controller(sc->kbdc, command_byte);
123341016Sdfr        printf("psm%d: unable to set the command byte.\n", unit);
123441016Sdfr        endprobe(ENXIO);
123541016Sdfr    }
123641016Sdfr
123741016Sdfr    /* done */
123841016Sdfr    kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS);
123941016Sdfr    kbdc_lock(sc->kbdc, FALSE);
124041016Sdfr    return (0);
124141016Sdfr}
124241016Sdfr
124341016Sdfrstatic int
124441016Sdfrpsmattach(device_t dev)
124541016Sdfr{
124641016Sdfr    int unit = device_get_unit(dev);
124741016Sdfr    struct psm_softc *sc = device_get_softc(dev);
124858230Syokota    int error;
124958230Syokota    int rid;
125041016Sdfr
125141016Sdfr    /* Setup initial state */
125241016Sdfr    sc->state = PSM_VALID;
125358230Syokota    callout_handle_init(&sc->callout);
125441016Sdfr
125558230Syokota    /* Setup our interrupt handler */
125683147Syokota    rid = KBDC_RID_AUX;
1257127135Snjl    sc->intr = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
1258127135Snjl				      RF_SHAREABLE | RF_ACTIVE);
125958230Syokota    if (sc->intr == NULL)
126058230Syokota	return (ENXIO);
126183147Syokota    error = bus_setup_intr(dev, sc->intr, INTR_TYPE_TTY, psmintr, sc, &sc->ih);
126258230Syokota    if (error) {
126358230Syokota	bus_release_resource(dev, SYS_RES_IRQ, rid, sc->intr);
126458230Syokota	return (error);
126558230Syokota    }
126658230Syokota
126741016Sdfr    /* Done */
126858230Syokota    sc->dev = make_dev(&psm_cdevsw, PSM_MKMINOR(unit, FALSE), 0, 0, 0666,
126958230Syokota		       "psm%d", unit);
127058230Syokota    sc->bdev = make_dev(&psm_cdevsw, PSM_MKMINOR(unit, TRUE), 0, 0, 0666,
127158230Syokota			"bpsm%d", unit);
127241016Sdfr
127341016Sdfr    if (!verbose) {
127441016Sdfr        printf("psm%d: model %s, device ID %d\n",
127548778Syokota	    unit, model_name(sc->hw.model), sc->hw.hwid & 0x00ff);
127641016Sdfr    } else {
127748778Syokota        printf("psm%d: model %s, device ID %d-%02x, %d buttons\n",
127848778Syokota	    unit, model_name(sc->hw.model),
127948778Syokota	    sc->hw.hwid & 0x00ff, sc->hw.hwid >> 8, sc->hw.buttons);
128041016Sdfr	printf("psm%d: config:%08x, flags:%08x, packet size:%d\n",
128141016Sdfr	    unit, sc->config, sc->flags, sc->mode.packetsize);
128241016Sdfr	printf("psm%d: syncmask:%02x, syncbits:%02x\n",
128341016Sdfr	    unit, sc->mode.syncmask[0], sc->mode.syncmask[1]);
128441016Sdfr    }
128541016Sdfr
128641016Sdfr    if (bootverbose)
128741016Sdfr        --verbose;
128841016Sdfr
128941016Sdfr    return (0);
129041016Sdfr}
129141016Sdfr
129241016Sdfrstatic int
129358230Syokotapsmdetach(device_t dev)
129458230Syokota{
129558230Syokota    struct psm_softc *sc;
129658230Syokota    int rid;
129758230Syokota
129858230Syokota    sc = device_get_softc(dev);
129958230Syokota    if (sc->state & PSM_OPEN)
130058230Syokota	return EBUSY;
130158230Syokota
130283147Syokota    rid = KBDC_RID_AUX;
130383147Syokota    bus_teardown_intr(dev, sc->intr, sc->ih);
130458230Syokota    bus_release_resource(dev, SYS_RES_IRQ, rid, sc->intr);
130558230Syokota
130658230Syokota    destroy_dev(sc->dev);
130758230Syokota    destroy_dev(sc->bdev);
130858230Syokota
130958230Syokota    return 0;
131058230Syokota}
131158230Syokota
131258230Syokotastatic int
1313130585Sphkpsmopen(struct cdev *dev, int flag, int fmt, struct thread *td)
131441016Sdfr{
131541016Sdfr    int unit = PSM_UNIT(dev);
131641016Sdfr    struct psm_softc *sc;
131741016Sdfr    int command_byte;
131841016Sdfr    int err;
131941016Sdfr    int s;
132041016Sdfr
132141016Sdfr    /* Get device data */
132241016Sdfr    sc = PSM_SOFTC(unit);
132341016Sdfr    if ((sc == NULL) || (sc->state & PSM_VALID) == 0)
132441016Sdfr	/* the device is no longer valid/functioning */
132541016Sdfr        return (ENXIO);
132641016Sdfr
132741016Sdfr    /* Disallow multiple opens */
132841016Sdfr    if (sc->state & PSM_OPEN)
132941016Sdfr        return (EBUSY);
133041016Sdfr
133141016Sdfr    device_busy(devclass_get_device(psm_devclass, unit));
133241016Sdfr
133341016Sdfr    /* Initialize state */
133441016Sdfr    sc->mode.level = sc->dflt_mode.level;
133541016Sdfr    sc->mode.protocol = sc->dflt_mode.protocol;
133658230Syokota    sc->watchdog = FALSE;
133741016Sdfr
133841016Sdfr    /* flush the event queue */
133941016Sdfr    sc->queue.count = 0;
134041016Sdfr    sc->queue.head = 0;
134141016Sdfr    sc->queue.tail = 0;
134241016Sdfr    sc->status.flags = 0;
134341016Sdfr    sc->status.button = 0;
134441016Sdfr    sc->status.obutton = 0;
134541016Sdfr    sc->status.dx = 0;
134641016Sdfr    sc->status.dy = 0;
134741016Sdfr    sc->status.dz = 0;
134841016Sdfr    sc->button = 0;
1349123442Salfred    sc->pqueue_start = 0;
1350123442Salfred    sc->pqueue_end = 0;
135141016Sdfr
135241016Sdfr    /* empty input buffer */
1353123442Salfred    flushpackets(sc);
135484880Syokota    sc->syncerrors = 0;
1355134405Sgibbs    sc->pkterrors = 0;
135641016Sdfr
135741016Sdfr    /* don't let timeout routines in the keyboard driver to poll the kbdc */
135841016Sdfr    if (!kbdc_lock(sc->kbdc, TRUE))
135941016Sdfr	return (EIO);
136041016Sdfr
136141016Sdfr    /* save the current controller command byte */
136241016Sdfr    s = spltty();
136341016Sdfr    command_byte = get_controller_command_byte(sc->kbdc);
136441016Sdfr
136541016Sdfr    /* enable the aux port and temporalily disable the keyboard */
136641016Sdfr    if ((command_byte == -1)
136741016Sdfr        || !set_controller_command_byte(sc->kbdc,
136841016Sdfr	    kbdc_get_device_mask(sc->kbdc),
136941016Sdfr  	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
137041016Sdfr	        | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
137141016Sdfr        /* CONTROLLER ERROR; do you know how to get out of this? */
137241016Sdfr        kbdc_lock(sc->kbdc, FALSE);
137341016Sdfr	splx(s);
137441016Sdfr	log(LOG_ERR, "psm%d: unable to set the command byte (psmopen).\n",
137541016Sdfr	    unit);
137641016Sdfr	return (EIO);
137741016Sdfr    }
137841016Sdfr    /*
137941016Sdfr     * Now that the keyboard controller is told not to generate
138041016Sdfr     * the keyboard and mouse interrupts, call `splx()' to allow
138141016Sdfr     * the other tty interrupts. The clock interrupt may also occur,
138241016Sdfr     * but timeout routines will be blocked by the poll flag set
138341016Sdfr     * via `kbdc_lock()'
138441016Sdfr     */
138541016Sdfr    splx(s);
138641016Sdfr
138741016Sdfr    /* enable the mouse device */
138884880Syokota    err = doopen(sc, command_byte);
138941016Sdfr
139041016Sdfr    /* done */
139141016Sdfr    if (err == 0)
139241016Sdfr        sc->state |= PSM_OPEN;
139341016Sdfr    kbdc_lock(sc->kbdc, FALSE);
139441016Sdfr    return (err);
139541016Sdfr}
139641016Sdfr
139741016Sdfrstatic int
1398130585Sphkpsmclose(struct cdev *dev, int flag, int fmt, struct thread *td)
139941016Sdfr{
140041016Sdfr    int unit = PSM_UNIT(dev);
140141016Sdfr    struct psm_softc *sc = PSM_SOFTC(unit);
140241016Sdfr    int stat[3];
140341016Sdfr    int command_byte;
140441016Sdfr    int s;
140541016Sdfr
140641016Sdfr    /* don't let timeout routines in the keyboard driver to poll the kbdc */
140741016Sdfr    if (!kbdc_lock(sc->kbdc, TRUE))
140841016Sdfr	return (EIO);
140941016Sdfr
141041016Sdfr    /* save the current controller command byte */
141141016Sdfr    s = spltty();
141241016Sdfr    command_byte = get_controller_command_byte(sc->kbdc);
141341016Sdfr    if (command_byte == -1) {
141441016Sdfr        kbdc_lock(sc->kbdc, FALSE);
141541016Sdfr	splx(s);
141641016Sdfr	return (EIO);
141741016Sdfr    }
141841016Sdfr
141941016Sdfr    /* disable the aux interrupt and temporalily disable the keyboard */
142041016Sdfr    if (!set_controller_command_byte(sc->kbdc,
142141016Sdfr	    kbdc_get_device_mask(sc->kbdc),
142241016Sdfr  	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
142341016Sdfr	        | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
142441016Sdfr	log(LOG_ERR, "psm%d: failed to disable the aux int (psmclose).\n",
142558230Syokota	    unit);
142641016Sdfr	/* CONTROLLER ERROR;
142741016Sdfr	 * NOTE: we shall force our way through. Because the only
142841016Sdfr	 * ill effect we shall see is that we may not be able
142941016Sdfr	 * to read ACK from the mouse, and it doesn't matter much
143041016Sdfr	 * so long as the mouse will accept the DISABLE command.
143141016Sdfr	 */
143241016Sdfr    }
143341016Sdfr    splx(s);
143441016Sdfr
143558230Syokota    /* stop the watchdog timer */
143684880Syokota    untimeout(psmtimeout, (void *)(uintptr_t)sc, sc->callout);
143758230Syokota    callout_handle_init(&sc->callout);
143858230Syokota
143941016Sdfr    /* remove anything left in the output buffer */
144041016Sdfr    empty_aux_buffer(sc->kbdc, 10);
144141016Sdfr
144241016Sdfr    /* disable the aux device, port and interrupt */
144341016Sdfr    if (sc->state & PSM_VALID) {
144441016Sdfr        if (!disable_aux_dev(sc->kbdc)) {
144541016Sdfr	    /* MOUSE ERROR;
144641016Sdfr	     * NOTE: we don't return error and continue, pretending
144741016Sdfr	     * we have successfully disabled the device. It's OK because
144841016Sdfr	     * the interrupt routine will discard any data from the mouse
144941016Sdfr	     * hereafter.
145041016Sdfr	     */
145141016Sdfr	    log(LOG_ERR, "psm%d: failed to disable the device (psmclose).\n",
145258230Syokota		unit);
145341016Sdfr        }
145441016Sdfr
145541016Sdfr        if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3)
1456134405Sgibbs            log(LOG_DEBUG, "psm%d: failed to get status (psmclose).\n", unit);
145741016Sdfr    }
145841016Sdfr
145941016Sdfr    if (!set_controller_command_byte(sc->kbdc,
146041016Sdfr	    kbdc_get_device_mask(sc->kbdc),
146141016Sdfr	    (command_byte & KBD_KBD_CONTROL_BITS)
146241016Sdfr	        | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
146341016Sdfr	/* CONTROLLER ERROR;
146441016Sdfr	 * we shall ignore this error; see the above comment.
146541016Sdfr	 */
146641016Sdfr	log(LOG_ERR, "psm%d: failed to disable the aux port (psmclose).\n",
146758230Syokota	    unit);
146841016Sdfr    }
146941016Sdfr
147041016Sdfr    /* remove anything left in the output buffer */
147141016Sdfr    empty_aux_buffer(sc->kbdc, 10);
147241016Sdfr
147341016Sdfr    /* close is almost always successful */
147441016Sdfr    sc->state &= ~PSM_OPEN;
147541016Sdfr    kbdc_lock(sc->kbdc, FALSE);
147641016Sdfr    device_unbusy(devclass_get_device(psm_devclass, unit));
147741016Sdfr    return (0);
147841016Sdfr}
147941016Sdfr
148041016Sdfrstatic int
1481123442Salfredtame_mouse(struct psm_softc *sc, packetbuf_t *pb, mousestatus_t *status, unsigned char *buf)
148241016Sdfr{
148341016Sdfr    static unsigned char butmapps2[8] = {
148441016Sdfr        0,
148541016Sdfr        MOUSE_PS2_BUTTON1DOWN,
148641016Sdfr        MOUSE_PS2_BUTTON2DOWN,
148741016Sdfr        MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN,
148841016Sdfr        MOUSE_PS2_BUTTON3DOWN,
148941016Sdfr        MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON3DOWN,
149041016Sdfr        MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN,
149141016Sdfr        MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN,
149241016Sdfr    };
149341016Sdfr    static unsigned char butmapmsc[8] = {
149441016Sdfr        MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
149541016Sdfr        MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
149641016Sdfr        MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP,
149741016Sdfr        MOUSE_MSC_BUTTON3UP,
149841016Sdfr        MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP,
149941016Sdfr        MOUSE_MSC_BUTTON2UP,
150041016Sdfr        MOUSE_MSC_BUTTON1UP,
150141016Sdfr        0,
150241016Sdfr    };
150341016Sdfr    int mapped;
150441016Sdfr    int i;
150541016Sdfr
150641016Sdfr    if (sc->mode.level == PSM_LEVEL_BASE) {
150741016Sdfr        mapped = status->button & ~MOUSE_BUTTON4DOWN;
150841016Sdfr        if (status->button & MOUSE_BUTTON4DOWN)
150941016Sdfr	    mapped |= MOUSE_BUTTON1DOWN;
151041016Sdfr        status->button = mapped;
151141016Sdfr        buf[0] = MOUSE_PS2_SYNC | butmapps2[mapped & MOUSE_STDBUTTONS];
1512109269Smdodd        i = imax(imin(status->dx, 255), -256);
151341016Sdfr	if (i < 0)
151441016Sdfr	    buf[0] |= MOUSE_PS2_XNEG;
151541016Sdfr        buf[1] = i;
1516109269Smdodd        i = imax(imin(status->dy, 255), -256);
151741016Sdfr	if (i < 0)
151841016Sdfr	    buf[0] |= MOUSE_PS2_YNEG;
151941016Sdfr        buf[2] = i;
152041016Sdfr	return MOUSE_PS2_PACKETSIZE;
152141016Sdfr    } else if (sc->mode.level == PSM_LEVEL_STANDARD) {
152241016Sdfr        buf[0] = MOUSE_MSC_SYNC | butmapmsc[status->button & MOUSE_STDBUTTONS];
1523109269Smdodd        i = imax(imin(status->dx, 255), -256);
152441016Sdfr        buf[1] = i >> 1;
152541016Sdfr        buf[3] = i - buf[1];
1526109269Smdodd        i = imax(imin(status->dy, 255), -256);
152741016Sdfr        buf[2] = i >> 1;
152841016Sdfr        buf[4] = i - buf[2];
1529109269Smdodd        i = imax(imin(status->dz, 127), -128);
153041016Sdfr        buf[5] = (i >> 1) & 0x7f;
153141016Sdfr        buf[6] = (i - (i >> 1)) & 0x7f;
153241016Sdfr        buf[7] = (~status->button >> 3) & 0x7f;
153341016Sdfr	return MOUSE_SYS_PACKETSIZE;
153441016Sdfr    }
1535123442Salfred    return pb->inputbytes;
153641016Sdfr}
153741016Sdfr
153841016Sdfrstatic int
1539130585Sphkpsmread(struct cdev *dev, struct uio *uio, int flag)
154041016Sdfr{
154141016Sdfr    register struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev));
154241016Sdfr    unsigned char buf[PSM_SMALLBUFSIZE];
154341016Sdfr    int error = 0;
154441016Sdfr    int s;
154541016Sdfr    int l;
154641016Sdfr
154741016Sdfr    if ((sc->state & PSM_VALID) == 0)
154841016Sdfr	return EIO;
154941016Sdfr
155041016Sdfr    /* block until mouse activity occured */
155141016Sdfr    s = spltty();
155241016Sdfr    while (sc->queue.count <= 0) {
155341016Sdfr        if (PSM_NBLOCKIO(dev)) {
155441016Sdfr            splx(s);
155541016Sdfr            return EWOULDBLOCK;
155641016Sdfr        }
155741016Sdfr        sc->state |= PSM_ASLP;
1558111748Sdes        error = tsleep( sc, PZERO | PCATCH, "psmrea", 0);
155941016Sdfr        sc->state &= ~PSM_ASLP;
156041016Sdfr        if (error) {
156141016Sdfr            splx(s);
156241016Sdfr            return error;
156341016Sdfr        } else if ((sc->state & PSM_VALID) == 0) {
156441016Sdfr            /* the device disappeared! */
156541016Sdfr            splx(s);
156641016Sdfr            return EIO;
156741016Sdfr	}
156841016Sdfr    }
156941016Sdfr    splx(s);
157041016Sdfr
157141016Sdfr    /* copy data to the user land */
157241016Sdfr    while ((sc->queue.count > 0) && (uio->uio_resid > 0)) {
157341016Sdfr        s = spltty();
1574109269Smdodd	l = imin(sc->queue.count, uio->uio_resid);
157541016Sdfr	if (l > sizeof(buf))
157641016Sdfr	    l = sizeof(buf);
157741016Sdfr	if (l > sizeof(sc->queue.buf) - sc->queue.head) {
157841016Sdfr	    bcopy(&sc->queue.buf[sc->queue.head], &buf[0],
157941016Sdfr		sizeof(sc->queue.buf) - sc->queue.head);
158041016Sdfr	    bcopy(&sc->queue.buf[0],
158141016Sdfr		&buf[sizeof(sc->queue.buf) - sc->queue.head],
158241016Sdfr		l - (sizeof(sc->queue.buf) - sc->queue.head));
158341016Sdfr	} else {
158441016Sdfr	    bcopy(&sc->queue.buf[sc->queue.head], &buf[0], l);
158541016Sdfr	}
158641016Sdfr	sc->queue.count -= l;
158741016Sdfr	sc->queue.head = (sc->queue.head + l) % sizeof(sc->queue.buf);
158841016Sdfr        splx(s);
158941016Sdfr        error = uiomove(buf, l, uio);
159041016Sdfr        if (error)
159141016Sdfr	    break;
159241016Sdfr    }
159341016Sdfr
159441016Sdfr    return error;
159541016Sdfr}
159641016Sdfr
159741016Sdfrstatic int
159841016Sdfrblock_mouse_data(struct psm_softc *sc, int *c)
159941016Sdfr{
160041016Sdfr    int s;
160141016Sdfr
160241016Sdfr    if (!kbdc_lock(sc->kbdc, TRUE))
160341016Sdfr	return EIO;
160441016Sdfr
160541016Sdfr    s = spltty();
160641016Sdfr    *c = get_controller_command_byte(sc->kbdc);
160741016Sdfr    if ((*c == -1)
160841016Sdfr	|| !set_controller_command_byte(sc->kbdc,
160941016Sdfr	    kbdc_get_device_mask(sc->kbdc),
161041016Sdfr            KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
161141016Sdfr                | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
161241016Sdfr        /* this is CONTROLLER ERROR */
161341016Sdfr	splx(s);
161441016Sdfr        kbdc_lock(sc->kbdc, FALSE);
161541016Sdfr	return EIO;
161641016Sdfr    }
161741016Sdfr
161841016Sdfr    /*
161941016Sdfr     * The device may be in the middle of status data transmission.
162041016Sdfr     * The transmission will be interrupted, thus, incomplete status
162141016Sdfr     * data must be discarded. Although the aux interrupt is disabled
162241016Sdfr     * at the keyboard controller level, at most one aux interrupt
162341016Sdfr     * may have already been pending and a data byte is in the
162441016Sdfr     * output buffer; throw it away. Note that the second argument
162541016Sdfr     * to `empty_aux_buffer()' is zero, so that the call will just
162641016Sdfr     * flush the internal queue.
162741016Sdfr     * `psmintr()' will be invoked after `splx()' if an interrupt is
162841016Sdfr     * pending; it will see no data and returns immediately.
162941016Sdfr     */
163041016Sdfr    empty_aux_buffer(sc->kbdc, 0);	/* flush the queue */
163141016Sdfr    read_aux_data_no_wait(sc->kbdc);	/* throw away data if any */
1632123442Salfred    flushpackets(sc);
163341016Sdfr    splx(s);
163441016Sdfr
163541016Sdfr    return 0;
163641016Sdfr}
163741016Sdfr
1638123442Salfredstatic void
1639123442Salfreddropqueue(struct psm_softc *sc)
1640123442Salfred{
1641123442Salfred
1642123442Salfred    	sc->queue.count = 0;
1643123442Salfred   	sc->queue.head = 0;
1644123442Salfred    	sc->queue.tail = 0;
1645123442Salfred	if ((sc->state & PSM_SOFTARMED) != 0) {
1646123442Salfred		sc->state &= ~PSM_SOFTARMED;
1647123442Salfred		untimeout(psmsoftintr, (void *)(uintptr_t)sc, sc->softcallout);
1648123442Salfred	}
1649123442Salfred	sc->pqueue_start = sc->pqueue_end;
1650123442Salfred}
1651123442Salfred
1652123442Salfredstatic void
1653123442Salfredflushpackets(struct psm_softc *sc)
1654123442Salfred{
1655123442Salfred
1656123442Salfred	dropqueue(sc);
1657123442Salfred	bzero(&sc->pqueue, sizeof(sc->pqueue));
1658123442Salfred}
1659123442Salfred
166041016Sdfrstatic int
166141016Sdfrunblock_mouse_data(struct psm_softc *sc, int c)
166241016Sdfr{
166341016Sdfr    int error = 0;
166441016Sdfr
166541016Sdfr    /*
166641016Sdfr     * We may have seen a part of status data during `set_mouse_XXX()'.
166741016Sdfr     * they have been queued; flush it.
166841016Sdfr     */
166941016Sdfr    empty_aux_buffer(sc->kbdc, 0);
167041016Sdfr
167141016Sdfr    /* restore ports and interrupt */
167241016Sdfr    if (!set_controller_command_byte(sc->kbdc,
167341016Sdfr            kbdc_get_device_mask(sc->kbdc),
167441016Sdfr	    c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) {
167541016Sdfr        /* CONTROLLER ERROR; this is serious, we may have
167641016Sdfr         * been left with the inaccessible keyboard and
167741016Sdfr         * the disabled mouse interrupt.
167841016Sdfr         */
167941016Sdfr        error = EIO;
168041016Sdfr    }
168141016Sdfr
168241016Sdfr    kbdc_lock(sc->kbdc, FALSE);
168341016Sdfr    return error;
168441016Sdfr}
168541016Sdfr
168641016Sdfrstatic int
1687130585Sphkpsmioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
168841016Sdfr{
168941016Sdfr    struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev));
169041016Sdfr    mousemode_t mode;
169141016Sdfr    mousestatus_t status;
169241016Sdfr#if (defined(MOUSE_GETVARS))
169341016Sdfr    mousevar_t *var;
169441016Sdfr#endif
169541016Sdfr    mousedata_t *data;
169641016Sdfr    int stat[3];
169741016Sdfr    int command_byte;
169841016Sdfr    int error = 0;
169941016Sdfr    int s;
170041016Sdfr
170141016Sdfr    /* Perform IOCTL command */
170241016Sdfr    switch (cmd) {
170341016Sdfr
170441016Sdfr    case OLD_MOUSE_GETHWINFO:
170541016Sdfr	s = spltty();
170641016Sdfr        ((old_mousehw_t *)addr)->buttons = sc->hw.buttons;
170741016Sdfr        ((old_mousehw_t *)addr)->iftype = sc->hw.iftype;
170841016Sdfr        ((old_mousehw_t *)addr)->type = sc->hw.type;
170948778Syokota        ((old_mousehw_t *)addr)->hwid = sc->hw.hwid & 0x00ff;
171041016Sdfr	splx(s);
171141016Sdfr        break;
171241016Sdfr
171341016Sdfr    case MOUSE_GETHWINFO:
171441016Sdfr	s = spltty();
171541016Sdfr        *(mousehw_t *)addr = sc->hw;
171641016Sdfr	if (sc->mode.level == PSM_LEVEL_BASE)
171741016Sdfr	    ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC;
171841016Sdfr	splx(s);
171941016Sdfr        break;
172041016Sdfr
1721132865Snjl    case MOUSE_SYN_GETHWINFO:
1722132865Snjl	s = spltty();
1723135945Sphilip	if (synaptics_support && sc->hw.model == MOUSE_MODEL_SYNAPTICS)
1724132865Snjl	    *(synapticshw_t *)addr = sc->synhw;
1725132865Snjl	else
1726132865Snjl	    error = EINVAL;
1727132865Snjl	splx(s);
1728132865Snjl	break;
1729132865Snjl
173041016Sdfr    case OLD_MOUSE_GETMODE:
173141016Sdfr	s = spltty();
173241016Sdfr	switch (sc->mode.level) {
173341016Sdfr	case PSM_LEVEL_BASE:
173441016Sdfr	    ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
173541016Sdfr	    break;
173641016Sdfr	case PSM_LEVEL_STANDARD:
173741016Sdfr	    ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE;
173841016Sdfr	    break;
173941016Sdfr	case PSM_LEVEL_NATIVE:
174041016Sdfr	    ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
174141016Sdfr	    break;
174241016Sdfr	}
174341016Sdfr        ((old_mousemode_t *)addr)->rate = sc->mode.rate;
174441016Sdfr        ((old_mousemode_t *)addr)->resolution = sc->mode.resolution;
174541016Sdfr        ((old_mousemode_t *)addr)->accelfactor = sc->mode.accelfactor;
174641016Sdfr	splx(s);
174741016Sdfr        break;
174841016Sdfr
174941016Sdfr    case MOUSE_GETMODE:
175041016Sdfr	s = spltty();
175141016Sdfr        *(mousemode_t *)addr = sc->mode;
1752134405Sgibbs	if ((sc->flags & PSM_NEED_SYNCBITS) != 0) {
1753134405Sgibbs	    ((mousemode_t *)addr)->syncmask[0] = 0;
1754134405Sgibbs	    ((mousemode_t *)addr)->syncmask[1] = 0;
1755134405Sgibbs	}
175641016Sdfr        ((mousemode_t *)addr)->resolution =
175741016Sdfr	    MOUSE_RES_LOW - sc->mode.resolution;
175841016Sdfr	switch (sc->mode.level) {
175941016Sdfr	case PSM_LEVEL_BASE:
176041016Sdfr	    ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
176141016Sdfr	    ((mousemode_t *)addr)->packetsize = MOUSE_PS2_PACKETSIZE;
176241016Sdfr	    break;
176341016Sdfr	case PSM_LEVEL_STANDARD:
176441016Sdfr	    ((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE;
176541016Sdfr	    ((mousemode_t *)addr)->packetsize = MOUSE_SYS_PACKETSIZE;
176641016Sdfr	    ((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK;
176741016Sdfr	    ((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC;
176841016Sdfr	    break;
176941016Sdfr	case PSM_LEVEL_NATIVE:
177041016Sdfr	    /* FIXME: this isn't quite correct... XXX */
177141016Sdfr	    ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
177241016Sdfr	    break;
177341016Sdfr	}
177441016Sdfr	splx(s);
177541016Sdfr        break;
177641016Sdfr
177741016Sdfr    case OLD_MOUSE_SETMODE:
177841016Sdfr    case MOUSE_SETMODE:
177941016Sdfr	if (cmd == OLD_MOUSE_SETMODE) {
178041016Sdfr	    mode.rate = ((old_mousemode_t *)addr)->rate;
178141016Sdfr	    /*
178241016Sdfr	     * resolution  old I/F   new I/F
178341016Sdfr	     * default        0         0
178441016Sdfr	     * low            1        -2
178541016Sdfr	     * medium low     2        -3
178641016Sdfr	     * medium high    3        -4
178741016Sdfr	     * high           4        -5
178841016Sdfr	     */
178941016Sdfr	    if (((old_mousemode_t *)addr)->resolution > 0)
179041016Sdfr	        mode.resolution = -((old_mousemode_t *)addr)->resolution - 1;
179141016Sdfr	    mode.accelfactor = ((old_mousemode_t *)addr)->accelfactor;
179241016Sdfr	    mode.level = -1;
179341016Sdfr	} else {
179441016Sdfr	    mode = *(mousemode_t *)addr;
179541016Sdfr	}
179641016Sdfr
179741016Sdfr	/* adjust and validate parameters. */
179841016Sdfr	if (mode.rate > UCHAR_MAX)
179941016Sdfr	    return EINVAL;
180041016Sdfr        if (mode.rate == 0)
180141016Sdfr            mode.rate = sc->dflt_mode.rate;
180241016Sdfr	else if (mode.rate == -1)
180341016Sdfr	    /* don't change the current setting */
180441016Sdfr	    ;
180541016Sdfr	else if (mode.rate < 0)
180641016Sdfr	    return EINVAL;
180741016Sdfr	if (mode.resolution >= UCHAR_MAX)
180841016Sdfr	    return EINVAL;
180941016Sdfr	if (mode.resolution >= 200)
181041016Sdfr	    mode.resolution = MOUSE_RES_HIGH;
181141016Sdfr	else if (mode.resolution >= 100)
181241016Sdfr	    mode.resolution = MOUSE_RES_MEDIUMHIGH;
181341016Sdfr	else if (mode.resolution >= 50)
181441016Sdfr	    mode.resolution = MOUSE_RES_MEDIUMLOW;
181541016Sdfr	else if (mode.resolution > 0)
181641016Sdfr	    mode.resolution = MOUSE_RES_LOW;
181741016Sdfr        if (mode.resolution == MOUSE_RES_DEFAULT)
181841016Sdfr            mode.resolution = sc->dflt_mode.resolution;
181941016Sdfr        else if (mode.resolution == -1)
182041016Sdfr	    /* don't change the current setting */
182141016Sdfr	    ;
182241016Sdfr        else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */
182341016Sdfr            mode.resolution = MOUSE_RES_LOW - mode.resolution;
182441016Sdfr	if (mode.level == -1)
182541016Sdfr	    /* don't change the current setting */
182641016Sdfr	    mode.level = sc->mode.level;
182741016Sdfr	else if ((mode.level < PSM_LEVEL_MIN) || (mode.level > PSM_LEVEL_MAX))
182841016Sdfr	    return EINVAL;
182941016Sdfr        if (mode.accelfactor == -1)
183041016Sdfr	    /* don't change the current setting */
183141016Sdfr	    mode.accelfactor = sc->mode.accelfactor;
183241016Sdfr        else if (mode.accelfactor < 0)
183341016Sdfr	    return EINVAL;
183441016Sdfr
183541016Sdfr	/* don't allow anybody to poll the keyboard controller */
183641016Sdfr	error = block_mouse_data(sc, &command_byte);
183741016Sdfr	if (error)
183841016Sdfr            return error;
183941016Sdfr
184041016Sdfr        /* set mouse parameters */
184141016Sdfr	if (mode.rate > 0)
184241016Sdfr	    mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate);
184341016Sdfr	if (mode.resolution >= 0)
184441016Sdfr	    mode.resolution = set_mouse_resolution(sc->kbdc, mode.resolution);
184541016Sdfr	set_mouse_scaling(sc->kbdc, 1);
184641016Sdfr	get_mouse_status(sc->kbdc, stat, 0, 3);
184741016Sdfr
184841016Sdfr        s = spltty();
184941016Sdfr    	sc->mode.rate = mode.rate;
185041016Sdfr    	sc->mode.resolution = mode.resolution;
185141016Sdfr    	sc->mode.accelfactor = mode.accelfactor;
185241016Sdfr    	sc->mode.level = mode.level;
185341016Sdfr        splx(s);
185441016Sdfr
185541016Sdfr	unblock_mouse_data(sc, command_byte);
185641016Sdfr        break;
185741016Sdfr
185841016Sdfr    case MOUSE_GETLEVEL:
185941016Sdfr	*(int *)addr = sc->mode.level;
186041016Sdfr        break;
186141016Sdfr
186241016Sdfr    case MOUSE_SETLEVEL:
186341016Sdfr	if ((*(int *)addr < PSM_LEVEL_MIN) || (*(int *)addr > PSM_LEVEL_MAX))
186441016Sdfr	    return EINVAL;
186541016Sdfr	sc->mode.level = *(int *)addr;
186641016Sdfr        break;
186741016Sdfr
186841016Sdfr    case MOUSE_GETSTATUS:
186941016Sdfr        s = spltty();
187041016Sdfr	status = sc->status;
187141016Sdfr	sc->status.flags = 0;
187241016Sdfr	sc->status.obutton = sc->status.button;
187341016Sdfr	sc->status.button = 0;
187441016Sdfr	sc->status.dx = 0;
187541016Sdfr	sc->status.dy = 0;
187641016Sdfr	sc->status.dz = 0;
187741016Sdfr        splx(s);
187841016Sdfr        *(mousestatus_t *)addr = status;
187941016Sdfr        break;
188041016Sdfr
188141016Sdfr#if (defined(MOUSE_GETVARS))
188241016Sdfr    case MOUSE_GETVARS:
188341016Sdfr	var = (mousevar_t *)addr;
188441016Sdfr	bzero(var, sizeof(*var));
188541016Sdfr	s = spltty();
188641016Sdfr        var->var[0] = MOUSE_VARS_PS2_SIG;
188741016Sdfr        var->var[1] = sc->config;
188841016Sdfr        var->var[2] = sc->flags;
188941016Sdfr	splx(s);
189041016Sdfr        break;
189141016Sdfr
189241016Sdfr    case MOUSE_SETVARS:
189341016Sdfr	return ENODEV;
189441016Sdfr#endif /* MOUSE_GETVARS */
189541016Sdfr
189641016Sdfr    case MOUSE_READSTATE:
189741016Sdfr    case MOUSE_READDATA:
189841016Sdfr	data = (mousedata_t *)addr;
189941016Sdfr	if (data->len > sizeof(data->buf)/sizeof(data->buf[0]))
190041016Sdfr	    return EINVAL;
190141016Sdfr
190241016Sdfr	error = block_mouse_data(sc, &command_byte);
190341016Sdfr	if (error)
190441016Sdfr            return error;
190541016Sdfr        if ((data->len = get_mouse_status(sc->kbdc, data->buf,
190641016Sdfr		(cmd == MOUSE_READDATA) ? 1 : 0, data->len)) <= 0)
190741016Sdfr            error = EIO;
190841016Sdfr	unblock_mouse_data(sc, command_byte);
190941016Sdfr	break;
191041016Sdfr
191141016Sdfr#if (defined(MOUSE_SETRESOLUTION))
191241016Sdfr    case MOUSE_SETRESOLUTION:
191341016Sdfr	mode.resolution = *(int *)addr;
191441016Sdfr	if (mode.resolution >= UCHAR_MAX)
191541016Sdfr	    return EINVAL;
191641016Sdfr	else if (mode.resolution >= 200)
191741016Sdfr	    mode.resolution = MOUSE_RES_HIGH;
191841016Sdfr	else if (mode.resolution >= 100)
191941016Sdfr	    mode.resolution = MOUSE_RES_MEDIUMHIGH;
192041016Sdfr	else if (mode.resolution >= 50)
192141016Sdfr	    mode.resolution = MOUSE_RES_MEDIUMLOW;
192241016Sdfr	else if (mode.resolution > 0)
192341016Sdfr	    mode.resolution = MOUSE_RES_LOW;
192441016Sdfr        if (mode.resolution == MOUSE_RES_DEFAULT)
192541016Sdfr            mode.resolution = sc->dflt_mode.resolution;
192641016Sdfr        else if (mode.resolution == -1)
192741016Sdfr	    mode.resolution = sc->mode.resolution;
192841016Sdfr        else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */
192941016Sdfr            mode.resolution = MOUSE_RES_LOW - mode.resolution;
193041016Sdfr
193141016Sdfr	error = block_mouse_data(sc, &command_byte);
193241016Sdfr	if (error)
193341016Sdfr            return error;
193441016Sdfr        sc->mode.resolution = set_mouse_resolution(sc->kbdc, mode.resolution);
193541016Sdfr	if (sc->mode.resolution != mode.resolution)
193641016Sdfr	    error = EIO;
193741016Sdfr	unblock_mouse_data(sc, command_byte);
193841016Sdfr        break;
193941016Sdfr#endif /* MOUSE_SETRESOLUTION */
194041016Sdfr
194141016Sdfr#if (defined(MOUSE_SETRATE))
194241016Sdfr    case MOUSE_SETRATE:
194341016Sdfr	mode.rate = *(int *)addr;
194441016Sdfr	if (mode.rate > UCHAR_MAX)
194541016Sdfr	    return EINVAL;
194641016Sdfr        if (mode.rate == 0)
194741016Sdfr            mode.rate = sc->dflt_mode.rate;
194841016Sdfr	else if (mode.rate < 0)
194941016Sdfr	    mode.rate = sc->mode.rate;
195041016Sdfr
195141016Sdfr	error = block_mouse_data(sc, &command_byte);
195241016Sdfr	if (error)
195341016Sdfr            return error;
195441016Sdfr        sc->mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate);
195541016Sdfr	if (sc->mode.rate != mode.rate)
195641016Sdfr	    error = EIO;
195741016Sdfr	unblock_mouse_data(sc, command_byte);
195841016Sdfr        break;
195941016Sdfr#endif /* MOUSE_SETRATE */
196041016Sdfr
196141016Sdfr#if (defined(MOUSE_SETSCALING))
196241016Sdfr    case MOUSE_SETSCALING:
196341016Sdfr	if ((*(int *)addr <= 0) || (*(int *)addr > 2))
196441016Sdfr	    return EINVAL;
196541016Sdfr
196641016Sdfr	error = block_mouse_data(sc, &command_byte);
196741016Sdfr	if (error)
196841016Sdfr            return error;
196941016Sdfr        if (!set_mouse_scaling(sc->kbdc, *(int *)addr))
197041016Sdfr	    error = EIO;
197141016Sdfr	unblock_mouse_data(sc, command_byte);
197241016Sdfr        break;
197341016Sdfr#endif /* MOUSE_SETSCALING */
197441016Sdfr
197541016Sdfr#if (defined(MOUSE_GETHWID))
197641016Sdfr    case MOUSE_GETHWID:
197741016Sdfr	error = block_mouse_data(sc, &command_byte);
197841016Sdfr	if (error)
197941016Sdfr            return error;
198048778Syokota        sc->hw.hwid &= ~0x00ff;
198148778Syokota        sc->hw.hwid |= get_aux_id(sc->kbdc);
198248778Syokota	*(int *)addr = sc->hw.hwid & 0x00ff;
198341016Sdfr	unblock_mouse_data(sc, command_byte);
198441016Sdfr        break;
198541016Sdfr#endif /* MOUSE_GETHWID */
198641016Sdfr
198741016Sdfr    default:
198841016Sdfr	return ENOTTY;
198941016Sdfr    }
199041016Sdfr
199141016Sdfr    return error;
199241016Sdfr}
199341016Sdfr
199441016Sdfrstatic void
199558230Syokotapsmtimeout(void *arg)
199658230Syokota{
199758230Syokota    struct psm_softc *sc;
199865045Syokota    int s;
199958230Syokota
200084880Syokota    sc = (struct psm_softc *)arg;
200165045Syokota    s = spltty();
200263746Syokota    if (sc->watchdog && kbdc_lock(sc->kbdc, TRUE)) {
2003134405Sgibbs	VLOG(4, (LOG_DEBUG, "psm%d: lost interrupt?\n", sc->unit));
200458230Syokota	psmintr(sc);
200563746Syokota	kbdc_lock(sc->kbdc, FALSE);
200658230Syokota    }
200758230Syokota    sc->watchdog = TRUE;
200865045Syokota    splx(s);
200984880Syokota    sc->callout = timeout(psmtimeout, (void *)(uintptr_t)sc, hz);
201058230Syokota}
201158230Syokota
2012123442Salfredstatic int psmhz = 20;
2013123442SalfredSYSCTL_INT(_debug, OID_AUTO, psmhz, CTLFLAG_RW, &psmhz, 0, "");
2014123442Salfred
2015123442Salfredstatic int psm_soft_timeout = 500000; /* 0.5 sec */
2016123442SalfredSYSCTL_INT(_debug, OID_AUTO, psm_soft_timeout, CTLFLAG_RW,
2017123442Salfred    &psm_soft_timeout, 0, "");
2018123442Salfred
2019123442Salfredstatic int psmerrsecs = 2;
2020123442SalfredSYSCTL_INT(_debug, OID_AUTO, psmerrsecs, CTLFLAG_RW, &psmerrsecs, 0, "");
2021123442Salfredstatic int psmerrusecs = 0;
2022123442SalfredSYSCTL_INT(_debug, OID_AUTO, psmerrusecs, CTLFLAG_RW, &psmerrusecs, 0, "");
2023123442Salfredstatic int psmsecs = 0;
2024123442SalfredSYSCTL_INT(_debug, OID_AUTO, psmsecs, CTLFLAG_RW, &psmsecs, 0, "");
2025123442Salfredstatic int psmusecs = 500000;
2026123442SalfredSYSCTL_INT(_debug, OID_AUTO, psmusecs, CTLFLAG_RW, &psmusecs, 0, "");
2027123442Salfred
2028134405Sgibbsstatic int psmpkterrthresh = 2;
2029134405SgibbsSYSCTL_INT(_debug, OID_AUTO, psmpkterrthresh, CTLFLAG_RW,
2030134405Sgibbs    &psmpkterrthresh, 0, "");
2031134405Sgibbs
2032134405SgibbsSYSCTL_INT(_debug, OID_AUTO, psmloglevel, CTLFLAG_RW, &verbose, 0, "");
2033134405Sgibbs
2034134405Sgibbs
203558230Syokotastatic void
203641016Sdfrpsmintr(void *arg)
203741016Sdfr{
2038123442Salfred    struct psm_softc *sc = arg;
2039123442Salfred    struct timeval now;
204041016Sdfr    int c;
2041123442Salfred    packetbuf_t *pb;
204241016Sdfr
2043123442Salfred
204441016Sdfr    /* read until there is nothing to read */
204541016Sdfr    while((c = read_aux_data_no_wait(sc->kbdc)) != -1) {
204641016Sdfr
2047123442Salfred        pb = &sc->pqueue[sc->pqueue_end];
204841016Sdfr        /* discard the byte if the device is not open */
204941016Sdfr        if ((sc->state & PSM_OPEN) == 0)
205041016Sdfr            continue;
205141016Sdfr
2052123442Salfred	getmicrouptime(&now);
2053123442Salfred	if ((pb->inputbytes > 0) && timevalcmp(&now, &sc->inputtimeout, >)) {
2054134405Sgibbs	    VLOG(3, (LOG_DEBUG, "psmintr: delay too long; "
2055134405Sgibbs		 "resetting byte count\n"));
2056123442Salfred	    pb->inputbytes = 0;
205784880Syokota	    sc->syncerrors = 0;
2058134405Sgibbs	    sc->pkterrors = 0;
205984880Syokota	}
206084880Syokota	sc->inputtimeout.tv_sec = PSM_INPUT_TIMEOUT/1000000;
206184880Syokota	sc->inputtimeout.tv_usec = PSM_INPUT_TIMEOUT%1000000;
2062123442Salfred	timevaladd(&sc->inputtimeout, &now);
206384880Syokota
2064123442Salfred        pb->ipacket[pb->inputbytes++] = c;
2065123442Salfred        if (pb->inputbytes < sc->mode.packetsize)
206641016Sdfr	    continue;
206741016Sdfr
2068134405Sgibbs	VLOG(4, (LOG_DEBUG, "psmintr: %02x %02x %02x %02x %02x %02x\n",
2069134405Sgibbs	     pb->ipacket[0], pb->ipacket[1], pb->ipacket[2],
2070134405Sgibbs	     pb->ipacket[3], pb->ipacket[4], pb->ipacket[5]));
207141016Sdfr
2072123442Salfred	c = pb->ipacket[0];
207341016Sdfr
2074134405Sgibbs	if ((sc->flags & PSM_NEED_SYNCBITS) != 0) {
2075134405Sgibbs	    sc->mode.syncmask[1] = (c & sc->mode.syncmask[0]);
2076134405Sgibbs	    sc->flags &= ~PSM_NEED_SYNCBITS;
2077134405Sgibbs	    VLOG(2, (LOG_DEBUG, "psmintr: Sync bytes now %04x,%04x\n",
2078134405Sgibbs	    	 sc->mode.syncmask[0], sc->mode.syncmask[0]));
2079134405Sgibbs	} else if ((c & sc->mode.syncmask[0]) != sc->mode.syncmask[1]) {
2080134405Sgibbs	    VLOG(3, (LOG_DEBUG, "psmintr: out of sync (%04x != %04x) %d"
2081134405Sgibbs		 " cmds since last error.\n",
2082134405Sgibbs		 c & sc->mode.syncmask[0], sc->mode.syncmask[1],
2083134405Sgibbs		 sc->cmdcount - sc->lasterr));
2084123442Salfred	    sc->lasterr = sc->cmdcount;
2085134405Sgibbs	    /*
2086134405Sgibbs	     * The sync byte test is a weak measure of packet
2087134405Sgibbs	     * validity.  Conservatively discard any input yet
2088134405Sgibbs	     * to be seen by userland when we detect a sync
2089134405Sgibbs	     * error since there is a good chance some of
2090134405Sgibbs	     * the queued packets have undetected errors.
2091134405Sgibbs	     */
2092123442Salfred	    dropqueue(sc);
2093134405Sgibbs	    if (sc->syncerrors == 0)
2094134405Sgibbs		sc->pkterrors++;
209584880Syokota	    ++sc->syncerrors;
2096123442Salfred	    sc->lastinputerr = now;
2097134405Sgibbs	    if (sc->syncerrors >= sc->mode.packetsize * 2 ||
2098134405Sgibbs	        sc->pkterrors >= psmpkterrthresh) {
2099134405Sgibbs
2100134405Sgibbs		/*
2101134405Sgibbs		 * If we've failed to find a single sync byte in 2
2102134405Sgibbs		 * packets worth of data, or we've seen persistent
2103134405Sgibbs		 * packet errors during the validation period,
2104134405Sgibbs		 * reinitialize the mouse in hopes of returning it
2105134405Sgibbs		 * to the expected mode.
2106134405Sgibbs		 */
2107134405Sgibbs		VLOG(3, (LOG_DEBUG, "psmintr: reset the mouse.\n"));
2108134405Sgibbs		reinitialize(sc, TRUE);
210984880Syokota	    } else if (sc->syncerrors == sc->mode.packetsize) {
2110134405Sgibbs
2111134405Sgibbs		/*
2112134405Sgibbs		 * Try a soft reset after searching for a sync
2113134405Sgibbs		 * byte through a packet length of bytes.
2114134405Sgibbs		 */
2115134405Sgibbs		VLOG(3, (LOG_DEBUG, "psmintr: re-enable the mouse.\n"));
2116123442Salfred		pb->inputbytes = 0;
211769439Syokota		disable_aux_dev(sc->kbdc);
211869439Syokota		enable_aux_dev(sc->kbdc);
2119134405Sgibbs	    } else {
2120134405Sgibbs		VLOG(3, (LOG_DEBUG, "psmintr: discard a byte (%d)\n",
2121134405Sgibbs		     sc->syncerrors));
2122134405Sgibbs		pb->inputbytes--;
2123123442Salfred		bcopy(&pb->ipacket[1], &pb->ipacket[0], pb->inputbytes);
212469439Syokota	    }
212584880Syokota	    continue;
212663746Syokota	}
2127134405Sgibbs
2128134405Sgibbs	/*
2129134405Sgibbs	 * We have what appears to be a valid packet.
2130134405Sgibbs	 * Reset the error counters.
2131134405Sgibbs	 */
2132134405Sgibbs	sc->syncerrors = 0;
2133134405Sgibbs
2134134405Sgibbs	/*
2135134405Sgibbs	 * Drop even good packets if they occur within a timeout
2136134405Sgibbs	 * period of a sync error.  This allows the detection of
2137134405Sgibbs	 * a change in the mouse's packet mode without exposing
2138134405Sgibbs	 * erratic mouse behavior to the user.  Some KVMs forget
2139134405Sgibbs	 * enhanced mouse modes during switch events.
2140134405Sgibbs	 */
2141134405Sgibbs	if (!timeelapsed(&sc->lastinputerr, psmerrsecs, psmerrusecs, &now)) {
2142123442Salfred		pb->inputbytes = 0;
2143123442Salfred		continue;
2144123442Salfred	}
214563746Syokota
2146134405Sgibbs	/*
2147134405Sgibbs	 * Now that we're out of the validation period, reset
2148134405Sgibbs	 * the packet error count.
2149134405Sgibbs	 */
2150134405Sgibbs	sc->pkterrors = 0;
2151134405Sgibbs
2152123442Salfred	sc->cmdcount++;
2153123442Salfred	if (++sc->pqueue_end >= PSM_PACKETQUEUE)
2154123442Salfred		sc->pqueue_end = 0;
2155123442Salfred	/*
2156123442Salfred	 * If we've filled the queue then call the softintr ourselves,
2157123442Salfred	 * otherwise schedule the interrupt for later.
2158123442Salfred	 */
2159123442Salfred	if (!timeelapsed(&sc->lastsoftintr, psmsecs, psmusecs, &now) ||
2160123442Salfred	    (sc->pqueue_end == sc->pqueue_start)) {
2161123442Salfred    		if ((sc->state & PSM_SOFTARMED) != 0) {
2162123442Salfred			sc->state &= ~PSM_SOFTARMED;
2163123442Salfred			untimeout(psmsoftintr, arg, sc->softcallout);
2164123442Salfred		}
2165123442Salfred		psmsoftintr(arg);
2166123442Salfred	} else if ((sc->state & PSM_SOFTARMED) == 0) {
2167123442Salfred		sc->state |= PSM_SOFTARMED;
2168123442Salfred		sc->softcallout = timeout(psmsoftintr, arg,
2169123442Salfred		    psmhz < 1 ? 1 : (hz/psmhz));
2170123442Salfred	}
2171123442Salfred    }
2172123442Salfred}
2173123442Salfred
2174123442Salfredstatic void
2175123442Salfredpsmsoftintr(void *arg)
2176123442Salfred{
2177123442Salfred    /*
2178123442Salfred     * the table to turn PS/2 mouse button bits (MOUSE_PS2_BUTTON?DOWN)
2179123442Salfred     * into `mousestatus' button bits (MOUSE_BUTTON?DOWN).
2180123442Salfred     */
2181123442Salfred    static int butmap[8] = {
2182123442Salfred        0,
2183123442Salfred	MOUSE_BUTTON1DOWN,
2184123442Salfred	MOUSE_BUTTON3DOWN,
2185123442Salfred	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
2186123442Salfred	MOUSE_BUTTON2DOWN,
2187123442Salfred	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
2188123442Salfred	MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
2189123442Salfred        MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
2190123442Salfred    };
2191123442Salfred    static int butmap_versapad[8] = {
2192123442Salfred	0,
2193123442Salfred	MOUSE_BUTTON3DOWN,
2194123442Salfred	0,
2195123442Salfred	MOUSE_BUTTON3DOWN,
2196123442Salfred	MOUSE_BUTTON1DOWN,
2197123442Salfred	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
2198123442Salfred	MOUSE_BUTTON1DOWN,
2199123442Salfred	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN
2200123442Salfred    };
2201133882Sphilip    static int touchpad_buttons;
2202133882Sphilip    static int guest_buttons;
2203123442Salfred    register struct psm_softc *sc = arg;
2204123442Salfred    mousestatus_t ms;
2205132865Snjl    int w, x, y, z;
2206123442Salfred    int c;
2207123442Salfred    int l;
2208123442Salfred    int x0, y0;
2209123442Salfred    int s;
2210123442Salfred    packetbuf_t *pb;
2211123442Salfred
2212123442Salfred    getmicrouptime(&sc->lastsoftintr);
2213123442Salfred
2214123442Salfred    s = spltty();
2215123442Salfred
2216123442Salfred    do {
2217123442Salfred
2218123442Salfred	pb = &sc->pqueue[sc->pqueue_start];
2219123442Salfred	c = pb->ipacket[0];
222049965Syokota	/*
222141016Sdfr	 * A kludge for Kensington device!
222241016Sdfr	 * The MSB of the horizontal count appears to be stored in
222358230Syokota	 * a strange place.
222441016Sdfr	 */
222558230Syokota	if (sc->hw.model == MOUSE_MODEL_THINK)
2226123442Salfred	    pb->ipacket[1] |= (c & MOUSE_PS2_XOVERFLOW) ? 0x80 : 0;
222741016Sdfr
222841016Sdfr        /* ignore the overflow bits... */
2229123442Salfred        x = (c & MOUSE_PS2_XNEG) ?  pb->ipacket[1] - 256 : pb->ipacket[1];
2230123442Salfred        y = (c & MOUSE_PS2_YNEG) ?  pb->ipacket[2] - 256 : pb->ipacket[2];
223141016Sdfr	z = 0;
223241016Sdfr        ms.obutton = sc->button;		  /* previous button state */
223341016Sdfr        ms.button = butmap[c & MOUSE_PS2_BUTTONS];
223445789Speter	/* `tapping' action */
223545789Speter	if (sc->config & PSM_CONFIG_FORCETAP)
223645789Speter	    ms.button |= ((c & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN;
223741016Sdfr
223841016Sdfr	switch (sc->hw.model) {
223941016Sdfr
224058230Syokota	case MOUSE_MODEL_EXPLORER:
224158230Syokota	    /*
224258230Syokota	     *          b7 b6 b5 b4 b3 b2 b1 b0
224358230Syokota	     * byte 1:  oy ox sy sx 1  M  R  L
224458230Syokota	     * byte 2:  x  x  x  x  x  x  x  x
224558230Syokota	     * byte 3:  y  y  y  y  y  y  y  y
224658230Syokota	     * byte 4:  *  *  S2 S1 s  d2 d1 d0
224758230Syokota	     *
224858230Syokota	     * L, M, R, S1, S2: left, middle, right and side buttons
224958230Syokota	     * s: wheel data sign bit
225058230Syokota	     * d2-d0: wheel data
225158230Syokota	     */
2252123442Salfred	    z = (pb->ipacket[3] & MOUSE_EXPLORER_ZNEG)
2253123442Salfred		? (pb->ipacket[3] & 0x0f) - 16 : (pb->ipacket[3] & 0x0f);
2254123442Salfred	    ms.button |= (pb->ipacket[3] & MOUSE_EXPLORER_BUTTON4DOWN)
225558230Syokota		? MOUSE_BUTTON4DOWN : 0;
2256123442Salfred	    ms.button |= (pb->ipacket[3] & MOUSE_EXPLORER_BUTTON5DOWN)
225758230Syokota		? MOUSE_BUTTON5DOWN : 0;
225858230Syokota	    break;
225958230Syokota
226041016Sdfr	case MOUSE_MODEL_INTELLI:
226141016Sdfr	case MOUSE_MODEL_NET:
226241016Sdfr	    /* wheel data is in the fourth byte */
2263123442Salfred	    z = (char)pb->ipacket[3];
226458230Syokota	    /* some mice may send 7 when there is no Z movement?! XXX */
226558230Syokota	    if ((z >= 7) || (z <= -7))
226658230Syokota		z = 0;
226758230Syokota	    /* some compatible mice have additional buttons */
226858230Syokota	    ms.button |= (c & MOUSE_PS2INTELLI_BUTTON4DOWN)
226958230Syokota		? MOUSE_BUTTON4DOWN : 0;
227058230Syokota	    ms.button |= (c & MOUSE_PS2INTELLI_BUTTON5DOWN)
227158230Syokota		? MOUSE_BUTTON5DOWN : 0;
227241016Sdfr	    break;
227341016Sdfr
227441016Sdfr	case MOUSE_MODEL_MOUSEMANPLUS:
227548778Syokota	    /*
227648778Syokota	     * PS2++ protocl packet
227748778Syokota	     *
227848778Syokota	     *          b7 b6 b5 b4 b3 b2 b1 b0
227948778Syokota	     * byte 1:  *  1  p3 p2 1  *  *  *
228048778Syokota	     * byte 2:  c1 c2 p1 p0 d1 d0 1  0
228148778Syokota	     *
228248778Syokota	     * p3-p0: packet type
228348778Syokota	     * c1, c2: c1 & c2 == 1, if p2 == 0
228448778Syokota	     *         c1 & c2 == 0, if p2 == 1
228548778Syokota	     *
228648778Syokota	     * packet type: 0 (device type)
228748778Syokota	     * See comments in enable_mmanplus() below.
228848778Syokota	     *
228948778Syokota	     * packet type: 1 (wheel data)
229048778Syokota	     *
229148778Syokota	     *          b7 b6 b5 b4 b3 b2 b1 b0
229248778Syokota	     * byte 3:  h  *  B5 B4 s  d2 d1 d0
229348778Syokota	     *
229448778Syokota	     * h: 1, if horizontal roller data
229548778Syokota	     *    0, if vertical roller data
229648778Syokota	     * B4, B5: button 4 and 5
229748778Syokota	     * s: sign bit
229848778Syokota	     * d2-d0: roller data
229948778Syokota	     *
230048778Syokota	     * packet type: 2 (reserved)
230148778Syokota	     */
230248778Syokota	    if (((c & MOUSE_PS2PLUS_SYNCMASK) == MOUSE_PS2PLUS_SYNC)
230348778Syokota		    && (abs(x) > 191)
2304123442Salfred		    && MOUSE_PS2PLUS_CHECKBITS(pb->ipacket)) {
230541016Sdfr		/* the extended data packet encodes button and wheel events */
2306123442Salfred		switch (MOUSE_PS2PLUS_PACKET_TYPE(pb->ipacket)) {
230748778Syokota		case 1:
230848778Syokota		    /* wheel data packet */
230948778Syokota		    x = y = 0;
2310123442Salfred		    if (pb->ipacket[2] & 0x80) {
231148778Syokota			/* horizontal roller count - ignore it XXX*/
231248778Syokota		    } else {
231348778Syokota			/* vertical roller count */
2314123442Salfred			z = (pb->ipacket[2] & MOUSE_PS2PLUS_ZNEG)
2315123442Salfred			    ? (pb->ipacket[2] & 0x0f) - 16
2316123442Salfred			    : (pb->ipacket[2] & 0x0f);
231748778Syokota		    }
2318123442Salfred		    ms.button |= (pb->ipacket[2] & MOUSE_PS2PLUS_BUTTON4DOWN)
231948778Syokota			? MOUSE_BUTTON4DOWN : 0;
2320123442Salfred		    ms.button |= (pb->ipacket[2] & MOUSE_PS2PLUS_BUTTON5DOWN)
232148778Syokota			? MOUSE_BUTTON5DOWN : 0;
232248778Syokota		    break;
232348778Syokota		case 2:
232458230Syokota		    /* this packet type is reserved by Logitech... */
232558230Syokota		    /*
232658230Syokota		     * IBM ScrollPoint Mouse uses this packet type to
232758230Syokota		     * encode both vertical and horizontal scroll movement.
232858230Syokota		     */
232958230Syokota		    x = y = 0;
233058230Syokota		    /* horizontal count */
2331123442Salfred		    if (pb->ipacket[2] & 0x0f)
2332123442Salfred			z = (pb->ipacket[2] & MOUSE_SPOINT_WNEG) ? -2 : 2;
233358230Syokota		    /* vertical count */
2334123442Salfred		    if (pb->ipacket[2] & 0xf0)
2335123442Salfred			z = (pb->ipacket[2] & MOUSE_SPOINT_ZNEG) ? -1 : 1;
233658230Syokota#if 0
233758230Syokota		    /* vertical count */
2338123442Salfred		    z = (pb->ipacket[2] & MOUSE_SPOINT_ZNEG)
2339123442Salfred			? ((pb->ipacket[2] >> 4) & 0x0f) - 16
2340123442Salfred			: ((pb->ipacket[2] >> 4) & 0x0f);
234158230Syokota		    /* horizontal count */
2342123442Salfred		    w = (pb->ipacket[2] & MOUSE_SPOINT_WNEG)
2343123442Salfred			? (pb->ipacket[2] & 0x0f) - 16
2344123442Salfred			: (pb->ipacket[2] & 0x0f);
234558230Syokota#endif
234658230Syokota		    break;
234748778Syokota		case 0:
234848778Syokota		    /* device type packet - shouldn't happen */
2349102412Scharnier		    /* FALLTHROUGH */
235048778Syokota		default:
235148778Syokota		    x = y = 0;
235248778Syokota		    ms.button = ms.obutton;
2353134405Sgibbs		    VLOG(1, (LOG_DEBUG, "psmintr: unknown PS2++ packet type %d:"
2354134405Sgibbs		         " 0x%02x 0x%02x 0x%02x\n",
2355134405Sgibbs			 MOUSE_PS2PLUS_PACKET_TYPE(pb->ipacket),
2356134405Sgibbs			 pb->ipacket[0], pb->ipacket[1], pb->ipacket[2]));
235748778Syokota		    break;
235848778Syokota		}
235941016Sdfr	    } else {
236041016Sdfr		/* preserve button states */
236141016Sdfr		ms.button |= ms.obutton & MOUSE_EXTBUTTONS;
236241016Sdfr	    }
236341016Sdfr	    break;
236441016Sdfr
236541016Sdfr	case MOUSE_MODEL_GLIDEPOINT:
236641016Sdfr	    /* `tapping' action */
236741016Sdfr	    ms.button |= ((c & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN;
236841016Sdfr	    break;
236941016Sdfr
237041016Sdfr	case MOUSE_MODEL_NETSCROLL:
237158230Syokota	    /* three addtional bytes encode buttons and wheel events */
2372123442Salfred	    ms.button |= (pb->ipacket[3] & MOUSE_PS2_BUTTON3DOWN)
237341016Sdfr		? MOUSE_BUTTON4DOWN : 0;
2374123442Salfred	    ms.button |= (pb->ipacket[3] & MOUSE_PS2_BUTTON1DOWN)
237558230Syokota		? MOUSE_BUTTON5DOWN : 0;
2376123442Salfred	    z = (pb->ipacket[3] & MOUSE_PS2_XNEG)
2377123442Salfred		? pb->ipacket[4] - 256 : pb->ipacket[4];
237841016Sdfr	    break;
237941016Sdfr
238041016Sdfr	case MOUSE_MODEL_THINK:
238141016Sdfr	    /* the fourth button state in the first byte */
238241016Sdfr	    ms.button |= (c & MOUSE_PS2_TAP) ? MOUSE_BUTTON4DOWN : 0;
238341016Sdfr	    break;
238441016Sdfr
238549965Syokota	case MOUSE_MODEL_VERSAPAD:
238649965Syokota	    /* VersaPad PS/2 absolute mode message format
238749965Syokota	     *
238849965Syokota	     * [packet1]     7   6   5   4   3   2   1   0(LSB)
238949965Syokota	     *  ipacket[0]:  1   1   0   A   1   L   T   R
239049965Syokota	     *  ipacket[1]: H7  H6  H5  H4  H3  H2  H1  H0
239149965Syokota	     *  ipacket[2]: V7  V6  V5  V4  V3  V2  V1  V0
239249965Syokota	     *  ipacket[3]:  1   1   1   A   1   L   T   R
239349965Syokota	     *  ipacket[4]:V11 V10  V9  V8 H11 H10  H9  H8
239449965Syokota	     *  ipacket[5]:  0  P6  P5  P4  P3  P2  P1  P0
239549965Syokota	     *
239649965Syokota	     * [note]
239749965Syokota	     *  R: right physical mouse button (1=on)
239849965Syokota	     *  T: touch pad virtual button (1=tapping)
239949965Syokota	     *  L: left physical mouse button (1=on)
240049965Syokota	     *  A: position data is valid (1=valid)
240149965Syokota	     *  H: horizontal data (12bit signed integer. H11 is sign bit.)
240249965Syokota	     *  V: vertical data (12bit signed integer. V11 is sign bit.)
240349965Syokota	     *  P: pressure data
240449965Syokota	     *
240549965Syokota	     * Tapping is mapped to MOUSE_BUTTON4.
240649965Syokota	     */
240749965Syokota	    ms.button = butmap_versapad[c & MOUSE_PS2VERSA_BUTTONS];
240849965Syokota	    ms.button |= (c & MOUSE_PS2VERSA_TAP) ? MOUSE_BUTTON4DOWN : 0;
240949965Syokota	    x = y = 0;
241049965Syokota	    if (c & MOUSE_PS2VERSA_IN_USE) {
2411123442Salfred		x0 = pb->ipacket[1] | (((pb->ipacket[4]) & 0x0f) << 8);
2412123442Salfred		y0 = pb->ipacket[2] | (((pb->ipacket[4]) & 0xf0) << 4);
241349965Syokota		if (x0 & 0x800)
241449965Syokota		    x0 -= 0x1000;
241549965Syokota		if (y0 & 0x800)
241649965Syokota		    y0 -= 0x1000;
241749965Syokota		if (sc->flags & PSM_FLAGS_FINGERDOWN) {
241849965Syokota		    x = sc->xold - x0;
241949965Syokota		    y = y0 - sc->yold;
242049965Syokota		    if (x < 0)	/* XXX */
242149965Syokota			x++;
242249965Syokota		    else if (x)
242349965Syokota			x--;
242449965Syokota		    if (y < 0)
242549965Syokota			y++;
242649965Syokota		    else if (y)
242749965Syokota			y--;
242849965Syokota		} else {
242949965Syokota		    sc->flags |= PSM_FLAGS_FINGERDOWN;
243049965Syokota		}
243149965Syokota		sc->xold = x0;
243249965Syokota		sc->yold = y0;
243349965Syokota	    } else {
243449965Syokota		sc->flags &= ~PSM_FLAGS_FINGERDOWN;
243549965Syokota	    }
243649965Syokota	    c = ((x < 0) ? MOUSE_PS2_XNEG : 0)
243749965Syokota		| ((y < 0) ? MOUSE_PS2_YNEG : 0);
243849965Syokota	    break;
243949965Syokota
244058230Syokota	case MOUSE_MODEL_4D:
244158230Syokota	    /*
244258230Syokota	     *          b7 b6 b5 b4 b3 b2 b1 b0
244358230Syokota	     * byte 1:  s2 d2 s1 d1 1  M  R  L
244458230Syokota	     * byte 2:  sx x  x  x  x  x  x  x
244558230Syokota	     * byte 3:  sy y  y  y  y  y  y  y
244658230Syokota	     *
244758230Syokota	     * s1: wheel 1 direction
244858230Syokota	     * d1: wheel 1 data
244958230Syokota	     * s2: wheel 2 direction
245058230Syokota	     * d2: wheel 2 data
245158230Syokota	     */
2452123442Salfred	    x = (pb->ipacket[1] & 0x80) ? pb->ipacket[1] - 256 : pb->ipacket[1];
2453123442Salfred	    y = (pb->ipacket[2] & 0x80) ? pb->ipacket[2] - 256 : pb->ipacket[2];
245458230Syokota	    switch (c & MOUSE_4D_WHEELBITS) {
245558230Syokota	    case 0x10:
245658230Syokota		z = 1;
245758230Syokota		break;
245858230Syokota	    case 0x30:
245958230Syokota		z = -1;
246058230Syokota		break;
246158230Syokota	    case 0x40:	/* 2nd wheel turning right XXX */
246258230Syokota		z = 2;
246358230Syokota		break;
246458230Syokota	    case 0xc0:	/* 2nd wheel turning left XXX */
246558230Syokota		z = -2;
246658230Syokota		break;
246758230Syokota	    }
246858230Syokota	    break;
246958230Syokota
247058230Syokota	case MOUSE_MODEL_4DPLUS:
247158230Syokota	    if ((x < 16 - 256) && (y < 16 - 256)) {
247258230Syokota		/*
247358230Syokota		 *          b7 b6 b5 b4 b3 b2 b1 b0
247458230Syokota		 * byte 1:  0  0  1  1  1  M  R  L
247558230Syokota		 * byte 2:  0  0  0  0  1  0  0  0
247658230Syokota		 * byte 3:  0  0  0  0  S  s  d1 d0
247758230Syokota		 *
247858230Syokota		 * L, M, R, S: left, middle, right and side buttons
247958230Syokota		 * s: wheel data sign bit
248058230Syokota		 * d1-d0: wheel data
248158230Syokota		 */
248258230Syokota		x = y = 0;
2483123442Salfred		if (pb->ipacket[2] & MOUSE_4DPLUS_BUTTON4DOWN)
248458230Syokota		    ms.button |= MOUSE_BUTTON4DOWN;
2485123442Salfred		z = (pb->ipacket[2] & MOUSE_4DPLUS_ZNEG)
2486123442Salfred			? ((pb->ipacket[2] & 0x07) - 8)
2487123442Salfred			: (pb->ipacket[2] & 0x07) ;
248858230Syokota	    } else {
248958230Syokota		/* preserve previous button states */
249058230Syokota		ms.button |= ms.obutton & MOUSE_EXTBUTTONS;
249158230Syokota	    }
249258230Syokota	    break;
249358230Syokota
2494132865Snjl	case MOUSE_MODEL_SYNAPTICS:
2495132865Snjl	    /* TouchPad PS/2 absolute mode message format
2496132865Snjl	     *
2497132865Snjl	     *  Bits:        7   6   5   4   3   2   1   0 (LSB)
2498132865Snjl	     *  ------------------------------------------------
2499132865Snjl	     *  ipacket[0]:  1   0  W3  W2   0  W1   R   L
2500132865Snjl	     *  ipacket[1]: Yb  Ya  Y9  Y8  Xb  Xa  X9  X8
2501132865Snjl	     *  ipacket[2]: Z7  Z6  Z5  Z4  Z3  Z2  Z1  Z0
2502132865Snjl	     *  ipacket[3]:  1   1  Yc  Xc   0  W0   D   U
2503132865Snjl	     *  ipacket[4]: X7  X6  X5  X4  X3  X2  X1  X0
2504132865Snjl	     *  ipacket[5]: Y7  Y6  Y5  Y4  Y3  Y2  Y1  Y0
2505132865Snjl	     *
2506132865Snjl	     * Legend:
2507132865Snjl	     *  L: left physical mouse button
2508132865Snjl	     *  R: right physical mouse button
2509132865Snjl	     *  D: down button
2510132865Snjl	     *  U: up button
2511132865Snjl	     *  W: "wrist" value
2512132865Snjl	     *  X: x position
2513132865Snjl	     *  Y: x position
2514132865Snjl	     *  Z: pressure
2515132865Snjl	     *
2516132865Snjl	     * Absolute reportable limits:    0 - 6143.
2517132865Snjl	     * Typical bezel limits:       1472 - 5472.
2518132865Snjl	     * Typical edge marings:       1632 - 5312.
2519132865Snjl	     *
2520132865Snjl	     * w = 3 Passthrough Packet
2521132865Snjl	     *
2522132865Snjl	     * Byte 2,5,6 == Byte 1,2,3 of "Guest"
2523132865Snjl	     */
2524132865Snjl
2525135945Sphilip	    if (!synaptics_support)
2526135945Sphilip		break;
2527135945Sphilip
2528132865Snjl	    /* Sanity check for out of sync packets. */
2529132865Snjl	    if ((pb->ipacket[0] & 0xc8) != 0x80 ||
2530132865Snjl		(pb->ipacket[3] & 0xc8) != 0xc0)
2531132865Snjl		continue;
2532132865Snjl
2533132865Snjl	    x = y = x0 = y0 = 0;
2534132865Snjl
2535132865Snjl	    /* Pressure value. */
2536132865Snjl	    z = pb->ipacket[2];
2537132865Snjl
2538133301Sphilip	    /* Finger width value */
2539133301Sphilip	    if (sc->synhw.capExtended) {
2540133301Sphilip		w = ((pb->ipacket[0] & 0x30) >> 2) |
2541133301Sphilip		    ((pb->ipacket[0] & 0x04) >> 1) |
2542133301Sphilip		    ((pb->ipacket[3] & 0x04) >> 2);
2543133301Sphilip	    } else {
2544133301Sphilip		/* Assume a finger of regular width */
2545133301Sphilip		w = 4;
2546133301Sphilip	    }
2547133301Sphilip
2548133868Sphilip	    /* Handle packets from the guest device */
2549133868Sphilip	    if (w == 3 && sc->synhw.capPassthrough) {
2550133868Sphilip		x = ((pb->ipacket[1] & 0x10) ?
2551133868Sphilip		    pb->ipacket[4] - 256 : pb->ipacket[4]);
2552133868Sphilip		y = ((pb->ipacket[1] & 0x20) ?
2553133868Sphilip		    pb->ipacket[5] - 256 : pb->ipacket[5]);
2554133868Sphilip		z = 0;
2555133868Sphilip
2556133868Sphilip		guest_buttons = 0;
2557133868Sphilip		if (pb->ipacket[1] & 0x01)
2558133868Sphilip		    guest_buttons |= MOUSE_BUTTON1DOWN;
2559133868Sphilip		if (pb->ipacket[1] & 0x04)
2560133868Sphilip		    guest_buttons |= MOUSE_BUTTON2DOWN;
2561133868Sphilip		if (pb->ipacket[1] & 0x02)
2562133868Sphilip		    guest_buttons |= MOUSE_BUTTON3DOWN;
2563133868Sphilip
2564133868Sphilip		ms.button = touchpad_buttons | guest_buttons;
2565133868Sphilip		break;
2566133868Sphilip	    }
2567133868Sphilip
2568133296Sphilip	    /* Button presses */
2569133868Sphilip	    touchpad_buttons = 0;
2570132865Snjl	    if (pb->ipacket[0] & 0x01)
2571133868Sphilip		  touchpad_buttons |= MOUSE_BUTTON1DOWN;
2572132865Snjl	    if (pb->ipacket[0] & 0x02)
2573133868Sphilip		  touchpad_buttons |= MOUSE_BUTTON3DOWN;
2574132865Snjl
2575133296Sphilip	    if (sc->synhw.capExtended && sc->synhw.capFourButtons) {
2576133296Sphilip		if ((pb->ipacket[3] & 0x01) && (pb->ipacket[0] & 0x01) == 0)
2577133868Sphilip		    touchpad_buttons |= MOUSE_BUTTON4DOWN;
2578133296Sphilip		if ((pb->ipacket[3] & 0x02) && (pb->ipacket[0] & 0x02) == 0)
2579133868Sphilip		    touchpad_buttons |= MOUSE_BUTTON5DOWN;
2580133296Sphilip	    }
2581132865Snjl
2582133868Sphilip	    ms.button = touchpad_buttons | guest_buttons;
2583133868Sphilip
2584132865Snjl	    /* There is a finger on the pad. */
2585132865Snjl	    if ((w >= 4 && w <= 7) && (z >= 16 && z < 200)) {
2586132865Snjl		x0 = ((pb->ipacket[3] & 0x10) << 8) |
2587132865Snjl		    ((pb->ipacket[1] & 0x0f) << 8) |
2588132865Snjl		    pb->ipacket[4];
2589132865Snjl		y0 = ((pb->ipacket[3] & 0x20) << 7) |
2590132865Snjl		    ((pb->ipacket[1] & 0xf0) << 4) |
2591132865Snjl		    pb->ipacket[5];
2592132865Snjl
2593132865Snjl		if (sc->flags & PSM_FLAGS_FINGERDOWN) {
2594132865Snjl		    x0 = (x0 + sc->xold * 3) / 4;
2595132865Snjl		    y0 = (y0 + sc->yold * 3) / 4;
2596132865Snjl
2597133298Sphilip		    x = (x0 - sc->xold) * 10 / 85;
2598133298Sphilip		    y = (y0 - sc->yold) * 10 / 85;
2599132865Snjl		} else {
2600132865Snjl		    sc->flags |= PSM_FLAGS_FINGERDOWN;
2601132865Snjl		}
2602132865Snjl
2603132865Snjl		sc->xold = x0;
2604132865Snjl		sc->yold = y0;
2605133297Sphilip		sc->zmax = imax(z, sc->zmax);
2606132865Snjl	    } else {
2607132865Snjl		sc->flags &= ~PSM_FLAGS_FINGERDOWN;
2608133297Sphilip
2609133297Sphilip		if (sc->zmax > PSM_TAP_THRESHOLD &&
2610133297Sphilip		    timevalcmp(&sc->lastsoftintr, &sc->taptimeout, <=)) {
2611133297Sphilip			if (w == 0)
2612133297Sphilip			    ms.button |= MOUSE_BUTTON3DOWN;
2613133297Sphilip			else if (w == 1)
2614133297Sphilip			    ms.button |= MOUSE_BUTTON2DOWN;
2615133297Sphilip			else
2616133297Sphilip			    ms.button |= MOUSE_BUTTON1DOWN;
2617133297Sphilip		}
2618133297Sphilip
2619133297Sphilip		sc->zmax = 0;
2620133297Sphilip		sc->taptimeout.tv_sec = PSM_TAP_TIMEOUT / 1000000;
2621133297Sphilip		sc->taptimeout.tv_usec = PSM_TAP_TIMEOUT % 1000000;
2622133297Sphilip		timevaladd(&sc->taptimeout, &sc->lastsoftintr);
2623132865Snjl	    }
2624133296Sphilip
2625133296Sphilip	    /* Use the extra buttons as a scrollwheel */
2626133296Sphilip	    if (ms.button & MOUSE_BUTTON4DOWN)
2627133296Sphilip		z = -1;
2628133296Sphilip	    else if (ms.button & MOUSE_BUTTON5DOWN)
2629133296Sphilip		z = 1;
2630133296Sphilip	    else
2631133296Sphilip		z = 0;
2632133296Sphilip
2633132865Snjl	    break;
2634132865Snjl
263541016Sdfr	case MOUSE_MODEL_GENERIC:
263641016Sdfr	default:
263741016Sdfr	    break;
263841016Sdfr	}
263941016Sdfr
264041016Sdfr        /* scale values */
264141016Sdfr        if (sc->mode.accelfactor >= 1) {
264241016Sdfr            if (x != 0) {
264341016Sdfr                x = x * x / sc->mode.accelfactor;
264441016Sdfr                if (x == 0)
264541016Sdfr                    x = 1;
264641016Sdfr                if (c & MOUSE_PS2_XNEG)
264741016Sdfr                    x = -x;
264841016Sdfr            }
264941016Sdfr            if (y != 0) {
265041016Sdfr                y = y * y / sc->mode.accelfactor;
265141016Sdfr                if (y == 0)
265241016Sdfr                    y = 1;
265341016Sdfr                if (c & MOUSE_PS2_YNEG)
265441016Sdfr                    y = -y;
265541016Sdfr            }
265641016Sdfr        }
265741016Sdfr
265841016Sdfr        ms.dx = x;
265941016Sdfr        ms.dy = y;
266041016Sdfr        ms.dz = z;
266141016Sdfr        ms.flags = ((x || y || z) ? MOUSE_POSCHANGED : 0)
266241016Sdfr	    | (ms.obutton ^ ms.button);
266341016Sdfr
266441016Sdfr	if (sc->mode.level < PSM_LEVEL_NATIVE)
2665123442Salfred	    pb->inputbytes = tame_mouse(sc, pb, &ms, pb->ipacket);
266641016Sdfr
266741016Sdfr        sc->status.flags |= ms.flags;
266841016Sdfr        sc->status.dx += ms.dx;
266941016Sdfr        sc->status.dy += ms.dy;
267041016Sdfr        sc->status.dz += ms.dz;
267141016Sdfr        sc->status.button = ms.button;
267241016Sdfr        sc->button = ms.button;
267341016Sdfr
267458230Syokota	sc->watchdog = FALSE;
267558230Syokota
267641016Sdfr        /* queue data */
2677123442Salfred        if (sc->queue.count + pb->inputbytes < sizeof(sc->queue.buf)) {
2678123442Salfred	    l = imin(pb->inputbytes, sizeof(sc->queue.buf) - sc->queue.tail);
2679123442Salfred	    bcopy(&pb->ipacket[0], &sc->queue.buf[sc->queue.tail], l);
2680123442Salfred	    if (pb->inputbytes > l)
2681123442Salfred	        bcopy(&pb->ipacket[l], &sc->queue.buf[0], pb->inputbytes - l);
268241016Sdfr            sc->queue.tail =
2683123442Salfred		(sc->queue.tail + pb->inputbytes) % sizeof(sc->queue.buf);
2684123442Salfred            sc->queue.count += pb->inputbytes;
268541016Sdfr	}
2686123442Salfred        pb->inputbytes = 0;
268741016Sdfr
2688123442Salfred	if (++sc->pqueue_start >= PSM_PACKETQUEUE)
2689123442Salfred		sc->pqueue_start = 0;
2690123442Salfred    } while (sc->pqueue_start != sc->pqueue_end);
2691123442Salfred    if (sc->state & PSM_ASLP) {
2692123442Salfred        sc->state &= ~PSM_ASLP;
2693123442Salfred        wakeup( sc);
269441016Sdfr    }
2695123442Salfred    selwakeuppri(&sc->rsel, PZERO);
2696123442Salfred    sc->state &= ~PSM_SOFTARMED;
2697123442Salfred    splx(s);
269841016Sdfr}
269941016Sdfr
270041016Sdfrstatic int
2701130585Sphkpsmpoll(struct cdev *dev, int events, struct thread *td)
270241016Sdfr{
270341016Sdfr    struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev));
270441016Sdfr    int s;
270541016Sdfr    int revents = 0;
270641016Sdfr
270741016Sdfr    /* Return true if a mouse event available */
270841016Sdfr    s = spltty();
270945789Speter    if (events & (POLLIN | POLLRDNORM)) {
271041016Sdfr	if (sc->queue.count > 0)
271141016Sdfr	    revents |= events & (POLLIN | POLLRDNORM);
271241016Sdfr	else
271383366Sjulian	    selrecord(td, &sc->rsel);
271445789Speter    }
271541016Sdfr    splx(s);
271641016Sdfr
271741016Sdfr    return (revents);
271841016Sdfr}
271941016Sdfr
272041016Sdfr/* vendor/model specific routines */
272141016Sdfr
272241016Sdfrstatic int mouse_id_proc1(KBDC kbdc, int res, int scale, int *status)
272341016Sdfr{
272441016Sdfr    if (set_mouse_resolution(kbdc, res) != res)
272541016Sdfr        return FALSE;
272641016Sdfr    if (set_mouse_scaling(kbdc, scale)
272741016Sdfr	&& set_mouse_scaling(kbdc, scale)
272841016Sdfr	&& set_mouse_scaling(kbdc, scale)
272941016Sdfr	&& (get_mouse_status(kbdc, status, 0, 3) >= 3))
273041016Sdfr	return TRUE;
273141016Sdfr    return FALSE;
273241016Sdfr}
273341016Sdfr
273469438Syokotastatic int
273569438Syokotamouse_ext_command(KBDC kbdc, int command)
273669438Syokota{
273769438Syokota    int c;
273869438Syokota
273969438Syokota    c = (command >> 6) & 0x03;
274069438Syokota    if (set_mouse_resolution(kbdc, c) != c)
274169438Syokota	return FALSE;
274269438Syokota    c = (command >> 4) & 0x03;
274369438Syokota    if (set_mouse_resolution(kbdc, c) != c)
274469438Syokota	return FALSE;
274569438Syokota    c = (command >> 2) & 0x03;
274669438Syokota    if (set_mouse_resolution(kbdc, c) != c)
274769438Syokota	return FALSE;
274869438Syokota    c = (command >> 0) & 0x03;
274969438Syokota    if (set_mouse_resolution(kbdc, c) != c)
275069438Syokota	return FALSE;
275169438Syokota    return TRUE;
275269438Syokota}
275369438Syokota
275441016Sdfr#if notyet
275541016Sdfr/* Logitech MouseMan Cordless II */
275641016Sdfrstatic int
275741016Sdfrenable_lcordless(struct psm_softc *sc)
275841016Sdfr{
275941016Sdfr    int status[3];
276041016Sdfr    int ch;
276141016Sdfr
276241016Sdfr    if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 2, status))
276341016Sdfr        return FALSE;
276441016Sdfr    if (status[1] == PSMD_RES_HIGH)
276541016Sdfr	return FALSE;
276641016Sdfr    ch = (status[0] & 0x07) - 1;	/* channel # */
276741016Sdfr    if ((ch <= 0) || (ch > 4))
276841016Sdfr	return FALSE;
276941016Sdfr    /*
277041016Sdfr     * status[1]: always one?
277141016Sdfr     * status[2]: battery status? (0-100)
277241016Sdfr     */
277341016Sdfr    return TRUE;
277441016Sdfr}
277541016Sdfr#endif /* notyet */
277641016Sdfr
277758230Syokota/* Genius NetScroll Mouse, MouseSystems SmartScroll Mouse */
277841016Sdfrstatic int
277941016Sdfrenable_groller(struct psm_softc *sc)
278041016Sdfr{
278141016Sdfr    int status[3];
278241016Sdfr
278341016Sdfr    /*
278441016Sdfr     * The special sequence to enable the fourth button and the
278541016Sdfr     * roller. Immediately after this sequence check status bytes.
278641016Sdfr     * if the mouse is NetScroll, the second and the third bytes are
278741016Sdfr     * '3' and 'D'.
278841016Sdfr     */
278941016Sdfr
279041016Sdfr    /*
279141016Sdfr     * If the mouse is an ordinary PS/2 mouse, the status bytes should
279241016Sdfr     * look like the following.
279341016Sdfr     *
279441016Sdfr     * byte 1 bit 7 always 0
279541016Sdfr     *        bit 6 stream mode (0)
279641016Sdfr     *        bit 5 disabled (0)
279741016Sdfr     *        bit 4 1:1 scaling (0)
279841016Sdfr     *        bit 3 always 0
279941016Sdfr     *        bit 0-2 button status
280041016Sdfr     * byte 2 resolution (PSMD_RES_HIGH)
280141016Sdfr     * byte 3 report rate (?)
280241016Sdfr     */
280341016Sdfr
280441016Sdfr    if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status))
280541016Sdfr        return FALSE;
280641016Sdfr    if ((status[1] != '3') || (status[2] != 'D'))
280741016Sdfr        return FALSE;
280858230Syokota    /* FIXME: SmartScroll Mouse has 5 buttons! XXX */
280941016Sdfr    sc->hw.buttons = 4;
281041016Sdfr    return TRUE;
281141016Sdfr}
281241016Sdfr
281358230Syokota/* Genius NetMouse/NetMouse Pro, ASCII Mie Mouse, NetScroll Optical */
281441016Sdfrstatic int
281541016Sdfrenable_gmouse(struct psm_softc *sc)
281641016Sdfr{
281741016Sdfr    int status[3];
281841016Sdfr
281941016Sdfr    /*
282041016Sdfr     * The special sequence to enable the middle, "rubber" button.
282141016Sdfr     * Immediately after this sequence check status bytes.
282241016Sdfr     * if the mouse is NetMouse, NetMouse Pro, or ASCII MIE Mouse,
282341016Sdfr     * the second and the third bytes are '3' and 'U'.
282441016Sdfr     * NOTE: NetMouse reports that it has three buttons although it has
282541016Sdfr     * two buttons and a rubber button. NetMouse Pro and MIE Mouse
282641016Sdfr     * say they have three buttons too and they do have a button on the
282741016Sdfr     * side...
282841016Sdfr     */
282941016Sdfr    if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status))
283041016Sdfr        return FALSE;
283141016Sdfr    if ((status[1] != '3') || (status[2] != 'U'))
283241016Sdfr        return FALSE;
283341016Sdfr    return TRUE;
283441016Sdfr}
283541016Sdfr
283641016Sdfr/* ALPS GlidePoint */
283741016Sdfrstatic int
283841016Sdfrenable_aglide(struct psm_softc *sc)
283941016Sdfr{
284041016Sdfr    int status[3];
284141016Sdfr
284241016Sdfr    /*
284341016Sdfr     * The special sequence to obtain ALPS GlidePoint specific
284441016Sdfr     * information. Immediately after this sequence, status bytes will
284541016Sdfr     * contain something interesting.
284641016Sdfr     * NOTE: ALPS produces several models of GlidePoint. Some of those
284741016Sdfr     * do not respond to this sequence, thus, cannot be detected this way.
284841016Sdfr     */
284950149Syokota    if (set_mouse_sampling_rate(sc->kbdc, 100) != 100)
285050149Syokota	return FALSE;
285141016Sdfr    if (!mouse_id_proc1(sc->kbdc, PSMD_RES_LOW, 2, status))
285241016Sdfr        return FALSE;
285350149Syokota    if ((status[1] == PSMD_RES_LOW) || (status[2] == 100))
285441016Sdfr        return FALSE;
285541016Sdfr    return TRUE;
285641016Sdfr}
285741016Sdfr
285841016Sdfr/* Kensington ThinkingMouse/Trackball */
285941016Sdfrstatic int
286041016Sdfrenable_kmouse(struct psm_softc *sc)
286141016Sdfr{
286241016Sdfr    static unsigned char rate[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 };
286341016Sdfr    KBDC kbdc = sc->kbdc;
286441016Sdfr    int status[3];
286541016Sdfr    int id1;
286641016Sdfr    int id2;
286741016Sdfr    int i;
286841016Sdfr
286941016Sdfr    id1 = get_aux_id(kbdc);
287041016Sdfr    if (set_mouse_sampling_rate(kbdc, 10) != 10)
287141016Sdfr	return FALSE;
287241016Sdfr    /*
287341016Sdfr     * The device is now in the native mode? It returns a different
287441016Sdfr     * ID value...
287541016Sdfr     */
287641016Sdfr    id2 = get_aux_id(kbdc);
287741016Sdfr    if ((id1 == id2) || (id2 != 2))
287841016Sdfr	return FALSE;
287941016Sdfr
288041016Sdfr    if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW)
288141016Sdfr        return FALSE;
288241016Sdfr#if PSM_DEBUG >= 2
288341016Sdfr    /* at this point, resolution is LOW, sampling rate is 10/sec */
288441016Sdfr    if (get_mouse_status(kbdc, status, 0, 3) < 3)
288541016Sdfr        return FALSE;
288641016Sdfr#endif
288741016Sdfr
288841016Sdfr    /*
288941016Sdfr     * The special sequence to enable the third and fourth buttons.
289041016Sdfr     * Otherwise they behave like the first and second buttons.
289141016Sdfr     */
289241016Sdfr    for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) {
289341016Sdfr        if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i])
289441016Sdfr	    return FALSE;
289541016Sdfr    }
289641016Sdfr
289741016Sdfr    /*
289841016Sdfr     * At this point, the device is using default resolution and
289941016Sdfr     * sampling rate for the native mode.
290041016Sdfr     */
290141016Sdfr    if (get_mouse_status(kbdc, status, 0, 3) < 3)
290241016Sdfr        return FALSE;
290341016Sdfr    if ((status[1] == PSMD_RES_LOW) || (status[2] == rate[i - 1]))
290441016Sdfr        return FALSE;
290541016Sdfr
290641016Sdfr    /* the device appears be enabled by this sequence, diable it for now */
290741016Sdfr    disable_aux_dev(kbdc);
290841016Sdfr    empty_aux_buffer(kbdc, 5);
290941016Sdfr
291041016Sdfr    return TRUE;
291141016Sdfr}
291241016Sdfr
291358230Syokota/* Logitech MouseMan+/FirstMouse+, IBM ScrollPoint Mouse */
291441016Sdfrstatic int
291541016Sdfrenable_mmanplus(struct psm_softc *sc)
291641016Sdfr{
291741016Sdfr    KBDC kbdc = sc->kbdc;
291841016Sdfr    int data[3];
291941016Sdfr
292041016Sdfr    /* the special sequence to enable the fourth button and the roller. */
292158230Syokota    /*
292258230Syokota     * NOTE: for ScrollPoint to respond correctly, the SET_RESOLUTION
292358230Syokota     * must be called exactly three times since the last RESET command
292458230Syokota     * before this sequence. XXX
292558230Syokota     */
292669438Syokota    if (!set_mouse_scaling(kbdc, 1))
292769438Syokota	return FALSE;
292869438Syokota    if (!mouse_ext_command(kbdc, 0x39) || !mouse_ext_command(kbdc, 0xdb))
292969438Syokota	return FALSE;
293041016Sdfr    if (get_mouse_status(kbdc, data, 1, 3) < 3)
293141016Sdfr        return FALSE;
293241016Sdfr
293348778Syokota    /*
293448778Syokota     * PS2++ protocl, packet type 0
293541016Sdfr     *
293648778Syokota     *          b7 b6 b5 b4 b3 b2 b1 b0
293748778Syokota     * byte 1:  *  1  p3 p2 1  *  *  *
293848778Syokota     * byte 2:  1  1  p1 p0 m1 m0 1  0
293948778Syokota     * byte 3:  m7 m6 m5 m4 m3 m2 m1 m0
294048778Syokota     *
294148778Syokota     * p3-p0: packet type: 0
294258230Syokota     * m7-m0: model ID: MouseMan+:0x50, FirstMouse+:0x51, ScrollPoint:0x58...
294341016Sdfr     */
294448778Syokota    /* check constant bits */
294548778Syokota    if ((data[0] & MOUSE_PS2PLUS_SYNCMASK) != MOUSE_PS2PLUS_SYNC)
294641016Sdfr        return FALSE;
294748778Syokota    if ((data[1] & 0xc3) != 0xc2)
294848778Syokota        return FALSE;
294948778Syokota    /* check d3-d0 in byte 2 */
295048778Syokota    if (!MOUSE_PS2PLUS_CHECKBITS(data))
295148778Syokota        return FALSE;
295248778Syokota    /* check p3-p0 */
295348778Syokota    if (MOUSE_PS2PLUS_PACKET_TYPE(data) != 0)
295448778Syokota        return FALSE;
295541016Sdfr
295648778Syokota    sc->hw.hwid &= 0x00ff;
295748778Syokota    sc->hw.hwid |= data[2] << 8;	/* save model ID */
295848778Syokota
295941016Sdfr    /*
296041016Sdfr     * MouseMan+ (or FirstMouse+) is now in its native mode, in which
296141016Sdfr     * the wheel and the fourth button events are encoded in the
296241016Sdfr     * special data packet. The mouse may be put in the IntelliMouse mode
296341016Sdfr     * if it is initialized by the IntelliMouse's method.
296441016Sdfr     */
296541016Sdfr    return TRUE;
296641016Sdfr}
296741016Sdfr
296858230Syokota/* MS IntelliMouse Explorer */
296958230Syokotastatic int
297058230Syokotaenable_msexplorer(struct psm_softc *sc)
297158230Syokota{
297258923Syokota    static unsigned char rate0[] = { 200, 100, 80, };
297358923Syokota    static unsigned char rate1[] = { 200, 200, 80, };
297458230Syokota    KBDC kbdc = sc->kbdc;
297558230Syokota    int id;
297658230Syokota    int i;
297758230Syokota
297869438Syokota    /* the special sequence to enable the extra buttons and the roller. */
297969438Syokota    for (i = 0; i < sizeof(rate1)/sizeof(rate1[0]); ++i) {
298069438Syokota        if (set_mouse_sampling_rate(kbdc, rate1[i]) != rate1[i])
298169438Syokota	    return FALSE;
298269438Syokota    }
298369438Syokota    /* the device will give the genuine ID only after the above sequence */
298469438Syokota    id = get_aux_id(kbdc);
298569438Syokota    if (id != PSM_EXPLORER_ID)
298669438Syokota	return FALSE;
298769438Syokota
298869438Syokota    sc->hw.hwid = id;
298969438Syokota    sc->hw.buttons = 5;		/* IntelliMouse Explorer XXX */
299069438Syokota
299158923Syokota    /*
299258923Syokota     * XXX: this is a kludge to fool some KVM switch products
299358923Syokota     * which think they are clever enough to know the 4-byte IntelliMouse
299458923Syokota     * protocol, and assume any other protocols use 3-byte packets.
299558923Syokota     * They don't convey 4-byte data packets from the IntelliMouse Explorer
299658923Syokota     * correctly to the host computer because of this!
299758923Syokota     * The following sequence is actually IntelliMouse's "wake up"
299858923Syokota     * sequence; it will make the KVM think the mouse is IntelliMouse
299958923Syokota     * when it is in fact IntelliMouse Explorer.
300058923Syokota     */
300158923Syokota    for (i = 0; i < sizeof(rate0)/sizeof(rate0[0]); ++i) {
300258923Syokota        if (set_mouse_sampling_rate(kbdc, rate0[i]) != rate0[i])
300369438Syokota	    break;
300458923Syokota    }
300558923Syokota    id = get_aux_id(kbdc);
300658923Syokota
300758230Syokota    return TRUE;
300858230Syokota}
300958230Syokota
301041016Sdfr/* MS IntelliMouse */
301141016Sdfrstatic int
301241016Sdfrenable_msintelli(struct psm_softc *sc)
301341016Sdfr{
301441016Sdfr    /*
301541016Sdfr     * Logitech MouseMan+ and FirstMouse+ will also respond to this
301641016Sdfr     * probe routine and act like IntelliMouse.
301741016Sdfr     */
301841016Sdfr
301941016Sdfr    static unsigned char rate[] = { 200, 100, 80, };
302041016Sdfr    KBDC kbdc = sc->kbdc;
302141016Sdfr    int id;
302241016Sdfr    int i;
302341016Sdfr
302441016Sdfr    /* the special sequence to enable the third button and the roller. */
302541016Sdfr    for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) {
302641016Sdfr        if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i])
302741016Sdfr	    return FALSE;
302841016Sdfr    }
302941016Sdfr    /* the device will give the genuine ID only after the above sequence */
303041016Sdfr    id = get_aux_id(kbdc);
303141016Sdfr    if (id != PSM_INTELLI_ID)
303241016Sdfr	return FALSE;
303341016Sdfr
303441016Sdfr    sc->hw.hwid = id;
303541016Sdfr    sc->hw.buttons = 3;
303641016Sdfr
303741016Sdfr    return TRUE;
303841016Sdfr}
303941016Sdfr
304058230Syokota/* A4 Tech 4D Mouse */
304158230Syokotastatic int
304258230Syokotaenable_4dmouse(struct psm_softc *sc)
304358230Syokota{
304458230Syokota    /*
304558230Syokota     * Newer wheel mice from A4 Tech may use the 4D+ protocol.
304658230Syokota     */
304758230Syokota
304858230Syokota    static unsigned char rate[] = { 200, 100, 80, 60, 40, 20 };
304958230Syokota    KBDC kbdc = sc->kbdc;
305058230Syokota    int id;
305158230Syokota    int i;
305258230Syokota
305358230Syokota    for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) {
305458230Syokota        if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i])
305558230Syokota	    return FALSE;
305658230Syokota    }
305758230Syokota    id = get_aux_id(kbdc);
305858230Syokota    /*
305958923Syokota     * WinEasy 4D, 4 Way Scroll 4D: 6
306058230Syokota     * Cable-Free 4D: 8 (4DPLUS)
306158923Syokota     * WinBest 4D+, 4 Way Scroll 4D+: 8 (4DPLUS)
306258230Syokota     */
306358230Syokota    if (id != PSM_4DMOUSE_ID)
306458230Syokota	return FALSE;
306558230Syokota
306658230Syokota    sc->hw.hwid = id;
306758230Syokota    sc->hw.buttons = 3;		/* XXX some 4D mice have 4? */
306858230Syokota
306958230Syokota    return TRUE;
307058230Syokota}
307158230Syokota
307258230Syokota/* A4 Tech 4D+ Mouse */
307358230Syokotastatic int
307458230Syokotaenable_4dplus(struct psm_softc *sc)
307558230Syokota{
307658230Syokota    /*
307758230Syokota     * Newer wheel mice from A4 Tech seem to use this protocol.
307858230Syokota     * Older models are recognized as either 4D Mouse or IntelliMouse.
307958230Syokota     */
308058230Syokota    KBDC kbdc = sc->kbdc;
308158230Syokota    int id;
308258230Syokota
308358230Syokota    /*
308458230Syokota     * enable_4dmouse() already issued the following ID sequence...
308558230Syokota    static unsigned char rate[] = { 200, 100, 80, 60, 40, 20 };
308658230Syokota    int i;
308758230Syokota
308858230Syokota    for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) {
308958230Syokota        if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i])
309058230Syokota	    return FALSE;
309158230Syokota    }
309258230Syokota    */
309358230Syokota
309458230Syokota    id = get_aux_id(kbdc);
3095117478Smikeh    switch (id) {
3096117478Smikeh    case PSM_4DPLUS_ID:
3097117478Smikeh	    sc->hw.buttons = 4;
3098117478Smikeh	    break;
3099117478Smikeh    case PSM_4DPLUS_RFSW35_ID:
3100117478Smikeh	    sc->hw.buttons = 3;
3101117478Smikeh	    break;
3102117478Smikeh    default:
3103117478Smikeh	    return FALSE;
3104117478Smikeh    }
310558230Syokota
310658230Syokota    sc->hw.hwid = id;
310758230Syokota
310858230Syokota    return TRUE;
310958230Syokota}
311058230Syokota
3111132865Snjl/* Synaptics Touchpad */
3112132865Snjlstatic int
3113132865Snjlenable_synaptics(struct psm_softc *sc)
3114132865Snjl{
3115132865Snjl    int status[3];
3116132865Snjl    KBDC kbdc;
3117132865Snjl
3118135945Sphilip    if (!synaptics_support)
3119135945Sphilip	return (FALSE);
3120135945Sphilip
3121132865Snjl    kbdc = sc->kbdc;
3122132865Snjl    disable_aux_dev(kbdc);
3123132865Snjl
3124132865Snjl    /* Just to be on the safe side */
3125132865Snjl    set_mouse_scaling(kbdc, 1);
3126132865Snjl
3127133295Sphilip    /* Identify the Touchpad version */
3128132865Snjl    if (mouse_ext_command(kbdc, 0) == 0)
3129132865Snjl	return (FALSE);
3130132865Snjl    if (get_mouse_status(kbdc, status, 0, 3) != 3)
3131132865Snjl	return (FALSE);
3132132865Snjl    if (status[1] != 0x47)
3133132865Snjl	return (FALSE);
3134132865Snjl
3135132865Snjl    sc->synhw.infoMinor = status[0];
3136132865Snjl    sc->synhw.infoMajor = status[2] & 0x0f;
3137133295Sphilip
3138133295Sphilip    if (verbose >= 2)
3139133295Sphilip	printf("Synaptics Touchpad v%d.%d\n",
3140133295Sphilip	    sc->synhw.infoMajor, sc->synhw.infoMinor);
3141133295Sphilip
3142132865Snjl    if (sc->synhw.infoMajor < 4) {
3143133295Sphilip	printf("  Unsupported (pre-v4) Touchpad detected\n");
3144132865Snjl	return (FALSE);
3145132865Snjl    }
3146132865Snjl
3147133295Sphilip    /* Get the Touchpad model information */
3148132865Snjl    if (mouse_ext_command(kbdc, 3) == 0)
3149132865Snjl	return (FALSE);
3150132865Snjl    if (get_mouse_status(kbdc, status, 0, 3) != 3)
3151132865Snjl	return (FALSE);
3152132865Snjl    if ((status[1] & 0x01) != 0) {
3153133295Sphilip	printf("  Failed to read model information\n");
3154132865Snjl	return (FALSE);
3155132865Snjl    }
3156132865Snjl
3157132865Snjl    sc->synhw.infoRot180   = (status[0] & 0x80) >> 7;
3158132865Snjl    sc->synhw.infoPortrait = (status[0] & 0x40) >> 6;
3159132865Snjl    sc->synhw.infoSensor   =  status[0] & 0x3f;
3160132865Snjl    sc->synhw.infoHardware = (status[1] & 0xfe) >> 1;
3161132865Snjl    sc->synhw.infoNewAbs   = (status[2] & 0x80) >> 7;
3162132865Snjl    sc->synhw.capPen       = (status[2] & 0x40) >> 6;
3163132865Snjl    sc->synhw.infoSimplC   = (status[2] & 0x20) >> 5;
3164132865Snjl    sc->synhw.infoGeometry =  status[2] & 0x0f;
3165133295Sphilip
3166132865Snjl    if (verbose >= 2) {
3167133295Sphilip	printf("  Model information:\n");
3168133295Sphilip	printf("   infoRot180: %d\n", sc->synhw.infoRot180);
3169133295Sphilip	printf("   infoPortrait: %d\n", sc->synhw.infoPortrait);
3170133295Sphilip	printf("   infoSensor: %d\n", sc->synhw.infoSensor);
3171133295Sphilip	printf("   infoHardware: %d\n", sc->synhw.infoHardware);
3172133295Sphilip	printf("   infoNewAbs: %d\n", sc->synhw.infoNewAbs);
3173133295Sphilip	printf("   capPen: %d\n", sc->synhw.capPen);
3174133295Sphilip	printf("   infoSimplC: %d\n", sc->synhw.infoSimplC);
3175133295Sphilip	printf("   infoGeometry: %d\n", sc->synhw.infoGeometry);
3176132865Snjl    }
3177132865Snjl
3178133295Sphilip    /* Read the extended capability bits */
3179132865Snjl    if (mouse_ext_command(kbdc, 2) == 0)
3180132865Snjl	return (FALSE);
3181132865Snjl    if (get_mouse_status(kbdc, status, 0, 3) != 3)
3182132865Snjl	return (FALSE);
3183132865Snjl    if (status[1] != 0x47) {
3184133295Sphilip	printf("  Failed to read extended capability bits\n");
3185132865Snjl	return (FALSE);
3186132865Snjl    }
3187132865Snjl
3188133295Sphilip    /* Set the different capabilities when they exist */
3189133295Sphilip    if ((status[0] & 0x80) >> 7) {
3190133295Sphilip	sc->synhw.capExtended    = (status[0] & 0x80) >> 7;
3191133295Sphilip    	sc->synhw.capPassthrough = (status[2] & 0x80) >> 7;
3192133295Sphilip    	sc->synhw.capSleep       = (status[2] & 0x10) >> 4;
3193133295Sphilip    	sc->synhw.capFourButtons = (status[2] & 0x08) >> 3;
3194133295Sphilip    	sc->synhw.capMultiFinger = (status[2] & 0x02) >> 1;
3195133295Sphilip    	sc->synhw.capPalmDetect  = (status[2] & 0x01);
3196133295Sphilip
3197133295Sphilip	if (verbose >= 2) {
3198133295Sphilip	    printf("  Extended capabilities:\n");
3199133295Sphilip	    printf("   capExtended: %d\n", sc->synhw.capExtended);
3200133295Sphilip	    printf("   capPassthrough: %d\n", sc->synhw.capPassthrough);
3201133295Sphilip	    printf("   capSleep: %d\n", sc->synhw.capSleep);
3202133295Sphilip	    printf("   capFourButtons: %d\n", sc->synhw.capFourButtons);
3203133295Sphilip	    printf("   capMultiFinger: %d\n", sc->synhw.capMultiFinger);
3204133295Sphilip	    printf("   capPalmDetect: %d\n", sc->synhw.capPalmDetect);
3205133295Sphilip	}
3206133295Sphilip    } else {
3207133295Sphilip	sc->synhw.capExtended = 0;
3208133295Sphilip
3209133295Sphilip	if (verbose >= 2)
3210133295Sphilip	    printf("  No extended capabilities\n");
3211133295Sphilip    }
3212133295Sphilip
3213133295Sphilip    /*
3214133295Sphilip     * Read the mode byte
3215133295Sphilip     *
3216133295Sphilip     * XXX: Note the Synaptics documentation also defines the first
3217133295Sphilip     * byte of the response to this query to be a constant 0x3b, this
3218133295Sphilip     * does not appear to be true for Touchpads with guest devices.
3219133295Sphilip     */
3220132865Snjl    if (mouse_ext_command(kbdc, 1) == 0)
3221132865Snjl	return (FALSE);
3222132865Snjl    if (get_mouse_status(kbdc, status, 0, 3) != 3)
3223132865Snjl	return (FALSE);
3224133295Sphilip    if (status[1] != 0x47) {
3225133295Sphilip	printf("  Failed to read mode byte\n");
3226132865Snjl	return (FALSE);
3227132865Snjl    }
3228132865Snjl
3229133295Sphilip    /* Set the mode byte -- request wmode where available */
3230133295Sphilip    if (sc->synhw.capExtended)
3231133295Sphilip	mouse_ext_command(kbdc, 0xc1);
3232133295Sphilip    else
3233133295Sphilip	mouse_ext_command(kbdc, 0xc0);
3234132865Snjl
3235133295Sphilip    /* Reset the sampling rate */
3236132865Snjl    set_mouse_sampling_rate(kbdc, 20);
3237132865Snjl
3238133296Sphilip    /*
3239133296Sphilip     * Report the correct number of buttons
3240133296Sphilip     *
3241133296Sphilip     * XXX: I'm not sure this is used anywhere.
3242133296Sphilip     */
3243133296Sphilip    if (sc->synhw.capExtended && sc->synhw.capFourButtons)
3244133296Sphilip	sc->hw.buttons = 4;
3245133296Sphilip    else
3246133296Sphilip	sc->hw.buttons = 3;
3247133296Sphilip
3248132865Snjl    return (TRUE);
3249132865Snjl}
3250133295Sphilip
325149965Syokota/* Interlink electronics VersaPad */
325249965Syokotastatic int
325349965Syokotaenable_versapad(struct psm_softc *sc)
325449965Syokota{
325549965Syokota    KBDC kbdc = sc->kbdc;
325649965Syokota    int data[3];
325749965Syokota
325849965Syokota    set_mouse_resolution(kbdc, PSMD_RES_MEDIUM_HIGH); /* set res. 2 */
325949965Syokota    set_mouse_sampling_rate(kbdc, 100);		/* set rate 100 */
326049965Syokota    set_mouse_scaling(kbdc, 1);			/* set scale 1:1 */
326149965Syokota    set_mouse_scaling(kbdc, 1);			/* set scale 1:1 */
326249965Syokota    set_mouse_scaling(kbdc, 1);			/* set scale 1:1 */
326349965Syokota    set_mouse_scaling(kbdc, 1);			/* set scale 1:1 */
326449965Syokota    if (get_mouse_status(kbdc, data, 0, 3) < 3)	/* get status */
326549965Syokota	return FALSE;
326649965Syokota    if (data[2] != 0xa || data[1] != 0 )	/* rate == 0xa && res. == 0 */
326749965Syokota	return FALSE;
326849965Syokota    set_mouse_scaling(kbdc, 1);			/* set scale 1:1 */
326949965Syokota
327058230Syokota    sc->config |= PSM_CONFIG_HOOKRESUME | PSM_CONFIG_INITAFTERSUSPEND;
327158230Syokota
327249965Syokota    return TRUE;				/* PS/2 absolute mode */
327349965Syokota}
327449965Syokota
327541016Sdfrstatic int
327654629Syokotapsmresume(device_t dev)
327741016Sdfr{
327854629Syokota    struct psm_softc *sc = device_get_softc(dev);
327954629Syokota    int unit = device_get_unit(dev);
328084880Syokota    int err;
328141016Sdfr
3282134405Sgibbs    VLOG(2, (LOG_NOTICE, "psm%d: system resume hook called.\n", unit));
328341016Sdfr
328458230Syokota    if (!(sc->config & PSM_CONFIG_HOOKRESUME))
328558230Syokota	return (0);
328658230Syokota
328784880Syokota    err = reinitialize(sc, sc->config & PSM_CONFIG_INITAFTERSUSPEND);
328841016Sdfr
328941016Sdfr    if ((sc->state & PSM_ASLP) && !(sc->state & PSM_VALID)) {
329041016Sdfr	/*
329141016Sdfr	 * Release the blocked process; it must be notified that the device
329241016Sdfr	 * cannot be accessed anymore.
329341016Sdfr	 */
329441016Sdfr        sc->state &= ~PSM_ASLP;
3295111748Sdes        wakeup(sc);
329641016Sdfr    }
329741016Sdfr
3298134405Sgibbs    VLOG(2, (LOG_DEBUG, "psm%d: system resume hook exiting.\n", unit));
329941016Sdfr
330041016Sdfr    return (err);
330141016Sdfr}
330241016Sdfr
330352997SpeterDRIVER_MODULE(psm, atkbdc, psm_driver, psm_devclass, 0, 0);
330483147Syokota
330583147Syokota/*
330683147Syokota * This sucks up assignments from PNPBIOS and ACPI.
330783147Syokota */
330883147Syokota
330983931Syokota/*
331083931Syokota * When the PS/2 mouse device is reported by ACPI or PnP BIOS, it may
331183931Syokota * appear BEFORE the AT keyboard controller.  As the PS/2 mouse device
331283931Syokota * can be probed and attached only after the AT keyboard controller is
331383931Syokota * attached, we shall quietly reserve the IRQ resource for later use.
331483931Syokota * If the PS/2 mouse device is reported to us AFTER the keyboard controller,
331583931Syokota * copy the IRQ resource to the PS/2 mouse device instance hanging
331683931Syokota * under the keyboard controller, then probe and attach it.
331783931Syokota */
331883147Syokota
331983147Syokotastatic	devclass_t			psmcpnp_devclass;
332083147Syokota
332183147Syokotastatic	device_probe_t			psmcpnp_probe;
332283147Syokotastatic	device_attach_t			psmcpnp_attach;
332383147Syokota
332483147Syokotastatic device_method_t psmcpnp_methods[] = {
332583147Syokota	DEVMETHOD(device_probe,		psmcpnp_probe),
332683147Syokota	DEVMETHOD(device_attach,	psmcpnp_attach),
332783147Syokota
332883147Syokota	{ 0, 0 }
332983147Syokota};
333083147Syokota
333183147Syokotastatic driver_t psmcpnp_driver = {
333283147Syokota	PSMCPNP_DRIVER_NAME,
333383147Syokota	psmcpnp_methods,
333483147Syokota	1,			/* no softc */
333583147Syokota};
333683147Syokota
333783147Syokotastatic struct isa_pnp_id psmcpnp_ids[] = {
333888188Ssheldonh	{ 0x030fd041, "PS/2 mouse port" },		/* PNP0F03 */
333983147Syokota	{ 0x130fd041, "PS/2 mouse port" },		/* PNP0F13 */
334083147Syokota	{ 0x1303d041, "PS/2 port" },			/* PNP0313, XXX */
3341117117Smikeh	{ 0x02002e4f, "Dell PS/2 mouse port" },		/* Lat. X200, Dell */
334283492Syokota	{ 0x80374d24, "IBM PS/2 mouse port" },		/* IBM3780, ThinkPad */
334384407Stakawata	{ 0x81374d24, "IBM PS/2 mouse port" },		/* IBM3781, ThinkPad */
3344109679Shsu	{ 0x0190d94d, "SONY VAIO PS/2 mouse port"},     /* SNY9001, Vaio */
3345109679Shsu	{ 0x0290d94d, "SONY VAIO PS/2 mouse port"},	/* SNY9002, Vaio */
3346109710Smarcel	{ 0x0390d94d, "SONY VAIO PS/2 mouse port"},	/* SNY9003, Vaio */
3347109679Shsu	{ 0x0490d94d, "SONY VAIO PS/2 mouse port"},     /* SNY9004, Vaio */
334883147Syokota	{ 0 }
334983147Syokota};
335083147Syokota
335183147Syokotastatic int
335283147Syokotacreate_a_copy(device_t atkbdc, device_t me)
335383147Syokota{
335483147Syokota	device_t psm;
335583147Syokota	u_long irq;
335683147Syokota
335783931Syokota	/* find the PS/2 mouse device instance under the keyboard controller */
335883931Syokota	psm = device_find_child(atkbdc, PSM_DRIVER_NAME,
335983931Syokota				device_get_unit(atkbdc));
336083147Syokota	if (psm == NULL)
336183147Syokota		return ENXIO;
336283931Syokota	if (device_get_state(psm) != DS_NOTPRESENT)
336383931Syokota		return 0;
336483147Syokota
336583931Syokota	/* move our resource to the found device */
336683931Syokota	irq = bus_get_resource_start(me, SYS_RES_IRQ, 0);
336783147Syokota	bus_set_resource(psm, SYS_RES_IRQ, KBDC_RID_AUX, irq, 1);
336883147Syokota
336983147Syokota	/* ...then probe and attach it */
337083147Syokota	return device_probe_and_attach(psm);
337183147Syokota}
337283147Syokota
337383147Syokotastatic int
337483147Syokotapsmcpnp_probe(device_t dev)
337583147Syokota{
337683931Syokota	struct resource *res;
337783931Syokota	u_long irq;
337883931Syokota	int rid;
337983147Syokota
338083147Syokota	if (ISA_PNP_PROBE(device_get_parent(dev), dev, psmcpnp_ids))
338183147Syokota		return ENXIO;
338283147Syokota
338383931Syokota	/*
338483931Syokota	 * The PnP BIOS and ACPI are supposed to assign an IRQ (12)
338583931Syokota	 * to the PS/2 mouse device node. But, some buggy PnP BIOS
338683931Syokota	 * declares the PS/2 mouse device node without an IRQ resource!
338783931Syokota	 * If this happens, we shall refer to device hints.
338883931Syokota	 * If we still don't find it there, use a hardcoded value... XXX
338983931Syokota	 */
339083931Syokota	rid = 0;
339183931Syokota	irq = bus_get_resource_start(dev, SYS_RES_IRQ, rid);
339283931Syokota	if (irq <= 0) {
339383931Syokota		if (resource_long_value(PSM_DRIVER_NAME,
339483931Syokota					device_get_unit(dev), "irq", &irq) != 0)
339583931Syokota			irq = 12;	/* XXX */
339683931Syokota		device_printf(dev, "irq resource info is missing; "
339783931Syokota			      "assuming irq %ld\n", irq);
339883931Syokota		bus_set_resource(dev, SYS_RES_IRQ, rid, irq, 1);
339983931Syokota	}
3400127135Snjl	res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
3401127135Snjl				     RF_SHAREABLE);
340283931Syokota	bus_release_resource(dev, SYS_RES_IRQ, rid, res);
340383147Syokota
340483147Syokota	/* keep quiet */
340583147Syokota	if (!bootverbose)
340683147Syokota		device_quiet(dev);
340783492Syokota
340883931Syokota	return ((res == NULL) ? ENXIO : 0);
340983147Syokota}
341083147Syokota
341183147Syokotastatic int
341283147Syokotapsmcpnp_attach(device_t dev)
341383147Syokota{
341483492Syokota	device_t atkbdc;
341583931Syokota	int rid;
341683147Syokota
341783931Syokota	/* find the keyboard controller, which may be on acpi* or isa* bus */
341883931Syokota	atkbdc = devclass_get_device(devclass_find(ATKBDC_DRIVER_NAME),
341983931Syokota				     device_get_unit(dev));
342083931Syokota	if ((atkbdc != NULL) && (device_get_state(atkbdc) == DS_ATTACHED)) {
342183492Syokota		create_a_copy(atkbdc, dev);
342283931Syokota	} else {
342383931Syokota		/*
342483931Syokota		 * If we don't have the AT keyboard controller yet,
342583931Syokota		 * just reserve the IRQ for later use...
342683931Syokota		 * (See psmidentify() above.)
342783931Syokota		 */
342883931Syokota		rid = 0;
3429127135Snjl		bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE);
343083931Syokota	}
343183492Syokota
343283147Syokota	return 0;
343383147Syokota}
343483147Syokota
3435123442Salfred/*
3436123442Salfred * Return true if 'now' is earlier than (start + (secs.usecs)).
3437123442Salfred * Now may be NULL and the function will fetch the current time from
3438123442Salfred * getmicrouptime(), or a cached 'now' can be passed in.
3439123442Salfred * All values should be numbers derived from getmicrouptime().
3440123442Salfred */
3441123442Salfredstatic int
3442123442Salfredtimeelapsed(start, secs, usecs, now)
3443123442Salfred	const struct timeval *start, *now;
3444123442Salfred	int secs, usecs;
3445123442Salfred{
3446123442Salfred	struct timeval snow, tv;
3447123442Salfred
3448123442Salfred	/* if there is no 'now' passed in, the get it as a convience. */
3449123442Salfred	if (now == NULL) {
3450123442Salfred		getmicrouptime(&snow);
3451123442Salfred		now = &snow;
3452123442Salfred	}
3453123442Salfred
3454123442Salfred	tv.tv_sec = secs;
3455123442Salfred	tv.tv_usec = usecs;
3456123442Salfred	timevaladd(&tv, start);
3457123442Salfred	return (timevalcmp(&tv, now, <));
3458123442Salfred}
3459123442Salfred
346083147SyokotaDRIVER_MODULE(psmcpnp, isa, psmcpnp_driver, psmcpnp_devclass, 0, 0);
346183147SyokotaDRIVER_MODULE(psmcpnp, acpi, psmcpnp_driver, psmcpnp_devclass, 0, 0);
3462