psm.c revision 48778
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 *
2348778Syokota * $Id: psm.c,v 1.13 1999/07/12 13:40:21 yokota Exp $
2441016Sdfr */
2541016Sdfr
2641016Sdfr/*
2741016Sdfr *  Ported to 386bsd Oct 17, 1992
2841016Sdfr *  Sandi Donno, Computer Science, University of Cape Town, South Africa
2941016Sdfr *  Please send bug reports to sandi@cs.uct.ac.za
3041016Sdfr *
3141016Sdfr *  Thanks are also due to Rick Macklem, rick@snowhite.cis.uoguelph.ca -
3241016Sdfr *  although I was only partially successful in getting the alpha release
3341016Sdfr *  of his "driver for the Logitech and ATI Inport Bus mice for use with
3441016Sdfr *  386bsd and the X386 port" to work with my Microsoft mouse, I nevertheless
3541016Sdfr *  found his code to be an invaluable reference when porting this driver
3641016Sdfr *  to 386bsd.
3741016Sdfr *
3841016Sdfr *  Further modifications for latest 386BSD+patchkit and port to NetBSD,
3941016Sdfr *  Andrew Herbert <andrew@werple.apana.org.au> - 8 June 1993
4041016Sdfr *
4141016Sdfr *  Cloned from the Microsoft Bus Mouse driver, also by Erik Forsberg, by
4241016Sdfr *  Andrew Herbert - 12 June 1993
4341016Sdfr *
4441016Sdfr *  Modified for PS/2 mouse by Charles Hannum <mycroft@ai.mit.edu>
4541016Sdfr *  - 13 June 1993
4641016Sdfr *
4741016Sdfr *  Modified for PS/2 AUX mouse by Shoji Yuen <yuen@nuie.nagoya-u.ac.jp>
4841016Sdfr *  - 24 October 1993
4941016Sdfr *
5041016Sdfr *  Hardware access routines and probe logic rewritten by
5141016Sdfr *  Kazutaka Yokota <yokota@zodiac.mech.utsunomiya-u.ac.jp>
5241016Sdfr *  - 3, 14, 22 October 1996.
5341016Sdfr *  - 12 November 1996. IOCTLs and rearranging `psmread', `psmioctl'...
5441016Sdfr *  - 14, 30 November 1996. Uses `kbdio.c'.
5541016Sdfr *  - 13 December 1996. Uses queuing version of `kbdio.c'.
5641016Sdfr *  - January/February 1997. Tweaked probe logic for
5741016Sdfr *    HiNote UltraII/Latitude/Armada laptops.
5841016Sdfr *  - 30 July 1997. Added APM support.
5941016Sdfr *  - 5 March 1997. Defined driver configuration flags (PSM_CONFIG_XXX).
6041016Sdfr *    Improved sync check logic.
6141016Sdfr *    Vendor specific support routines.
6241016Sdfr */
6341016Sdfr
6441016Sdfr#include "psm.h"
6546763Syokota#ifdef __i386__
6646763Syokota#include "apm.h"
6746763Syokota#endif
6841016Sdfr#include "opt_devfs.h"
6941016Sdfr#include "opt_psm.h"
7041016Sdfr
7141016Sdfr#if NPSM > 0
7241016Sdfr
7341016Sdfr#include <sys/param.h>
7441016Sdfr#include <sys/systm.h>
7541016Sdfr#include <sys/kernel.h>
7641016Sdfr#include <sys/module.h>
7741016Sdfr#include <sys/bus.h>
7841016Sdfr#include <sys/conf.h>
7941016Sdfr#include <sys/poll.h>
8041016Sdfr#include <sys/syslog.h>
8141016Sdfr#include <sys/malloc.h>
8245720Speter#include <machine/bus.h>
8341181Sdfr#include <sys/rman.h>
8441016Sdfr#ifdef DEVFS
8541016Sdfr#include <sys/devfsext.h>
8641016Sdfr#endif
8741016Sdfr#include <sys/select.h>
8841016Sdfr#include <sys/uio.h>
8941016Sdfr
9046763Syokota#ifdef __i386__
9146763Syokota#include <machine/apm_bios.h>
9246763Syokota#endif
9341016Sdfr#include <machine/clock.h>
9441016Sdfr#include <machine/limits.h>
9541016Sdfr#include <machine/mouse.h>
9641181Sdfr#include <machine/resource.h>
9741016Sdfr
9841016Sdfr#include <isa/isareg.h>
9941016Sdfr#include <isa/isavar.h>
10043105Sdfr#include <dev/kbd/atkbdcreg.h>
10141016Sdfr
10241016Sdfr/*
10341016Sdfr * Driver specific options: the following options may be set by
10441016Sdfr * `options' statements in the kernel configuration file.
10541016Sdfr */
10641016Sdfr
10741016Sdfr/* debugging */
10841016Sdfr#ifndef PSM_DEBUG
10941016Sdfr#define PSM_DEBUG	0	/* logging: 0: none, 1: brief, 2: verbose */
11041016Sdfr#endif
11141016Sdfr
11241016Sdfr/* features */
11341016Sdfr
11441016Sdfr/* #define PSM_HOOKAPM	   	   hook the APM resume event */
11541016Sdfr/* #define PSM_RESETAFTERSUSPEND   reset the device at the resume event */
11641016Sdfr
11741016Sdfr#if NAPM <= 0
11841016Sdfr#undef PSM_HOOKAPM
11941016Sdfr#endif /* NAPM */
12041016Sdfr
12141016Sdfr#ifndef PSM_HOOKAPM
12241016Sdfr#undef PSM_RESETAFTERSUSPEND
12341016Sdfr#endif /* PSM_HOOKAPM */
12441016Sdfr
12541016Sdfr/* end of driver specific options */
12641016Sdfr
12741016Sdfr/* input queue */
12841016Sdfr#define PSM_BUFSIZE		960
12941016Sdfr#define PSM_SMALLBUFSIZE	240
13041016Sdfr
13141016Sdfr/* operation levels */
13241016Sdfr#define PSM_LEVEL_BASE		0
13341016Sdfr#define PSM_LEVEL_STANDARD	1
13441016Sdfr#define PSM_LEVEL_NATIVE	2
13541016Sdfr#define PSM_LEVEL_MIN		PSM_LEVEL_BASE
13641016Sdfr#define PSM_LEVEL_MAX		PSM_LEVEL_NATIVE
13741016Sdfr
13848778Syokota/* Logitech PS2++ protocol */
13948778Syokota#define MOUSE_PS2PLUS_CHECKBITS(b)	\
14048778Syokota				((((b[2] & 0x03) << 2) | 0x02) == (b[1] & 0x0f))
14148778Syokota#define MOUSE_PS2PLUS_PACKET_TYPE(b)	\
14248778Syokota				(((b[0] & 0x30) >> 2) | ((b[1] & 0x30) >> 4))
14348778Syokota
14441016Sdfr/* some macros */
14541016Sdfr#define PSM_UNIT(dev)		(minor(dev) >> 1)
14641016Sdfr#define PSM_NBLOCKIO(dev)	(minor(dev) & 1)
14741016Sdfr#define PSM_MKMINOR(unit,block)	(((unit) << 1) | ((block) ? 0:1))
14841016Sdfr
14941016Sdfr#ifndef max
15041016Sdfr#define max(x,y)		((x) > (y) ? (x) : (y))
15141016Sdfr#endif
15241016Sdfr#ifndef min
15341016Sdfr#define min(x,y)		((x) < (y) ? (x) : (y))
15441016Sdfr#endif
15541016Sdfr
15648778Syokota#define abs(x)			(((x) < 0) ? -(x) : (x))
15748778Syokota
15841016Sdfr/* ring buffer */
15941016Sdfrtypedef struct ringbuf {
16041016Sdfr    int           count;	/* # of valid elements in the buffer */
16141016Sdfr    int           head;		/* head pointer */
16241016Sdfr    int           tail;		/* tail poiner */
16341016Sdfr    unsigned char buf[PSM_BUFSIZE];
16441016Sdfr} ringbuf_t;
16541016Sdfr
16641016Sdfr/* driver control block */
16741016Sdfrstruct psm_softc {		/* Driver status information */
16841016Sdfr    struct selinfo rsel;	/* Process selecting for Input */
16941016Sdfr    unsigned char state;	/* Mouse driver state */
17041016Sdfr    int           config;	/* driver configuration flags */
17141016Sdfr    int           flags;	/* other flags */
17241016Sdfr    KBDC          kbdc;		/* handle to access the keyboard controller */
17341016Sdfr    int           addr;		/* I/O port address */
17441016Sdfr    mousehw_t     hw;		/* 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 */
17941016Sdfr    unsigned char ipacket[16];	/* interim input buffer */
18041016Sdfr    int           inputbytes;	/* # of bytes in the input buffer */
18141016Sdfr    int           button;	/* the latest button state */
18241016Sdfr#ifdef DEVFS
18341016Sdfr    void          *devfs_token;
18441016Sdfr    void          *b_devfs_token;
18541016Sdfr#endif
18641016Sdfr#ifdef PSM_HOOKAPM
18741016Sdfr    struct apmhook resumehook;
18841016Sdfr#endif
18941016Sdfr};
19041016Sdfrdevclass_t psm_devclass;
19141016Sdfr#define PSM_SOFTC(unit)	((struct psm_softc*)devclass_get_softc(psm_devclass, unit))
19241016Sdfr
19341016Sdfr/* driver state flags (state) */
19441016Sdfr#define PSM_VALID		0x80
19541016Sdfr#define PSM_OPEN		1	/* Device is open */
19641016Sdfr#define PSM_ASLP		2	/* Waiting for mouse data */
19741016Sdfr
19841016Sdfr/* driver configuration flags (config) */
19941016Sdfr#define PSM_CONFIG_RESOLUTION	0x000f	/* resolution */
20041016Sdfr#define PSM_CONFIG_ACCEL	0x00f0  /* acceleration factor */
20141016Sdfr#define PSM_CONFIG_NOCHECKSYNC	0x0100  /* disable sync. test */
20245789Speter#define PSM_CONFIG_NOIDPROBE	0x0200  /* disable mouse model probe */
20345789Speter#define PSM_CONFIG_NORESET	0x0400  /* don't reset the mouse */
20445789Speter#define PSM_CONFIG_FORCETAP	0x0800  /* assume `tap' action exists */
20545789Speter#define PSM_CONFIG_IGNPORTERROR	0x1000  /* ignore error in aux port test */
20641016Sdfr
20741016Sdfr#define PSM_CONFIG_FLAGS	(PSM_CONFIG_RESOLUTION 		\
20841016Sdfr				    | PSM_CONFIG_ACCEL		\
20945789Speter				    | PSM_CONFIG_NOCHECKSYNC	\
21045789Speter				    | PSM_CONFIG_NOIDPROBE	\
21145789Speter				    | PSM_CONFIG_NORESET	\
21245789Speter				    | PSM_CONFIG_FORCETAP	\
21345789Speter				    | PSM_CONFIG_IGNPORTERROR)
21441016Sdfr
21541016Sdfr/* other flags (flags) */
21641016Sdfr
21741016Sdfr/* for backward compatibility */
21841016Sdfr#define OLD_MOUSE_GETHWINFO	_IOR('M', 1, old_mousehw_t)
21941016Sdfr#define OLD_MOUSE_GETMODE	_IOR('M', 2, old_mousemode_t)
22041016Sdfr#define OLD_MOUSE_SETMODE	_IOW('M', 3, old_mousemode_t)
22141016Sdfr
22241016Sdfrtypedef struct old_mousehw {
22341016Sdfr    int buttons;
22441016Sdfr    int iftype;
22541016Sdfr    int type;
22641016Sdfr    int hwid;
22741016Sdfr} old_mousehw_t;
22841016Sdfr
22941016Sdfrtypedef struct old_mousemode {
23041016Sdfr    int protocol;
23141016Sdfr    int rate;
23241016Sdfr    int resolution;
23341016Sdfr    int accelfactor;
23441016Sdfr} old_mousemode_t;
23541016Sdfr
23641016Sdfr/* packet formatting function */
23741016Sdfrtypedef int packetfunc_t __P((struct psm_softc *, unsigned char *,
23841016Sdfr			      int *, int, mousestatus_t *));
23941016Sdfr
24041016Sdfr/* function prototypes */
24141016Sdfrstatic int psmprobe __P((device_t));
24241016Sdfrstatic int psmattach __P((device_t));
24341016Sdfr#ifdef PSM_HOOKAPM
24441016Sdfrstatic int psmresume __P((void *));
24541016Sdfr#endif
24641016Sdfr
24741016Sdfrstatic d_open_t psmopen;
24841016Sdfrstatic d_close_t psmclose;
24941016Sdfrstatic d_read_t psmread;
25041016Sdfrstatic d_ioctl_t psmioctl;
25141016Sdfrstatic d_poll_t psmpoll;
25241016Sdfr
25341016Sdfrstatic int enable_aux_dev __P((KBDC));
25441016Sdfrstatic int disable_aux_dev __P((KBDC));
25541016Sdfrstatic int get_mouse_status __P((KBDC, int *, int, int));
25641016Sdfrstatic int get_aux_id __P((KBDC));
25741016Sdfrstatic int set_mouse_sampling_rate __P((KBDC, int));
25841016Sdfrstatic int set_mouse_scaling __P((KBDC, int));
25941016Sdfrstatic int set_mouse_resolution __P((KBDC, int));
26045789Speter#ifdef PSM_RESETAFTERSUSPEND
26141016Sdfrstatic int set_mouse_mode __P((KBDC));
26245789Speter#endif /* PSM_RESETAFTERSUSPEND */
26341016Sdfrstatic int get_mouse_buttons __P((KBDC));
26441016Sdfrstatic int is_a_mouse __P((int));
26541016Sdfrstatic void recover_from_error __P((KBDC));
26641016Sdfrstatic int restore_controller __P((KBDC, int));
26745789Speter#ifdef PSM_RESETAFTERSUSPEND
26841016Sdfrstatic int reinitialize __P((int, mousemode_t *));
26945789Speter#endif
27041016Sdfrstatic int doopen __P((int, int));
27141016Sdfrstatic char *model_name(int);
27241016Sdfrstatic void psmintr(void*);
27341016Sdfr
27441016Sdfr/* vendor specific features */
27541016Sdfrtypedef int probefunc_t __P((struct psm_softc *));
27641016Sdfr
27741016Sdfrstatic int mouse_id_proc1 __P((KBDC, int, int, int *));
27841016Sdfrstatic probefunc_t enable_groller;
27941016Sdfrstatic probefunc_t enable_gmouse;
28041016Sdfrstatic probefunc_t enable_aglide;
28141016Sdfrstatic probefunc_t enable_kmouse;
28241016Sdfrstatic probefunc_t enable_msintelli;
28341016Sdfrstatic probefunc_t enable_mmanplus;
28441016Sdfrstatic int tame_mouse __P((struct psm_softc *, mousestatus_t *, unsigned char *));
28541016Sdfr
28641016Sdfrstatic struct {
28741016Sdfr    int                 model;
28841016Sdfr    unsigned char	syncmask;
28941016Sdfr    int 		packetsize;
29041016Sdfr    probefunc_t 	*probefunc;
29141016Sdfr} vendortype[] = {
29241016Sdfr    { MOUSE_MODEL_NET,			/* Genius NetMouse */
29341016Sdfr      0xc8, MOUSE_INTELLI_PACKETSIZE, enable_gmouse, },
29441016Sdfr    { MOUSE_MODEL_NETSCROLL,		/* Genius NetScroll */
29541016Sdfr      0xc8, 6, enable_groller, },
29641016Sdfr    { MOUSE_MODEL_GLIDEPOINT,		/* ALPS GlidePoint */
29741016Sdfr      0xc0, MOUSE_PS2_PACKETSIZE, enable_aglide, },
29841016Sdfr    { MOUSE_MODEL_MOUSEMANPLUS,		/* Logitech MouseMan+ */
29941016Sdfr      0x08, MOUSE_PS2_PACKETSIZE, enable_mmanplus, },
30041016Sdfr    { MOUSE_MODEL_THINK,		/* Kensignton ThinkingMouse */
30141016Sdfr      0x80, MOUSE_PS2_PACKETSIZE, enable_kmouse, },
30241016Sdfr    { MOUSE_MODEL_INTELLI,		/* Microsoft IntelliMouse */
30341016Sdfr      0xc8, MOUSE_INTELLI_PACKETSIZE, enable_msintelli, },
30441016Sdfr    { MOUSE_MODEL_GENERIC,
30541016Sdfr      0xc0, MOUSE_PS2_PACKETSIZE, NULL, },
30641016Sdfr};
30745789Speter#define GENERIC_MOUSE_ENTRY	6
30841016Sdfr
30941016Sdfr/* device driver declarateion */
31041016Sdfrstatic device_method_t psm_methods[] = {
31141016Sdfr	/* Device interface */
31241016Sdfr	DEVMETHOD(device_probe,		psmprobe),
31341016Sdfr	DEVMETHOD(device_attach,	psmattach),
31441016Sdfr
31541016Sdfr	{ 0, 0 }
31641016Sdfr};
31741016Sdfr
31841016Sdfrstatic driver_t psm_driver = {
31941016Sdfr    "psm",
32041016Sdfr    psm_methods,
32141016Sdfr    sizeof(struct psm_softc),
32241016Sdfr};
32341016Sdfr
32441016Sdfr#define CDEV_MAJOR        21
32541016Sdfr
32647625Sphkstatic struct cdevsw psm_cdevsw = {
32747625Sphk	/* open */	psmopen,
32847625Sphk	/* close */	psmclose,
32947625Sphk	/* read */	psmread,
33047625Sphk	/* write */	nowrite,
33147625Sphk	/* ioctl */	psmioctl,
33247625Sphk	/* stop */	nostop,
33347625Sphk	/* reset */	noreset,
33447625Sphk	/* devtotty */	nodevtotty,
33547625Sphk	/* poll */	psmpoll,
33647625Sphk	/* mmap */	nommap,
33747625Sphk	/* strategy */	nostrategy,
33847625Sphk	/* name */	"psm",
33947625Sphk	/* parms */	noparms,
34047625Sphk	/* maj */	CDEV_MAJOR,
34147625Sphk	/* dump */	nodump,
34247625Sphk	/* psize */	nopsize,
34347625Sphk	/* flags */	0,
34447625Sphk	/* maxio */	0,
34547625Sphk	/* bmaj */	-1
34641016Sdfr};
34741016Sdfr
34841016Sdfr/* debug message level */
34941016Sdfrstatic int verbose = PSM_DEBUG;
35041016Sdfr
35141016Sdfr/* device I/O routines */
35241016Sdfrstatic int
35341016Sdfrenable_aux_dev(KBDC kbdc)
35441016Sdfr{
35541016Sdfr    int res;
35641016Sdfr
35741016Sdfr    res = send_aux_command(kbdc, PSMC_ENABLE_DEV);
35841016Sdfr    if (verbose >= 2)
35941016Sdfr        log(LOG_DEBUG, "psm: ENABLE_DEV return code:%04x\n", res);
36041016Sdfr
36141016Sdfr    return (res == PSM_ACK);
36241016Sdfr}
36341016Sdfr
36441016Sdfrstatic int
36541016Sdfrdisable_aux_dev(KBDC kbdc)
36641016Sdfr{
36741016Sdfr    int res;
36841016Sdfr
36941016Sdfr    res = send_aux_command(kbdc, PSMC_DISABLE_DEV);
37041016Sdfr    if (verbose >= 2)
37141016Sdfr        log(LOG_DEBUG, "psm: DISABLE_DEV return code:%04x\n", res);
37241016Sdfr
37341016Sdfr    return (res == PSM_ACK);
37441016Sdfr}
37541016Sdfr
37641016Sdfrstatic int
37741016Sdfrget_mouse_status(KBDC kbdc, int *status, int flag, int len)
37841016Sdfr{
37941016Sdfr    int cmd;
38041016Sdfr    int res;
38141016Sdfr    int i;
38241016Sdfr
38341016Sdfr    switch (flag) {
38441016Sdfr    case 0:
38541016Sdfr    default:
38641016Sdfr	cmd = PSMC_SEND_DEV_STATUS;
38741016Sdfr	break;
38841016Sdfr    case 1:
38941016Sdfr	cmd = PSMC_SEND_DEV_DATA;
39041016Sdfr	break;
39141016Sdfr    }
39241016Sdfr    empty_aux_buffer(kbdc, 5);
39341016Sdfr    res = send_aux_command(kbdc, cmd);
39441016Sdfr    if (verbose >= 2)
39541016Sdfr        log(LOG_DEBUG, "psm: SEND_AUX_DEV_%s return code:%04x\n",
39641016Sdfr	    (flag == 1) ? "DATA" : "STATUS", res);
39741016Sdfr    if (res != PSM_ACK)
39841016Sdfr        return 0;
39941016Sdfr
40041016Sdfr    for (i = 0; i < len; ++i) {
40141016Sdfr        status[i] = read_aux_data(kbdc);
40241016Sdfr	if (status[i] < 0)
40341016Sdfr	    break;
40441016Sdfr    }
40541016Sdfr
40641016Sdfr    if (verbose) {
40741016Sdfr        log(LOG_DEBUG, "psm: %s %02x %02x %02x\n",
40841016Sdfr            (flag == 1) ? "data" : "status", status[0], status[1], status[2]);
40941016Sdfr    }
41041016Sdfr
41141016Sdfr    return i;
41241016Sdfr}
41341016Sdfr
41441016Sdfrstatic int
41541016Sdfrget_aux_id(KBDC kbdc)
41641016Sdfr{
41741016Sdfr    int res;
41841016Sdfr    int id;
41941016Sdfr
42041016Sdfr    empty_aux_buffer(kbdc, 5);
42141016Sdfr    res = send_aux_command(kbdc, PSMC_SEND_DEV_ID);
42241016Sdfr    if (verbose >= 2)
42341016Sdfr        log(LOG_DEBUG, "psm: SEND_DEV_ID return code:%04x\n", res);
42441016Sdfr    if (res != PSM_ACK)
42541016Sdfr	return (-1);
42641016Sdfr
42741016Sdfr    /* 10ms delay */
42841016Sdfr    DELAY(10000);
42941016Sdfr
43041016Sdfr    id = read_aux_data(kbdc);
43141016Sdfr    if (verbose >= 2)
43241016Sdfr        log(LOG_DEBUG, "psm: device ID: %04x\n", id);
43341016Sdfr
43441016Sdfr    return id;
43541016Sdfr}
43641016Sdfr
43741016Sdfrstatic int
43841016Sdfrset_mouse_sampling_rate(KBDC kbdc, int rate)
43941016Sdfr{
44041016Sdfr    int res;
44141016Sdfr
44241016Sdfr    res = send_aux_command_and_data(kbdc, PSMC_SET_SAMPLING_RATE, rate);
44341016Sdfr    if (verbose >= 2)
44441016Sdfr        log(LOG_DEBUG, "psm: SET_SAMPLING_RATE (%d) %04x\n", rate, res);
44541016Sdfr
44641016Sdfr    return ((res == PSM_ACK) ? rate : -1);
44741016Sdfr}
44841016Sdfr
44941016Sdfrstatic int
45041016Sdfrset_mouse_scaling(KBDC kbdc, int scale)
45141016Sdfr{
45241016Sdfr    int res;
45341016Sdfr
45441016Sdfr    switch (scale) {
45541016Sdfr    case 1:
45641016Sdfr    default:
45741016Sdfr	scale = PSMC_SET_SCALING11;
45841016Sdfr	break;
45941016Sdfr    case 2:
46041016Sdfr	scale = PSMC_SET_SCALING21;
46141016Sdfr	break;
46241016Sdfr    }
46341016Sdfr    res = send_aux_command(kbdc, scale);
46441016Sdfr    if (verbose >= 2)
46541016Sdfr        log(LOG_DEBUG, "psm: SET_SCALING%s return code:%04x\n",
46641016Sdfr	    (scale == PSMC_SET_SCALING21) ? "21" : "11", res);
46741016Sdfr
46841016Sdfr    return (res == PSM_ACK);
46941016Sdfr}
47041016Sdfr
47141016Sdfr/* `val' must be 0 through PSMD_MAX_RESOLUTION */
47241016Sdfrstatic int
47341016Sdfrset_mouse_resolution(KBDC kbdc, int val)
47441016Sdfr{
47541016Sdfr    int res;
47641016Sdfr
47741016Sdfr    res = send_aux_command_and_data(kbdc, PSMC_SET_RESOLUTION, val);
47841016Sdfr    if (verbose >= 2)
47941016Sdfr        log(LOG_DEBUG, "psm: SET_RESOLUTION (%d) %04x\n", val, res);
48041016Sdfr
48141016Sdfr    return ((res == PSM_ACK) ? val : -1);
48241016Sdfr}
48341016Sdfr
48445789Speter#ifdef PSM_RESETAFTERSUSPEND
48541016Sdfr/*
48641016Sdfr * NOTE: once `set_mouse_mode()' is called, the mouse device must be
48741016Sdfr * re-enabled by calling `enable_aux_dev()'
48841016Sdfr */
48941016Sdfrstatic int
49041016Sdfrset_mouse_mode(KBDC kbdc)
49141016Sdfr{
49241016Sdfr    int res;
49341016Sdfr
49441016Sdfr    res = send_aux_command(kbdc, PSMC_SET_STREAM_MODE);
49541016Sdfr    if (verbose >= 2)
49641016Sdfr        log(LOG_DEBUG, "psm: SET_STREAM_MODE return code:%04x\n", res);
49741016Sdfr
49841016Sdfr    return (res == PSM_ACK);
49941016Sdfr}
50045789Speter#endif /* PSM_RESETAFTERSUSPEND */
50141016Sdfr
50245789Speter
50341016Sdfrstatic int
50441016Sdfrget_mouse_buttons(KBDC kbdc)
50541016Sdfr{
50641016Sdfr    int c = 2;		/* assume two buttons by default */
50741016Sdfr    int status[3];
50841016Sdfr
50941016Sdfr    /*
51041016Sdfr     * NOTE: a special sequence to obtain Logitech Mouse specific
51141016Sdfr     * information: set resolution to 25 ppi, set scaling to 1:1, set
51241016Sdfr     * scaling to 1:1, set scaling to 1:1. Then the second byte of the
51341016Sdfr     * mouse status bytes is the number of available buttons.
51441016Sdfr     * Some manufactures also support this sequence.
51541016Sdfr     */
51641016Sdfr    if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW)
51741016Sdfr        return c;
51841016Sdfr    if (set_mouse_scaling(kbdc, 1) && set_mouse_scaling(kbdc, 1)
51941016Sdfr        && set_mouse_scaling(kbdc, 1)
52041016Sdfr	&& (get_mouse_status(kbdc, status, 0, 3) >= 3)) {
52141016Sdfr        if (status[1] != 0)
52241016Sdfr            return status[1];
52341016Sdfr    }
52441016Sdfr    return c;
52541016Sdfr}
52641016Sdfr
52741016Sdfr/* misc subroutines */
52841016Sdfr/*
52941016Sdfr * Someday, I will get the complete list of valid pointing devices and
53041016Sdfr * their IDs... XXX
53141016Sdfr */
53241016Sdfrstatic int
53341016Sdfris_a_mouse(int id)
53441016Sdfr{
53541016Sdfr#if 0
53641016Sdfr    static int valid_ids[] = {
53741016Sdfr        PSM_MOUSE_ID,		/* mouse */
53841016Sdfr        PSM_BALLPOINT_ID,	/* ballpoint device */
53941016Sdfr        PSM_INTELLI_ID,		/* Intellimouse */
54041016Sdfr        -1			/* end of table */
54141016Sdfr    };
54241016Sdfr    int i;
54341016Sdfr
54441016Sdfr    for (i = 0; valid_ids[i] >= 0; ++i)
54541016Sdfr        if (valid_ids[i] == id)
54641016Sdfr            return TRUE;
54741016Sdfr    return FALSE;
54841016Sdfr#else
54941016Sdfr    return TRUE;
55041016Sdfr#endif
55141016Sdfr}
55241016Sdfr
55341016Sdfrstatic char *
55441016Sdfrmodel_name(int model)
55541016Sdfr{
55641016Sdfr    static struct {
55741016Sdfr	int model_code;
55841016Sdfr	char *model_name;
55941016Sdfr    } models[] = {
56041016Sdfr        { MOUSE_MODEL_NETSCROLL,	"NetScroll Mouse" },
56141016Sdfr        { MOUSE_MODEL_NET,		"NetMouse" },
56241016Sdfr        { MOUSE_MODEL_GLIDEPOINT,	"GlidePoint" },
56341016Sdfr        { MOUSE_MODEL_THINK,		"ThinkingMouse" },
56441016Sdfr        { MOUSE_MODEL_INTELLI,		"IntelliMouse" },
56541016Sdfr        { MOUSE_MODEL_MOUSEMANPLUS,	"MouseMan+" },
56641016Sdfr        { MOUSE_MODEL_GENERIC,		"Generic PS/2 mouse" },
56741016Sdfr        { MOUSE_MODEL_UNKNOWN,		NULL },
56841016Sdfr    };
56941016Sdfr    int i;
57041016Sdfr
57141016Sdfr    for (i = 0; models[i].model_code != MOUSE_MODEL_UNKNOWN; ++i) {
57241016Sdfr	if (models[i].model_code == model)
57341016Sdfr	    return models[i].model_name;
57441016Sdfr    }
57541016Sdfr    return "Unknown";
57641016Sdfr}
57741016Sdfr
57841016Sdfrstatic void
57941016Sdfrrecover_from_error(KBDC kbdc)
58041016Sdfr{
58141016Sdfr    /* discard anything left in the output buffer */
58241016Sdfr    empty_both_buffers(kbdc, 10);
58341016Sdfr
58441016Sdfr#if 0
58541016Sdfr    /*
58641016Sdfr     * NOTE: KBDC_RESET_KBD may not restore the communication between the
58741016Sdfr     * keyboard and the controller.
58841016Sdfr     */
58941016Sdfr    reset_kbd(kbdc);
59041016Sdfr#else
59141016Sdfr    /*
59241016Sdfr     * NOTE: somehow diagnostic and keyboard port test commands bring the
59341016Sdfr     * keyboard back.
59441016Sdfr     */
59541016Sdfr    if (!test_controller(kbdc))
59641016Sdfr        log(LOG_ERR, "psm: keyboard controller failed.\n");
59741016Sdfr    /* if there isn't a keyboard in the system, the following error is OK */
59841016Sdfr    if (test_kbd_port(kbdc) != 0) {
59941016Sdfr	if (verbose)
60041016Sdfr	    log(LOG_ERR, "psm: keyboard port failed.\n");
60141016Sdfr    }
60241016Sdfr#endif
60341016Sdfr}
60441016Sdfr
60541016Sdfrstatic int
60641016Sdfrrestore_controller(KBDC kbdc, int command_byte)
60741016Sdfr{
60841016Sdfr    empty_both_buffers(kbdc, 10);
60941016Sdfr
61041016Sdfr    if (!set_controller_command_byte(kbdc, 0xff, command_byte)) {
61141016Sdfr	log(LOG_ERR, "psm: failed to restore the keyboard controller "
61241016Sdfr		     "command byte.\n");
61341016Sdfr	return FALSE;
61441016Sdfr    } else {
61541016Sdfr	return TRUE;
61641016Sdfr    }
61741016Sdfr}
61841016Sdfr
61945789Speter#ifdef PSM_RESETAFTERSUSPEND
62041016Sdfr/*
62141016Sdfr * Re-initialize the aux port and device. The aux port must be enabled
62241016Sdfr * and its interrupt must be disabled before calling this routine.
62341016Sdfr * The aux device will be disabled before returning.
62441016Sdfr * The keyboard controller must be locked via `kbdc_lock()' before
62541016Sdfr * calling this routine.
62641016Sdfr */
62741016Sdfrstatic int
62841016Sdfrreinitialize(int unit, mousemode_t *mode)
62941016Sdfr{
63041016Sdfr    struct psm_softc *sc = PSM_SOFTC(unit);
63141016Sdfr    KBDC kbdc = sc->kbdc;
63241016Sdfr    int stat[3];
63341016Sdfr    int i;
63441016Sdfr
63541016Sdfr    switch((i = test_aux_port(kbdc))) {
63641016Sdfr    case 1:	/* ignore this error */
63741016Sdfr    case PSM_ACK:
63841016Sdfr	if (verbose)
63941016Sdfr	    log(LOG_DEBUG, "psm%d: strange result for test aux port (%d).\n",
64041016Sdfr	        unit, i);
64141016Sdfr	/* fall though */
64241016Sdfr    case 0:	/* no error */
64341016Sdfr    	break;
64441016Sdfr    case -1: 	/* time out */
64541016Sdfr    default: 	/* error */
64641016Sdfr    	recover_from_error(kbdc);
64745789Speter	if (sc->config & PSM_CONFIG_IGNPORTERROR)
64845789Speter	    break;
64941016Sdfr    	log(LOG_ERR, "psm%d: the aux port is not functioning (%d).\n",
65041016Sdfr    	    unit, i);
65141016Sdfr    	return FALSE;
65241016Sdfr    }
65341016Sdfr
65445789Speter    if (sc->config & PSM_CONFIG_NORESET) {
65545789Speter	/*
65645789Speter	 * Don't try to reset the pointing device.  It may possibly be
65745789Speter	 * left in the unknown state, though...
65845789Speter	 */
65945789Speter    } else {
66045789Speter	/*
66145789Speter	 * NOTE: some controllers appears to hang the `keyboard' when
66245789Speter	 * the aux port doesn't exist and `PSMC_RESET_DEV' is issued.
66345789Speter	 */
66445789Speter	if (!reset_aux_dev(kbdc)) {
66545789Speter            recover_from_error(kbdc);
66645789Speter            log(LOG_ERR, "psm%d: failed to reset the aux device.\n", unit);
66745789Speter            return FALSE;
66845789Speter	}
66941016Sdfr    }
67041016Sdfr
67141016Sdfr    /*
67241016Sdfr     * both the aux port and the aux device is functioning, see
67341016Sdfr     * if the device can be enabled.
67441016Sdfr     */
67541016Sdfr    if (!enable_aux_dev(kbdc) || !disable_aux_dev(kbdc)) {
67641016Sdfr        log(LOG_ERR, "psm%d: failed to enable the aux device.\n", unit);
67741016Sdfr        return FALSE;
67841016Sdfr    }
67941016Sdfr    empty_both_buffers(kbdc, 10);	/* remove stray data if any */
68041016Sdfr
68145789Speter    if (sc->config & PSM_CONFIG_NOIDPROBE) {
68245789Speter	i = GENERIC_MOUSE_ENTRY;
68345789Speter    } else {
68445789Speter	/* FIXME: hardware ID, mouse buttons? */
68541016Sdfr
68645789Speter	/* other parameters */
68745789Speter	for (i = 0; vendortype[i].probefunc != NULL; ++i) {
68845789Speter	    if ((*vendortype[i].probefunc)(sc)) {
68945789Speter		if (verbose >= 2)
69045789Speter		    log(LOG_ERR, "psm%d: found %s\n",
69145789Speter			unit, model_name(vendortype[i].model));
69245789Speter		break;
69345789Speter	    }
69441016Sdfr	}
69541016Sdfr    }
69641016Sdfr
69741016Sdfr    sc->hw.model = vendortype[i].model;
69841016Sdfr    sc->mode.packetsize = vendortype[i].packetsize;
69941016Sdfr
70041016Sdfr    /* set mouse parameters */
70141016Sdfr    if (mode != (mousemode_t *)NULL) {
70241016Sdfr	if (mode->rate > 0)
70341016Sdfr            mode->rate = set_mouse_sampling_rate(kbdc, mode->rate);
70441016Sdfr	if (mode->resolution >= 0)
70541016Sdfr            mode->resolution = set_mouse_resolution(kbdc, mode->resolution);
70641016Sdfr        set_mouse_scaling(kbdc, 1);
70741016Sdfr        set_mouse_mode(kbdc);
70841016Sdfr    }
70941016Sdfr
71041016Sdfr    /* request a data packet and extract sync. bits */
71141016Sdfr    if (get_mouse_status(kbdc, stat, 1, 3) < 3) {
71241016Sdfr        log(LOG_DEBUG, "psm%d: failed to get data (reinitialize).\n", unit);
71341016Sdfr        sc->mode.syncmask[0] = 0;
71441016Sdfr    } else {
71541016Sdfr        sc->mode.syncmask[1] = stat[0] & sc->mode.syncmask[0];	/* syncbits */
71641016Sdfr	/* the NetScroll Mouse will send three more bytes... Ignore them */
71741016Sdfr	empty_aux_buffer(kbdc, 5);
71841016Sdfr    }
71941016Sdfr
72041016Sdfr    /* just check the status of the mouse */
72141016Sdfr    if (get_mouse_status(kbdc, stat, 0, 3) < 3)
72241016Sdfr        log(LOG_DEBUG, "psm%d: failed to get status (reinitialize).\n", unit);
72341016Sdfr
72441016Sdfr    return TRUE;
72541016Sdfr}
72645789Speter#endif /* PSM_RESETAFTERSUSPEND */
72741016Sdfr
72841016Sdfrstatic int
72941016Sdfrdoopen(int unit, int command_byte)
73041016Sdfr{
73141016Sdfr    struct psm_softc *sc = PSM_SOFTC(unit);
73241016Sdfr    int stat[3];
73341016Sdfr
73441016Sdfr    /* enable the mouse device */
73541016Sdfr    if (!enable_aux_dev(sc->kbdc)) {
73641016Sdfr	/* MOUSE ERROR: failed to enable the mouse because:
73741016Sdfr	 * 1) the mouse is faulty,
73841016Sdfr	 * 2) the mouse has been removed(!?)
73941016Sdfr	 * In the latter case, the keyboard may have hung, and need
74041016Sdfr	 * recovery procedure...
74141016Sdfr	 */
74241016Sdfr	recover_from_error(sc->kbdc);
74341016Sdfr#if 0
74441016Sdfr	/* FIXME: we could reset the mouse here and try to enable
74541016Sdfr	 * it again. But it will take long time and it's not a good
74641016Sdfr	 * idea to disable the keyboard that long...
74741016Sdfr	 */
74841016Sdfr	if (!reinitialize(unit, &sc->mode) || !enable_aux_dev(sc->kbdc)) {
74941016Sdfr	    recover_from_error(sc->kbdc);
75041016Sdfr#else
75141016Sdfr        {
75241016Sdfr#endif
75341016Sdfr            restore_controller(sc->kbdc, command_byte);
75441016Sdfr	    /* mark this device is no longer available */
75541016Sdfr	    sc->state &= ~PSM_VALID;
75641016Sdfr	    log(LOG_ERR, "psm%d: failed to enable the device (doopen).\n",
75741016Sdfr		unit);
75841016Sdfr	    return (EIO);
75941016Sdfr	}
76041016Sdfr    }
76141016Sdfr
76241016Sdfr    if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3)
76341016Sdfr        log(LOG_DEBUG, "psm%d: failed to get status (doopen).\n", unit);
76441016Sdfr
76541016Sdfr    /* enable the aux port and interrupt */
76641016Sdfr    if (!set_controller_command_byte(sc->kbdc,
76741016Sdfr	    kbdc_get_device_mask(sc->kbdc),
76841016Sdfr	    (command_byte & KBD_KBD_CONTROL_BITS)
76941016Sdfr		| KBD_ENABLE_AUX_PORT | KBD_ENABLE_AUX_INT)) {
77041016Sdfr	/* CONTROLLER ERROR */
77141016Sdfr	disable_aux_dev(sc->kbdc);
77241016Sdfr        restore_controller(sc->kbdc, command_byte);
77341016Sdfr	log(LOG_ERR, "psm%d: failed to enable the aux interrupt (doopen).\n",
77441016Sdfr	    unit);
77541016Sdfr	return (EIO);
77641016Sdfr    }
77741016Sdfr
77841016Sdfr    return (0);
77941016Sdfr}
78041016Sdfr
78141016Sdfr/* psm driver entry points */
78241016Sdfr
78341016Sdfr#define endprobe(v)	{   if (bootverbose) 				\
78441016Sdfr				--verbose;   				\
78541016Sdfr                            kbdc_set_device_mask(sc->kbdc, mask);	\
78641016Sdfr			    kbdc_lock(sc->kbdc, FALSE);			\
78741016Sdfr 	                    free(sc, M_DEVBUF);                         \
78841016Sdfr			    return (v);	     				\
78941016Sdfr			}
79041016Sdfr
79141016Sdfrstatic int
79241016Sdfrpsmprobe(device_t dev)
79341016Sdfr{
79441016Sdfr    int unit = device_get_unit(dev);
79541016Sdfr    struct psm_softc *sc = device_get_softc(dev);
79645720Speter    uintptr_t port;
79745720Speter    uintptr_t flags;
79841016Sdfr    int stat[3];
79941016Sdfr    int command_byte;
80041016Sdfr    int mask;
80141016Sdfr    int i;
80241016Sdfr
80341016Sdfr#if 0
80441016Sdfr    kbdc_debug(TRUE);
80541016Sdfr#endif
80643105Sdfr    BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port);
80743105Sdfr    BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags);
80843105Sdfr
80943105Sdfr    sc->addr = port;
81041016Sdfr    sc->kbdc = kbdc_open(sc->addr);
81143105Sdfr    sc->config = flags & PSM_CONFIG_FLAGS;
81241016Sdfr    sc->flags = 0;
81341016Sdfr    if (bootverbose)
81441016Sdfr        ++verbose;
81541016Sdfr
81643105Sdfr    device_set_desc(dev, "PS/2 Mouse");
81743105Sdfr
81841016Sdfr    if (!kbdc_lock(sc->kbdc, TRUE)) {
81941016Sdfr        printf("psm%d: unable to lock the controller.\n", unit);
82041016Sdfr        if (bootverbose)
82141016Sdfr            --verbose;
82241016Sdfr	return (ENXIO);
82341016Sdfr    }
82441016Sdfr
82541016Sdfr    /*
82641016Sdfr     * NOTE: two bits in the command byte controls the operation of the
82741016Sdfr     * aux port (mouse port): the aux port disable bit (bit 5) and the aux
82841016Sdfr     * port interrupt (IRQ 12) enable bit (bit 2).
82941016Sdfr     */
83041016Sdfr
83141016Sdfr    /* discard anything left after the keyboard initialization */
83241016Sdfr    empty_both_buffers(sc->kbdc, 10);
83341016Sdfr
83441016Sdfr    /* save the current command byte; it will be used later */
83541016Sdfr    mask = kbdc_get_device_mask(sc->kbdc) & ~KBD_AUX_CONTROL_BITS;
83641016Sdfr    command_byte = get_controller_command_byte(sc->kbdc);
83741016Sdfr    if (verbose)
83841016Sdfr        printf("psm%d: current command byte:%04x\n", unit, command_byte);
83941016Sdfr    if (command_byte == -1) {
84041016Sdfr        /* CONTROLLER ERROR */
84141016Sdfr        printf("psm%d: unable to get the current command byte value.\n",
84241016Sdfr            unit);
84341016Sdfr        endprobe(ENXIO);
84441016Sdfr    }
84541016Sdfr
84641016Sdfr    /*
84741016Sdfr     * disable the keyboard port while probing the aux port, which must be
84841016Sdfr     * enabled during this routine
84941016Sdfr     */
85041016Sdfr    if (!set_controller_command_byte(sc->kbdc,
85141016Sdfr	    KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS,
85241016Sdfr  	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
85341016Sdfr                | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
85441016Sdfr        /*
85541016Sdfr	 * this is CONTROLLER ERROR; I don't know how to recover
85641016Sdfr         * from this error...
85741016Sdfr	 */
85841016Sdfr        restore_controller(sc->kbdc, command_byte);
85941016Sdfr        printf("psm%d: unable to set the command byte.\n", unit);
86041016Sdfr        endprobe(ENXIO);
86141016Sdfr    }
86245789Speter    write_controller_command(sc->kbdc, KBDC_ENABLE_AUX_PORT);
86341016Sdfr
86441016Sdfr    /*
86541016Sdfr     * NOTE: `test_aux_port()' is designed to return with zero if the aux
86641016Sdfr     * port exists and is functioning. However, some controllers appears
86741016Sdfr     * to respond with zero even when the aux port doesn't exist. (It may
86841016Sdfr     * be that this is only the case when the controller DOES have the aux
86941016Sdfr     * port but the port is not wired on the motherboard.) The keyboard
87041016Sdfr     * controllers without the port, such as the original AT, are
87141016Sdfr     * supporsed to return with an error code or simply time out. In any
87241016Sdfr     * case, we have to continue probing the port even when the controller
87341016Sdfr     * passes this test.
87441016Sdfr     *
87541016Sdfr     * XXX: some controllers erroneously return the error code 1 when
87641016Sdfr     * it has the perfectly functional aux port. We have to ignore this
87741016Sdfr     * error code. Even if the controller HAS error with the aux port,
87841016Sdfr     * it will be detected later...
87941016Sdfr     * XXX: another incompatible controller returns PSM_ACK (0xfa)...
88041016Sdfr     */
88141016Sdfr    switch ((i = test_aux_port(sc->kbdc))) {
88241016Sdfr    case 1:	   /* ignore this error */
88341016Sdfr    case PSM_ACK:
88441016Sdfr        if (verbose)
88541016Sdfr	    printf("psm%d: strange result for test aux port (%d).\n",
88641016Sdfr	        unit, i);
88741016Sdfr	/* fall though */
88841016Sdfr    case 0:        /* no error */
88941016Sdfr        break;
89041016Sdfr    case -1:        /* time out */
89141016Sdfr    default:        /* error */
89241016Sdfr        recover_from_error(sc->kbdc);
89345789Speter	if (sc->config & PSM_CONFIG_IGNPORTERROR)
89445789Speter	    break;
89541016Sdfr        restore_controller(sc->kbdc, command_byte);
89641016Sdfr        if (verbose)
89741016Sdfr            printf("psm%d: the aux port is not functioning (%d).\n",
89841016Sdfr                unit, i);
89941016Sdfr        endprobe(ENXIO);
90041016Sdfr    }
90141016Sdfr
90245789Speter    if (sc->config & PSM_CONFIG_NORESET) {
90345789Speter	/*
90445789Speter	 * Don't try to reset the pointing device.  It may possibly be
90545789Speter	 * left in the unknown state, though...
90645789Speter	 */
90745789Speter    } else {
90845789Speter	/*
90945789Speter	 * NOTE: some controllers appears to hang the `keyboard' when the aux
91045789Speter	 * port doesn't exist and `PSMC_RESET_DEV' is issued.
91145789Speter	 */
91245789Speter	if (!reset_aux_dev(sc->kbdc)) {
91345789Speter            recover_from_error(sc->kbdc);
91445789Speter            restore_controller(sc->kbdc, command_byte);
91545789Speter            if (verbose)
91645789Speter        	printf("psm%d: failed to reset the aux device.\n", unit);
91745789Speter            endprobe(ENXIO);
91845789Speter	}
91941016Sdfr    }
92045789Speter
92141016Sdfr    /*
92241016Sdfr     * both the aux port and the aux device is functioning, see if the
92341016Sdfr     * device can be enabled. NOTE: when enabled, the device will start
92441016Sdfr     * sending data; we shall immediately disable the device once we know
92541016Sdfr     * the device can be enabled.
92641016Sdfr     */
92741016Sdfr    if (!enable_aux_dev(sc->kbdc) || !disable_aux_dev(sc->kbdc)) {
92845789Speter        /* MOUSE ERROR */
92945789Speter	recover_from_error(sc->kbdc);
93045789Speter	restore_controller(sc->kbdc, command_byte);
93145789Speter	if (verbose)
93245789Speter	    printf("psm%d: failed to enable the aux device.\n", unit);
93341016Sdfr        endprobe(ENXIO);
93441016Sdfr    }
93541016Sdfr
93641016Sdfr    /* save the default values after reset */
93741016Sdfr    if (get_mouse_status(sc->kbdc, stat, 0, 3) >= 3) {
93841016Sdfr	sc->dflt_mode.rate = sc->mode.rate = stat[2];
93941016Sdfr	sc->dflt_mode.resolution = sc->mode.resolution = stat[1];
94041016Sdfr    } else {
94141016Sdfr	sc->dflt_mode.rate = sc->mode.rate = -1;
94241016Sdfr	sc->dflt_mode.resolution = sc->mode.resolution = -1;
94341016Sdfr    }
94441016Sdfr
94541016Sdfr    /* hardware information */
94641016Sdfr    sc->hw.iftype = MOUSE_IF_PS2;
94741016Sdfr
94841016Sdfr    /* verify the device is a mouse */
94941016Sdfr    sc->hw.hwid = get_aux_id(sc->kbdc);
95041016Sdfr    if (!is_a_mouse(sc->hw.hwid)) {
95141016Sdfr        restore_controller(sc->kbdc, command_byte);
95241016Sdfr        if (verbose)
95341016Sdfr            printf("psm%d: unknown device type (%d).\n", unit, sc->hw.hwid);
95441016Sdfr        endprobe(ENXIO);
95541016Sdfr    }
95641016Sdfr    switch (sc->hw.hwid) {
95741016Sdfr    case PSM_BALLPOINT_ID:
95841016Sdfr        sc->hw.type = MOUSE_TRACKBALL;
95941016Sdfr        break;
96041016Sdfr    case PSM_MOUSE_ID:
96141016Sdfr    case PSM_INTELLI_ID:
96241016Sdfr        sc->hw.type = MOUSE_MOUSE;
96341016Sdfr        break;
96441016Sdfr    default:
96541016Sdfr        sc->hw.type = MOUSE_UNKNOWN;
96641016Sdfr        break;
96741016Sdfr    }
96841016Sdfr
96945789Speter    if (sc->config & PSM_CONFIG_NOIDPROBE) {
97045789Speter	sc->hw.buttons = 2;
97145789Speter	i = GENERIC_MOUSE_ENTRY;
97245789Speter    } else {
97345789Speter	/* # of buttons */
97445789Speter	sc->hw.buttons = get_mouse_buttons(sc->kbdc);
97541016Sdfr
97645789Speter	/* other parameters */
97745789Speter	for (i = 0; vendortype[i].probefunc != NULL; ++i) {
97845789Speter	    if ((*vendortype[i].probefunc)(sc)) {
97945789Speter		if (verbose >= 2)
98045789Speter		    printf("psm%d: found %s\n",
98145789Speter			   unit, model_name(vendortype[i].model));
98245789Speter		break;
98345789Speter	    }
98441016Sdfr	}
98541016Sdfr    }
98641016Sdfr
98741016Sdfr    sc->hw.model = vendortype[i].model;
98841016Sdfr
98941016Sdfr    sc->dflt_mode.level = PSM_LEVEL_BASE;
99041016Sdfr    sc->dflt_mode.packetsize = MOUSE_PS2_PACKETSIZE;
99141016Sdfr    sc->dflt_mode.accelfactor = (sc->config & PSM_CONFIG_ACCEL) >> 4;
99241016Sdfr    if (sc->config & PSM_CONFIG_NOCHECKSYNC)
99341016Sdfr        sc->dflt_mode.syncmask[0] = 0;
99441016Sdfr    else
99541016Sdfr        sc->dflt_mode.syncmask[0] = vendortype[i].syncmask;
99645789Speter    if (sc->config & PSM_CONFIG_FORCETAP)
99745789Speter        sc->mode.syncmask[0] &= ~MOUSE_PS2_TAP;
99841016Sdfr    sc->dflt_mode.syncmask[1] = 0;	/* syncbits */
99941016Sdfr    sc->mode = sc->dflt_mode;
100041016Sdfr    sc->mode.packetsize = vendortype[i].packetsize;
100141016Sdfr
100241016Sdfr    /* set mouse parameters */
100348773Syokota#if 0
100448773Syokota    /*
100548773Syokota     * A version of Logitech FirstMouse+ won't report wheel movement,
100648773Syokota     * if SET_DEFAULTS is sent...  Don't use this command.
100748773Syokota     * This fix was found by Takashi Nishida.
100848773Syokota     */
100941016Sdfr    i = send_aux_command(sc->kbdc, PSMC_SET_DEFAULTS);
101041016Sdfr    if (verbose >= 2)
101141016Sdfr	printf("psm%d: SET_DEFAULTS return code:%04x\n", unit, i);
101248773Syokota#endif
101341016Sdfr    if (sc->config & PSM_CONFIG_RESOLUTION) {
101441016Sdfr        sc->mode.resolution
101541016Sdfr	    = set_mouse_resolution(sc->kbdc,
101648773Syokota				   (sc->config & PSM_CONFIG_RESOLUTION) - 1);
101748773Syokota    } else if (sc->mode.resolution >= 0) {
101848773Syokota	sc->mode.resolution
101948773Syokota	    = set_mouse_resolution(sc->kbdc, sc->dflt_mode.resolution);
102041016Sdfr    }
102148773Syokota    if (sc->mode.rate > 0) {
102248773Syokota	sc->mode.rate = set_mouse_sampling_rate(sc->kbdc, sc->dflt_mode.rate);
102348773Syokota    }
102448773Syokota    set_mouse_scaling(sc->kbdc, 1);
102541016Sdfr
102641016Sdfr    /* request a data packet and extract sync. bits */
102741016Sdfr    if (get_mouse_status(sc->kbdc, stat, 1, 3) < 3) {
102841016Sdfr        printf("psm%d: failed to get data.\n", unit);
102941016Sdfr        sc->mode.syncmask[0] = 0;
103041016Sdfr    } else {
103141016Sdfr        sc->mode.syncmask[1] = stat[0] & sc->mode.syncmask[0];	/* syncbits */
103241016Sdfr	/* the NetScroll Mouse will send three more bytes... Ignore them */
103341016Sdfr	empty_aux_buffer(sc->kbdc, 5);
103441016Sdfr    }
103541016Sdfr
103641016Sdfr    /* just check the status of the mouse */
103741016Sdfr    /*
103841016Sdfr     * NOTE: XXX there are some arcane controller/mouse combinations out
103941016Sdfr     * there, which hung the controller unless there is data transmission
104041016Sdfr     * after ACK from the mouse.
104141016Sdfr     */
104241016Sdfr    if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) {
104341016Sdfr        printf("psm%d: failed to get status.\n", unit);
104441016Sdfr    } else {
104541016Sdfr	/*
104641016Sdfr	 * When in its native mode, some mice operate with different
104741016Sdfr	 * default parameters than in the PS/2 compatible mode.
104841016Sdfr	 */
104941016Sdfr        sc->dflt_mode.rate = sc->mode.rate = stat[2];
105041016Sdfr        sc->dflt_mode.resolution = sc->mode.resolution = stat[1];
105141016Sdfr     }
105241016Sdfr
105341016Sdfr    /* disable the aux port for now... */
105441016Sdfr    if (!set_controller_command_byte(sc->kbdc,
105541016Sdfr	    KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS,
105641016Sdfr            (command_byte & KBD_KBD_CONTROL_BITS)
105741016Sdfr                | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
105841016Sdfr        /*
105941016Sdfr	 * this is CONTROLLER ERROR; I don't know the proper way to
106041016Sdfr         * recover from this error...
106141016Sdfr	 */
106241016Sdfr        restore_controller(sc->kbdc, command_byte);
106341016Sdfr        printf("psm%d: unable to set the command byte.\n", unit);
106441016Sdfr        endprobe(ENXIO);
106541016Sdfr    }
106641016Sdfr
106741016Sdfr    /* done */
106841016Sdfr    kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS);
106941016Sdfr    kbdc_lock(sc->kbdc, FALSE);
107041016Sdfr    return (0);
107141016Sdfr}
107241016Sdfr
107341016Sdfrstatic int
107441016Sdfrpsmattach(device_t dev)
107541016Sdfr{
107641016Sdfr    int unit = device_get_unit(dev);
107741016Sdfr    struct psm_softc *sc = device_get_softc(dev);
107841016Sdfr    void *ih;
107941181Sdfr    struct resource *res;
108045720Speter    uintptr_t irq;
108141181Sdfr    int zero = 0;
108241016Sdfr
108341016Sdfr    if (sc == NULL)    /* shouldn't happen */
108441016Sdfr	return (ENXIO);
108541016Sdfr
108641016Sdfr    /* Setup initial state */
108741016Sdfr    sc->state = PSM_VALID;
108841016Sdfr
108941016Sdfr    /* Done */
109041016Sdfr#ifdef    DEVFS
109141016Sdfr    sc->devfs_token =
109241016Sdfr        devfs_add_devswf(&psm_cdevsw, PSM_MKMINOR(unit, FALSE),
109341016Sdfr        DV_CHR, 0, 0, 0666, "psm%d", unit);
109441016Sdfr    sc->b_devfs_token =
109541016Sdfr        devfs_add_devswf(&psm_cdevsw, PSM_MKMINOR(unit, TRUE),
109641016Sdfr        DV_CHR, 0, 0, 0666, "bpsm%d", unit);
109741016Sdfr#endif /* DEVFS */
109841016Sdfr
109941016Sdfr#ifdef PSM_HOOKAPM
110041016Sdfr    sc->resumehook.ah_name = "PS/2 mouse";
110141016Sdfr    sc->resumehook.ah_fun = psmresume;
110241016Sdfr    sc->resumehook.ah_arg = (void *)unit;
110341016Sdfr    sc->resumehook.ah_order = APM_MID_ORDER;
110441016Sdfr    apm_hook_establish(APM_HOOK_RESUME , &sc->resumehook);
110541016Sdfr    if (verbose)
110641016Sdfr        printf("psm%d: APM hooks installed.\n", unit);
110741016Sdfr#endif /* PSM_HOOKAPM */
110841016Sdfr
110941016Sdfr    if (!verbose) {
111041016Sdfr        printf("psm%d: model %s, device ID %d\n",
111148778Syokota	    unit, model_name(sc->hw.model), sc->hw.hwid & 0x00ff);
111241016Sdfr    } else {
111348778Syokota        printf("psm%d: model %s, device ID %d-%02x, %d buttons\n",
111448778Syokota	    unit, model_name(sc->hw.model),
111548778Syokota	    sc->hw.hwid & 0x00ff, sc->hw.hwid >> 8, sc->hw.buttons);
111641016Sdfr	printf("psm%d: config:%08x, flags:%08x, packet size:%d\n",
111741016Sdfr	    unit, sc->config, sc->flags, sc->mode.packetsize);
111841016Sdfr	printf("psm%d: syncmask:%02x, syncbits:%02x\n",
111941016Sdfr	    unit, sc->mode.syncmask[0], sc->mode.syncmask[1]);
112041016Sdfr    }
112141016Sdfr
112241016Sdfr    if (bootverbose)
112341016Sdfr        --verbose;
112441016Sdfr
112543105Sdfr    BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq);
112643105Sdfr    res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1,
112741181Sdfr			     RF_SHAREABLE | RF_ACTIVE);
112846743Sdfr    BUS_SETUP_INTR(device_get_parent(dev), dev, res, INTR_TYPE_TTY,
112946743Sdfr		   psmintr, sc, &ih);
113041016Sdfr
113141016Sdfr    return (0);
113241016Sdfr}
113341016Sdfr
113441016Sdfrstatic int
113541016Sdfrpsmopen(dev_t dev, int flag, int fmt, struct proc *p)
113641016Sdfr{
113741016Sdfr    int unit = PSM_UNIT(dev);
113841016Sdfr    struct psm_softc *sc;
113941016Sdfr    int command_byte;
114041016Sdfr    int err;
114141016Sdfr    int s;
114241016Sdfr
114341016Sdfr    /* Validate unit number */
114441016Sdfr    if (unit >= NPSM)
114541016Sdfr        return (ENXIO);
114641016Sdfr
114741016Sdfr    /* Get device data */
114841016Sdfr    sc = PSM_SOFTC(unit);
114941016Sdfr    if ((sc == NULL) || (sc->state & PSM_VALID) == 0)
115041016Sdfr	/* the device is no longer valid/functioning */
115141016Sdfr        return (ENXIO);
115241016Sdfr
115341016Sdfr    /* Disallow multiple opens */
115441016Sdfr    if (sc->state & PSM_OPEN)
115541016Sdfr        return (EBUSY);
115641016Sdfr
115741016Sdfr    device_busy(devclass_get_device(psm_devclass, unit));
115841016Sdfr
115941016Sdfr    /* Initialize state */
116041016Sdfr    sc->rsel.si_flags = 0;
116141016Sdfr    sc->rsel.si_pid = 0;
116241016Sdfr    sc->mode.level = sc->dflt_mode.level;
116341016Sdfr    sc->mode.protocol = sc->dflt_mode.protocol;
116441016Sdfr
116541016Sdfr    /* flush the event queue */
116641016Sdfr    sc->queue.count = 0;
116741016Sdfr    sc->queue.head = 0;
116841016Sdfr    sc->queue.tail = 0;
116941016Sdfr    sc->status.flags = 0;
117041016Sdfr    sc->status.button = 0;
117141016Sdfr    sc->status.obutton = 0;
117241016Sdfr    sc->status.dx = 0;
117341016Sdfr    sc->status.dy = 0;
117441016Sdfr    sc->status.dz = 0;
117541016Sdfr    sc->button = 0;
117641016Sdfr
117741016Sdfr    /* empty input buffer */
117841016Sdfr    bzero(sc->ipacket, sizeof(sc->ipacket));
117941016Sdfr    sc->inputbytes = 0;
118041016Sdfr
118141016Sdfr    /* don't let timeout routines in the keyboard driver to poll the kbdc */
118241016Sdfr    if (!kbdc_lock(sc->kbdc, TRUE))
118341016Sdfr	return (EIO);
118441016Sdfr
118541016Sdfr    /* save the current controller command byte */
118641016Sdfr    s = spltty();
118741016Sdfr    command_byte = get_controller_command_byte(sc->kbdc);
118841016Sdfr
118941016Sdfr    /* enable the aux port and temporalily disable the keyboard */
119041016Sdfr    if ((command_byte == -1)
119141016Sdfr        || !set_controller_command_byte(sc->kbdc,
119241016Sdfr	    kbdc_get_device_mask(sc->kbdc),
119341016Sdfr  	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
119441016Sdfr	        | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
119541016Sdfr        /* CONTROLLER ERROR; do you know how to get out of this? */
119641016Sdfr        kbdc_lock(sc->kbdc, FALSE);
119741016Sdfr	splx(s);
119841016Sdfr	log(LOG_ERR, "psm%d: unable to set the command byte (psmopen).\n",
119941016Sdfr	    unit);
120041016Sdfr	return (EIO);
120141016Sdfr    }
120241016Sdfr    /*
120341016Sdfr     * Now that the keyboard controller is told not to generate
120441016Sdfr     * the keyboard and mouse interrupts, call `splx()' to allow
120541016Sdfr     * the other tty interrupts. The clock interrupt may also occur,
120641016Sdfr     * but timeout routines will be blocked by the poll flag set
120741016Sdfr     * via `kbdc_lock()'
120841016Sdfr     */
120941016Sdfr    splx(s);
121041016Sdfr
121141016Sdfr    /* enable the mouse device */
121241016Sdfr    err = doopen(unit, command_byte);
121341016Sdfr
121441016Sdfr    /* done */
121541016Sdfr    if (err == 0)
121641016Sdfr        sc->state |= PSM_OPEN;
121741016Sdfr    kbdc_lock(sc->kbdc, FALSE);
121841016Sdfr    return (err);
121941016Sdfr}
122041016Sdfr
122141016Sdfrstatic int
122241016Sdfrpsmclose(dev_t dev, int flag, int fmt, struct proc *p)
122341016Sdfr{
122441016Sdfr    int unit = PSM_UNIT(dev);
122541016Sdfr    struct psm_softc *sc = PSM_SOFTC(unit);
122641016Sdfr    int stat[3];
122741016Sdfr    int command_byte;
122841016Sdfr    int s;
122941016Sdfr
123041016Sdfr    /* don't let timeout routines in the keyboard driver to poll the kbdc */
123141016Sdfr    if (!kbdc_lock(sc->kbdc, TRUE))
123241016Sdfr	return (EIO);
123341016Sdfr
123441016Sdfr    /* save the current controller command byte */
123541016Sdfr    s = spltty();
123641016Sdfr    command_byte = get_controller_command_byte(sc->kbdc);
123741016Sdfr    if (command_byte == -1) {
123841016Sdfr        kbdc_lock(sc->kbdc, FALSE);
123941016Sdfr	splx(s);
124041016Sdfr	return (EIO);
124141016Sdfr    }
124241016Sdfr
124341016Sdfr    /* disable the aux interrupt and temporalily disable the keyboard */
124441016Sdfr    if (!set_controller_command_byte(sc->kbdc,
124541016Sdfr	    kbdc_get_device_mask(sc->kbdc),
124641016Sdfr  	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
124741016Sdfr	        | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
124841016Sdfr	log(LOG_ERR, "psm%d: failed to disable the aux int (psmclose).\n",
124941016Sdfr	    PSM_UNIT(dev));
125041016Sdfr	/* CONTROLLER ERROR;
125141016Sdfr	 * NOTE: we shall force our way through. Because the only
125241016Sdfr	 * ill effect we shall see is that we may not be able
125341016Sdfr	 * to read ACK from the mouse, and it doesn't matter much
125441016Sdfr	 * so long as the mouse will accept the DISABLE command.
125541016Sdfr	 */
125641016Sdfr    }
125741016Sdfr    splx(s);
125841016Sdfr
125941016Sdfr    /* remove anything left in the output buffer */
126041016Sdfr    empty_aux_buffer(sc->kbdc, 10);
126141016Sdfr
126241016Sdfr    /* disable the aux device, port and interrupt */
126341016Sdfr    if (sc->state & PSM_VALID) {
126441016Sdfr        if (!disable_aux_dev(sc->kbdc)) {
126541016Sdfr	    /* MOUSE ERROR;
126641016Sdfr	     * NOTE: we don't return error and continue, pretending
126741016Sdfr	     * we have successfully disabled the device. It's OK because
126841016Sdfr	     * the interrupt routine will discard any data from the mouse
126941016Sdfr	     * hereafter.
127041016Sdfr	     */
127141016Sdfr	    log(LOG_ERR, "psm%d: failed to disable the device (psmclose).\n",
127241016Sdfr	        PSM_UNIT(dev));
127341016Sdfr        }
127441016Sdfr
127541016Sdfr        if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3)
127641016Sdfr            log(LOG_DEBUG, "psm%d: failed to get status (psmclose).\n",
127741016Sdfr	        PSM_UNIT(dev));
127841016Sdfr    }
127941016Sdfr
128041016Sdfr    if (!set_controller_command_byte(sc->kbdc,
128141016Sdfr	    kbdc_get_device_mask(sc->kbdc),
128241016Sdfr	    (command_byte & KBD_KBD_CONTROL_BITS)
128341016Sdfr	        | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
128441016Sdfr	/* CONTROLLER ERROR;
128541016Sdfr	 * we shall ignore this error; see the above comment.
128641016Sdfr	 */
128741016Sdfr	log(LOG_ERR, "psm%d: failed to disable the aux port (psmclose).\n",
128841016Sdfr	    PSM_UNIT(dev));
128941016Sdfr    }
129041016Sdfr
129141016Sdfr    /* remove anything left in the output buffer */
129241016Sdfr    empty_aux_buffer(sc->kbdc, 10);
129341016Sdfr
129441016Sdfr    /* close is almost always successful */
129541016Sdfr    sc->state &= ~PSM_OPEN;
129641016Sdfr    kbdc_lock(sc->kbdc, FALSE);
129741016Sdfr    device_unbusy(devclass_get_device(psm_devclass, unit));
129841016Sdfr    return (0);
129941016Sdfr}
130041016Sdfr
130141016Sdfrstatic int
130241016Sdfrtame_mouse(struct psm_softc *sc, mousestatus_t *status, unsigned char *buf)
130341016Sdfr{
130441016Sdfr    static unsigned char butmapps2[8] = {
130541016Sdfr        0,
130641016Sdfr        MOUSE_PS2_BUTTON1DOWN,
130741016Sdfr        MOUSE_PS2_BUTTON2DOWN,
130841016Sdfr        MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN,
130941016Sdfr        MOUSE_PS2_BUTTON3DOWN,
131041016Sdfr        MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON3DOWN,
131141016Sdfr        MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN,
131241016Sdfr        MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN,
131341016Sdfr    };
131441016Sdfr    static unsigned char butmapmsc[8] = {
131541016Sdfr        MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
131641016Sdfr        MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
131741016Sdfr        MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP,
131841016Sdfr        MOUSE_MSC_BUTTON3UP,
131941016Sdfr        MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP,
132041016Sdfr        MOUSE_MSC_BUTTON2UP,
132141016Sdfr        MOUSE_MSC_BUTTON1UP,
132241016Sdfr        0,
132341016Sdfr    };
132441016Sdfr    int mapped;
132541016Sdfr    int i;
132641016Sdfr
132741016Sdfr    if (sc->mode.level == PSM_LEVEL_BASE) {
132841016Sdfr        mapped = status->button & ~MOUSE_BUTTON4DOWN;
132941016Sdfr        if (status->button & MOUSE_BUTTON4DOWN)
133041016Sdfr	    mapped |= MOUSE_BUTTON1DOWN;
133141016Sdfr        status->button = mapped;
133241016Sdfr        buf[0] = MOUSE_PS2_SYNC | butmapps2[mapped & MOUSE_STDBUTTONS];
133341016Sdfr        i = max(min(status->dx, 255), -256);
133441016Sdfr	if (i < 0)
133541016Sdfr	    buf[0] |= MOUSE_PS2_XNEG;
133641016Sdfr        buf[1] = i;
133741016Sdfr        i = max(min(status->dy, 255), -256);
133841016Sdfr	if (i < 0)
133941016Sdfr	    buf[0] |= MOUSE_PS2_YNEG;
134041016Sdfr        buf[2] = i;
134141016Sdfr	return MOUSE_PS2_PACKETSIZE;
134241016Sdfr    } else if (sc->mode.level == PSM_LEVEL_STANDARD) {
134341016Sdfr        buf[0] = MOUSE_MSC_SYNC | butmapmsc[status->button & MOUSE_STDBUTTONS];
134441016Sdfr        i = max(min(status->dx, 255), -256);
134541016Sdfr        buf[1] = i >> 1;
134641016Sdfr        buf[3] = i - buf[1];
134741016Sdfr        i = max(min(status->dy, 255), -256);
134841016Sdfr        buf[2] = i >> 1;
134941016Sdfr        buf[4] = i - buf[2];
135041016Sdfr        i = max(min(status->dz, 127), -128);
135141016Sdfr        buf[5] = (i >> 1) & 0x7f;
135241016Sdfr        buf[6] = (i - (i >> 1)) & 0x7f;
135341016Sdfr        buf[7] = (~status->button >> 3) & 0x7f;
135441016Sdfr	return MOUSE_SYS_PACKETSIZE;
135541016Sdfr    }
135641016Sdfr    return sc->inputbytes;;
135741016Sdfr}
135841016Sdfr
135941016Sdfrstatic int
136041016Sdfrpsmread(dev_t dev, struct uio *uio, int flag)
136141016Sdfr{
136241016Sdfr    register struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev));
136341016Sdfr    unsigned char buf[PSM_SMALLBUFSIZE];
136441016Sdfr    int error = 0;
136541016Sdfr    int s;
136641016Sdfr    int l;
136741016Sdfr
136841016Sdfr    if ((sc->state & PSM_VALID) == 0)
136941016Sdfr	return EIO;
137041016Sdfr
137141016Sdfr    /* block until mouse activity occured */
137241016Sdfr    s = spltty();
137341016Sdfr    while (sc->queue.count <= 0) {
137441016Sdfr        if (PSM_NBLOCKIO(dev)) {
137541016Sdfr            splx(s);
137641016Sdfr            return EWOULDBLOCK;
137741016Sdfr        }
137841016Sdfr        sc->state |= PSM_ASLP;
137941016Sdfr        error = tsleep((caddr_t) sc, PZERO | PCATCH, "psmrea", 0);
138041016Sdfr        sc->state &= ~PSM_ASLP;
138141016Sdfr        if (error) {
138241016Sdfr            splx(s);
138341016Sdfr            return error;
138441016Sdfr        } else if ((sc->state & PSM_VALID) == 0) {
138541016Sdfr            /* the device disappeared! */
138641016Sdfr            splx(s);
138741016Sdfr            return EIO;
138841016Sdfr	}
138941016Sdfr    }
139041016Sdfr    splx(s);
139141016Sdfr
139241016Sdfr    /* copy data to the user land */
139341016Sdfr    while ((sc->queue.count > 0) && (uio->uio_resid > 0)) {
139441016Sdfr        s = spltty();
139541016Sdfr	l = min(sc->queue.count, uio->uio_resid);
139641016Sdfr	if (l > sizeof(buf))
139741016Sdfr	    l = sizeof(buf);
139841016Sdfr	if (l > sizeof(sc->queue.buf) - sc->queue.head) {
139941016Sdfr	    bcopy(&sc->queue.buf[sc->queue.head], &buf[0],
140041016Sdfr		sizeof(sc->queue.buf) - sc->queue.head);
140141016Sdfr	    bcopy(&sc->queue.buf[0],
140241016Sdfr		&buf[sizeof(sc->queue.buf) - sc->queue.head],
140341016Sdfr		l - (sizeof(sc->queue.buf) - sc->queue.head));
140441016Sdfr	} else {
140541016Sdfr	    bcopy(&sc->queue.buf[sc->queue.head], &buf[0], l);
140641016Sdfr	}
140741016Sdfr	sc->queue.count -= l;
140841016Sdfr	sc->queue.head = (sc->queue.head + l) % sizeof(sc->queue.buf);
140941016Sdfr        splx(s);
141041016Sdfr        error = uiomove(buf, l, uio);
141141016Sdfr        if (error)
141241016Sdfr	    break;
141341016Sdfr    }
141441016Sdfr
141541016Sdfr    return error;
141641016Sdfr}
141741016Sdfr
141841016Sdfrstatic int
141941016Sdfrblock_mouse_data(struct psm_softc *sc, int *c)
142041016Sdfr{
142141016Sdfr    int s;
142241016Sdfr
142341016Sdfr    if (!kbdc_lock(sc->kbdc, TRUE))
142441016Sdfr	return EIO;
142541016Sdfr
142641016Sdfr    s = spltty();
142741016Sdfr    *c = get_controller_command_byte(sc->kbdc);
142841016Sdfr    if ((*c == -1)
142941016Sdfr	|| !set_controller_command_byte(sc->kbdc,
143041016Sdfr	    kbdc_get_device_mask(sc->kbdc),
143141016Sdfr            KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
143241016Sdfr                | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
143341016Sdfr        /* this is CONTROLLER ERROR */
143441016Sdfr	splx(s);
143541016Sdfr        kbdc_lock(sc->kbdc, FALSE);
143641016Sdfr	return EIO;
143741016Sdfr    }
143841016Sdfr
143941016Sdfr    /*
144041016Sdfr     * The device may be in the middle of status data transmission.
144141016Sdfr     * The transmission will be interrupted, thus, incomplete status
144241016Sdfr     * data must be discarded. Although the aux interrupt is disabled
144341016Sdfr     * at the keyboard controller level, at most one aux interrupt
144441016Sdfr     * may have already been pending and a data byte is in the
144541016Sdfr     * output buffer; throw it away. Note that the second argument
144641016Sdfr     * to `empty_aux_buffer()' is zero, so that the call will just
144741016Sdfr     * flush the internal queue.
144841016Sdfr     * `psmintr()' will be invoked after `splx()' if an interrupt is
144941016Sdfr     * pending; it will see no data and returns immediately.
145041016Sdfr     */
145141016Sdfr    empty_aux_buffer(sc->kbdc, 0);	/* flush the queue */
145241016Sdfr    read_aux_data_no_wait(sc->kbdc);	/* throw away data if any */
145341016Sdfr    sc->inputbytes = 0;
145441016Sdfr    splx(s);
145541016Sdfr
145641016Sdfr    return 0;
145741016Sdfr}
145841016Sdfr
145941016Sdfrstatic int
146041016Sdfrunblock_mouse_data(struct psm_softc *sc, int c)
146141016Sdfr{
146241016Sdfr    int error = 0;
146341016Sdfr
146441016Sdfr    /*
146541016Sdfr     * We may have seen a part of status data during `set_mouse_XXX()'.
146641016Sdfr     * they have been queued; flush it.
146741016Sdfr     */
146841016Sdfr    empty_aux_buffer(sc->kbdc, 0);
146941016Sdfr
147041016Sdfr    /* restore ports and interrupt */
147141016Sdfr    if (!set_controller_command_byte(sc->kbdc,
147241016Sdfr            kbdc_get_device_mask(sc->kbdc),
147341016Sdfr	    c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) {
147441016Sdfr        /* CONTROLLER ERROR; this is serious, we may have
147541016Sdfr         * been left with the inaccessible keyboard and
147641016Sdfr         * the disabled mouse interrupt.
147741016Sdfr         */
147841016Sdfr        error = EIO;
147941016Sdfr    }
148041016Sdfr
148141016Sdfr    kbdc_lock(sc->kbdc, FALSE);
148241016Sdfr    return error;
148341016Sdfr}
148441016Sdfr
148541016Sdfrstatic int
148641016Sdfrpsmioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
148741016Sdfr{
148841016Sdfr    struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev));
148941016Sdfr    mousemode_t mode;
149041016Sdfr    mousestatus_t status;
149141016Sdfr#if (defined(MOUSE_GETVARS))
149241016Sdfr    mousevar_t *var;
149341016Sdfr#endif
149441016Sdfr    mousedata_t *data;
149541016Sdfr    int stat[3];
149641016Sdfr    int command_byte;
149741016Sdfr    int error = 0;
149841016Sdfr    int s;
149941016Sdfr
150041016Sdfr    /* Perform IOCTL command */
150141016Sdfr    switch (cmd) {
150241016Sdfr
150341016Sdfr    case OLD_MOUSE_GETHWINFO:
150441016Sdfr	s = spltty();
150541016Sdfr        ((old_mousehw_t *)addr)->buttons = sc->hw.buttons;
150641016Sdfr        ((old_mousehw_t *)addr)->iftype = sc->hw.iftype;
150741016Sdfr        ((old_mousehw_t *)addr)->type = sc->hw.type;
150848778Syokota        ((old_mousehw_t *)addr)->hwid = sc->hw.hwid & 0x00ff;
150941016Sdfr	splx(s);
151041016Sdfr        break;
151141016Sdfr
151241016Sdfr    case MOUSE_GETHWINFO:
151341016Sdfr	s = spltty();
151441016Sdfr        *(mousehw_t *)addr = sc->hw;
151541016Sdfr	if (sc->mode.level == PSM_LEVEL_BASE)
151641016Sdfr	    ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC;
151741016Sdfr	splx(s);
151841016Sdfr        break;
151941016Sdfr
152041016Sdfr    case OLD_MOUSE_GETMODE:
152141016Sdfr	s = spltty();
152241016Sdfr	switch (sc->mode.level) {
152341016Sdfr	case PSM_LEVEL_BASE:
152441016Sdfr	    ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
152541016Sdfr	    break;
152641016Sdfr	case PSM_LEVEL_STANDARD:
152741016Sdfr	    ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE;
152841016Sdfr	    break;
152941016Sdfr	case PSM_LEVEL_NATIVE:
153041016Sdfr	    ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
153141016Sdfr	    break;
153241016Sdfr	}
153341016Sdfr        ((old_mousemode_t *)addr)->rate = sc->mode.rate;
153441016Sdfr        ((old_mousemode_t *)addr)->resolution = sc->mode.resolution;
153541016Sdfr        ((old_mousemode_t *)addr)->accelfactor = sc->mode.accelfactor;
153641016Sdfr	splx(s);
153741016Sdfr        break;
153841016Sdfr
153941016Sdfr    case MOUSE_GETMODE:
154041016Sdfr	s = spltty();
154141016Sdfr        *(mousemode_t *)addr = sc->mode;
154241016Sdfr        ((mousemode_t *)addr)->resolution =
154341016Sdfr	    MOUSE_RES_LOW - sc->mode.resolution;
154441016Sdfr	switch (sc->mode.level) {
154541016Sdfr	case PSM_LEVEL_BASE:
154641016Sdfr	    ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
154741016Sdfr	    ((mousemode_t *)addr)->packetsize = MOUSE_PS2_PACKETSIZE;
154841016Sdfr	    break;
154941016Sdfr	case PSM_LEVEL_STANDARD:
155041016Sdfr	    ((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE;
155141016Sdfr	    ((mousemode_t *)addr)->packetsize = MOUSE_SYS_PACKETSIZE;
155241016Sdfr	    ((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK;
155341016Sdfr	    ((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC;
155441016Sdfr	    break;
155541016Sdfr	case PSM_LEVEL_NATIVE:
155641016Sdfr	    /* FIXME: this isn't quite correct... XXX */
155741016Sdfr	    ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
155841016Sdfr	    break;
155941016Sdfr	}
156041016Sdfr	splx(s);
156141016Sdfr        break;
156241016Sdfr
156341016Sdfr    case OLD_MOUSE_SETMODE:
156441016Sdfr    case MOUSE_SETMODE:
156541016Sdfr	if (cmd == OLD_MOUSE_SETMODE) {
156641016Sdfr	    mode.rate = ((old_mousemode_t *)addr)->rate;
156741016Sdfr	    /*
156841016Sdfr	     * resolution  old I/F   new I/F
156941016Sdfr	     * default        0         0
157041016Sdfr	     * low            1        -2
157141016Sdfr	     * medium low     2        -3
157241016Sdfr	     * medium high    3        -4
157341016Sdfr	     * high           4        -5
157441016Sdfr	     */
157541016Sdfr	    if (((old_mousemode_t *)addr)->resolution > 0)
157641016Sdfr	        mode.resolution = -((old_mousemode_t *)addr)->resolution - 1;
157741016Sdfr	    mode.accelfactor = ((old_mousemode_t *)addr)->accelfactor;
157841016Sdfr	    mode.level = -1;
157941016Sdfr	} else {
158041016Sdfr	    mode = *(mousemode_t *)addr;
158141016Sdfr	}
158241016Sdfr
158341016Sdfr	/* adjust and validate parameters. */
158441016Sdfr	if (mode.rate > UCHAR_MAX)
158541016Sdfr	    return EINVAL;
158641016Sdfr        if (mode.rate == 0)
158741016Sdfr            mode.rate = sc->dflt_mode.rate;
158841016Sdfr	else if (mode.rate == -1)
158941016Sdfr	    /* don't change the current setting */
159041016Sdfr	    ;
159141016Sdfr	else if (mode.rate < 0)
159241016Sdfr	    return EINVAL;
159341016Sdfr	if (mode.resolution >= UCHAR_MAX)
159441016Sdfr	    return EINVAL;
159541016Sdfr	if (mode.resolution >= 200)
159641016Sdfr	    mode.resolution = MOUSE_RES_HIGH;
159741016Sdfr	else if (mode.resolution >= 100)
159841016Sdfr	    mode.resolution = MOUSE_RES_MEDIUMHIGH;
159941016Sdfr	else if (mode.resolution >= 50)
160041016Sdfr	    mode.resolution = MOUSE_RES_MEDIUMLOW;
160141016Sdfr	else if (mode.resolution > 0)
160241016Sdfr	    mode.resolution = MOUSE_RES_LOW;
160341016Sdfr        if (mode.resolution == MOUSE_RES_DEFAULT)
160441016Sdfr            mode.resolution = sc->dflt_mode.resolution;
160541016Sdfr        else if (mode.resolution == -1)
160641016Sdfr	    /* don't change the current setting */
160741016Sdfr	    ;
160841016Sdfr        else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */
160941016Sdfr            mode.resolution = MOUSE_RES_LOW - mode.resolution;
161041016Sdfr	if (mode.level == -1)
161141016Sdfr	    /* don't change the current setting */
161241016Sdfr	    mode.level = sc->mode.level;
161341016Sdfr	else if ((mode.level < PSM_LEVEL_MIN) || (mode.level > PSM_LEVEL_MAX))
161441016Sdfr	    return EINVAL;
161541016Sdfr        if (mode.accelfactor == -1)
161641016Sdfr	    /* don't change the current setting */
161741016Sdfr	    mode.accelfactor = sc->mode.accelfactor;
161841016Sdfr        else if (mode.accelfactor < 0)
161941016Sdfr	    return EINVAL;
162041016Sdfr
162141016Sdfr	/* don't allow anybody to poll the keyboard controller */
162241016Sdfr	error = block_mouse_data(sc, &command_byte);
162341016Sdfr	if (error)
162441016Sdfr            return error;
162541016Sdfr
162641016Sdfr        /* set mouse parameters */
162741016Sdfr	if (mode.rate > 0)
162841016Sdfr	    mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate);
162941016Sdfr	if (mode.resolution >= 0)
163041016Sdfr	    mode.resolution = set_mouse_resolution(sc->kbdc, mode.resolution);
163141016Sdfr	set_mouse_scaling(sc->kbdc, 1);
163241016Sdfr	get_mouse_status(sc->kbdc, stat, 0, 3);
163341016Sdfr
163441016Sdfr        s = spltty();
163541016Sdfr    	sc->mode.rate = mode.rate;
163641016Sdfr    	sc->mode.resolution = mode.resolution;
163741016Sdfr    	sc->mode.accelfactor = mode.accelfactor;
163841016Sdfr    	sc->mode.level = mode.level;
163941016Sdfr        splx(s);
164041016Sdfr
164141016Sdfr	unblock_mouse_data(sc, command_byte);
164241016Sdfr        break;
164341016Sdfr
164441016Sdfr    case MOUSE_GETLEVEL:
164541016Sdfr	*(int *)addr = sc->mode.level;
164641016Sdfr        break;
164741016Sdfr
164841016Sdfr    case MOUSE_SETLEVEL:
164941016Sdfr	if ((*(int *)addr < PSM_LEVEL_MIN) || (*(int *)addr > PSM_LEVEL_MAX))
165041016Sdfr	    return EINVAL;
165141016Sdfr	sc->mode.level = *(int *)addr;
165241016Sdfr        break;
165341016Sdfr
165441016Sdfr    case MOUSE_GETSTATUS:
165541016Sdfr        s = spltty();
165641016Sdfr	status = sc->status;
165741016Sdfr	sc->status.flags = 0;
165841016Sdfr	sc->status.obutton = sc->status.button;
165941016Sdfr	sc->status.button = 0;
166041016Sdfr	sc->status.dx = 0;
166141016Sdfr	sc->status.dy = 0;
166241016Sdfr	sc->status.dz = 0;
166341016Sdfr        splx(s);
166441016Sdfr        *(mousestatus_t *)addr = status;
166541016Sdfr        break;
166641016Sdfr
166741016Sdfr#if (defined(MOUSE_GETVARS))
166841016Sdfr    case MOUSE_GETVARS:
166941016Sdfr	var = (mousevar_t *)addr;
167041016Sdfr	bzero(var, sizeof(*var));
167141016Sdfr	s = spltty();
167241016Sdfr        var->var[0] = MOUSE_VARS_PS2_SIG;
167341016Sdfr        var->var[1] = sc->config;
167441016Sdfr        var->var[2] = sc->flags;
167541016Sdfr	splx(s);
167641016Sdfr        break;
167741016Sdfr
167841016Sdfr    case MOUSE_SETVARS:
167941016Sdfr	return ENODEV;
168041016Sdfr#endif /* MOUSE_GETVARS */
168141016Sdfr
168241016Sdfr    case MOUSE_READSTATE:
168341016Sdfr    case MOUSE_READDATA:
168441016Sdfr	data = (mousedata_t *)addr;
168541016Sdfr	if (data->len > sizeof(data->buf)/sizeof(data->buf[0]))
168641016Sdfr	    return EINVAL;
168741016Sdfr
168841016Sdfr	error = block_mouse_data(sc, &command_byte);
168941016Sdfr	if (error)
169041016Sdfr            return error;
169141016Sdfr        if ((data->len = get_mouse_status(sc->kbdc, data->buf,
169241016Sdfr		(cmd == MOUSE_READDATA) ? 1 : 0, data->len)) <= 0)
169341016Sdfr            error = EIO;
169441016Sdfr	unblock_mouse_data(sc, command_byte);
169541016Sdfr	break;
169641016Sdfr
169741016Sdfr#if (defined(MOUSE_SETRESOLUTION))
169841016Sdfr    case MOUSE_SETRESOLUTION:
169941016Sdfr	mode.resolution = *(int *)addr;
170041016Sdfr	if (mode.resolution >= UCHAR_MAX)
170141016Sdfr	    return EINVAL;
170241016Sdfr	else if (mode.resolution >= 200)
170341016Sdfr	    mode.resolution = MOUSE_RES_HIGH;
170441016Sdfr	else if (mode.resolution >= 100)
170541016Sdfr	    mode.resolution = MOUSE_RES_MEDIUMHIGH;
170641016Sdfr	else if (mode.resolution >= 50)
170741016Sdfr	    mode.resolution = MOUSE_RES_MEDIUMLOW;
170841016Sdfr	else if (mode.resolution > 0)
170941016Sdfr	    mode.resolution = MOUSE_RES_LOW;
171041016Sdfr        if (mode.resolution == MOUSE_RES_DEFAULT)
171141016Sdfr            mode.resolution = sc->dflt_mode.resolution;
171241016Sdfr        else if (mode.resolution == -1)
171341016Sdfr	    mode.resolution = sc->mode.resolution;
171441016Sdfr        else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */
171541016Sdfr            mode.resolution = MOUSE_RES_LOW - mode.resolution;
171641016Sdfr
171741016Sdfr	error = block_mouse_data(sc, &command_byte);
171841016Sdfr	if (error)
171941016Sdfr            return error;
172041016Sdfr        sc->mode.resolution = set_mouse_resolution(sc->kbdc, mode.resolution);
172141016Sdfr	if (sc->mode.resolution != mode.resolution)
172241016Sdfr	    error = EIO;
172341016Sdfr	unblock_mouse_data(sc, command_byte);
172441016Sdfr        break;
172541016Sdfr#endif /* MOUSE_SETRESOLUTION */
172641016Sdfr
172741016Sdfr#if (defined(MOUSE_SETRATE))
172841016Sdfr    case MOUSE_SETRATE:
172941016Sdfr	mode.rate = *(int *)addr;
173041016Sdfr	if (mode.rate > UCHAR_MAX)
173141016Sdfr	    return EINVAL;
173241016Sdfr        if (mode.rate == 0)
173341016Sdfr            mode.rate = sc->dflt_mode.rate;
173441016Sdfr	else if (mode.rate < 0)
173541016Sdfr	    mode.rate = sc->mode.rate;
173641016Sdfr
173741016Sdfr	error = block_mouse_data(sc, &command_byte);
173841016Sdfr	if (error)
173941016Sdfr            return error;
174041016Sdfr        sc->mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate);
174141016Sdfr	if (sc->mode.rate != mode.rate)
174241016Sdfr	    error = EIO;
174341016Sdfr	unblock_mouse_data(sc, command_byte);
174441016Sdfr        break;
174541016Sdfr#endif /* MOUSE_SETRATE */
174641016Sdfr
174741016Sdfr#if (defined(MOUSE_SETSCALING))
174841016Sdfr    case MOUSE_SETSCALING:
174941016Sdfr	if ((*(int *)addr <= 0) || (*(int *)addr > 2))
175041016Sdfr	    return EINVAL;
175141016Sdfr
175241016Sdfr	error = block_mouse_data(sc, &command_byte);
175341016Sdfr	if (error)
175441016Sdfr            return error;
175541016Sdfr        if (!set_mouse_scaling(sc->kbdc, *(int *)addr))
175641016Sdfr	    error = EIO;
175741016Sdfr	unblock_mouse_data(sc, command_byte);
175841016Sdfr        break;
175941016Sdfr#endif /* MOUSE_SETSCALING */
176041016Sdfr
176141016Sdfr#if (defined(MOUSE_GETHWID))
176241016Sdfr    case MOUSE_GETHWID:
176341016Sdfr	error = block_mouse_data(sc, &command_byte);
176441016Sdfr	if (error)
176541016Sdfr            return error;
176648778Syokota        sc->hw.hwid &= ~0x00ff;
176748778Syokota        sc->hw.hwid |= get_aux_id(sc->kbdc);
176848778Syokota	*(int *)addr = sc->hw.hwid & 0x00ff;
176941016Sdfr	unblock_mouse_data(sc, command_byte);
177041016Sdfr        break;
177141016Sdfr#endif /* MOUSE_GETHWID */
177241016Sdfr
177341016Sdfr    default:
177441016Sdfr	return ENOTTY;
177541016Sdfr    }
177641016Sdfr
177741016Sdfr    return error;
177841016Sdfr}
177941016Sdfr
178041016Sdfrstatic void
178141016Sdfrpsmintr(void *arg)
178241016Sdfr{
178341016Sdfr    /*
178441016Sdfr     * the table to turn PS/2 mouse button bits (MOUSE_PS2_BUTTON?DOWN)
178541016Sdfr     * into `mousestatus' button bits (MOUSE_BUTTON?DOWN).
178641016Sdfr     */
178741016Sdfr    static int butmap[8] = {
178841016Sdfr        0,
178941016Sdfr	MOUSE_BUTTON1DOWN,
179041016Sdfr	MOUSE_BUTTON3DOWN,
179141016Sdfr	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
179241016Sdfr	MOUSE_BUTTON2DOWN,
179341016Sdfr	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
179441016Sdfr	MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
179541016Sdfr        MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
179641016Sdfr    };
179741016Sdfr    register struct psm_softc *sc = arg;
179841016Sdfr    mousestatus_t ms;
179941016Sdfr    int x, y, z;
180041016Sdfr    int c;
180141016Sdfr    int l;
180241016Sdfr
180341016Sdfr    /* read until there is nothing to read */
180441016Sdfr    while((c = read_aux_data_no_wait(sc->kbdc)) != -1) {
180541016Sdfr
180641016Sdfr        /* discard the byte if the device is not open */
180741016Sdfr        if ((sc->state & PSM_OPEN) == 0)
180841016Sdfr            continue;
180941016Sdfr
181041016Sdfr        /*
181141016Sdfr	 * Check sync bits. We check for overflow bits and the bit 3
181241016Sdfr	 * for most mice. True, the code doesn't work if overflow
181341016Sdfr	 * condition occurs. But we expect it rarely happens...
181441016Sdfr	 */
181541016Sdfr	if ((sc->inputbytes == 0)
181641016Sdfr		&& ((c & sc->mode.syncmask[0]) != sc->mode.syncmask[1])) {
181741016Sdfr            log(LOG_DEBUG, "psmintr: out of sync (%04x != %04x).\n",
181841016Sdfr		c & sc->mode.syncmask[0], sc->mode.syncmask[1]);
181941016Sdfr            continue;
182041016Sdfr	}
182141016Sdfr
182241016Sdfr        sc->ipacket[sc->inputbytes++] = c;
182341016Sdfr        if (sc->inputbytes < sc->mode.packetsize)
182441016Sdfr	    continue;
182541016Sdfr
182641016Sdfr#if 0
182741016Sdfr        log(LOG_DEBUG, "psmintr: %02x %02x %02x %02x %02x %02x\n",
182841016Sdfr	    sc->ipacket[0], sc->ipacket[1], sc->ipacket[2],
182941016Sdfr	    sc->ipacket[3], sc->ipacket[4], sc->ipacket[5]);
183041016Sdfr#endif
183141016Sdfr
183241016Sdfr	c = sc->ipacket[0];
183341016Sdfr
183441016Sdfr        /*
183541016Sdfr	 * A kludge for Kensington device!
183641016Sdfr	 * The MSB of the horizontal count appears to be stored in
183741016Sdfr	 * a strange place. This kludge doesn't affect other mice
183841016Sdfr	 * because the bit is the overflow bit which is, in most cases,
183941016Sdfr	 * expected to be zero when we reach here. XXX
184041016Sdfr	 */
184141016Sdfr        sc->ipacket[1] |= (c & MOUSE_PS2_XOVERFLOW) ? 0x80 : 0;
184241016Sdfr
184341016Sdfr        /* ignore the overflow bits... */
184441016Sdfr        x = (c & MOUSE_PS2_XNEG) ?  sc->ipacket[1] - 256 : sc->ipacket[1];
184541016Sdfr        y = (c & MOUSE_PS2_YNEG) ?  sc->ipacket[2] - 256 : sc->ipacket[2];
184641016Sdfr	z = 0;
184741016Sdfr        ms.obutton = sc->button;		  /* previous button state */
184841016Sdfr        ms.button = butmap[c & MOUSE_PS2_BUTTONS];
184945789Speter	/* `tapping' action */
185045789Speter	if (sc->config & PSM_CONFIG_FORCETAP)
185145789Speter	    ms.button |= ((c & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN;
185241016Sdfr
185341016Sdfr	switch (sc->hw.model) {
185441016Sdfr
185541016Sdfr	case MOUSE_MODEL_INTELLI:
185641016Sdfr	case MOUSE_MODEL_NET:
185741016Sdfr	    /* wheel data is in the fourth byte */
185841016Sdfr	    z = (char)sc->ipacket[3];
185941016Sdfr	    break;
186041016Sdfr
186141016Sdfr	case MOUSE_MODEL_MOUSEMANPLUS:
186248778Syokota	    /*
186348778Syokota	     * PS2++ protocl packet
186448778Syokota	     *
186548778Syokota	     *          b7 b6 b5 b4 b3 b2 b1 b0
186648778Syokota	     * byte 1:  *  1  p3 p2 1  *  *  *
186748778Syokota	     * byte 2:  c1 c2 p1 p0 d1 d0 1  0
186848778Syokota	     *
186948778Syokota	     * p3-p0: packet type
187048778Syokota	     * c1, c2: c1 & c2 == 1, if p2 == 0
187148778Syokota	     *         c1 & c2 == 0, if p2 == 1
187248778Syokota	     *
187348778Syokota	     * packet type: 0 (device type)
187448778Syokota	     * See comments in enable_mmanplus() below.
187548778Syokota	     *
187648778Syokota	     * packet type: 1 (wheel data)
187748778Syokota	     *
187848778Syokota	     *          b7 b6 b5 b4 b3 b2 b1 b0
187948778Syokota	     * byte 3:  h  *  B5 B4 s  d2 d1 d0
188048778Syokota	     *
188148778Syokota	     * h: 1, if horizontal roller data
188248778Syokota	     *    0, if vertical roller data
188348778Syokota	     * B4, B5: button 4 and 5
188448778Syokota	     * s: sign bit
188548778Syokota	     * d2-d0: roller data
188648778Syokota	     *
188748778Syokota	     * packet type: 2 (reserved)
188848778Syokota	     */
188948778Syokota	    if (((c & MOUSE_PS2PLUS_SYNCMASK) == MOUSE_PS2PLUS_SYNC)
189048778Syokota		    && (abs(x) > 191)
189148778Syokota		    && MOUSE_PS2PLUS_CHECKBITS(sc->ipacket)) {
189241016Sdfr		/* the extended data packet encodes button and wheel events */
189348778Syokota		switch (MOUSE_PS2PLUS_PACKET_TYPE(sc->ipacket)) {
189448778Syokota		case 1:
189548778Syokota		    /* wheel data packet */
189648778Syokota		    x = y = 0;
189748778Syokota		    if (sc->ipacket[2] & 0x80) {
189848778Syokota			/* horizontal roller count - ignore it XXX*/
189948778Syokota		    } else {
190048778Syokota			/* vertical roller count */
190148778Syokota			z = (sc->ipacket[2] & MOUSE_PS2PLUS_ZNEG)
190248778Syokota			    ? (sc->ipacket[2] & 0x0f) - 16
190348778Syokota			    : (sc->ipacket[2] & 0x0f);
190448778Syokota		    }
190548778Syokota		    ms.button |= (sc->ipacket[2] & MOUSE_PS2PLUS_BUTTON4DOWN)
190648778Syokota			? MOUSE_BUTTON4DOWN : 0;
190748778Syokota		    ms.button |= (sc->ipacket[2] & MOUSE_PS2PLUS_BUTTON5DOWN)
190848778Syokota			? MOUSE_BUTTON5DOWN : 0;
190948778Syokota		    break;
191048778Syokota		case 2:
191148778Syokota		    /* this packet type is reserved, and currently ignored */
191248778Syokota		    /* FALL THROUGH */
191348778Syokota		case 0:
191448778Syokota		    /* device type packet - shouldn't happen */
191548778Syokota		    /* FALL THROUGH */
191648778Syokota		default:
191748778Syokota		    x = y = 0;
191848778Syokota		    ms.button = ms.obutton;
191948778Syokota            	    log(LOG_DEBUG, "psmintr: unknown PS2++ packet type %d: "
192048778Syokota				   "0x%02x 0x%02x 0x%02x\n",
192148778Syokota			MOUSE_PS2PLUS_PACKET_TYPE(sc->ipacket),
192248778Syokota			sc->ipacket[0], sc->ipacket[1], sc->ipacket[2]);
192348778Syokota		    break;
192448778Syokota		}
192541016Sdfr	    } else {
192641016Sdfr		/* preserve button states */
192741016Sdfr		ms.button |= ms.obutton & MOUSE_EXTBUTTONS;
192841016Sdfr	    }
192941016Sdfr	    break;
193041016Sdfr
193141016Sdfr	case MOUSE_MODEL_GLIDEPOINT:
193241016Sdfr	    /* `tapping' action */
193341016Sdfr	    ms.button |= ((c & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN;
193441016Sdfr	    break;
193541016Sdfr
193641016Sdfr	case MOUSE_MODEL_NETSCROLL:
193741016Sdfr	    /* three addtional bytes encode button and wheel events */
193841016Sdfr	    ms.button |= (sc->ipacket[3] & MOUSE_PS2_BUTTON3DOWN)
193941016Sdfr		? MOUSE_BUTTON4DOWN : 0;
194041016Sdfr	    z = (sc->ipacket[3] & MOUSE_PS2_XNEG)
194141016Sdfr		? sc->ipacket[4] - 256 : sc->ipacket[4];
194241016Sdfr	    break;
194341016Sdfr
194441016Sdfr	case MOUSE_MODEL_THINK:
194541016Sdfr	    /* the fourth button state in the first byte */
194641016Sdfr	    ms.button |= (c & MOUSE_PS2_TAP) ? MOUSE_BUTTON4DOWN : 0;
194741016Sdfr	    break;
194841016Sdfr
194941016Sdfr	case MOUSE_MODEL_GENERIC:
195041016Sdfr	default:
195141016Sdfr	    break;
195241016Sdfr	}
195341016Sdfr
195441016Sdfr        /* scale values */
195541016Sdfr        if (sc->mode.accelfactor >= 1) {
195641016Sdfr            if (x != 0) {
195741016Sdfr                x = x * x / sc->mode.accelfactor;
195841016Sdfr                if (x == 0)
195941016Sdfr                    x = 1;
196041016Sdfr                if (c & MOUSE_PS2_XNEG)
196141016Sdfr                    x = -x;
196241016Sdfr            }
196341016Sdfr            if (y != 0) {
196441016Sdfr                y = y * y / sc->mode.accelfactor;
196541016Sdfr                if (y == 0)
196641016Sdfr                    y = 1;
196741016Sdfr                if (c & MOUSE_PS2_YNEG)
196841016Sdfr                    y = -y;
196941016Sdfr            }
197041016Sdfr        }
197141016Sdfr
197241016Sdfr        ms.dx = x;
197341016Sdfr        ms.dy = y;
197441016Sdfr        ms.dz = z;
197541016Sdfr        ms.flags = ((x || y || z) ? MOUSE_POSCHANGED : 0)
197641016Sdfr	    | (ms.obutton ^ ms.button);
197741016Sdfr
197841016Sdfr	if (sc->mode.level < PSM_LEVEL_NATIVE)
197941016Sdfr	    sc->inputbytes = tame_mouse(sc, &ms, sc->ipacket);
198041016Sdfr
198141016Sdfr        sc->status.flags |= ms.flags;
198241016Sdfr        sc->status.dx += ms.dx;
198341016Sdfr        sc->status.dy += ms.dy;
198441016Sdfr        sc->status.dz += ms.dz;
198541016Sdfr        sc->status.button = ms.button;
198641016Sdfr        sc->button = ms.button;
198741016Sdfr
198841016Sdfr        /* queue data */
198941016Sdfr        if (sc->queue.count + sc->inputbytes < sizeof(sc->queue.buf)) {
199041016Sdfr	    l = min(sc->inputbytes, sizeof(sc->queue.buf) - sc->queue.tail);
199141016Sdfr	    bcopy(&sc->ipacket[0], &sc->queue.buf[sc->queue.tail], l);
199241016Sdfr	    if (sc->inputbytes > l)
199341016Sdfr	        bcopy(&sc->ipacket[l], &sc->queue.buf[0], sc->inputbytes - l);
199441016Sdfr            sc->queue.tail =
199541016Sdfr		(sc->queue.tail + sc->inputbytes) % sizeof(sc->queue.buf);
199641016Sdfr            sc->queue.count += sc->inputbytes;
199741016Sdfr	}
199841016Sdfr        sc->inputbytes = 0;
199941016Sdfr
200041016Sdfr        if (sc->state & PSM_ASLP) {
200141016Sdfr            sc->state &= ~PSM_ASLP;
200241016Sdfr            wakeup((caddr_t) sc);
200341016Sdfr    	}
200441016Sdfr        selwakeup(&sc->rsel);
200541016Sdfr    }
200641016Sdfr}
200741016Sdfr
200841016Sdfrstatic int
200941016Sdfrpsmpoll(dev_t dev, int events, struct proc *p)
201041016Sdfr{
201141016Sdfr    struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev));
201241016Sdfr    int s;
201341016Sdfr    int revents = 0;
201441016Sdfr
201541016Sdfr    /* Return true if a mouse event available */
201641016Sdfr    s = spltty();
201745789Speter    if (events & (POLLIN | POLLRDNORM)) {
201841016Sdfr	if (sc->queue.count > 0)
201941016Sdfr	    revents |= events & (POLLIN | POLLRDNORM);
202041016Sdfr	else
202141016Sdfr	    selrecord(p, &sc->rsel);
202245789Speter    }
202341016Sdfr    splx(s);
202441016Sdfr
202541016Sdfr    return (revents);
202641016Sdfr}
202741016Sdfr
202841016Sdfr/* vendor/model specific routines */
202941016Sdfr
203041016Sdfrstatic int mouse_id_proc1(KBDC kbdc, int res, int scale, int *status)
203141016Sdfr{
203241016Sdfr    if (set_mouse_resolution(kbdc, res) != res)
203341016Sdfr        return FALSE;
203441016Sdfr    if (set_mouse_scaling(kbdc, scale)
203541016Sdfr	&& set_mouse_scaling(kbdc, scale)
203641016Sdfr	&& set_mouse_scaling(kbdc, scale)
203741016Sdfr	&& (get_mouse_status(kbdc, status, 0, 3) >= 3))
203841016Sdfr	return TRUE;
203941016Sdfr    return FALSE;
204041016Sdfr}
204141016Sdfr
204241016Sdfr#if notyet
204341016Sdfr/* Logitech MouseMan Cordless II */
204441016Sdfrstatic int
204541016Sdfrenable_lcordless(struct psm_softc *sc)
204641016Sdfr{
204741016Sdfr    int status[3];
204841016Sdfr    int ch;
204941016Sdfr
205041016Sdfr    if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 2, status))
205141016Sdfr        return FALSE;
205241016Sdfr    if (status[1] == PSMD_RES_HIGH)
205341016Sdfr	return FALSE;
205441016Sdfr    ch = (status[0] & 0x07) - 1;	/* channel # */
205541016Sdfr    if ((ch <= 0) || (ch > 4))
205641016Sdfr	return FALSE;
205741016Sdfr    /*
205841016Sdfr     * status[1]: always one?
205941016Sdfr     * status[2]: battery status? (0-100)
206041016Sdfr     */
206141016Sdfr    return TRUE;
206241016Sdfr}
206341016Sdfr#endif /* notyet */
206441016Sdfr
206541016Sdfr/* Genius NetScroll Mouse */
206641016Sdfrstatic int
206741016Sdfrenable_groller(struct psm_softc *sc)
206841016Sdfr{
206941016Sdfr    int status[3];
207041016Sdfr
207141016Sdfr    /*
207241016Sdfr     * The special sequence to enable the fourth button and the
207341016Sdfr     * roller. Immediately after this sequence check status bytes.
207441016Sdfr     * if the mouse is NetScroll, the second and the third bytes are
207541016Sdfr     * '3' and 'D'.
207641016Sdfr     */
207741016Sdfr
207841016Sdfr    /*
207941016Sdfr     * If the mouse is an ordinary PS/2 mouse, the status bytes should
208041016Sdfr     * look like the following.
208141016Sdfr     *
208241016Sdfr     * byte 1 bit 7 always 0
208341016Sdfr     *        bit 6 stream mode (0)
208441016Sdfr     *        bit 5 disabled (0)
208541016Sdfr     *        bit 4 1:1 scaling (0)
208641016Sdfr     *        bit 3 always 0
208741016Sdfr     *        bit 0-2 button status
208841016Sdfr     * byte 2 resolution (PSMD_RES_HIGH)
208941016Sdfr     * byte 3 report rate (?)
209041016Sdfr     */
209141016Sdfr
209241016Sdfr    if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status))
209341016Sdfr        return FALSE;
209441016Sdfr    if ((status[1] != '3') || (status[2] != 'D'))
209541016Sdfr        return FALSE;
209641016Sdfr    /* FIXME!! */
209741016Sdfr    sc->hw.buttons = get_mouse_buttons(sc->kbdc);
209841016Sdfr    sc->hw.buttons = 4;
209941016Sdfr    return TRUE;
210041016Sdfr}
210141016Sdfr
210241016Sdfr/* Genius NetMouse/NetMouse Pro */
210341016Sdfrstatic int
210441016Sdfrenable_gmouse(struct psm_softc *sc)
210541016Sdfr{
210641016Sdfr    int status[3];
210741016Sdfr
210841016Sdfr    /*
210941016Sdfr     * The special sequence to enable the middle, "rubber" button.
211041016Sdfr     * Immediately after this sequence check status bytes.
211141016Sdfr     * if the mouse is NetMouse, NetMouse Pro, or ASCII MIE Mouse,
211241016Sdfr     * the second and the third bytes are '3' and 'U'.
211341016Sdfr     * NOTE: NetMouse reports that it has three buttons although it has
211441016Sdfr     * two buttons and a rubber button. NetMouse Pro and MIE Mouse
211541016Sdfr     * say they have three buttons too and they do have a button on the
211641016Sdfr     * side...
211741016Sdfr     */
211841016Sdfr    if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status))
211941016Sdfr        return FALSE;
212041016Sdfr    if ((status[1] != '3') || (status[2] != 'U'))
212141016Sdfr        return FALSE;
212241016Sdfr    return TRUE;
212341016Sdfr}
212441016Sdfr
212541016Sdfr/* ALPS GlidePoint */
212641016Sdfrstatic int
212741016Sdfrenable_aglide(struct psm_softc *sc)
212841016Sdfr{
212941016Sdfr    int status[3];
213041016Sdfr
213141016Sdfr    /*
213241016Sdfr     * The special sequence to obtain ALPS GlidePoint specific
213341016Sdfr     * information. Immediately after this sequence, status bytes will
213441016Sdfr     * contain something interesting.
213541016Sdfr     * NOTE: ALPS produces several models of GlidePoint. Some of those
213641016Sdfr     * do not respond to this sequence, thus, cannot be detected this way.
213741016Sdfr     */
213841016Sdfr    if (!mouse_id_proc1(sc->kbdc, PSMD_RES_LOW, 2, status))
213941016Sdfr        return FALSE;
214041016Sdfr    if ((status[0] & 0x10) || (status[1] == PSMD_RES_LOW))
214141016Sdfr        return FALSE;
214241016Sdfr    return TRUE;
214341016Sdfr}
214441016Sdfr
214541016Sdfr/* Kensington ThinkingMouse/Trackball */
214641016Sdfrstatic int
214741016Sdfrenable_kmouse(struct psm_softc *sc)
214841016Sdfr{
214941016Sdfr    static unsigned char rate[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 };
215041016Sdfr    KBDC kbdc = sc->kbdc;
215141016Sdfr    int status[3];
215241016Sdfr    int id1;
215341016Sdfr    int id2;
215441016Sdfr    int i;
215541016Sdfr
215641016Sdfr    id1 = get_aux_id(kbdc);
215741016Sdfr    if (set_mouse_sampling_rate(kbdc, 10) != 10)
215841016Sdfr	return FALSE;
215941016Sdfr    /*
216041016Sdfr     * The device is now in the native mode? It returns a different
216141016Sdfr     * ID value...
216241016Sdfr     */
216341016Sdfr    id2 = get_aux_id(kbdc);
216441016Sdfr    if ((id1 == id2) || (id2 != 2))
216541016Sdfr	return FALSE;
216641016Sdfr
216741016Sdfr    if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW)
216841016Sdfr        return FALSE;
216941016Sdfr#if PSM_DEBUG >= 2
217041016Sdfr    /* at this point, resolution is LOW, sampling rate is 10/sec */
217141016Sdfr    if (get_mouse_status(kbdc, status, 0, 3) < 3)
217241016Sdfr        return FALSE;
217341016Sdfr#endif
217441016Sdfr
217541016Sdfr    /*
217641016Sdfr     * The special sequence to enable the third and fourth buttons.
217741016Sdfr     * Otherwise they behave like the first and second buttons.
217841016Sdfr     */
217941016Sdfr    for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) {
218041016Sdfr        if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i])
218141016Sdfr	    return FALSE;
218241016Sdfr    }
218341016Sdfr
218441016Sdfr    /*
218541016Sdfr     * At this point, the device is using default resolution and
218641016Sdfr     * sampling rate for the native mode.
218741016Sdfr     */
218841016Sdfr    if (get_mouse_status(kbdc, status, 0, 3) < 3)
218941016Sdfr        return FALSE;
219041016Sdfr    if ((status[1] == PSMD_RES_LOW) || (status[2] == rate[i - 1]))
219141016Sdfr        return FALSE;
219241016Sdfr
219341016Sdfr    /* the device appears be enabled by this sequence, diable it for now */
219441016Sdfr    disable_aux_dev(kbdc);
219541016Sdfr    empty_aux_buffer(kbdc, 5);
219641016Sdfr
219741016Sdfr    return TRUE;
219841016Sdfr}
219941016Sdfr
220041016Sdfr/* Logitech MouseMan+/FirstMouse+ */
220141016Sdfrstatic int
220241016Sdfrenable_mmanplus(struct psm_softc *sc)
220341016Sdfr{
220441016Sdfr    static char res[] = {
220541016Sdfr	-1, PSMD_RES_LOW, PSMD_RES_HIGH, PSMD_RES_MEDIUM_HIGH,
220641016Sdfr	PSMD_RES_MEDIUM_LOW, -1, PSMD_RES_HIGH, PSMD_RES_MEDIUM_LOW,
220741016Sdfr	PSMD_RES_MEDIUM_HIGH, PSMD_RES_HIGH,
220841016Sdfr    };
220941016Sdfr    KBDC kbdc = sc->kbdc;
221041016Sdfr    int data[3];
221141016Sdfr    int i;
221241016Sdfr
221341016Sdfr    /* the special sequence to enable the fourth button and the roller. */
221441016Sdfr    for (i = 0; i < sizeof(res)/sizeof(res[0]); ++i) {
221541016Sdfr	if (res[i] < 0) {
221641016Sdfr	    if (!set_mouse_scaling(kbdc, 1))
221741016Sdfr		return FALSE;
221841016Sdfr	} else {
221941016Sdfr	    if (set_mouse_resolution(kbdc, res[i]) != res[i])
222041016Sdfr		return FALSE;
222141016Sdfr	}
222241016Sdfr    }
222341016Sdfr
222441016Sdfr    if (get_mouse_status(kbdc, data, 1, 3) < 3)
222541016Sdfr        return FALSE;
222641016Sdfr
222748778Syokota    /*
222848778Syokota     * PS2++ protocl, packet type 0
222941016Sdfr     *
223048778Syokota     *          b7 b6 b5 b4 b3 b2 b1 b0
223148778Syokota     * byte 1:  *  1  p3 p2 1  *  *  *
223248778Syokota     * byte 2:  1  1  p1 p0 m1 m0 1  0
223348778Syokota     * byte 3:  m7 m6 m5 m4 m3 m2 m1 m0
223448778Syokota     *
223548778Syokota     * p3-p0: packet type: 0
223648778Syokota     * m7-m0: model ID: MouseMan+:0x50, FirstMouse+:0x51,...
223741016Sdfr     */
223848778Syokota    /* check constant bits */
223948778Syokota    if ((data[0] & MOUSE_PS2PLUS_SYNCMASK) != MOUSE_PS2PLUS_SYNC)
224041016Sdfr        return FALSE;
224148778Syokota    if ((data[1] & 0xc3) != 0xc2)
224248778Syokota        return FALSE;
224348778Syokota    /* check d3-d0 in byte 2 */
224448778Syokota    if (!MOUSE_PS2PLUS_CHECKBITS(data))
224548778Syokota        return FALSE;
224648778Syokota    /* check p3-p0 */
224748778Syokota    if (MOUSE_PS2PLUS_PACKET_TYPE(data) != 0)
224848778Syokota        return FALSE;
224941016Sdfr
225048778Syokota    sc->hw.hwid &= 0x00ff;
225148778Syokota    sc->hw.hwid |= data[2] << 8;	/* save model ID */
225248778Syokota
225341016Sdfr    /*
225441016Sdfr     * MouseMan+ (or FirstMouse+) is now in its native mode, in which
225541016Sdfr     * the wheel and the fourth button events are encoded in the
225641016Sdfr     * special data packet. The mouse may be put in the IntelliMouse mode
225741016Sdfr     * if it is initialized by the IntelliMouse's method.
225841016Sdfr     */
225941016Sdfr    return TRUE;
226041016Sdfr}
226141016Sdfr
226241016Sdfr/* MS IntelliMouse */
226341016Sdfrstatic int
226441016Sdfrenable_msintelli(struct psm_softc *sc)
226541016Sdfr{
226641016Sdfr    /*
226741016Sdfr     * Logitech MouseMan+ and FirstMouse+ will also respond to this
226841016Sdfr     * probe routine and act like IntelliMouse.
226941016Sdfr     */
227041016Sdfr
227141016Sdfr    static unsigned char rate[] = { 200, 100, 80, };
227241016Sdfr    KBDC kbdc = sc->kbdc;
227341016Sdfr    int id;
227441016Sdfr    int i;
227541016Sdfr
227641016Sdfr    /* the special sequence to enable the third button and the roller. */
227741016Sdfr    for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) {
227841016Sdfr        if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i])
227941016Sdfr	    return FALSE;
228041016Sdfr    }
228141016Sdfr    /* the device will give the genuine ID only after the above sequence */
228241016Sdfr    id = get_aux_id(kbdc);
228341016Sdfr    if (id != PSM_INTELLI_ID)
228441016Sdfr	return FALSE;
228541016Sdfr
228641016Sdfr    sc->hw.hwid = id;
228741016Sdfr    sc->hw.buttons = 3;
228841016Sdfr
228941016Sdfr    return TRUE;
229041016Sdfr}
229141016Sdfr
229241016Sdfr#ifdef PSM_HOOKAPM
229341016Sdfrstatic int
229441016Sdfrpsmresume(void *dummy)
229541016Sdfr{
229646763Syokota    struct psm_softc *sc = PSM_SOFTC((int)dummy);
229741016Sdfr    int unit = (int)dummy;
229841016Sdfr    int err = 0;
229941016Sdfr    int s;
230041016Sdfr    int c;
230141016Sdfr
230241016Sdfr    if (verbose >= 2)
230341016Sdfr        log(LOG_NOTICE, "psm%d: APM resume hook called.\n", unit);
230441016Sdfr
230541016Sdfr    /* don't let anybody mess with the aux device */
230641016Sdfr    if (!kbdc_lock(sc->kbdc, TRUE))
230741016Sdfr	return (EIO);
230841016Sdfr    s = spltty();
230941016Sdfr
231041016Sdfr    /* save the current controller command byte */
231141016Sdfr    empty_both_buffers(sc->kbdc, 10);
231241016Sdfr    c = get_controller_command_byte(sc->kbdc);
231341016Sdfr    if (verbose >= 2)
231441016Sdfr        log(LOG_DEBUG, "psm%d: current command byte: %04x (psmresume).\n",
231541016Sdfr	    unit, c);
231641016Sdfr
231741016Sdfr    /* enable the aux port but disable the aux interrupt and the keyboard */
231841016Sdfr    if ((c == -1) || !set_controller_command_byte(sc->kbdc,
231941016Sdfr	    kbdc_get_device_mask(sc->kbdc),
232041016Sdfr  	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
232141016Sdfr	        | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
232241016Sdfr        /* CONTROLLER ERROR */
232341016Sdfr	splx(s);
232441016Sdfr        kbdc_lock(sc->kbdc, FALSE);
232541016Sdfr	log(LOG_ERR, "psm%d: unable to set the command byte (psmresume).\n",
232641016Sdfr	    unit);
232741016Sdfr	return (EIO);
232841016Sdfr    }
232941016Sdfr
233041016Sdfr    /* flush any data */
233141016Sdfr    if (sc->state & PSM_VALID) {
233241016Sdfr	disable_aux_dev(sc->kbdc);	/* this may fail; but never mind... */
233341016Sdfr	empty_aux_buffer(sc->kbdc, 10);
233441016Sdfr    }
233541016Sdfr    sc->inputbytes = 0;
233641016Sdfr
233741016Sdfr#ifdef PSM_RESETAFTERSUSPEND
233841016Sdfr    /* try to detect the aux device; are you still there? */
233941016Sdfr    if (reinitialize(unit, &sc->mode)) {
234041016Sdfr	/* yes */
234141016Sdfr	sc->state |= PSM_VALID;
234241016Sdfr    } else {
234341016Sdfr	/* the device has gone! */
234441016Sdfr        restore_controller(sc->kbdc, c);
234541016Sdfr	sc->state &= ~PSM_VALID;
234641016Sdfr	log(LOG_ERR, "psm%d: the aux device has gone! (psmresume).\n",
234741016Sdfr	    unit);
234841016Sdfr	err = ENXIO;
234941016Sdfr    }
235041016Sdfr#endif /* PSM_RESETAFTERSUSPEND */
235141016Sdfr    splx(s);
235241016Sdfr
235341016Sdfr    /* restore the driver state */
235441016Sdfr    if ((sc->state & PSM_OPEN) && (err == 0)) {
235541016Sdfr        /* enable the aux device and the port again */
235641016Sdfr	err = doopen(unit, c);
235741016Sdfr	if (err != 0)
235841016Sdfr	    log(LOG_ERR, "psm%d: failed to enable the device (psmresume).\n",
235941016Sdfr		unit);
236041016Sdfr    } else {
236141016Sdfr        /* restore the keyboard port and disable the aux port */
236241016Sdfr        if (!set_controller_command_byte(sc->kbdc,
236341016Sdfr                kbdc_get_device_mask(sc->kbdc),
236441016Sdfr                (c & KBD_KBD_CONTROL_BITS)
236541016Sdfr                    | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
236641016Sdfr            /* CONTROLLER ERROR */
236741016Sdfr            log(LOG_ERR, "psm%d: failed to disable the aux port (psmresume).\n",
236841016Sdfr                unit);
236941016Sdfr            err = EIO;
237041016Sdfr	}
237141016Sdfr    }
237241016Sdfr
237341016Sdfr    /* done */
237441016Sdfr    kbdc_lock(sc->kbdc, FALSE);
237541016Sdfr    if ((sc->state & PSM_ASLP) && !(sc->state & PSM_VALID)) {
237641016Sdfr	/*
237741016Sdfr	 * Release the blocked process; it must be notified that the device
237841016Sdfr	 * cannot be accessed anymore.
237941016Sdfr	 */
238041016Sdfr        sc->state &= ~PSM_ASLP;
238141016Sdfr        wakeup((caddr_t)sc);
238241016Sdfr    }
238341016Sdfr
238441016Sdfr    if (verbose >= 2)
238541016Sdfr        log(LOG_DEBUG, "psm%d: APM resume hook exiting.\n", unit);
238641016Sdfr
238741016Sdfr    return (err);
238841016Sdfr}
238941016Sdfr#endif /* PSM_HOOKAPM */
239041016Sdfr
239148557SphkDEV_DRIVER_MODULE(psm, atkbdc, psm_driver, psm_devclass, psm_cdevsw, 0, 0);
239241016Sdfr
239341016Sdfr#endif /* NPSM > 0 */
2394