psm.c revision 47625
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 *
2347625Sphk * $Id: psm.c,v 1.9 1999/05/09 13:00:43 phk 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
13841016Sdfr/* some macros */
13941016Sdfr#define PSM_UNIT(dev)		(minor(dev) >> 1)
14041016Sdfr#define PSM_NBLOCKIO(dev)	(minor(dev) & 1)
14141016Sdfr#define PSM_MKMINOR(unit,block)	(((unit) << 1) | ((block) ? 0:1))
14241016Sdfr
14341016Sdfr#ifndef max
14441016Sdfr#define max(x,y)		((x) > (y) ? (x) : (y))
14541016Sdfr#endif
14641016Sdfr#ifndef min
14741016Sdfr#define min(x,y)		((x) < (y) ? (x) : (y))
14841016Sdfr#endif
14941016Sdfr
15041016Sdfr/* ring buffer */
15141016Sdfrtypedef struct ringbuf {
15241016Sdfr    int           count;	/* # of valid elements in the buffer */
15341016Sdfr    int           head;		/* head pointer */
15441016Sdfr    int           tail;		/* tail poiner */
15541016Sdfr    unsigned char buf[PSM_BUFSIZE];
15641016Sdfr} ringbuf_t;
15741016Sdfr
15841016Sdfr/* driver control block */
15941016Sdfrstruct psm_softc {		/* Driver status information */
16041016Sdfr    struct selinfo rsel;	/* Process selecting for Input */
16141016Sdfr    unsigned char state;	/* Mouse driver state */
16241016Sdfr    int           config;	/* driver configuration flags */
16341016Sdfr    int           flags;	/* other flags */
16441016Sdfr    KBDC          kbdc;		/* handle to access the keyboard controller */
16541016Sdfr    int           addr;		/* I/O port address */
16641016Sdfr    mousehw_t     hw;		/* hardware information */
16741016Sdfr    mousemode_t   mode;		/* operation mode */
16841016Sdfr    mousemode_t   dflt_mode;	/* default operation mode */
16941016Sdfr    mousestatus_t status;	/* accumulated mouse movement */
17041016Sdfr    ringbuf_t     queue;	/* mouse status queue */
17141016Sdfr    unsigned char ipacket[16];	/* interim input buffer */
17241016Sdfr    int           inputbytes;	/* # of bytes in the input buffer */
17341016Sdfr    int           button;	/* the latest button state */
17441016Sdfr#ifdef DEVFS
17541016Sdfr    void          *devfs_token;
17641016Sdfr    void          *b_devfs_token;
17741016Sdfr#endif
17841016Sdfr#ifdef PSM_HOOKAPM
17941016Sdfr    struct apmhook resumehook;
18041016Sdfr#endif
18141016Sdfr};
18241016Sdfrdevclass_t psm_devclass;
18341016Sdfr#define PSM_SOFTC(unit)	((struct psm_softc*)devclass_get_softc(psm_devclass, unit))
18441016Sdfr
18541016Sdfr/* driver state flags (state) */
18641016Sdfr#define PSM_VALID		0x80
18741016Sdfr#define PSM_OPEN		1	/* Device is open */
18841016Sdfr#define PSM_ASLP		2	/* Waiting for mouse data */
18941016Sdfr
19041016Sdfr/* driver configuration flags (config) */
19141016Sdfr#define PSM_CONFIG_RESOLUTION	0x000f	/* resolution */
19241016Sdfr#define PSM_CONFIG_ACCEL	0x00f0  /* acceleration factor */
19341016Sdfr#define PSM_CONFIG_NOCHECKSYNC	0x0100  /* disable sync. test */
19445789Speter#define PSM_CONFIG_NOIDPROBE	0x0200  /* disable mouse model probe */
19545789Speter#define PSM_CONFIG_NORESET	0x0400  /* don't reset the mouse */
19645789Speter#define PSM_CONFIG_FORCETAP	0x0800  /* assume `tap' action exists */
19745789Speter#define PSM_CONFIG_IGNPORTERROR	0x1000  /* ignore error in aux port test */
19841016Sdfr
19941016Sdfr#define PSM_CONFIG_FLAGS	(PSM_CONFIG_RESOLUTION 		\
20041016Sdfr				    | PSM_CONFIG_ACCEL		\
20145789Speter				    | PSM_CONFIG_NOCHECKSYNC	\
20245789Speter				    | PSM_CONFIG_NOIDPROBE	\
20345789Speter				    | PSM_CONFIG_NORESET	\
20445789Speter				    | PSM_CONFIG_FORCETAP	\
20545789Speter				    | PSM_CONFIG_IGNPORTERROR)
20641016Sdfr
20741016Sdfr/* other flags (flags) */
20841016Sdfr
20941016Sdfr/* for backward compatibility */
21041016Sdfr#define OLD_MOUSE_GETHWINFO	_IOR('M', 1, old_mousehw_t)
21141016Sdfr#define OLD_MOUSE_GETMODE	_IOR('M', 2, old_mousemode_t)
21241016Sdfr#define OLD_MOUSE_SETMODE	_IOW('M', 3, old_mousemode_t)
21341016Sdfr
21441016Sdfrtypedef struct old_mousehw {
21541016Sdfr    int buttons;
21641016Sdfr    int iftype;
21741016Sdfr    int type;
21841016Sdfr    int hwid;
21941016Sdfr} old_mousehw_t;
22041016Sdfr
22141016Sdfrtypedef struct old_mousemode {
22241016Sdfr    int protocol;
22341016Sdfr    int rate;
22441016Sdfr    int resolution;
22541016Sdfr    int accelfactor;
22641016Sdfr} old_mousemode_t;
22741016Sdfr
22841016Sdfr/* packet formatting function */
22941016Sdfrtypedef int packetfunc_t __P((struct psm_softc *, unsigned char *,
23041016Sdfr			      int *, int, mousestatus_t *));
23141016Sdfr
23241016Sdfr/* function prototypes */
23341016Sdfrstatic int psmprobe __P((device_t));
23441016Sdfrstatic int psmattach __P((device_t));
23541016Sdfr#ifdef PSM_HOOKAPM
23641016Sdfrstatic int psmresume __P((void *));
23741016Sdfr#endif
23841016Sdfr
23941016Sdfrstatic d_open_t psmopen;
24041016Sdfrstatic d_close_t psmclose;
24141016Sdfrstatic d_read_t psmread;
24241016Sdfrstatic d_ioctl_t psmioctl;
24341016Sdfrstatic d_poll_t psmpoll;
24441016Sdfr
24541016Sdfrstatic int enable_aux_dev __P((KBDC));
24641016Sdfrstatic int disable_aux_dev __P((KBDC));
24741016Sdfrstatic int get_mouse_status __P((KBDC, int *, int, int));
24841016Sdfrstatic int get_aux_id __P((KBDC));
24941016Sdfrstatic int set_mouse_sampling_rate __P((KBDC, int));
25041016Sdfrstatic int set_mouse_scaling __P((KBDC, int));
25141016Sdfrstatic int set_mouse_resolution __P((KBDC, int));
25245789Speter#ifdef PSM_RESETAFTERSUSPEND
25341016Sdfrstatic int set_mouse_mode __P((KBDC));
25445789Speter#endif /* PSM_RESETAFTERSUSPEND */
25541016Sdfrstatic int get_mouse_buttons __P((KBDC));
25641016Sdfrstatic int is_a_mouse __P((int));
25741016Sdfrstatic void recover_from_error __P((KBDC));
25841016Sdfrstatic int restore_controller __P((KBDC, int));
25945789Speter#ifdef PSM_RESETAFTERSUSPEND
26041016Sdfrstatic int reinitialize __P((int, mousemode_t *));
26145789Speter#endif
26241016Sdfrstatic int doopen __P((int, int));
26341016Sdfrstatic char *model_name(int);
26441016Sdfrstatic void psmintr(void*);
26541016Sdfr
26641016Sdfr/* vendor specific features */
26741016Sdfrtypedef int probefunc_t __P((struct psm_softc *));
26841016Sdfr
26941016Sdfrstatic int mouse_id_proc1 __P((KBDC, int, int, int *));
27041016Sdfrstatic probefunc_t enable_groller;
27141016Sdfrstatic probefunc_t enable_gmouse;
27241016Sdfrstatic probefunc_t enable_aglide;
27341016Sdfrstatic probefunc_t enable_kmouse;
27441016Sdfrstatic probefunc_t enable_msintelli;
27541016Sdfrstatic probefunc_t enable_mmanplus;
27641016Sdfrstatic int tame_mouse __P((struct psm_softc *, mousestatus_t *, unsigned char *));
27741016Sdfr
27841016Sdfrstatic struct {
27941016Sdfr    int                 model;
28041016Sdfr    unsigned char	syncmask;
28141016Sdfr    int 		packetsize;
28241016Sdfr    probefunc_t 	*probefunc;
28341016Sdfr} vendortype[] = {
28441016Sdfr    { MOUSE_MODEL_NET,			/* Genius NetMouse */
28541016Sdfr      0xc8, MOUSE_INTELLI_PACKETSIZE, enable_gmouse, },
28641016Sdfr    { MOUSE_MODEL_NETSCROLL,		/* Genius NetScroll */
28741016Sdfr      0xc8, 6, enable_groller, },
28841016Sdfr    { MOUSE_MODEL_GLIDEPOINT,		/* ALPS GlidePoint */
28941016Sdfr      0xc0, MOUSE_PS2_PACKETSIZE, enable_aglide, },
29041016Sdfr    { MOUSE_MODEL_MOUSEMANPLUS,		/* Logitech MouseMan+ */
29141016Sdfr      0x08, MOUSE_PS2_PACKETSIZE, enable_mmanplus, },
29241016Sdfr    { MOUSE_MODEL_THINK,		/* Kensignton ThinkingMouse */
29341016Sdfr      0x80, MOUSE_PS2_PACKETSIZE, enable_kmouse, },
29441016Sdfr    { MOUSE_MODEL_INTELLI,		/* Microsoft IntelliMouse */
29541016Sdfr      0xc8, MOUSE_INTELLI_PACKETSIZE, enable_msintelli, },
29641016Sdfr    { MOUSE_MODEL_GENERIC,
29741016Sdfr      0xc0, MOUSE_PS2_PACKETSIZE, NULL, },
29841016Sdfr};
29945789Speter#define GENERIC_MOUSE_ENTRY	6
30041016Sdfr
30141016Sdfr/* device driver declarateion */
30241016Sdfrstatic device_method_t psm_methods[] = {
30341016Sdfr	/* Device interface */
30441016Sdfr	DEVMETHOD(device_probe,		psmprobe),
30541016Sdfr	DEVMETHOD(device_attach,	psmattach),
30641016Sdfr
30741016Sdfr	{ 0, 0 }
30841016Sdfr};
30941016Sdfr
31041016Sdfrstatic driver_t psm_driver = {
31141016Sdfr    "psm",
31241016Sdfr    psm_methods,
31341016Sdfr    sizeof(struct psm_softc),
31441016Sdfr};
31541016Sdfr
31641016Sdfr#define CDEV_MAJOR        21
31741016Sdfr
31847625Sphkstatic struct cdevsw psm_cdevsw = {
31947625Sphk	/* open */	psmopen,
32047625Sphk	/* close */	psmclose,
32147625Sphk	/* read */	psmread,
32247625Sphk	/* write */	nowrite,
32347625Sphk	/* ioctl */	psmioctl,
32447625Sphk	/* stop */	nostop,
32547625Sphk	/* reset */	noreset,
32647625Sphk	/* devtotty */	nodevtotty,
32747625Sphk	/* poll */	psmpoll,
32847625Sphk	/* mmap */	nommap,
32947625Sphk	/* strategy */	nostrategy,
33047625Sphk	/* name */	"psm",
33147625Sphk	/* parms */	noparms,
33247625Sphk	/* maj */	CDEV_MAJOR,
33347625Sphk	/* dump */	nodump,
33447625Sphk	/* psize */	nopsize,
33547625Sphk	/* flags */	0,
33647625Sphk	/* maxio */	0,
33747625Sphk	/* bmaj */	-1
33841016Sdfr};
33941016Sdfr
34041016Sdfr/* debug message level */
34141016Sdfrstatic int verbose = PSM_DEBUG;
34241016Sdfr
34341016Sdfr/* device I/O routines */
34441016Sdfrstatic int
34541016Sdfrenable_aux_dev(KBDC kbdc)
34641016Sdfr{
34741016Sdfr    int res;
34841016Sdfr
34941016Sdfr    res = send_aux_command(kbdc, PSMC_ENABLE_DEV);
35041016Sdfr    if (verbose >= 2)
35141016Sdfr        log(LOG_DEBUG, "psm: ENABLE_DEV return code:%04x\n", res);
35241016Sdfr
35341016Sdfr    return (res == PSM_ACK);
35441016Sdfr}
35541016Sdfr
35641016Sdfrstatic int
35741016Sdfrdisable_aux_dev(KBDC kbdc)
35841016Sdfr{
35941016Sdfr    int res;
36041016Sdfr
36141016Sdfr    res = send_aux_command(kbdc, PSMC_DISABLE_DEV);
36241016Sdfr    if (verbose >= 2)
36341016Sdfr        log(LOG_DEBUG, "psm: DISABLE_DEV return code:%04x\n", res);
36441016Sdfr
36541016Sdfr    return (res == PSM_ACK);
36641016Sdfr}
36741016Sdfr
36841016Sdfrstatic int
36941016Sdfrget_mouse_status(KBDC kbdc, int *status, int flag, int len)
37041016Sdfr{
37141016Sdfr    int cmd;
37241016Sdfr    int res;
37341016Sdfr    int i;
37441016Sdfr
37541016Sdfr    switch (flag) {
37641016Sdfr    case 0:
37741016Sdfr    default:
37841016Sdfr	cmd = PSMC_SEND_DEV_STATUS;
37941016Sdfr	break;
38041016Sdfr    case 1:
38141016Sdfr	cmd = PSMC_SEND_DEV_DATA;
38241016Sdfr	break;
38341016Sdfr    }
38441016Sdfr    empty_aux_buffer(kbdc, 5);
38541016Sdfr    res = send_aux_command(kbdc, cmd);
38641016Sdfr    if (verbose >= 2)
38741016Sdfr        log(LOG_DEBUG, "psm: SEND_AUX_DEV_%s return code:%04x\n",
38841016Sdfr	    (flag == 1) ? "DATA" : "STATUS", res);
38941016Sdfr    if (res != PSM_ACK)
39041016Sdfr        return 0;
39141016Sdfr
39241016Sdfr    for (i = 0; i < len; ++i) {
39341016Sdfr        status[i] = read_aux_data(kbdc);
39441016Sdfr	if (status[i] < 0)
39541016Sdfr	    break;
39641016Sdfr    }
39741016Sdfr
39841016Sdfr    if (verbose) {
39941016Sdfr        log(LOG_DEBUG, "psm: %s %02x %02x %02x\n",
40041016Sdfr            (flag == 1) ? "data" : "status", status[0], status[1], status[2]);
40141016Sdfr    }
40241016Sdfr
40341016Sdfr    return i;
40441016Sdfr}
40541016Sdfr
40641016Sdfrstatic int
40741016Sdfrget_aux_id(KBDC kbdc)
40841016Sdfr{
40941016Sdfr    int res;
41041016Sdfr    int id;
41141016Sdfr
41241016Sdfr    empty_aux_buffer(kbdc, 5);
41341016Sdfr    res = send_aux_command(kbdc, PSMC_SEND_DEV_ID);
41441016Sdfr    if (verbose >= 2)
41541016Sdfr        log(LOG_DEBUG, "psm: SEND_DEV_ID return code:%04x\n", res);
41641016Sdfr    if (res != PSM_ACK)
41741016Sdfr	return (-1);
41841016Sdfr
41941016Sdfr    /* 10ms delay */
42041016Sdfr    DELAY(10000);
42141016Sdfr
42241016Sdfr    id = read_aux_data(kbdc);
42341016Sdfr    if (verbose >= 2)
42441016Sdfr        log(LOG_DEBUG, "psm: device ID: %04x\n", id);
42541016Sdfr
42641016Sdfr    return id;
42741016Sdfr}
42841016Sdfr
42941016Sdfrstatic int
43041016Sdfrset_mouse_sampling_rate(KBDC kbdc, int rate)
43141016Sdfr{
43241016Sdfr    int res;
43341016Sdfr
43441016Sdfr    res = send_aux_command_and_data(kbdc, PSMC_SET_SAMPLING_RATE, rate);
43541016Sdfr    if (verbose >= 2)
43641016Sdfr        log(LOG_DEBUG, "psm: SET_SAMPLING_RATE (%d) %04x\n", rate, res);
43741016Sdfr
43841016Sdfr    return ((res == PSM_ACK) ? rate : -1);
43941016Sdfr}
44041016Sdfr
44141016Sdfrstatic int
44241016Sdfrset_mouse_scaling(KBDC kbdc, int scale)
44341016Sdfr{
44441016Sdfr    int res;
44541016Sdfr
44641016Sdfr    switch (scale) {
44741016Sdfr    case 1:
44841016Sdfr    default:
44941016Sdfr	scale = PSMC_SET_SCALING11;
45041016Sdfr	break;
45141016Sdfr    case 2:
45241016Sdfr	scale = PSMC_SET_SCALING21;
45341016Sdfr	break;
45441016Sdfr    }
45541016Sdfr    res = send_aux_command(kbdc, scale);
45641016Sdfr    if (verbose >= 2)
45741016Sdfr        log(LOG_DEBUG, "psm: SET_SCALING%s return code:%04x\n",
45841016Sdfr	    (scale == PSMC_SET_SCALING21) ? "21" : "11", res);
45941016Sdfr
46041016Sdfr    return (res == PSM_ACK);
46141016Sdfr}
46241016Sdfr
46341016Sdfr/* `val' must be 0 through PSMD_MAX_RESOLUTION */
46441016Sdfrstatic int
46541016Sdfrset_mouse_resolution(KBDC kbdc, int val)
46641016Sdfr{
46741016Sdfr    int res;
46841016Sdfr
46941016Sdfr    res = send_aux_command_and_data(kbdc, PSMC_SET_RESOLUTION, val);
47041016Sdfr    if (verbose >= 2)
47141016Sdfr        log(LOG_DEBUG, "psm: SET_RESOLUTION (%d) %04x\n", val, res);
47241016Sdfr
47341016Sdfr    return ((res == PSM_ACK) ? val : -1);
47441016Sdfr}
47541016Sdfr
47645789Speter#ifdef PSM_RESETAFTERSUSPEND
47741016Sdfr/*
47841016Sdfr * NOTE: once `set_mouse_mode()' is called, the mouse device must be
47941016Sdfr * re-enabled by calling `enable_aux_dev()'
48041016Sdfr */
48141016Sdfrstatic int
48241016Sdfrset_mouse_mode(KBDC kbdc)
48341016Sdfr{
48441016Sdfr    int res;
48541016Sdfr
48641016Sdfr    res = send_aux_command(kbdc, PSMC_SET_STREAM_MODE);
48741016Sdfr    if (verbose >= 2)
48841016Sdfr        log(LOG_DEBUG, "psm: SET_STREAM_MODE return code:%04x\n", res);
48941016Sdfr
49041016Sdfr    return (res == PSM_ACK);
49141016Sdfr}
49245789Speter#endif /* PSM_RESETAFTERSUSPEND */
49341016Sdfr
49445789Speter
49541016Sdfrstatic int
49641016Sdfrget_mouse_buttons(KBDC kbdc)
49741016Sdfr{
49841016Sdfr    int c = 2;		/* assume two buttons by default */
49941016Sdfr    int status[3];
50041016Sdfr
50141016Sdfr    /*
50241016Sdfr     * NOTE: a special sequence to obtain Logitech Mouse specific
50341016Sdfr     * information: set resolution to 25 ppi, set scaling to 1:1, set
50441016Sdfr     * scaling to 1:1, set scaling to 1:1. Then the second byte of the
50541016Sdfr     * mouse status bytes is the number of available buttons.
50641016Sdfr     * Some manufactures also support this sequence.
50741016Sdfr     */
50841016Sdfr    if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW)
50941016Sdfr        return c;
51041016Sdfr    if (set_mouse_scaling(kbdc, 1) && set_mouse_scaling(kbdc, 1)
51141016Sdfr        && set_mouse_scaling(kbdc, 1)
51241016Sdfr	&& (get_mouse_status(kbdc, status, 0, 3) >= 3)) {
51341016Sdfr        if (status[1] != 0)
51441016Sdfr            return status[1];
51541016Sdfr    }
51641016Sdfr    return c;
51741016Sdfr}
51841016Sdfr
51941016Sdfr/* misc subroutines */
52041016Sdfr/*
52141016Sdfr * Someday, I will get the complete list of valid pointing devices and
52241016Sdfr * their IDs... XXX
52341016Sdfr */
52441016Sdfrstatic int
52541016Sdfris_a_mouse(int id)
52641016Sdfr{
52741016Sdfr#if 0
52841016Sdfr    static int valid_ids[] = {
52941016Sdfr        PSM_MOUSE_ID,		/* mouse */
53041016Sdfr        PSM_BALLPOINT_ID,	/* ballpoint device */
53141016Sdfr        PSM_INTELLI_ID,		/* Intellimouse */
53241016Sdfr        -1			/* end of table */
53341016Sdfr    };
53441016Sdfr    int i;
53541016Sdfr
53641016Sdfr    for (i = 0; valid_ids[i] >= 0; ++i)
53741016Sdfr        if (valid_ids[i] == id)
53841016Sdfr            return TRUE;
53941016Sdfr    return FALSE;
54041016Sdfr#else
54141016Sdfr    return TRUE;
54241016Sdfr#endif
54341016Sdfr}
54441016Sdfr
54541016Sdfrstatic char *
54641016Sdfrmodel_name(int model)
54741016Sdfr{
54841016Sdfr    static struct {
54941016Sdfr	int model_code;
55041016Sdfr	char *model_name;
55141016Sdfr    } models[] = {
55241016Sdfr        { MOUSE_MODEL_NETSCROLL,	"NetScroll Mouse" },
55341016Sdfr        { MOUSE_MODEL_NET,		"NetMouse" },
55441016Sdfr        { MOUSE_MODEL_GLIDEPOINT,	"GlidePoint" },
55541016Sdfr        { MOUSE_MODEL_THINK,		"ThinkingMouse" },
55641016Sdfr        { MOUSE_MODEL_INTELLI,		"IntelliMouse" },
55741016Sdfr        { MOUSE_MODEL_MOUSEMANPLUS,	"MouseMan+" },
55841016Sdfr        { MOUSE_MODEL_GENERIC,		"Generic PS/2 mouse" },
55941016Sdfr        { MOUSE_MODEL_UNKNOWN,		NULL },
56041016Sdfr    };
56141016Sdfr    int i;
56241016Sdfr
56341016Sdfr    for (i = 0; models[i].model_code != MOUSE_MODEL_UNKNOWN; ++i) {
56441016Sdfr	if (models[i].model_code == model)
56541016Sdfr	    return models[i].model_name;
56641016Sdfr    }
56741016Sdfr    return "Unknown";
56841016Sdfr}
56941016Sdfr
57041016Sdfrstatic void
57141016Sdfrrecover_from_error(KBDC kbdc)
57241016Sdfr{
57341016Sdfr    /* discard anything left in the output buffer */
57441016Sdfr    empty_both_buffers(kbdc, 10);
57541016Sdfr
57641016Sdfr#if 0
57741016Sdfr    /*
57841016Sdfr     * NOTE: KBDC_RESET_KBD may not restore the communication between the
57941016Sdfr     * keyboard and the controller.
58041016Sdfr     */
58141016Sdfr    reset_kbd(kbdc);
58241016Sdfr#else
58341016Sdfr    /*
58441016Sdfr     * NOTE: somehow diagnostic and keyboard port test commands bring the
58541016Sdfr     * keyboard back.
58641016Sdfr     */
58741016Sdfr    if (!test_controller(kbdc))
58841016Sdfr        log(LOG_ERR, "psm: keyboard controller failed.\n");
58941016Sdfr    /* if there isn't a keyboard in the system, the following error is OK */
59041016Sdfr    if (test_kbd_port(kbdc) != 0) {
59141016Sdfr	if (verbose)
59241016Sdfr	    log(LOG_ERR, "psm: keyboard port failed.\n");
59341016Sdfr    }
59441016Sdfr#endif
59541016Sdfr}
59641016Sdfr
59741016Sdfrstatic int
59841016Sdfrrestore_controller(KBDC kbdc, int command_byte)
59941016Sdfr{
60041016Sdfr    empty_both_buffers(kbdc, 10);
60141016Sdfr
60241016Sdfr    if (!set_controller_command_byte(kbdc, 0xff, command_byte)) {
60341016Sdfr	log(LOG_ERR, "psm: failed to restore the keyboard controller "
60441016Sdfr		     "command byte.\n");
60541016Sdfr	return FALSE;
60641016Sdfr    } else {
60741016Sdfr	return TRUE;
60841016Sdfr    }
60941016Sdfr}
61041016Sdfr
61145789Speter#ifdef PSM_RESETAFTERSUSPEND
61241016Sdfr/*
61341016Sdfr * Re-initialize the aux port and device. The aux port must be enabled
61441016Sdfr * and its interrupt must be disabled before calling this routine.
61541016Sdfr * The aux device will be disabled before returning.
61641016Sdfr * The keyboard controller must be locked via `kbdc_lock()' before
61741016Sdfr * calling this routine.
61841016Sdfr */
61941016Sdfrstatic int
62041016Sdfrreinitialize(int unit, mousemode_t *mode)
62141016Sdfr{
62241016Sdfr    struct psm_softc *sc = PSM_SOFTC(unit);
62341016Sdfr    KBDC kbdc = sc->kbdc;
62441016Sdfr    int stat[3];
62541016Sdfr    int i;
62641016Sdfr
62741016Sdfr    switch((i = test_aux_port(kbdc))) {
62841016Sdfr    case 1:	/* ignore this error */
62941016Sdfr    case PSM_ACK:
63041016Sdfr	if (verbose)
63141016Sdfr	    log(LOG_DEBUG, "psm%d: strange result for test aux port (%d).\n",
63241016Sdfr	        unit, i);
63341016Sdfr	/* fall though */
63441016Sdfr    case 0:	/* no error */
63541016Sdfr    	break;
63641016Sdfr    case -1: 	/* time out */
63741016Sdfr    default: 	/* error */
63841016Sdfr    	recover_from_error(kbdc);
63945789Speter	if (sc->config & PSM_CONFIG_IGNPORTERROR)
64045789Speter	    break;
64141016Sdfr    	log(LOG_ERR, "psm%d: the aux port is not functioning (%d).\n",
64241016Sdfr    	    unit, i);
64341016Sdfr    	return FALSE;
64441016Sdfr    }
64541016Sdfr
64645789Speter    if (sc->config & PSM_CONFIG_NORESET) {
64745789Speter	/*
64845789Speter	 * Don't try to reset the pointing device.  It may possibly be
64945789Speter	 * left in the unknown state, though...
65045789Speter	 */
65145789Speter    } else {
65245789Speter	/*
65345789Speter	 * NOTE: some controllers appears to hang the `keyboard' when
65445789Speter	 * the aux port doesn't exist and `PSMC_RESET_DEV' is issued.
65545789Speter	 */
65645789Speter	if (!reset_aux_dev(kbdc)) {
65745789Speter            recover_from_error(kbdc);
65845789Speter            log(LOG_ERR, "psm%d: failed to reset the aux device.\n", unit);
65945789Speter            return FALSE;
66045789Speter	}
66141016Sdfr    }
66241016Sdfr
66341016Sdfr    /*
66441016Sdfr     * both the aux port and the aux device is functioning, see
66541016Sdfr     * if the device can be enabled.
66641016Sdfr     */
66741016Sdfr    if (!enable_aux_dev(kbdc) || !disable_aux_dev(kbdc)) {
66841016Sdfr        log(LOG_ERR, "psm%d: failed to enable the aux device.\n", unit);
66941016Sdfr        return FALSE;
67041016Sdfr    }
67141016Sdfr    empty_both_buffers(kbdc, 10);	/* remove stray data if any */
67241016Sdfr
67345789Speter    if (sc->config & PSM_CONFIG_NOIDPROBE) {
67445789Speter	i = GENERIC_MOUSE_ENTRY;
67545789Speter    } else {
67645789Speter	/* FIXME: hardware ID, mouse buttons? */
67741016Sdfr
67845789Speter	/* other parameters */
67945789Speter	for (i = 0; vendortype[i].probefunc != NULL; ++i) {
68045789Speter	    if ((*vendortype[i].probefunc)(sc)) {
68145789Speter		if (verbose >= 2)
68245789Speter		    log(LOG_ERR, "psm%d: found %s\n",
68345789Speter			unit, model_name(vendortype[i].model));
68445789Speter		break;
68545789Speter	    }
68641016Sdfr	}
68741016Sdfr    }
68841016Sdfr
68941016Sdfr    sc->hw.model = vendortype[i].model;
69041016Sdfr    sc->mode.packetsize = vendortype[i].packetsize;
69141016Sdfr
69241016Sdfr    /* set mouse parameters */
69341016Sdfr    if (mode != (mousemode_t *)NULL) {
69441016Sdfr	if (mode->rate > 0)
69541016Sdfr            mode->rate = set_mouse_sampling_rate(kbdc, mode->rate);
69641016Sdfr	if (mode->resolution >= 0)
69741016Sdfr            mode->resolution = set_mouse_resolution(kbdc, mode->resolution);
69841016Sdfr        set_mouse_scaling(kbdc, 1);
69941016Sdfr        set_mouse_mode(kbdc);
70041016Sdfr    }
70141016Sdfr
70241016Sdfr    /* request a data packet and extract sync. bits */
70341016Sdfr    if (get_mouse_status(kbdc, stat, 1, 3) < 3) {
70441016Sdfr        log(LOG_DEBUG, "psm%d: failed to get data (reinitialize).\n", unit);
70541016Sdfr        sc->mode.syncmask[0] = 0;
70641016Sdfr    } else {
70741016Sdfr        sc->mode.syncmask[1] = stat[0] & sc->mode.syncmask[0];	/* syncbits */
70841016Sdfr	/* the NetScroll Mouse will send three more bytes... Ignore them */
70941016Sdfr	empty_aux_buffer(kbdc, 5);
71041016Sdfr    }
71141016Sdfr
71241016Sdfr    /* just check the status of the mouse */
71341016Sdfr    if (get_mouse_status(kbdc, stat, 0, 3) < 3)
71441016Sdfr        log(LOG_DEBUG, "psm%d: failed to get status (reinitialize).\n", unit);
71541016Sdfr
71641016Sdfr    return TRUE;
71741016Sdfr}
71845789Speter#endif /* PSM_RESETAFTERSUSPEND */
71941016Sdfr
72041016Sdfrstatic int
72141016Sdfrdoopen(int unit, int command_byte)
72241016Sdfr{
72341016Sdfr    struct psm_softc *sc = PSM_SOFTC(unit);
72441016Sdfr    int stat[3];
72541016Sdfr
72641016Sdfr    /* enable the mouse device */
72741016Sdfr    if (!enable_aux_dev(sc->kbdc)) {
72841016Sdfr	/* MOUSE ERROR: failed to enable the mouse because:
72941016Sdfr	 * 1) the mouse is faulty,
73041016Sdfr	 * 2) the mouse has been removed(!?)
73141016Sdfr	 * In the latter case, the keyboard may have hung, and need
73241016Sdfr	 * recovery procedure...
73341016Sdfr	 */
73441016Sdfr	recover_from_error(sc->kbdc);
73541016Sdfr#if 0
73641016Sdfr	/* FIXME: we could reset the mouse here and try to enable
73741016Sdfr	 * it again. But it will take long time and it's not a good
73841016Sdfr	 * idea to disable the keyboard that long...
73941016Sdfr	 */
74041016Sdfr	if (!reinitialize(unit, &sc->mode) || !enable_aux_dev(sc->kbdc)) {
74141016Sdfr	    recover_from_error(sc->kbdc);
74241016Sdfr#else
74341016Sdfr        {
74441016Sdfr#endif
74541016Sdfr            restore_controller(sc->kbdc, command_byte);
74641016Sdfr	    /* mark this device is no longer available */
74741016Sdfr	    sc->state &= ~PSM_VALID;
74841016Sdfr	    log(LOG_ERR, "psm%d: failed to enable the device (doopen).\n",
74941016Sdfr		unit);
75041016Sdfr	    return (EIO);
75141016Sdfr	}
75241016Sdfr    }
75341016Sdfr
75441016Sdfr    if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3)
75541016Sdfr        log(LOG_DEBUG, "psm%d: failed to get status (doopen).\n", unit);
75641016Sdfr
75741016Sdfr    /* enable the aux port and interrupt */
75841016Sdfr    if (!set_controller_command_byte(sc->kbdc,
75941016Sdfr	    kbdc_get_device_mask(sc->kbdc),
76041016Sdfr	    (command_byte & KBD_KBD_CONTROL_BITS)
76141016Sdfr		| KBD_ENABLE_AUX_PORT | KBD_ENABLE_AUX_INT)) {
76241016Sdfr	/* CONTROLLER ERROR */
76341016Sdfr	disable_aux_dev(sc->kbdc);
76441016Sdfr        restore_controller(sc->kbdc, command_byte);
76541016Sdfr	log(LOG_ERR, "psm%d: failed to enable the aux interrupt (doopen).\n",
76641016Sdfr	    unit);
76741016Sdfr	return (EIO);
76841016Sdfr    }
76941016Sdfr
77041016Sdfr    return (0);
77141016Sdfr}
77241016Sdfr
77341016Sdfr/* psm driver entry points */
77441016Sdfr
77541016Sdfr#define endprobe(v)	{   if (bootverbose) 				\
77641016Sdfr				--verbose;   				\
77741016Sdfr                            kbdc_set_device_mask(sc->kbdc, mask);	\
77841016Sdfr			    kbdc_lock(sc->kbdc, FALSE);			\
77941016Sdfr 	                    free(sc, M_DEVBUF);                         \
78041016Sdfr			    return (v);	     				\
78141016Sdfr			}
78241016Sdfr
78341016Sdfrstatic int
78441016Sdfrpsmprobe(device_t dev)
78541016Sdfr{
78641016Sdfr    int unit = device_get_unit(dev);
78741016Sdfr    struct psm_softc *sc = device_get_softc(dev);
78845720Speter    uintptr_t port;
78945720Speter    uintptr_t flags;
79041016Sdfr    int stat[3];
79141016Sdfr    int command_byte;
79241016Sdfr    int mask;
79341016Sdfr    int i;
79441016Sdfr
79541016Sdfr#if 0
79641016Sdfr    kbdc_debug(TRUE);
79741016Sdfr#endif
79843105Sdfr    BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_PORT, &port);
79943105Sdfr    BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_FLAGS, &flags);
80043105Sdfr
80143105Sdfr    sc->addr = port;
80241016Sdfr    sc->kbdc = kbdc_open(sc->addr);
80343105Sdfr    sc->config = flags & PSM_CONFIG_FLAGS;
80441016Sdfr    sc->flags = 0;
80541016Sdfr    if (bootverbose)
80641016Sdfr        ++verbose;
80741016Sdfr
80843105Sdfr    device_set_desc(dev, "PS/2 Mouse");
80943105Sdfr
81041016Sdfr    if (!kbdc_lock(sc->kbdc, TRUE)) {
81141016Sdfr        printf("psm%d: unable to lock the controller.\n", unit);
81241016Sdfr        if (bootverbose)
81341016Sdfr            --verbose;
81441016Sdfr	return (ENXIO);
81541016Sdfr    }
81641016Sdfr
81741016Sdfr    /*
81841016Sdfr     * NOTE: two bits in the command byte controls the operation of the
81941016Sdfr     * aux port (mouse port): the aux port disable bit (bit 5) and the aux
82041016Sdfr     * port interrupt (IRQ 12) enable bit (bit 2).
82141016Sdfr     */
82241016Sdfr
82341016Sdfr    /* discard anything left after the keyboard initialization */
82441016Sdfr    empty_both_buffers(sc->kbdc, 10);
82541016Sdfr
82641016Sdfr    /* save the current command byte; it will be used later */
82741016Sdfr    mask = kbdc_get_device_mask(sc->kbdc) & ~KBD_AUX_CONTROL_BITS;
82841016Sdfr    command_byte = get_controller_command_byte(sc->kbdc);
82941016Sdfr    if (verbose)
83041016Sdfr        printf("psm%d: current command byte:%04x\n", unit, command_byte);
83141016Sdfr    if (command_byte == -1) {
83241016Sdfr        /* CONTROLLER ERROR */
83341016Sdfr        printf("psm%d: unable to get the current command byte value.\n",
83441016Sdfr            unit);
83541016Sdfr        endprobe(ENXIO);
83641016Sdfr    }
83741016Sdfr
83841016Sdfr    /*
83941016Sdfr     * disable the keyboard port while probing the aux port, which must be
84041016Sdfr     * enabled during this routine
84141016Sdfr     */
84241016Sdfr    if (!set_controller_command_byte(sc->kbdc,
84341016Sdfr	    KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS,
84441016Sdfr  	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
84541016Sdfr                | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
84641016Sdfr        /*
84741016Sdfr	 * this is CONTROLLER ERROR; I don't know how to recover
84841016Sdfr         * from this error...
84941016Sdfr	 */
85041016Sdfr        restore_controller(sc->kbdc, command_byte);
85141016Sdfr        printf("psm%d: unable to set the command byte.\n", unit);
85241016Sdfr        endprobe(ENXIO);
85341016Sdfr    }
85445789Speter    write_controller_command(sc->kbdc, KBDC_ENABLE_AUX_PORT);
85541016Sdfr
85641016Sdfr    /*
85741016Sdfr     * NOTE: `test_aux_port()' is designed to return with zero if the aux
85841016Sdfr     * port exists and is functioning. However, some controllers appears
85941016Sdfr     * to respond with zero even when the aux port doesn't exist. (It may
86041016Sdfr     * be that this is only the case when the controller DOES have the aux
86141016Sdfr     * port but the port is not wired on the motherboard.) The keyboard
86241016Sdfr     * controllers without the port, such as the original AT, are
86341016Sdfr     * supporsed to return with an error code or simply time out. In any
86441016Sdfr     * case, we have to continue probing the port even when the controller
86541016Sdfr     * passes this test.
86641016Sdfr     *
86741016Sdfr     * XXX: some controllers erroneously return the error code 1 when
86841016Sdfr     * it has the perfectly functional aux port. We have to ignore this
86941016Sdfr     * error code. Even if the controller HAS error with the aux port,
87041016Sdfr     * it will be detected later...
87141016Sdfr     * XXX: another incompatible controller returns PSM_ACK (0xfa)...
87241016Sdfr     */
87341016Sdfr    switch ((i = test_aux_port(sc->kbdc))) {
87441016Sdfr    case 1:	   /* ignore this error */
87541016Sdfr    case PSM_ACK:
87641016Sdfr        if (verbose)
87741016Sdfr	    printf("psm%d: strange result for test aux port (%d).\n",
87841016Sdfr	        unit, i);
87941016Sdfr	/* fall though */
88041016Sdfr    case 0:        /* no error */
88141016Sdfr        break;
88241016Sdfr    case -1:        /* time out */
88341016Sdfr    default:        /* error */
88441016Sdfr        recover_from_error(sc->kbdc);
88545789Speter	if (sc->config & PSM_CONFIG_IGNPORTERROR)
88645789Speter	    break;
88741016Sdfr        restore_controller(sc->kbdc, command_byte);
88841016Sdfr        if (verbose)
88941016Sdfr            printf("psm%d: the aux port is not functioning (%d).\n",
89041016Sdfr                unit, i);
89141016Sdfr        endprobe(ENXIO);
89241016Sdfr    }
89341016Sdfr
89445789Speter    if (sc->config & PSM_CONFIG_NORESET) {
89545789Speter	/*
89645789Speter	 * Don't try to reset the pointing device.  It may possibly be
89745789Speter	 * left in the unknown state, though...
89845789Speter	 */
89945789Speter    } else {
90045789Speter	/*
90145789Speter	 * NOTE: some controllers appears to hang the `keyboard' when the aux
90245789Speter	 * port doesn't exist and `PSMC_RESET_DEV' is issued.
90345789Speter	 */
90445789Speter	if (!reset_aux_dev(sc->kbdc)) {
90545789Speter            recover_from_error(sc->kbdc);
90645789Speter            restore_controller(sc->kbdc, command_byte);
90745789Speter            if (verbose)
90845789Speter        	printf("psm%d: failed to reset the aux device.\n", unit);
90945789Speter            endprobe(ENXIO);
91045789Speter	}
91141016Sdfr    }
91245789Speter
91341016Sdfr    /*
91441016Sdfr     * both the aux port and the aux device is functioning, see if the
91541016Sdfr     * device can be enabled. NOTE: when enabled, the device will start
91641016Sdfr     * sending data; we shall immediately disable the device once we know
91741016Sdfr     * the device can be enabled.
91841016Sdfr     */
91941016Sdfr    if (!enable_aux_dev(sc->kbdc) || !disable_aux_dev(sc->kbdc)) {
92045789Speter        /* MOUSE ERROR */
92145789Speter	recover_from_error(sc->kbdc);
92245789Speter	restore_controller(sc->kbdc, command_byte);
92345789Speter	if (verbose)
92445789Speter	    printf("psm%d: failed to enable the aux device.\n", unit);
92541016Sdfr        endprobe(ENXIO);
92641016Sdfr    }
92741016Sdfr
92841016Sdfr    /* save the default values after reset */
92941016Sdfr    if (get_mouse_status(sc->kbdc, stat, 0, 3) >= 3) {
93041016Sdfr	sc->dflt_mode.rate = sc->mode.rate = stat[2];
93141016Sdfr	sc->dflt_mode.resolution = sc->mode.resolution = stat[1];
93241016Sdfr    } else {
93341016Sdfr	sc->dflt_mode.rate = sc->mode.rate = -1;
93441016Sdfr	sc->dflt_mode.resolution = sc->mode.resolution = -1;
93541016Sdfr    }
93641016Sdfr
93741016Sdfr    /* hardware information */
93841016Sdfr    sc->hw.iftype = MOUSE_IF_PS2;
93941016Sdfr
94041016Sdfr    /* verify the device is a mouse */
94141016Sdfr    sc->hw.hwid = get_aux_id(sc->kbdc);
94241016Sdfr    if (!is_a_mouse(sc->hw.hwid)) {
94341016Sdfr        restore_controller(sc->kbdc, command_byte);
94441016Sdfr        if (verbose)
94541016Sdfr            printf("psm%d: unknown device type (%d).\n", unit, sc->hw.hwid);
94641016Sdfr        endprobe(ENXIO);
94741016Sdfr    }
94841016Sdfr    switch (sc->hw.hwid) {
94941016Sdfr    case PSM_BALLPOINT_ID:
95041016Sdfr        sc->hw.type = MOUSE_TRACKBALL;
95141016Sdfr        break;
95241016Sdfr    case PSM_MOUSE_ID:
95341016Sdfr    case PSM_INTELLI_ID:
95441016Sdfr        sc->hw.type = MOUSE_MOUSE;
95541016Sdfr        break;
95641016Sdfr    default:
95741016Sdfr        sc->hw.type = MOUSE_UNKNOWN;
95841016Sdfr        break;
95941016Sdfr    }
96041016Sdfr
96145789Speter    if (sc->config & PSM_CONFIG_NOIDPROBE) {
96245789Speter	sc->hw.buttons = 2;
96345789Speter	i = GENERIC_MOUSE_ENTRY;
96445789Speter    } else {
96545789Speter	/* # of buttons */
96645789Speter	sc->hw.buttons = get_mouse_buttons(sc->kbdc);
96741016Sdfr
96845789Speter	/* other parameters */
96945789Speter	for (i = 0; vendortype[i].probefunc != NULL; ++i) {
97045789Speter	    if ((*vendortype[i].probefunc)(sc)) {
97145789Speter		if (verbose >= 2)
97245789Speter		    printf("psm%d: found %s\n",
97345789Speter			   unit, model_name(vendortype[i].model));
97445789Speter		break;
97545789Speter	    }
97641016Sdfr	}
97741016Sdfr    }
97841016Sdfr
97941016Sdfr    sc->hw.model = vendortype[i].model;
98041016Sdfr
98141016Sdfr    sc->dflt_mode.level = PSM_LEVEL_BASE;
98241016Sdfr    sc->dflt_mode.packetsize = MOUSE_PS2_PACKETSIZE;
98341016Sdfr    sc->dflt_mode.accelfactor = (sc->config & PSM_CONFIG_ACCEL) >> 4;
98441016Sdfr    if (sc->config & PSM_CONFIG_NOCHECKSYNC)
98541016Sdfr        sc->dflt_mode.syncmask[0] = 0;
98641016Sdfr    else
98741016Sdfr        sc->dflt_mode.syncmask[0] = vendortype[i].syncmask;
98845789Speter    if (sc->config & PSM_CONFIG_FORCETAP)
98945789Speter        sc->mode.syncmask[0] &= ~MOUSE_PS2_TAP;
99041016Sdfr    sc->dflt_mode.syncmask[1] = 0;	/* syncbits */
99141016Sdfr    sc->mode = sc->dflt_mode;
99241016Sdfr    sc->mode.packetsize = vendortype[i].packetsize;
99341016Sdfr
99441016Sdfr    /* set mouse parameters */
99541016Sdfr    i = send_aux_command(sc->kbdc, PSMC_SET_DEFAULTS);
99641016Sdfr    if (verbose >= 2)
99741016Sdfr	printf("psm%d: SET_DEFAULTS return code:%04x\n", unit, i);
99841016Sdfr    if (sc->config & PSM_CONFIG_RESOLUTION) {
99941016Sdfr        sc->mode.resolution
100041016Sdfr	    = set_mouse_resolution(sc->kbdc,
100141016Sdfr	        (sc->config & PSM_CONFIG_RESOLUTION) - 1);
100241016Sdfr    }
100341016Sdfr
100441016Sdfr    /* request a data packet and extract sync. bits */
100541016Sdfr    if (get_mouse_status(sc->kbdc, stat, 1, 3) < 3) {
100641016Sdfr        printf("psm%d: failed to get data.\n", unit);
100741016Sdfr        sc->mode.syncmask[0] = 0;
100841016Sdfr    } else {
100941016Sdfr        sc->mode.syncmask[1] = stat[0] & sc->mode.syncmask[0];	/* syncbits */
101041016Sdfr	/* the NetScroll Mouse will send three more bytes... Ignore them */
101141016Sdfr	empty_aux_buffer(sc->kbdc, 5);
101241016Sdfr    }
101341016Sdfr
101441016Sdfr    /* just check the status of the mouse */
101541016Sdfr    /*
101641016Sdfr     * NOTE: XXX there are some arcane controller/mouse combinations out
101741016Sdfr     * there, which hung the controller unless there is data transmission
101841016Sdfr     * after ACK from the mouse.
101941016Sdfr     */
102041016Sdfr    if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3) {
102141016Sdfr        printf("psm%d: failed to get status.\n", unit);
102241016Sdfr    } else {
102341016Sdfr	/*
102441016Sdfr	 * When in its native mode, some mice operate with different
102541016Sdfr	 * default parameters than in the PS/2 compatible mode.
102641016Sdfr	 */
102741016Sdfr        sc->dflt_mode.rate = sc->mode.rate = stat[2];
102841016Sdfr        sc->dflt_mode.resolution = sc->mode.resolution = stat[1];
102941016Sdfr     }
103041016Sdfr
103141016Sdfr    /* disable the aux port for now... */
103241016Sdfr    if (!set_controller_command_byte(sc->kbdc,
103341016Sdfr	    KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS,
103441016Sdfr            (command_byte & KBD_KBD_CONTROL_BITS)
103541016Sdfr                | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
103641016Sdfr        /*
103741016Sdfr	 * this is CONTROLLER ERROR; I don't know the proper way to
103841016Sdfr         * recover from this error...
103941016Sdfr	 */
104041016Sdfr        restore_controller(sc->kbdc, command_byte);
104141016Sdfr        printf("psm%d: unable to set the command byte.\n", unit);
104241016Sdfr        endprobe(ENXIO);
104341016Sdfr    }
104441016Sdfr
104541016Sdfr    /* done */
104641016Sdfr    kbdc_set_device_mask(sc->kbdc, mask | KBD_AUX_CONTROL_BITS);
104741016Sdfr    kbdc_lock(sc->kbdc, FALSE);
104841016Sdfr    return (0);
104941016Sdfr}
105041016Sdfr
105141016Sdfrstatic int
105241016Sdfrpsmattach(device_t dev)
105341016Sdfr{
105441016Sdfr    int unit = device_get_unit(dev);
105541016Sdfr    struct psm_softc *sc = device_get_softc(dev);
105641016Sdfr    void *ih;
105741181Sdfr    struct resource *res;
105845720Speter    uintptr_t irq;
105941181Sdfr    int zero = 0;
106041016Sdfr
106141016Sdfr    if (sc == NULL)    /* shouldn't happen */
106241016Sdfr	return (ENXIO);
106341016Sdfr
106441016Sdfr    /* Setup initial state */
106541016Sdfr    sc->state = PSM_VALID;
106641016Sdfr
106741016Sdfr    /* Done */
106841016Sdfr#ifdef    DEVFS
106941016Sdfr    sc->devfs_token =
107041016Sdfr        devfs_add_devswf(&psm_cdevsw, PSM_MKMINOR(unit, FALSE),
107141016Sdfr        DV_CHR, 0, 0, 0666, "psm%d", unit);
107241016Sdfr    sc->b_devfs_token =
107341016Sdfr        devfs_add_devswf(&psm_cdevsw, PSM_MKMINOR(unit, TRUE),
107441016Sdfr        DV_CHR, 0, 0, 0666, "bpsm%d", unit);
107541016Sdfr#endif /* DEVFS */
107641016Sdfr
107741016Sdfr#ifdef PSM_HOOKAPM
107841016Sdfr    sc->resumehook.ah_name = "PS/2 mouse";
107941016Sdfr    sc->resumehook.ah_fun = psmresume;
108041016Sdfr    sc->resumehook.ah_arg = (void *)unit;
108141016Sdfr    sc->resumehook.ah_order = APM_MID_ORDER;
108241016Sdfr    apm_hook_establish(APM_HOOK_RESUME , &sc->resumehook);
108341016Sdfr    if (verbose)
108441016Sdfr        printf("psm%d: APM hooks installed.\n", unit);
108541016Sdfr#endif /* PSM_HOOKAPM */
108641016Sdfr
108741016Sdfr    if (!verbose) {
108841016Sdfr        printf("psm%d: model %s, device ID %d\n",
108941016Sdfr	    unit, model_name(sc->hw.model), sc->hw.hwid);
109041016Sdfr    } else {
109141016Sdfr        printf("psm%d: model %s, device ID %d, %d buttons\n",
109241016Sdfr	    unit, model_name(sc->hw.model), sc->hw.hwid, sc->hw.buttons);
109341016Sdfr	printf("psm%d: config:%08x, flags:%08x, packet size:%d\n",
109441016Sdfr	    unit, sc->config, sc->flags, sc->mode.packetsize);
109541016Sdfr	printf("psm%d: syncmask:%02x, syncbits:%02x\n",
109641016Sdfr	    unit, sc->mode.syncmask[0], sc->mode.syncmask[1]);
109741016Sdfr    }
109841016Sdfr
109941016Sdfr    if (bootverbose)
110041016Sdfr        --verbose;
110141016Sdfr
110243105Sdfr    BUS_READ_IVAR(device_get_parent(dev), dev, KBDC_IVAR_IRQ, &irq);
110343105Sdfr    res = bus_alloc_resource(dev, SYS_RES_IRQ, &zero, irq, irq, 1,
110441181Sdfr			     RF_SHAREABLE | RF_ACTIVE);
110546743Sdfr    BUS_SETUP_INTR(device_get_parent(dev), dev, res, INTR_TYPE_TTY,
110646743Sdfr		   psmintr, sc, &ih);
110741016Sdfr
110841016Sdfr    return (0);
110941016Sdfr}
111041016Sdfr
111141016Sdfrstatic int
111241016Sdfrpsmopen(dev_t dev, int flag, int fmt, struct proc *p)
111341016Sdfr{
111441016Sdfr    int unit = PSM_UNIT(dev);
111541016Sdfr    struct psm_softc *sc;
111641016Sdfr    int command_byte;
111741016Sdfr    int err;
111841016Sdfr    int s;
111941016Sdfr
112041016Sdfr    /* Validate unit number */
112141016Sdfr    if (unit >= NPSM)
112241016Sdfr        return (ENXIO);
112341016Sdfr
112441016Sdfr    /* Get device data */
112541016Sdfr    sc = PSM_SOFTC(unit);
112641016Sdfr    if ((sc == NULL) || (sc->state & PSM_VALID) == 0)
112741016Sdfr	/* the device is no longer valid/functioning */
112841016Sdfr        return (ENXIO);
112941016Sdfr
113041016Sdfr    /* Disallow multiple opens */
113141016Sdfr    if (sc->state & PSM_OPEN)
113241016Sdfr        return (EBUSY);
113341016Sdfr
113441016Sdfr    device_busy(devclass_get_device(psm_devclass, unit));
113541016Sdfr
113641016Sdfr    /* Initialize state */
113741016Sdfr    sc->rsel.si_flags = 0;
113841016Sdfr    sc->rsel.si_pid = 0;
113941016Sdfr    sc->mode.level = sc->dflt_mode.level;
114041016Sdfr    sc->mode.protocol = sc->dflt_mode.protocol;
114141016Sdfr
114241016Sdfr    /* flush the event queue */
114341016Sdfr    sc->queue.count = 0;
114441016Sdfr    sc->queue.head = 0;
114541016Sdfr    sc->queue.tail = 0;
114641016Sdfr    sc->status.flags = 0;
114741016Sdfr    sc->status.button = 0;
114841016Sdfr    sc->status.obutton = 0;
114941016Sdfr    sc->status.dx = 0;
115041016Sdfr    sc->status.dy = 0;
115141016Sdfr    sc->status.dz = 0;
115241016Sdfr    sc->button = 0;
115341016Sdfr
115441016Sdfr    /* empty input buffer */
115541016Sdfr    bzero(sc->ipacket, sizeof(sc->ipacket));
115641016Sdfr    sc->inputbytes = 0;
115741016Sdfr
115841016Sdfr    /* don't let timeout routines in the keyboard driver to poll the kbdc */
115941016Sdfr    if (!kbdc_lock(sc->kbdc, TRUE))
116041016Sdfr	return (EIO);
116141016Sdfr
116241016Sdfr    /* save the current controller command byte */
116341016Sdfr    s = spltty();
116441016Sdfr    command_byte = get_controller_command_byte(sc->kbdc);
116541016Sdfr
116641016Sdfr    /* enable the aux port and temporalily disable the keyboard */
116741016Sdfr    if ((command_byte == -1)
116841016Sdfr        || !set_controller_command_byte(sc->kbdc,
116941016Sdfr	    kbdc_get_device_mask(sc->kbdc),
117041016Sdfr  	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
117141016Sdfr	        | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
117241016Sdfr        /* CONTROLLER ERROR; do you know how to get out of this? */
117341016Sdfr        kbdc_lock(sc->kbdc, FALSE);
117441016Sdfr	splx(s);
117541016Sdfr	log(LOG_ERR, "psm%d: unable to set the command byte (psmopen).\n",
117641016Sdfr	    unit);
117741016Sdfr	return (EIO);
117841016Sdfr    }
117941016Sdfr    /*
118041016Sdfr     * Now that the keyboard controller is told not to generate
118141016Sdfr     * the keyboard and mouse interrupts, call `splx()' to allow
118241016Sdfr     * the other tty interrupts. The clock interrupt may also occur,
118341016Sdfr     * but timeout routines will be blocked by the poll flag set
118441016Sdfr     * via `kbdc_lock()'
118541016Sdfr     */
118641016Sdfr    splx(s);
118741016Sdfr
118841016Sdfr    /* enable the mouse device */
118941016Sdfr    err = doopen(unit, command_byte);
119041016Sdfr
119141016Sdfr    /* done */
119241016Sdfr    if (err == 0)
119341016Sdfr        sc->state |= PSM_OPEN;
119441016Sdfr    kbdc_lock(sc->kbdc, FALSE);
119541016Sdfr    return (err);
119641016Sdfr}
119741016Sdfr
119841016Sdfrstatic int
119941016Sdfrpsmclose(dev_t dev, int flag, int fmt, struct proc *p)
120041016Sdfr{
120141016Sdfr    int unit = PSM_UNIT(dev);
120241016Sdfr    struct psm_softc *sc = PSM_SOFTC(unit);
120341016Sdfr    int stat[3];
120441016Sdfr    int command_byte;
120541016Sdfr    int s;
120641016Sdfr
120741016Sdfr    /* don't let timeout routines in the keyboard driver to poll the kbdc */
120841016Sdfr    if (!kbdc_lock(sc->kbdc, TRUE))
120941016Sdfr	return (EIO);
121041016Sdfr
121141016Sdfr    /* save the current controller command byte */
121241016Sdfr    s = spltty();
121341016Sdfr    command_byte = get_controller_command_byte(sc->kbdc);
121441016Sdfr    if (command_byte == -1) {
121541016Sdfr        kbdc_lock(sc->kbdc, FALSE);
121641016Sdfr	splx(s);
121741016Sdfr	return (EIO);
121841016Sdfr    }
121941016Sdfr
122041016Sdfr    /* disable the aux interrupt and temporalily disable the keyboard */
122141016Sdfr    if (!set_controller_command_byte(sc->kbdc,
122241016Sdfr	    kbdc_get_device_mask(sc->kbdc),
122341016Sdfr  	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
122441016Sdfr	        | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
122541016Sdfr	log(LOG_ERR, "psm%d: failed to disable the aux int (psmclose).\n",
122641016Sdfr	    PSM_UNIT(dev));
122741016Sdfr	/* CONTROLLER ERROR;
122841016Sdfr	 * NOTE: we shall force our way through. Because the only
122941016Sdfr	 * ill effect we shall see is that we may not be able
123041016Sdfr	 * to read ACK from the mouse, and it doesn't matter much
123141016Sdfr	 * so long as the mouse will accept the DISABLE command.
123241016Sdfr	 */
123341016Sdfr    }
123441016Sdfr    splx(s);
123541016Sdfr
123641016Sdfr    /* remove anything left in the output buffer */
123741016Sdfr    empty_aux_buffer(sc->kbdc, 10);
123841016Sdfr
123941016Sdfr    /* disable the aux device, port and interrupt */
124041016Sdfr    if (sc->state & PSM_VALID) {
124141016Sdfr        if (!disable_aux_dev(sc->kbdc)) {
124241016Sdfr	    /* MOUSE ERROR;
124341016Sdfr	     * NOTE: we don't return error and continue, pretending
124441016Sdfr	     * we have successfully disabled the device. It's OK because
124541016Sdfr	     * the interrupt routine will discard any data from the mouse
124641016Sdfr	     * hereafter.
124741016Sdfr	     */
124841016Sdfr	    log(LOG_ERR, "psm%d: failed to disable the device (psmclose).\n",
124941016Sdfr	        PSM_UNIT(dev));
125041016Sdfr        }
125141016Sdfr
125241016Sdfr        if (get_mouse_status(sc->kbdc, stat, 0, 3) < 3)
125341016Sdfr            log(LOG_DEBUG, "psm%d: failed to get status (psmclose).\n",
125441016Sdfr	        PSM_UNIT(dev));
125541016Sdfr    }
125641016Sdfr
125741016Sdfr    if (!set_controller_command_byte(sc->kbdc,
125841016Sdfr	    kbdc_get_device_mask(sc->kbdc),
125941016Sdfr	    (command_byte & KBD_KBD_CONTROL_BITS)
126041016Sdfr	        | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
126141016Sdfr	/* CONTROLLER ERROR;
126241016Sdfr	 * we shall ignore this error; see the above comment.
126341016Sdfr	 */
126441016Sdfr	log(LOG_ERR, "psm%d: failed to disable the aux port (psmclose).\n",
126541016Sdfr	    PSM_UNIT(dev));
126641016Sdfr    }
126741016Sdfr
126841016Sdfr    /* remove anything left in the output buffer */
126941016Sdfr    empty_aux_buffer(sc->kbdc, 10);
127041016Sdfr
127141016Sdfr    /* close is almost always successful */
127241016Sdfr    sc->state &= ~PSM_OPEN;
127341016Sdfr    kbdc_lock(sc->kbdc, FALSE);
127441016Sdfr    device_unbusy(devclass_get_device(psm_devclass, unit));
127541016Sdfr    return (0);
127641016Sdfr}
127741016Sdfr
127841016Sdfrstatic int
127941016Sdfrtame_mouse(struct psm_softc *sc, mousestatus_t *status, unsigned char *buf)
128041016Sdfr{
128141016Sdfr    static unsigned char butmapps2[8] = {
128241016Sdfr        0,
128341016Sdfr        MOUSE_PS2_BUTTON1DOWN,
128441016Sdfr        MOUSE_PS2_BUTTON2DOWN,
128541016Sdfr        MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN,
128641016Sdfr        MOUSE_PS2_BUTTON3DOWN,
128741016Sdfr        MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON3DOWN,
128841016Sdfr        MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN,
128941016Sdfr        MOUSE_PS2_BUTTON1DOWN | MOUSE_PS2_BUTTON2DOWN | MOUSE_PS2_BUTTON3DOWN,
129041016Sdfr    };
129141016Sdfr    static unsigned char butmapmsc[8] = {
129241016Sdfr        MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
129341016Sdfr        MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
129441016Sdfr        MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP,
129541016Sdfr        MOUSE_MSC_BUTTON3UP,
129641016Sdfr        MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP,
129741016Sdfr        MOUSE_MSC_BUTTON2UP,
129841016Sdfr        MOUSE_MSC_BUTTON1UP,
129941016Sdfr        0,
130041016Sdfr    };
130141016Sdfr    int mapped;
130241016Sdfr    int i;
130341016Sdfr
130441016Sdfr    if (sc->mode.level == PSM_LEVEL_BASE) {
130541016Sdfr        mapped = status->button & ~MOUSE_BUTTON4DOWN;
130641016Sdfr        if (status->button & MOUSE_BUTTON4DOWN)
130741016Sdfr	    mapped |= MOUSE_BUTTON1DOWN;
130841016Sdfr        status->button = mapped;
130941016Sdfr        buf[0] = MOUSE_PS2_SYNC | butmapps2[mapped & MOUSE_STDBUTTONS];
131041016Sdfr        i = max(min(status->dx, 255), -256);
131141016Sdfr	if (i < 0)
131241016Sdfr	    buf[0] |= MOUSE_PS2_XNEG;
131341016Sdfr        buf[1] = i;
131441016Sdfr        i = max(min(status->dy, 255), -256);
131541016Sdfr	if (i < 0)
131641016Sdfr	    buf[0] |= MOUSE_PS2_YNEG;
131741016Sdfr        buf[2] = i;
131841016Sdfr	return MOUSE_PS2_PACKETSIZE;
131941016Sdfr    } else if (sc->mode.level == PSM_LEVEL_STANDARD) {
132041016Sdfr        buf[0] = MOUSE_MSC_SYNC | butmapmsc[status->button & MOUSE_STDBUTTONS];
132141016Sdfr        i = max(min(status->dx, 255), -256);
132241016Sdfr        buf[1] = i >> 1;
132341016Sdfr        buf[3] = i - buf[1];
132441016Sdfr        i = max(min(status->dy, 255), -256);
132541016Sdfr        buf[2] = i >> 1;
132641016Sdfr        buf[4] = i - buf[2];
132741016Sdfr        i = max(min(status->dz, 127), -128);
132841016Sdfr        buf[5] = (i >> 1) & 0x7f;
132941016Sdfr        buf[6] = (i - (i >> 1)) & 0x7f;
133041016Sdfr        buf[7] = (~status->button >> 3) & 0x7f;
133141016Sdfr	return MOUSE_SYS_PACKETSIZE;
133241016Sdfr    }
133341016Sdfr    return sc->inputbytes;;
133441016Sdfr}
133541016Sdfr
133641016Sdfrstatic int
133741016Sdfrpsmread(dev_t dev, struct uio *uio, int flag)
133841016Sdfr{
133941016Sdfr    register struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev));
134041016Sdfr    unsigned char buf[PSM_SMALLBUFSIZE];
134141016Sdfr    int error = 0;
134241016Sdfr    int s;
134341016Sdfr    int l;
134441016Sdfr
134541016Sdfr    if ((sc->state & PSM_VALID) == 0)
134641016Sdfr	return EIO;
134741016Sdfr
134841016Sdfr    /* block until mouse activity occured */
134941016Sdfr    s = spltty();
135041016Sdfr    while (sc->queue.count <= 0) {
135141016Sdfr        if (PSM_NBLOCKIO(dev)) {
135241016Sdfr            splx(s);
135341016Sdfr            return EWOULDBLOCK;
135441016Sdfr        }
135541016Sdfr        sc->state |= PSM_ASLP;
135641016Sdfr        error = tsleep((caddr_t) sc, PZERO | PCATCH, "psmrea", 0);
135741016Sdfr        sc->state &= ~PSM_ASLP;
135841016Sdfr        if (error) {
135941016Sdfr            splx(s);
136041016Sdfr            return error;
136141016Sdfr        } else if ((sc->state & PSM_VALID) == 0) {
136241016Sdfr            /* the device disappeared! */
136341016Sdfr            splx(s);
136441016Sdfr            return EIO;
136541016Sdfr	}
136641016Sdfr    }
136741016Sdfr    splx(s);
136841016Sdfr
136941016Sdfr    /* copy data to the user land */
137041016Sdfr    while ((sc->queue.count > 0) && (uio->uio_resid > 0)) {
137141016Sdfr        s = spltty();
137241016Sdfr	l = min(sc->queue.count, uio->uio_resid);
137341016Sdfr	if (l > sizeof(buf))
137441016Sdfr	    l = sizeof(buf);
137541016Sdfr	if (l > sizeof(sc->queue.buf) - sc->queue.head) {
137641016Sdfr	    bcopy(&sc->queue.buf[sc->queue.head], &buf[0],
137741016Sdfr		sizeof(sc->queue.buf) - sc->queue.head);
137841016Sdfr	    bcopy(&sc->queue.buf[0],
137941016Sdfr		&buf[sizeof(sc->queue.buf) - sc->queue.head],
138041016Sdfr		l - (sizeof(sc->queue.buf) - sc->queue.head));
138141016Sdfr	} else {
138241016Sdfr	    bcopy(&sc->queue.buf[sc->queue.head], &buf[0], l);
138341016Sdfr	}
138441016Sdfr	sc->queue.count -= l;
138541016Sdfr	sc->queue.head = (sc->queue.head + l) % sizeof(sc->queue.buf);
138641016Sdfr        splx(s);
138741016Sdfr        error = uiomove(buf, l, uio);
138841016Sdfr        if (error)
138941016Sdfr	    break;
139041016Sdfr    }
139141016Sdfr
139241016Sdfr    return error;
139341016Sdfr}
139441016Sdfr
139541016Sdfrstatic int
139641016Sdfrblock_mouse_data(struct psm_softc *sc, int *c)
139741016Sdfr{
139841016Sdfr    int s;
139941016Sdfr
140041016Sdfr    if (!kbdc_lock(sc->kbdc, TRUE))
140141016Sdfr	return EIO;
140241016Sdfr
140341016Sdfr    s = spltty();
140441016Sdfr    *c = get_controller_command_byte(sc->kbdc);
140541016Sdfr    if ((*c == -1)
140641016Sdfr	|| !set_controller_command_byte(sc->kbdc,
140741016Sdfr	    kbdc_get_device_mask(sc->kbdc),
140841016Sdfr            KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
140941016Sdfr                | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
141041016Sdfr        /* this is CONTROLLER ERROR */
141141016Sdfr	splx(s);
141241016Sdfr        kbdc_lock(sc->kbdc, FALSE);
141341016Sdfr	return EIO;
141441016Sdfr    }
141541016Sdfr
141641016Sdfr    /*
141741016Sdfr     * The device may be in the middle of status data transmission.
141841016Sdfr     * The transmission will be interrupted, thus, incomplete status
141941016Sdfr     * data must be discarded. Although the aux interrupt is disabled
142041016Sdfr     * at the keyboard controller level, at most one aux interrupt
142141016Sdfr     * may have already been pending and a data byte is in the
142241016Sdfr     * output buffer; throw it away. Note that the second argument
142341016Sdfr     * to `empty_aux_buffer()' is zero, so that the call will just
142441016Sdfr     * flush the internal queue.
142541016Sdfr     * `psmintr()' will be invoked after `splx()' if an interrupt is
142641016Sdfr     * pending; it will see no data and returns immediately.
142741016Sdfr     */
142841016Sdfr    empty_aux_buffer(sc->kbdc, 0);	/* flush the queue */
142941016Sdfr    read_aux_data_no_wait(sc->kbdc);	/* throw away data if any */
143041016Sdfr    sc->inputbytes = 0;
143141016Sdfr    splx(s);
143241016Sdfr
143341016Sdfr    return 0;
143441016Sdfr}
143541016Sdfr
143641016Sdfrstatic int
143741016Sdfrunblock_mouse_data(struct psm_softc *sc, int c)
143841016Sdfr{
143941016Sdfr    int error = 0;
144041016Sdfr
144141016Sdfr    /*
144241016Sdfr     * We may have seen a part of status data during `set_mouse_XXX()'.
144341016Sdfr     * they have been queued; flush it.
144441016Sdfr     */
144541016Sdfr    empty_aux_buffer(sc->kbdc, 0);
144641016Sdfr
144741016Sdfr    /* restore ports and interrupt */
144841016Sdfr    if (!set_controller_command_byte(sc->kbdc,
144941016Sdfr            kbdc_get_device_mask(sc->kbdc),
145041016Sdfr	    c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) {
145141016Sdfr        /* CONTROLLER ERROR; this is serious, we may have
145241016Sdfr         * been left with the inaccessible keyboard and
145341016Sdfr         * the disabled mouse interrupt.
145441016Sdfr         */
145541016Sdfr        error = EIO;
145641016Sdfr    }
145741016Sdfr
145841016Sdfr    kbdc_lock(sc->kbdc, FALSE);
145941016Sdfr    return error;
146041016Sdfr}
146141016Sdfr
146241016Sdfrstatic int
146341016Sdfrpsmioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
146441016Sdfr{
146541016Sdfr    struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev));
146641016Sdfr    mousemode_t mode;
146741016Sdfr    mousestatus_t status;
146841016Sdfr#if (defined(MOUSE_GETVARS))
146941016Sdfr    mousevar_t *var;
147041016Sdfr#endif
147141016Sdfr    mousedata_t *data;
147241016Sdfr    int stat[3];
147341016Sdfr    int command_byte;
147441016Sdfr    int error = 0;
147541016Sdfr    int s;
147641016Sdfr
147741016Sdfr    /* Perform IOCTL command */
147841016Sdfr    switch (cmd) {
147941016Sdfr
148041016Sdfr    case OLD_MOUSE_GETHWINFO:
148141016Sdfr	s = spltty();
148241016Sdfr        ((old_mousehw_t *)addr)->buttons = sc->hw.buttons;
148341016Sdfr        ((old_mousehw_t *)addr)->iftype = sc->hw.iftype;
148441016Sdfr        ((old_mousehw_t *)addr)->type = sc->hw.type;
148541016Sdfr        ((old_mousehw_t *)addr)->hwid = sc->hw.hwid;
148641016Sdfr	splx(s);
148741016Sdfr        break;
148841016Sdfr
148941016Sdfr    case MOUSE_GETHWINFO:
149041016Sdfr	s = spltty();
149141016Sdfr        *(mousehw_t *)addr = sc->hw;
149241016Sdfr	if (sc->mode.level == PSM_LEVEL_BASE)
149341016Sdfr	    ((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC;
149441016Sdfr	splx(s);
149541016Sdfr        break;
149641016Sdfr
149741016Sdfr    case OLD_MOUSE_GETMODE:
149841016Sdfr	s = spltty();
149941016Sdfr	switch (sc->mode.level) {
150041016Sdfr	case PSM_LEVEL_BASE:
150141016Sdfr	    ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
150241016Sdfr	    break;
150341016Sdfr	case PSM_LEVEL_STANDARD:
150441016Sdfr	    ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE;
150541016Sdfr	    break;
150641016Sdfr	case PSM_LEVEL_NATIVE:
150741016Sdfr	    ((old_mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
150841016Sdfr	    break;
150941016Sdfr	}
151041016Sdfr        ((old_mousemode_t *)addr)->rate = sc->mode.rate;
151141016Sdfr        ((old_mousemode_t *)addr)->resolution = sc->mode.resolution;
151241016Sdfr        ((old_mousemode_t *)addr)->accelfactor = sc->mode.accelfactor;
151341016Sdfr	splx(s);
151441016Sdfr        break;
151541016Sdfr
151641016Sdfr    case MOUSE_GETMODE:
151741016Sdfr	s = spltty();
151841016Sdfr        *(mousemode_t *)addr = sc->mode;
151941016Sdfr        ((mousemode_t *)addr)->resolution =
152041016Sdfr	    MOUSE_RES_LOW - sc->mode.resolution;
152141016Sdfr	switch (sc->mode.level) {
152241016Sdfr	case PSM_LEVEL_BASE:
152341016Sdfr	    ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
152441016Sdfr	    ((mousemode_t *)addr)->packetsize = MOUSE_PS2_PACKETSIZE;
152541016Sdfr	    break;
152641016Sdfr	case PSM_LEVEL_STANDARD:
152741016Sdfr	    ((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE;
152841016Sdfr	    ((mousemode_t *)addr)->packetsize = MOUSE_SYS_PACKETSIZE;
152941016Sdfr	    ((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK;
153041016Sdfr	    ((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC;
153141016Sdfr	    break;
153241016Sdfr	case PSM_LEVEL_NATIVE:
153341016Sdfr	    /* FIXME: this isn't quite correct... XXX */
153441016Sdfr	    ((mousemode_t *)addr)->protocol = MOUSE_PROTO_PS2;
153541016Sdfr	    break;
153641016Sdfr	}
153741016Sdfr	splx(s);
153841016Sdfr        break;
153941016Sdfr
154041016Sdfr    case OLD_MOUSE_SETMODE:
154141016Sdfr    case MOUSE_SETMODE:
154241016Sdfr	if (cmd == OLD_MOUSE_SETMODE) {
154341016Sdfr	    mode.rate = ((old_mousemode_t *)addr)->rate;
154441016Sdfr	    /*
154541016Sdfr	     * resolution  old I/F   new I/F
154641016Sdfr	     * default        0         0
154741016Sdfr	     * low            1        -2
154841016Sdfr	     * medium low     2        -3
154941016Sdfr	     * medium high    3        -4
155041016Sdfr	     * high           4        -5
155141016Sdfr	     */
155241016Sdfr	    if (((old_mousemode_t *)addr)->resolution > 0)
155341016Sdfr	        mode.resolution = -((old_mousemode_t *)addr)->resolution - 1;
155441016Sdfr	    mode.accelfactor = ((old_mousemode_t *)addr)->accelfactor;
155541016Sdfr	    mode.level = -1;
155641016Sdfr	} else {
155741016Sdfr	    mode = *(mousemode_t *)addr;
155841016Sdfr	}
155941016Sdfr
156041016Sdfr	/* adjust and validate parameters. */
156141016Sdfr	if (mode.rate > UCHAR_MAX)
156241016Sdfr	    return EINVAL;
156341016Sdfr        if (mode.rate == 0)
156441016Sdfr            mode.rate = sc->dflt_mode.rate;
156541016Sdfr	else if (mode.rate == -1)
156641016Sdfr	    /* don't change the current setting */
156741016Sdfr	    ;
156841016Sdfr	else if (mode.rate < 0)
156941016Sdfr	    return EINVAL;
157041016Sdfr	if (mode.resolution >= UCHAR_MAX)
157141016Sdfr	    return EINVAL;
157241016Sdfr	if (mode.resolution >= 200)
157341016Sdfr	    mode.resolution = MOUSE_RES_HIGH;
157441016Sdfr	else if (mode.resolution >= 100)
157541016Sdfr	    mode.resolution = MOUSE_RES_MEDIUMHIGH;
157641016Sdfr	else if (mode.resolution >= 50)
157741016Sdfr	    mode.resolution = MOUSE_RES_MEDIUMLOW;
157841016Sdfr	else if (mode.resolution > 0)
157941016Sdfr	    mode.resolution = MOUSE_RES_LOW;
158041016Sdfr        if (mode.resolution == MOUSE_RES_DEFAULT)
158141016Sdfr            mode.resolution = sc->dflt_mode.resolution;
158241016Sdfr        else if (mode.resolution == -1)
158341016Sdfr	    /* don't change the current setting */
158441016Sdfr	    ;
158541016Sdfr        else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */
158641016Sdfr            mode.resolution = MOUSE_RES_LOW - mode.resolution;
158741016Sdfr	if (mode.level == -1)
158841016Sdfr	    /* don't change the current setting */
158941016Sdfr	    mode.level = sc->mode.level;
159041016Sdfr	else if ((mode.level < PSM_LEVEL_MIN) || (mode.level > PSM_LEVEL_MAX))
159141016Sdfr	    return EINVAL;
159241016Sdfr        if (mode.accelfactor == -1)
159341016Sdfr	    /* don't change the current setting */
159441016Sdfr	    mode.accelfactor = sc->mode.accelfactor;
159541016Sdfr        else if (mode.accelfactor < 0)
159641016Sdfr	    return EINVAL;
159741016Sdfr
159841016Sdfr	/* don't allow anybody to poll the keyboard controller */
159941016Sdfr	error = block_mouse_data(sc, &command_byte);
160041016Sdfr	if (error)
160141016Sdfr            return error;
160241016Sdfr
160341016Sdfr        /* set mouse parameters */
160441016Sdfr	if (mode.rate > 0)
160541016Sdfr	    mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate);
160641016Sdfr	if (mode.resolution >= 0)
160741016Sdfr	    mode.resolution = set_mouse_resolution(sc->kbdc, mode.resolution);
160841016Sdfr	set_mouse_scaling(sc->kbdc, 1);
160941016Sdfr	get_mouse_status(sc->kbdc, stat, 0, 3);
161041016Sdfr
161141016Sdfr        s = spltty();
161241016Sdfr    	sc->mode.rate = mode.rate;
161341016Sdfr    	sc->mode.resolution = mode.resolution;
161441016Sdfr    	sc->mode.accelfactor = mode.accelfactor;
161541016Sdfr    	sc->mode.level = mode.level;
161641016Sdfr        splx(s);
161741016Sdfr
161841016Sdfr	unblock_mouse_data(sc, command_byte);
161941016Sdfr        break;
162041016Sdfr
162141016Sdfr    case MOUSE_GETLEVEL:
162241016Sdfr	*(int *)addr = sc->mode.level;
162341016Sdfr        break;
162441016Sdfr
162541016Sdfr    case MOUSE_SETLEVEL:
162641016Sdfr	if ((*(int *)addr < PSM_LEVEL_MIN) || (*(int *)addr > PSM_LEVEL_MAX))
162741016Sdfr	    return EINVAL;
162841016Sdfr	sc->mode.level = *(int *)addr;
162941016Sdfr        break;
163041016Sdfr
163141016Sdfr    case MOUSE_GETSTATUS:
163241016Sdfr        s = spltty();
163341016Sdfr	status = sc->status;
163441016Sdfr	sc->status.flags = 0;
163541016Sdfr	sc->status.obutton = sc->status.button;
163641016Sdfr	sc->status.button = 0;
163741016Sdfr	sc->status.dx = 0;
163841016Sdfr	sc->status.dy = 0;
163941016Sdfr	sc->status.dz = 0;
164041016Sdfr        splx(s);
164141016Sdfr        *(mousestatus_t *)addr = status;
164241016Sdfr        break;
164341016Sdfr
164441016Sdfr#if (defined(MOUSE_GETVARS))
164541016Sdfr    case MOUSE_GETVARS:
164641016Sdfr	var = (mousevar_t *)addr;
164741016Sdfr	bzero(var, sizeof(*var));
164841016Sdfr	s = spltty();
164941016Sdfr        var->var[0] = MOUSE_VARS_PS2_SIG;
165041016Sdfr        var->var[1] = sc->config;
165141016Sdfr        var->var[2] = sc->flags;
165241016Sdfr	splx(s);
165341016Sdfr        break;
165441016Sdfr
165541016Sdfr    case MOUSE_SETVARS:
165641016Sdfr	return ENODEV;
165741016Sdfr#endif /* MOUSE_GETVARS */
165841016Sdfr
165941016Sdfr    case MOUSE_READSTATE:
166041016Sdfr    case MOUSE_READDATA:
166141016Sdfr	data = (mousedata_t *)addr;
166241016Sdfr	if (data->len > sizeof(data->buf)/sizeof(data->buf[0]))
166341016Sdfr	    return EINVAL;
166441016Sdfr
166541016Sdfr	error = block_mouse_data(sc, &command_byte);
166641016Sdfr	if (error)
166741016Sdfr            return error;
166841016Sdfr        if ((data->len = get_mouse_status(sc->kbdc, data->buf,
166941016Sdfr		(cmd == MOUSE_READDATA) ? 1 : 0, data->len)) <= 0)
167041016Sdfr            error = EIO;
167141016Sdfr	unblock_mouse_data(sc, command_byte);
167241016Sdfr	break;
167341016Sdfr
167441016Sdfr#if (defined(MOUSE_SETRESOLUTION))
167541016Sdfr    case MOUSE_SETRESOLUTION:
167641016Sdfr	mode.resolution = *(int *)addr;
167741016Sdfr	if (mode.resolution >= UCHAR_MAX)
167841016Sdfr	    return EINVAL;
167941016Sdfr	else if (mode.resolution >= 200)
168041016Sdfr	    mode.resolution = MOUSE_RES_HIGH;
168141016Sdfr	else if (mode.resolution >= 100)
168241016Sdfr	    mode.resolution = MOUSE_RES_MEDIUMHIGH;
168341016Sdfr	else if (mode.resolution >= 50)
168441016Sdfr	    mode.resolution = MOUSE_RES_MEDIUMLOW;
168541016Sdfr	else if (mode.resolution > 0)
168641016Sdfr	    mode.resolution = MOUSE_RES_LOW;
168741016Sdfr        if (mode.resolution == MOUSE_RES_DEFAULT)
168841016Sdfr            mode.resolution = sc->dflt_mode.resolution;
168941016Sdfr        else if (mode.resolution == -1)
169041016Sdfr	    mode.resolution = sc->mode.resolution;
169141016Sdfr        else if (mode.resolution < 0) /* MOUSE_RES_LOW/MEDIUM/HIGH */
169241016Sdfr            mode.resolution = MOUSE_RES_LOW - mode.resolution;
169341016Sdfr
169441016Sdfr	error = block_mouse_data(sc, &command_byte);
169541016Sdfr	if (error)
169641016Sdfr            return error;
169741016Sdfr        sc->mode.resolution = set_mouse_resolution(sc->kbdc, mode.resolution);
169841016Sdfr	if (sc->mode.resolution != mode.resolution)
169941016Sdfr	    error = EIO;
170041016Sdfr	unblock_mouse_data(sc, command_byte);
170141016Sdfr        break;
170241016Sdfr#endif /* MOUSE_SETRESOLUTION */
170341016Sdfr
170441016Sdfr#if (defined(MOUSE_SETRATE))
170541016Sdfr    case MOUSE_SETRATE:
170641016Sdfr	mode.rate = *(int *)addr;
170741016Sdfr	if (mode.rate > UCHAR_MAX)
170841016Sdfr	    return EINVAL;
170941016Sdfr        if (mode.rate == 0)
171041016Sdfr            mode.rate = sc->dflt_mode.rate;
171141016Sdfr	else if (mode.rate < 0)
171241016Sdfr	    mode.rate = sc->mode.rate;
171341016Sdfr
171441016Sdfr	error = block_mouse_data(sc, &command_byte);
171541016Sdfr	if (error)
171641016Sdfr            return error;
171741016Sdfr        sc->mode.rate = set_mouse_sampling_rate(sc->kbdc, mode.rate);
171841016Sdfr	if (sc->mode.rate != mode.rate)
171941016Sdfr	    error = EIO;
172041016Sdfr	unblock_mouse_data(sc, command_byte);
172141016Sdfr        break;
172241016Sdfr#endif /* MOUSE_SETRATE */
172341016Sdfr
172441016Sdfr#if (defined(MOUSE_SETSCALING))
172541016Sdfr    case MOUSE_SETSCALING:
172641016Sdfr	if ((*(int *)addr <= 0) || (*(int *)addr > 2))
172741016Sdfr	    return EINVAL;
172841016Sdfr
172941016Sdfr	error = block_mouse_data(sc, &command_byte);
173041016Sdfr	if (error)
173141016Sdfr            return error;
173241016Sdfr        if (!set_mouse_scaling(sc->kbdc, *(int *)addr))
173341016Sdfr	    error = EIO;
173441016Sdfr	unblock_mouse_data(sc, command_byte);
173541016Sdfr        break;
173641016Sdfr#endif /* MOUSE_SETSCALING */
173741016Sdfr
173841016Sdfr#if (defined(MOUSE_GETHWID))
173941016Sdfr    case MOUSE_GETHWID:
174041016Sdfr	error = block_mouse_data(sc, &command_byte);
174141016Sdfr	if (error)
174241016Sdfr            return error;
174341016Sdfr        sc->hw.hwid = get_aux_id(sc->kbdc);
174441016Sdfr	*(int *)addr = sc->hw.hwid;
174541016Sdfr	unblock_mouse_data(sc, command_byte);
174641016Sdfr        break;
174741016Sdfr#endif /* MOUSE_GETHWID */
174841016Sdfr
174941016Sdfr    default:
175041016Sdfr	return ENOTTY;
175141016Sdfr    }
175241016Sdfr
175341016Sdfr    return error;
175441016Sdfr}
175541016Sdfr
175641016Sdfrstatic void
175741016Sdfrpsmintr(void *arg)
175841016Sdfr{
175941016Sdfr    /*
176041016Sdfr     * the table to turn PS/2 mouse button bits (MOUSE_PS2_BUTTON?DOWN)
176141016Sdfr     * into `mousestatus' button bits (MOUSE_BUTTON?DOWN).
176241016Sdfr     */
176341016Sdfr    static int butmap[8] = {
176441016Sdfr        0,
176541016Sdfr	MOUSE_BUTTON1DOWN,
176641016Sdfr	MOUSE_BUTTON3DOWN,
176741016Sdfr	MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
176841016Sdfr	MOUSE_BUTTON2DOWN,
176941016Sdfr	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
177041016Sdfr	MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
177141016Sdfr        MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
177241016Sdfr    };
177341016Sdfr    register struct psm_softc *sc = arg;
177441016Sdfr    mousestatus_t ms;
177541016Sdfr    int x, y, z;
177641016Sdfr    int c;
177741016Sdfr    int l;
177841016Sdfr
177941016Sdfr    /* read until there is nothing to read */
178041016Sdfr    while((c = read_aux_data_no_wait(sc->kbdc)) != -1) {
178141016Sdfr
178241016Sdfr        /* discard the byte if the device is not open */
178341016Sdfr        if ((sc->state & PSM_OPEN) == 0)
178441016Sdfr            continue;
178541016Sdfr
178641016Sdfr        /*
178741016Sdfr	 * Check sync bits. We check for overflow bits and the bit 3
178841016Sdfr	 * for most mice. True, the code doesn't work if overflow
178941016Sdfr	 * condition occurs. But we expect it rarely happens...
179041016Sdfr	 */
179141016Sdfr	if ((sc->inputbytes == 0)
179241016Sdfr		&& ((c & sc->mode.syncmask[0]) != sc->mode.syncmask[1])) {
179341016Sdfr            log(LOG_DEBUG, "psmintr: out of sync (%04x != %04x).\n",
179441016Sdfr		c & sc->mode.syncmask[0], sc->mode.syncmask[1]);
179541016Sdfr            continue;
179641016Sdfr	}
179741016Sdfr
179841016Sdfr        sc->ipacket[sc->inputbytes++] = c;
179941016Sdfr        if (sc->inputbytes < sc->mode.packetsize)
180041016Sdfr	    continue;
180141016Sdfr
180241016Sdfr#if 0
180341016Sdfr        log(LOG_DEBUG, "psmintr: %02x %02x %02x %02x %02x %02x\n",
180441016Sdfr	    sc->ipacket[0], sc->ipacket[1], sc->ipacket[2],
180541016Sdfr	    sc->ipacket[3], sc->ipacket[4], sc->ipacket[5]);
180641016Sdfr#endif
180741016Sdfr
180841016Sdfr	c = sc->ipacket[0];
180941016Sdfr
181041016Sdfr        /*
181141016Sdfr	 * A kludge for Kensington device!
181241016Sdfr	 * The MSB of the horizontal count appears to be stored in
181341016Sdfr	 * a strange place. This kludge doesn't affect other mice
181441016Sdfr	 * because the bit is the overflow bit which is, in most cases,
181541016Sdfr	 * expected to be zero when we reach here. XXX
181641016Sdfr	 */
181741016Sdfr        sc->ipacket[1] |= (c & MOUSE_PS2_XOVERFLOW) ? 0x80 : 0;
181841016Sdfr
181941016Sdfr        /* ignore the overflow bits... */
182041016Sdfr        x = (c & MOUSE_PS2_XNEG) ?  sc->ipacket[1] - 256 : sc->ipacket[1];
182141016Sdfr        y = (c & MOUSE_PS2_YNEG) ?  sc->ipacket[2] - 256 : sc->ipacket[2];
182241016Sdfr	z = 0;
182341016Sdfr        ms.obutton = sc->button;		  /* previous button state */
182441016Sdfr        ms.button = butmap[c & MOUSE_PS2_BUTTONS];
182545789Speter	/* `tapping' action */
182645789Speter	if (sc->config & PSM_CONFIG_FORCETAP)
182745789Speter	    ms.button |= ((c & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN;
182841016Sdfr
182941016Sdfr	switch (sc->hw.model) {
183041016Sdfr
183141016Sdfr	case MOUSE_MODEL_INTELLI:
183241016Sdfr	case MOUSE_MODEL_NET:
183341016Sdfr	    /* wheel data is in the fourth byte */
183441016Sdfr	    z = (char)sc->ipacket[3];
183541016Sdfr	    break;
183641016Sdfr
183741016Sdfr	case MOUSE_MODEL_MOUSEMANPLUS:
183841016Sdfr	    if ((c & ~MOUSE_PS2_BUTTONS) == 0xc8) {
183941016Sdfr		/* the extended data packet encodes button and wheel events */
184041016Sdfr		x = y = 0;
184141016Sdfr		z = (sc->ipacket[1] & MOUSE_PS2PLUS_ZNEG)
184241016Sdfr		    ? (sc->ipacket[2] & 0x0f) - 16 : (sc->ipacket[2] & 0x0f);
184341016Sdfr		ms.button |= (sc->ipacket[2] & MOUSE_PS2PLUS_BUTTON4DOWN)
184441016Sdfr		    ? MOUSE_BUTTON4DOWN : 0;
184541016Sdfr	    } else {
184641016Sdfr		/* preserve button states */
184741016Sdfr		ms.button |= ms.obutton & MOUSE_EXTBUTTONS;
184841016Sdfr	    }
184941016Sdfr	    break;
185041016Sdfr
185141016Sdfr	case MOUSE_MODEL_GLIDEPOINT:
185241016Sdfr	    /* `tapping' action */
185341016Sdfr	    ms.button |= ((c & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN;
185441016Sdfr	    break;
185541016Sdfr
185641016Sdfr	case MOUSE_MODEL_NETSCROLL:
185741016Sdfr	    /* three addtional bytes encode button and wheel events */
185841016Sdfr	    ms.button |= (sc->ipacket[3] & MOUSE_PS2_BUTTON3DOWN)
185941016Sdfr		? MOUSE_BUTTON4DOWN : 0;
186041016Sdfr	    z = (sc->ipacket[3] & MOUSE_PS2_XNEG)
186141016Sdfr		? sc->ipacket[4] - 256 : sc->ipacket[4];
186241016Sdfr	    break;
186341016Sdfr
186441016Sdfr	case MOUSE_MODEL_THINK:
186541016Sdfr	    /* the fourth button state in the first byte */
186641016Sdfr	    ms.button |= (c & MOUSE_PS2_TAP) ? MOUSE_BUTTON4DOWN : 0;
186741016Sdfr	    break;
186841016Sdfr
186941016Sdfr	case MOUSE_MODEL_GENERIC:
187041016Sdfr	default:
187141016Sdfr	    break;
187241016Sdfr	}
187341016Sdfr
187441016Sdfr        /* scale values */
187541016Sdfr        if (sc->mode.accelfactor >= 1) {
187641016Sdfr            if (x != 0) {
187741016Sdfr                x = x * x / sc->mode.accelfactor;
187841016Sdfr                if (x == 0)
187941016Sdfr                    x = 1;
188041016Sdfr                if (c & MOUSE_PS2_XNEG)
188141016Sdfr                    x = -x;
188241016Sdfr            }
188341016Sdfr            if (y != 0) {
188441016Sdfr                y = y * y / sc->mode.accelfactor;
188541016Sdfr                if (y == 0)
188641016Sdfr                    y = 1;
188741016Sdfr                if (c & MOUSE_PS2_YNEG)
188841016Sdfr                    y = -y;
188941016Sdfr            }
189041016Sdfr        }
189141016Sdfr
189241016Sdfr        ms.dx = x;
189341016Sdfr        ms.dy = y;
189441016Sdfr        ms.dz = z;
189541016Sdfr        ms.flags = ((x || y || z) ? MOUSE_POSCHANGED : 0)
189641016Sdfr	    | (ms.obutton ^ ms.button);
189741016Sdfr
189841016Sdfr	if (sc->mode.level < PSM_LEVEL_NATIVE)
189941016Sdfr	    sc->inputbytes = tame_mouse(sc, &ms, sc->ipacket);
190041016Sdfr
190141016Sdfr        sc->status.flags |= ms.flags;
190241016Sdfr        sc->status.dx += ms.dx;
190341016Sdfr        sc->status.dy += ms.dy;
190441016Sdfr        sc->status.dz += ms.dz;
190541016Sdfr        sc->status.button = ms.button;
190641016Sdfr        sc->button = ms.button;
190741016Sdfr
190841016Sdfr        /* queue data */
190941016Sdfr        if (sc->queue.count + sc->inputbytes < sizeof(sc->queue.buf)) {
191041016Sdfr	    l = min(sc->inputbytes, sizeof(sc->queue.buf) - sc->queue.tail);
191141016Sdfr	    bcopy(&sc->ipacket[0], &sc->queue.buf[sc->queue.tail], l);
191241016Sdfr	    if (sc->inputbytes > l)
191341016Sdfr	        bcopy(&sc->ipacket[l], &sc->queue.buf[0], sc->inputbytes - l);
191441016Sdfr            sc->queue.tail =
191541016Sdfr		(sc->queue.tail + sc->inputbytes) % sizeof(sc->queue.buf);
191641016Sdfr            sc->queue.count += sc->inputbytes;
191741016Sdfr	}
191841016Sdfr        sc->inputbytes = 0;
191941016Sdfr
192041016Sdfr        if (sc->state & PSM_ASLP) {
192141016Sdfr            sc->state &= ~PSM_ASLP;
192241016Sdfr            wakeup((caddr_t) sc);
192341016Sdfr    	}
192441016Sdfr        selwakeup(&sc->rsel);
192541016Sdfr    }
192641016Sdfr}
192741016Sdfr
192841016Sdfrstatic int
192941016Sdfrpsmpoll(dev_t dev, int events, struct proc *p)
193041016Sdfr{
193141016Sdfr    struct psm_softc *sc = PSM_SOFTC(PSM_UNIT(dev));
193241016Sdfr    int s;
193341016Sdfr    int revents = 0;
193441016Sdfr
193541016Sdfr    /* Return true if a mouse event available */
193641016Sdfr    s = spltty();
193745789Speter    if (events & (POLLIN | POLLRDNORM)) {
193841016Sdfr	if (sc->queue.count > 0)
193941016Sdfr	    revents |= events & (POLLIN | POLLRDNORM);
194041016Sdfr	else
194141016Sdfr	    selrecord(p, &sc->rsel);
194245789Speter    }
194341016Sdfr    splx(s);
194441016Sdfr
194541016Sdfr    return (revents);
194641016Sdfr}
194741016Sdfr
194841016Sdfr/* vendor/model specific routines */
194941016Sdfr
195041016Sdfrstatic int mouse_id_proc1(KBDC kbdc, int res, int scale, int *status)
195141016Sdfr{
195241016Sdfr    if (set_mouse_resolution(kbdc, res) != res)
195341016Sdfr        return FALSE;
195441016Sdfr    if (set_mouse_scaling(kbdc, scale)
195541016Sdfr	&& set_mouse_scaling(kbdc, scale)
195641016Sdfr	&& set_mouse_scaling(kbdc, scale)
195741016Sdfr	&& (get_mouse_status(kbdc, status, 0, 3) >= 3))
195841016Sdfr	return TRUE;
195941016Sdfr    return FALSE;
196041016Sdfr}
196141016Sdfr
196241016Sdfr#if notyet
196341016Sdfr/* Logitech MouseMan Cordless II */
196441016Sdfrstatic int
196541016Sdfrenable_lcordless(struct psm_softc *sc)
196641016Sdfr{
196741016Sdfr    int status[3];
196841016Sdfr    int ch;
196941016Sdfr
197041016Sdfr    if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 2, status))
197141016Sdfr        return FALSE;
197241016Sdfr    if (status[1] == PSMD_RES_HIGH)
197341016Sdfr	return FALSE;
197441016Sdfr    ch = (status[0] & 0x07) - 1;	/* channel # */
197541016Sdfr    if ((ch <= 0) || (ch > 4))
197641016Sdfr	return FALSE;
197741016Sdfr    /*
197841016Sdfr     * status[1]: always one?
197941016Sdfr     * status[2]: battery status? (0-100)
198041016Sdfr     */
198141016Sdfr    return TRUE;
198241016Sdfr}
198341016Sdfr#endif /* notyet */
198441016Sdfr
198541016Sdfr/* Genius NetScroll Mouse */
198641016Sdfrstatic int
198741016Sdfrenable_groller(struct psm_softc *sc)
198841016Sdfr{
198941016Sdfr    int status[3];
199041016Sdfr
199141016Sdfr    /*
199241016Sdfr     * The special sequence to enable the fourth button and the
199341016Sdfr     * roller. Immediately after this sequence check status bytes.
199441016Sdfr     * if the mouse is NetScroll, the second and the third bytes are
199541016Sdfr     * '3' and 'D'.
199641016Sdfr     */
199741016Sdfr
199841016Sdfr    /*
199941016Sdfr     * If the mouse is an ordinary PS/2 mouse, the status bytes should
200041016Sdfr     * look like the following.
200141016Sdfr     *
200241016Sdfr     * byte 1 bit 7 always 0
200341016Sdfr     *        bit 6 stream mode (0)
200441016Sdfr     *        bit 5 disabled (0)
200541016Sdfr     *        bit 4 1:1 scaling (0)
200641016Sdfr     *        bit 3 always 0
200741016Sdfr     *        bit 0-2 button status
200841016Sdfr     * byte 2 resolution (PSMD_RES_HIGH)
200941016Sdfr     * byte 3 report rate (?)
201041016Sdfr     */
201141016Sdfr
201241016Sdfr    if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status))
201341016Sdfr        return FALSE;
201441016Sdfr    if ((status[1] != '3') || (status[2] != 'D'))
201541016Sdfr        return FALSE;
201641016Sdfr    /* FIXME!! */
201741016Sdfr    sc->hw.buttons = get_mouse_buttons(sc->kbdc);
201841016Sdfr    sc->hw.buttons = 4;
201941016Sdfr    return TRUE;
202041016Sdfr}
202141016Sdfr
202241016Sdfr/* Genius NetMouse/NetMouse Pro */
202341016Sdfrstatic int
202441016Sdfrenable_gmouse(struct psm_softc *sc)
202541016Sdfr{
202641016Sdfr    int status[3];
202741016Sdfr
202841016Sdfr    /*
202941016Sdfr     * The special sequence to enable the middle, "rubber" button.
203041016Sdfr     * Immediately after this sequence check status bytes.
203141016Sdfr     * if the mouse is NetMouse, NetMouse Pro, or ASCII MIE Mouse,
203241016Sdfr     * the second and the third bytes are '3' and 'U'.
203341016Sdfr     * NOTE: NetMouse reports that it has three buttons although it has
203441016Sdfr     * two buttons and a rubber button. NetMouse Pro and MIE Mouse
203541016Sdfr     * say they have three buttons too and they do have a button on the
203641016Sdfr     * side...
203741016Sdfr     */
203841016Sdfr    if (!mouse_id_proc1(sc->kbdc, PSMD_RES_HIGH, 1, status))
203941016Sdfr        return FALSE;
204041016Sdfr    if ((status[1] != '3') || (status[2] != 'U'))
204141016Sdfr        return FALSE;
204241016Sdfr    return TRUE;
204341016Sdfr}
204441016Sdfr
204541016Sdfr/* ALPS GlidePoint */
204641016Sdfrstatic int
204741016Sdfrenable_aglide(struct psm_softc *sc)
204841016Sdfr{
204941016Sdfr    int status[3];
205041016Sdfr
205141016Sdfr    /*
205241016Sdfr     * The special sequence to obtain ALPS GlidePoint specific
205341016Sdfr     * information. Immediately after this sequence, status bytes will
205441016Sdfr     * contain something interesting.
205541016Sdfr     * NOTE: ALPS produces several models of GlidePoint. Some of those
205641016Sdfr     * do not respond to this sequence, thus, cannot be detected this way.
205741016Sdfr     */
205841016Sdfr    if (!mouse_id_proc1(sc->kbdc, PSMD_RES_LOW, 2, status))
205941016Sdfr        return FALSE;
206041016Sdfr    if ((status[0] & 0x10) || (status[1] == PSMD_RES_LOW))
206141016Sdfr        return FALSE;
206241016Sdfr    return TRUE;
206341016Sdfr}
206441016Sdfr
206541016Sdfr/* Kensington ThinkingMouse/Trackball */
206641016Sdfrstatic int
206741016Sdfrenable_kmouse(struct psm_softc *sc)
206841016Sdfr{
206941016Sdfr    static unsigned char rate[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 };
207041016Sdfr    KBDC kbdc = sc->kbdc;
207141016Sdfr    int status[3];
207241016Sdfr    int id1;
207341016Sdfr    int id2;
207441016Sdfr    int i;
207541016Sdfr
207641016Sdfr    id1 = get_aux_id(kbdc);
207741016Sdfr    if (set_mouse_sampling_rate(kbdc, 10) != 10)
207841016Sdfr	return FALSE;
207941016Sdfr    /*
208041016Sdfr     * The device is now in the native mode? It returns a different
208141016Sdfr     * ID value...
208241016Sdfr     */
208341016Sdfr    id2 = get_aux_id(kbdc);
208441016Sdfr    if ((id1 == id2) || (id2 != 2))
208541016Sdfr	return FALSE;
208641016Sdfr
208741016Sdfr    if (set_mouse_resolution(kbdc, PSMD_RES_LOW) != PSMD_RES_LOW)
208841016Sdfr        return FALSE;
208941016Sdfr#if PSM_DEBUG >= 2
209041016Sdfr    /* at this point, resolution is LOW, sampling rate is 10/sec */
209141016Sdfr    if (get_mouse_status(kbdc, status, 0, 3) < 3)
209241016Sdfr        return FALSE;
209341016Sdfr#endif
209441016Sdfr
209541016Sdfr    /*
209641016Sdfr     * The special sequence to enable the third and fourth buttons.
209741016Sdfr     * Otherwise they behave like the first and second buttons.
209841016Sdfr     */
209941016Sdfr    for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) {
210041016Sdfr        if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i])
210141016Sdfr	    return FALSE;
210241016Sdfr    }
210341016Sdfr
210441016Sdfr    /*
210541016Sdfr     * At this point, the device is using default resolution and
210641016Sdfr     * sampling rate for the native mode.
210741016Sdfr     */
210841016Sdfr    if (get_mouse_status(kbdc, status, 0, 3) < 3)
210941016Sdfr        return FALSE;
211041016Sdfr    if ((status[1] == PSMD_RES_LOW) || (status[2] == rate[i - 1]))
211141016Sdfr        return FALSE;
211241016Sdfr
211341016Sdfr    /* the device appears be enabled by this sequence, diable it for now */
211441016Sdfr    disable_aux_dev(kbdc);
211541016Sdfr    empty_aux_buffer(kbdc, 5);
211641016Sdfr
211741016Sdfr    return TRUE;
211841016Sdfr}
211941016Sdfr
212041016Sdfr/* Logitech MouseMan+/FirstMouse+ */
212141016Sdfrstatic int
212241016Sdfrenable_mmanplus(struct psm_softc *sc)
212341016Sdfr{
212441016Sdfr    static char res[] = {
212541016Sdfr	-1, PSMD_RES_LOW, PSMD_RES_HIGH, PSMD_RES_MEDIUM_HIGH,
212641016Sdfr	PSMD_RES_MEDIUM_LOW, -1, PSMD_RES_HIGH, PSMD_RES_MEDIUM_LOW,
212741016Sdfr	PSMD_RES_MEDIUM_HIGH, PSMD_RES_HIGH,
212841016Sdfr    };
212941016Sdfr    KBDC kbdc = sc->kbdc;
213041016Sdfr    int data[3];
213141016Sdfr    int i;
213241016Sdfr
213341016Sdfr    /* the special sequence to enable the fourth button and the roller. */
213441016Sdfr    for (i = 0; i < sizeof(res)/sizeof(res[0]); ++i) {
213541016Sdfr	if (res[i] < 0) {
213641016Sdfr	    if (!set_mouse_scaling(kbdc, 1))
213741016Sdfr		return FALSE;
213841016Sdfr	} else {
213941016Sdfr	    if (set_mouse_resolution(kbdc, res[i]) != res[i])
214041016Sdfr		return FALSE;
214141016Sdfr	}
214241016Sdfr    }
214341016Sdfr
214441016Sdfr    if (get_mouse_status(kbdc, data, 1, 3) < 3)
214541016Sdfr        return FALSE;
214641016Sdfr
214741016Sdfr    /*
214841016Sdfr     * MouseMan+ and FirstMouse+ return following data.
214941016Sdfr     *
215041016Sdfr     * byte 1 0xc8
215141016Sdfr     * byte 2 ?? (MouseMan+:0xc2, FirstMouse+:0xc6)
215241016Sdfr     * byte 3 model ID? MouseMan+:0x50, FirstMouse+:0x51
215341016Sdfr     */
215441016Sdfr    if ((data[0] & ~MOUSE_PS2_BUTTONS) != 0xc8)
215541016Sdfr        return FALSE;
215641016Sdfr
215741016Sdfr    /*
215841016Sdfr     * MouseMan+ (or FirstMouse+) is now in its native mode, in which
215941016Sdfr     * the wheel and the fourth button events are encoded in the
216041016Sdfr     * special data packet. The mouse may be put in the IntelliMouse mode
216141016Sdfr     * if it is initialized by the IntelliMouse's method.
216241016Sdfr     */
216341016Sdfr    return TRUE;
216441016Sdfr}
216541016Sdfr
216641016Sdfr/* MS IntelliMouse */
216741016Sdfrstatic int
216841016Sdfrenable_msintelli(struct psm_softc *sc)
216941016Sdfr{
217041016Sdfr    /*
217141016Sdfr     * Logitech MouseMan+ and FirstMouse+ will also respond to this
217241016Sdfr     * probe routine and act like IntelliMouse.
217341016Sdfr     */
217441016Sdfr
217541016Sdfr    static unsigned char rate[] = { 200, 100, 80, };
217641016Sdfr    KBDC kbdc = sc->kbdc;
217741016Sdfr    int id;
217841016Sdfr    int i;
217941016Sdfr
218041016Sdfr    /* the special sequence to enable the third button and the roller. */
218141016Sdfr    for (i = 0; i < sizeof(rate)/sizeof(rate[0]); ++i) {
218241016Sdfr        if (set_mouse_sampling_rate(kbdc, rate[i]) != rate[i])
218341016Sdfr	    return FALSE;
218441016Sdfr    }
218541016Sdfr    /* the device will give the genuine ID only after the above sequence */
218641016Sdfr    id = get_aux_id(kbdc);
218741016Sdfr    if (id != PSM_INTELLI_ID)
218841016Sdfr	return FALSE;
218941016Sdfr
219041016Sdfr    sc->hw.hwid = id;
219141016Sdfr    sc->hw.buttons = 3;
219241016Sdfr
219341016Sdfr    return TRUE;
219441016Sdfr}
219541016Sdfr
219641016Sdfr#ifdef PSM_HOOKAPM
219741016Sdfrstatic int
219841016Sdfrpsmresume(void *dummy)
219941016Sdfr{
220046763Syokota    struct psm_softc *sc = PSM_SOFTC((int)dummy);
220141016Sdfr    int unit = (int)dummy;
220241016Sdfr    int err = 0;
220341016Sdfr    int s;
220441016Sdfr    int c;
220541016Sdfr
220641016Sdfr    if (verbose >= 2)
220741016Sdfr        log(LOG_NOTICE, "psm%d: APM resume hook called.\n", unit);
220841016Sdfr
220941016Sdfr    /* don't let anybody mess with the aux device */
221041016Sdfr    if (!kbdc_lock(sc->kbdc, TRUE))
221141016Sdfr	return (EIO);
221241016Sdfr    s = spltty();
221341016Sdfr
221441016Sdfr    /* save the current controller command byte */
221541016Sdfr    empty_both_buffers(sc->kbdc, 10);
221641016Sdfr    c = get_controller_command_byte(sc->kbdc);
221741016Sdfr    if (verbose >= 2)
221841016Sdfr        log(LOG_DEBUG, "psm%d: current command byte: %04x (psmresume).\n",
221941016Sdfr	    unit, c);
222041016Sdfr
222141016Sdfr    /* enable the aux port but disable the aux interrupt and the keyboard */
222241016Sdfr    if ((c == -1) || !set_controller_command_byte(sc->kbdc,
222341016Sdfr	    kbdc_get_device_mask(sc->kbdc),
222441016Sdfr  	    KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT
222541016Sdfr	        | KBD_ENABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
222641016Sdfr        /* CONTROLLER ERROR */
222741016Sdfr	splx(s);
222841016Sdfr        kbdc_lock(sc->kbdc, FALSE);
222941016Sdfr	log(LOG_ERR, "psm%d: unable to set the command byte (psmresume).\n",
223041016Sdfr	    unit);
223141016Sdfr	return (EIO);
223241016Sdfr    }
223341016Sdfr
223441016Sdfr    /* flush any data */
223541016Sdfr    if (sc->state & PSM_VALID) {
223641016Sdfr	disable_aux_dev(sc->kbdc);	/* this may fail; but never mind... */
223741016Sdfr	empty_aux_buffer(sc->kbdc, 10);
223841016Sdfr    }
223941016Sdfr    sc->inputbytes = 0;
224041016Sdfr
224141016Sdfr#ifdef PSM_RESETAFTERSUSPEND
224241016Sdfr    /* try to detect the aux device; are you still there? */
224341016Sdfr    if (reinitialize(unit, &sc->mode)) {
224441016Sdfr	/* yes */
224541016Sdfr	sc->state |= PSM_VALID;
224641016Sdfr    } else {
224741016Sdfr	/* the device has gone! */
224841016Sdfr        restore_controller(sc->kbdc, c);
224941016Sdfr	sc->state &= ~PSM_VALID;
225041016Sdfr	log(LOG_ERR, "psm%d: the aux device has gone! (psmresume).\n",
225141016Sdfr	    unit);
225241016Sdfr	err = ENXIO;
225341016Sdfr    }
225441016Sdfr#endif /* PSM_RESETAFTERSUSPEND */
225541016Sdfr    splx(s);
225641016Sdfr
225741016Sdfr    /* restore the driver state */
225841016Sdfr    if ((sc->state & PSM_OPEN) && (err == 0)) {
225941016Sdfr        /* enable the aux device and the port again */
226041016Sdfr	err = doopen(unit, c);
226141016Sdfr	if (err != 0)
226241016Sdfr	    log(LOG_ERR, "psm%d: failed to enable the device (psmresume).\n",
226341016Sdfr		unit);
226441016Sdfr    } else {
226541016Sdfr        /* restore the keyboard port and disable the aux port */
226641016Sdfr        if (!set_controller_command_byte(sc->kbdc,
226741016Sdfr                kbdc_get_device_mask(sc->kbdc),
226841016Sdfr                (c & KBD_KBD_CONTROL_BITS)
226941016Sdfr                    | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) {
227041016Sdfr            /* CONTROLLER ERROR */
227141016Sdfr            log(LOG_ERR, "psm%d: failed to disable the aux port (psmresume).\n",
227241016Sdfr                unit);
227341016Sdfr            err = EIO;
227441016Sdfr	}
227541016Sdfr    }
227641016Sdfr
227741016Sdfr    /* done */
227841016Sdfr    kbdc_lock(sc->kbdc, FALSE);
227941016Sdfr    if ((sc->state & PSM_ASLP) && !(sc->state & PSM_VALID)) {
228041016Sdfr	/*
228141016Sdfr	 * Release the blocked process; it must be notified that the device
228241016Sdfr	 * cannot be accessed anymore.
228341016Sdfr	 */
228441016Sdfr        sc->state &= ~PSM_ASLP;
228541016Sdfr        wakeup((caddr_t)sc);
228641016Sdfr    }
228741016Sdfr
228841016Sdfr    if (verbose >= 2)
228941016Sdfr        log(LOG_DEBUG, "psm%d: APM resume hook exiting.\n", unit);
229041016Sdfr
229141016Sdfr    return (err);
229241016Sdfr}
229341016Sdfr#endif /* PSM_HOOKAPM */
229441016Sdfr
229546635SphkDEV_DRIVER_MODULE(psm, atkbdc, psm_driver, psm_devclass,
229646792Sphk		   CDEV_MAJOR, NOMAJ, psm_cdevsw, 0, 0);
229741016Sdfr
229841016Sdfr#endif /* NPSM > 0 */
2299