* Copyright (c) 1992, 1993 Erik Forsberg.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* Ported to 386bsd Oct 17, 1992
* Sandi Donno, Computer Science, University of Cape Town, South Africa
* Please send bug reports to sandi@cs.uct.ac.za
* Thanks are also due to Rick Macklem, rick@snowhite.cis.uoguelph.ca -
* although I was only partially successful in getting the alpha release
* of his "driver for the Logitech and ATI Inport Bus mice for use with
* 386bsd and the X386 port" to work with my Microsoft mouse, I nevertheless
* found his code to be an invaluable reference when porting this driver
* Further modifications for latest 386BSD+patchkit and port to NetBSD,
* Andrew Herbert <andrew@werple.apana.org.au> - 8 June 1993
* Cloned from the Microsoft Bus Mouse driver, also by Erik Forsberg, by
* Andrew Herbert - 12 June 1993
* Modified for PS/2 mouse by Charles Hannum <mycroft@ai.mit.edu>
* Modified for PS/2 AUX mouse by Shoji Yuen <yuen@nuie.nagoya-u.ac.jp>
#include "i386/include/mouse.h"
#include "i386/include/pio.h" /* Julian's fast IO macros */
#include "i386/isa/isa_device.h"
#include "syslog.h" /* For debugging */
#define DATA 0 /* Offset for data port, read-write */
#define CNTRL 4 /* Offset for control port, write-only */
#define STATUS 4 /* Offset for status port, read-only */
#define PSM_OUTPUT_ACK 0x02 /* output acknowledge */
/* controller commands */
#define PSM_ENABLE 0xa8 /* enable auxiliary port */
#define PSM_DISABLE 0xa7 /* disable auxiliary port */
#define PSM_INT_ENABLE 0x47 /* enable controller interrupts */
#define PSM_INT_DISABLE 0x65 /* disable controller interrupts */
#define PSM_SET_SCALE11 0xe6 /* set 1:1 scaling */
#define PSM_SET_SCALE21 0xe7 /* set 2:1 scaling */
#define PSM_SET_RES 0xe8 /* set resolution */
#define PSM_GET_SCALE 0xe9 /* set scaling factor */
#define PSM_SET_STREAM 0xea /* set streaming mode */
#define PSM_SET_SAMPLE 0xf3 /* set sampling rate */
#define PSM_DEV_ENABLE 0xf4 /* mouse on */
#define PSM_DEV_DISABLE 0xf5 /* mouse off */
#define PSM_RESET 0xff /* reset */
#define PSMUNIT(dev) (minor(dev) >> 1)
#define min(x,y) (x < y ? x : y)
int psmprobe (struct isa_device
*);
int psmattach (struct isa_device
*);
void psm_poll_status(void);
static int psmaddr
[NPSM
]; /* Base I/O port addresses per unit */
#define MSBSZ 1024 /* Output queue size (pwr of 2 is best) */
static struct psm_softc
{ /* Driver status information */
struct ringbuf inq
; /* Input queue */
pid_t rsel
; /* Process selecting for Input */
unsigned char state
; /* Mouse driver state */
unsigned char status
; /* Mouse button status */
unsigned char button
; /* Previous mouse button status bits */
int x
, y
; /* accumulated motion in the X,Y axis */
#define OPEN 1 /* Device is open */
#define ASLP 2 /* Waiting for mouse data */
struct isa_driver psmdriver
= { psmprobe
, psmattach
, "psm" };
#define AUX_PORT 0x60 /* AUX_PORT base (S.Yuen) */
static void psm_write_dev(int inport
, u_char value
)
outb(inport
+CNTRL
, 0xd4);
static inline void psm_command(int ioport
, u_char value
)
outb(ioport
+CNTRL
, 0x60);
outb(ioport
+DATA
, value
);
int psmprobe(struct isa_device
*dvp
)
/* XXX: Needs a real probe routine. */
psm_write_dev(ioport
,0xff); /* Reset aux device */
/* printf("PS/2 AUX mouse is not found\n");*/
psm_command(ioport
,0x65);
psmaddr
[unit
] = 0; /* Device not found */
/* printf("PS/2 AUX mouse found. Installing driver\n");*/
int psmattach(struct isa_device
*dvp
)
int ioport
= dvp
->id_iobase
;
struct psm_softc
*sc
= &psm_softc
[unit
];
/* Save I/O base address */
/* Disable mouse interrupts */
outb(ioport
+CNTRL
, PSM_ENABLE
);
psm_write(ioport
, PSM_SET_RES
);
psm_write(ioport
, 0x03); /* 8 counts/mm */
psm_write(ioport
, PSM_SET_SCALE
);
psm_write(ioport
, 0x02); /* 2:1 */
psm_write(ioport
, PSM_SET_SCALE21
);
psm_write(ioport
, PSM_SET_SAMPLE
);
psm_write(ioport
, 0x64); /* 100 samples/sec */
psm_write(ioport
, PSM_SET_STREAM
);
outb(ioport
+CNTRL
, PSM_DISABLE
);
psm_command(ioport
, PSM_INT_DISABLE
);
/* Setup initial state */
int psmopen(dev_t dev
, int flag
, int fmt
, struct proc
*p
)
/* Validate unit number */
/* If device does not exist */
/* Disallow multiple opens */
/* Allocate and initialize a ring buffer */
sc
->inq
.count
= sc
->inq
.first
= sc
->inq
.last
= 0;
/* Enable Bus Mouse interrupts */
psm_write_dev(ioport
, PSM_DEV_ENABLE
);
outb(ioport
+CNTRL
, PSM_ENABLE
);
psm_command(ioport
, PSM_INT_ENABLE
);
void psm_poll_status(void)
while(inb(AUX_PORT
+STATUS
)&0x03) {
if(inb(AUX_PORT
+STATUS
) & 0x2 == 0x2)
int psmclose(dev_t dev
, int flag
, int fmt
, struct proc
*p
)
/* Get unit and associated info */
/* Disable further mouse interrupts */
psm_command(ioport
,PSM_INT_DISABLE
);
outb(ioport
+CNTRL
,PSM_DISABLE
);
/* close is almost always successful */
int psmread(dev_t dev
, struct uio
*uio
, int flag
)
int error
= 0; /* keep compiler quiet, even though initialisation
unsigned char buffer
[100];
/* Get device information */
sc
= &psm_softc
[PSMUNIT(dev
)];
/* Block until mouse activity occured */
while (sc
->inq
.count
== 0) {
error
= tsleep((caddr_t
)sc
, PZERO
| PCATCH
, "psmrea", 0);
/* Transfer as many chunks as possible */
while (sc
->inq
.count
> 0 && uio
->uio_resid
> 0) {
length
= min(sc
->inq
.count
, uio
->uio_resid
);
if (length
> sizeof(buffer
))
/* Remove a small chunk from input queue */
if (sc
->inq
.first
+ length
>= MSBSZ
) {
bcopy(&sc
->inq
.queue
[sc
->inq
.first
],
buffer
, MSBSZ
- sc
->inq
.first
);
bcopy(sc
->inq
.queue
, &buffer
[MSBSZ
-sc
->inq
.first
],
length
- (MSBSZ
- sc
->inq
.first
));
bcopy(&sc
->inq
.queue
[sc
->inq
.first
], buffer
, length
);
sc
->inq
.first
= (sc
->inq
.first
+ length
) % MSBSZ
;
/* Copy data to user process */
error
= uiomove(buffer
, length
, uio
);
/* Allow interrupts again */
int psmioctl(dev_t dev
, caddr_t addr
, int cmd
, int flag
, struct proc
*p
)
/* Get device information */
sc
= &psm_softc
[PSMUNIT(dev
)];
/* Perform IOCTL command */
/* Don't modify info while calculating */
/* Build mouse status octet */
info
.status
= sc
->status
;
/* Encode X and Y motion as good as we can */
/* Reset historical information */
sc
->status
&= ~BUTCHNGMASK
;
/* Allow interrupts and copy result buffer */
error
= copyout(&info
, addr
, sizeof(struct mouseinfo
));
struct psm_softc
*sc
= &psm_softc
[unit
];
int ioport
= psmaddr
[unit
];
sc
->inq
.queue
[sc
->inq
.last
++ % MSBSZ
] = inb(ioport
+DATA
);
if (sc
-> state
& ASLP
) {
int psmselect(dev_t dev
, int rw
, struct proc
*p
)
struct psm_softc
*sc
= &psm_softc
[PSMUNIT(dev
)];
/* Silly to select for output */
/* Return true if a mouse event available */