Files
Arquivotheca.SunOS-4.1.4/sys/sundev/pi.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

473 lines
9.9 KiB
C

#ifndef lint
static char sccsid[] = "@(#)pi.c 1.1 94/10/31 SMI";
#endif
/*
* Copyright (c) 1987 by Sun Microsystems, Inc.
*/
/*
* Parallel Keyboard & Mouse drivers
* Minor 0 is keyboard, 1 is mouse
* Turns parallel data into a byte stream to be fed (usually)
* into the keyboard or mouse streams modules
*/
#include "pi.h"
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/file.h>
#include <sys/termios.h>
#include <sys/termio.h>
#include <sys/ttold.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/buf.h>
#include <sys/systm.h>
#include <sys/tty.h>
#include <sys/uio.h>
#include <machine/mmu.h>
#include <machine/scb.h>
#include <sun/consdev.h>
#include <sundev/mbvar.h>
#include <sundev/kbd.h>
/*
* Driver information for auto-configuration stuff.
*/
int piprobe();
struct mb_driver pidriver = {
piprobe, 0, 0, 0, 0, 0,
0, "pi", 0, 0, 0, 0,
};
/*
* One structure for keyboard, one for mouse.
*/
struct pi_softc {
int pi_isopen; /* 1 if open */
struct pireg *pi_addr; /* address of the interface */
queue_t *pi_readq; /* read queue for this device */
queue_t *pi_writeq; /* write queue for this device */
mblk_t *pi_iocpending; /* ioctl reply pending successful allocation */
int pi_wbufcid; /* id of pending write-side bufcall */
} pisoftc[2];
struct pireg {
u_char pi_kbd;
char pi_mouse; /* signed mouse delta or buttons */
};
struct pireg *piaddr, pilast; /* used by low level clock intr */
static int piopen();
static int piclose();
static int piwput();
static struct module_info pim_info = {
0,
"pi",
0,
INFPSZ,
2048,
128
};
static struct qinit pirinit = {
NULL,
NULL,
piopen,
piclose,
NULL,
&pim_info,
NULL
};
static struct qinit piwinit = {
piwput,
NULL,
NULL,
NULL,
NULL,
&pim_info,
NULL
};
static char *pimodlist[] = {
"ldterm",
"ttcompat",
NULL
};
struct streamtab piinfo = {
&pirinit,
&piwinit,
NULL,
NULL,
pimodlist
};
static void piioctl();
/*
* Probe for keyboard and mouse
* Diagnostics for unconnected keyboard or unpowered mouse
*/
piprobe(reg, unit)
caddr_t reg;
int unit;
{
register struct pireg *pi;
register int i;
if (unit)
return (0);
if (peek((short *)reg) == -1)
return (0);
pi = (struct pireg *)reg;
for (i=0; i<100000; i++)
if (pi->pi_kbd != 0xFF)
break;
pisoftc[0].pi_addr = (struct pireg *)reg;
pisoftc[1].pi_addr = (struct pireg *)reg;
piaddr = pi;
return (1);
}
/*
* Open keyboard or mouse - enable call to piintr
*/
/*ARGSUSED*/
static int
piopen(q, dev, flag, sflag)
register queue_t *q;
dev_t dev;
int flag;
{
int clkpiscan();
register struct pi_softc *sc;
register struct pireg *pi;
if (minor(dev) > 1)
return (OPENFAIL);
sc = &pisoftc[minor(dev)];
if (sc->pi_addr == 0)
return (OPENFAIL);
pi = sc->pi_addr;
if (minor(dev) == 0 && pi->pi_kbd == 0xFF)
printf("keyboard not connected\n");
if (minor(dev) == 1 && (pi->pi_mouse & 1) == 0)
printf("no mouse power jumper on CPU parallel port\n");
if (flag & FWRITE) {
u.u_error = EINVAL;
return (OPENFAIL);
}
scb.scb_autovec[5-1] = clkpiscan;
start_piscan_clock();
sc->pi_iocpending = NULL;
sc->pi_wbufcid = 0;
sc->pi_readq = q;
sc->pi_writeq = WR(q);
q->q_ptr = WR(q)->q_ptr = (caddr_t)sc;
sc->pi_isopen = 1;
return (dev);
}
/*
* Close keyboard or mouse
* We never disable clkpiscan because its too hard to synchronize
* all the opens and closes
*/
/*ARGSUSED*/
static int
piclose(q)
register queue_t *q;
{
register struct pi_softc *sc;
register mblk_t *mp;
sc = (struct pi_softc *)q->q_ptr;
sc->pi_isopen = 0;
/*
* Release pending "ioctl".
*/
if ((mp = sc->pi_iocpending) != NULL) {
sc->pi_iocpending = NULL;
freemsg(mp);
}
/*
* Cancel outstanding "bufcall" request.
*/
if (sc->pi_wbufcid) {
unbufcall(sc->pi_wbufcid);
sc->pi_wbufcid = 0;
}
q->q_ptr = WR(q)->q_ptr = NULL;
}
/*
* Put procedure for write queue.
* Respond to M_IOCTL and M_FLUSH messages here; discard everything else.
*/
static int
piwput(q, mp)
register queue_t *q;
register mblk_t *mp;
{
register struct pi_softc *sc;
register int s;
sc = (struct pi_softc *)q->q_ptr;
switch (mp->b_datap->db_type) {
case M_IOCTL:
piioctl(sc, q, mp);
break;
case M_FLUSH:
if (*mp->b_rptr & FLUSHW)
*mp->b_rptr &= ~FLUSHW; /* nothing to flush */
if (*mp->b_rptr & FLUSHR) {
s = spl5();
flushq(RD(q), FLUSHDATA);
(void) splx(s);
qreply(q, mp); /* give the read queues a crack at it */
} else
freemsg(mp);
break;
default:
/*
* "No, I don't want a subscription to Chain Store Age,
* thank you anyway."
*/
freemsg(mp);
break;
}
}
/*
* Retry an "ioctl", now that "bufcall" claims we may be able to allocate
* the buffer we need.
*/
static int
pireioctl(unit)
long unit;
{
register struct pi_softc *sc = &pisoftc[unit];
queue_t *q;
register mblk_t *mp;
/*
* The bufcall is no longer pending.
*/
sc->pi_wbufcid = 0;
if ((q = sc->pi_writeq) == NULL)
return;
if ((mp = sc->pi_iocpending) != NULL) {
sc->pi_iocpending = NULL; /* not pending any more */
piioctl(sc, q, mp);
}
}
/*
* Process an "ioctl" message sent down to us.
*/
/*ARGSUSED*/
static void
piioctl(sc, q, mp)
register struct pi_softc *sc;
queue_t *q;
register mblk_t *mp;
{
register struct iocblk *iocp;
int s;
if (sc->pi_iocpending != NULL) {
/*
* We were holding an "ioctl" response pending the
* availability of an "mblk" to hold data to be passed up;
* another "ioctl" came through, which means that "ioctl"
* must have timed out or been aborted.
*/
freemsg(sc->pi_iocpending);
sc->pi_iocpending = NULL;
}
iocp = (struct iocblk *)mp->b_rptr;
/*
* Turn the ioctl message into an ioctl ACK message.
*/
mp->b_datap->db_type = M_IOCACK;
switch (iocp->ioc_cmd) {
case TCGETS: {
register struct termios *cb;
register mblk_t *datap;
if ((datap = allocb(sizeof (struct termios), BPRI_HI)) == NULL)
goto allocfailure;
cb = (struct termios *)datap->b_wptr;
/*
* The only information we supply is the cflag word.
* Our copy of the iflag word is just that, a copy.
*/
bzero((caddr_t) cb, sizeof (struct termios));
cb->c_cflag = 0; /* we don't have a speed */
datap->b_wptr += (sizeof *cb)/(sizeof *datap->b_wptr);
iocp->ioc_count = sizeof (struct termios);
if (mp->b_cont != NULL)
freemsg(mp->b_cont);
mp->b_cont = datap;
break;
}
case TCSETSF:
case TCSETSW:
case TCSETS:
/*
* Do nothing, just ack, so the keyboard and mouse code
* above us doesn't have a canary.
*/
iocp->ioc_count = 0;
break;
default:
/*
* Yeah, yeah, we know, we know, S5 returns EINVAL.
* So sue us.
*/
iocp->ioc_count = 0;
iocp->ioc_error = ENOTTY;
mp->b_datap->db_type = M_IOCNAK;
break;
}
qreply(q, mp);
return;
allocfailure:
/*
* We needed to allocate something to handle this "ioctl", but
* couldn't; save this "ioctl" and arrange to get called back when
* it's more likely that we can get what we need.
* If there's already one being saved, throw it out, since it
* must have timed out.
* Call "bufcall" to request that we be called back when we
* stand a better chance of allocating the data.
*/
s = splstr();
if (sc->pi_iocpending != NULL)
freemsg(sc->pi_iocpending);
sc->pi_iocpending = mp; /* hold this ioctl */
/*
* Arrange to get called back only if there isn't an outstanding
* "bufcall" request since the allocation size is constant.
*/
if (sc->pi_wbufcid == 0)
sc->pi_wbufcid = bufcall(sizeof (struct termios), BPRI_HI,
pireioctl, (long)(sc - &pisoftc[0]));
(void) splx(s);
}
/*
* Mouse:
* The high 4 bits have value 8 for a button sample,
* otherwise the high 7 bits are a (signed) delta.
* Since we can't tell the difference between a delta X
* and a delta Y, we use the delta before the button sample as
* the delta Y, and the delta after the button push as the delta X.
* We re-arrange the order to button, deltaX, deltaY for the sake
* of the common mouse code.
*/
piintr()
{
register char m;
register short limit = 10;
short samp;
register struct pireg *pi = piaddr;
register struct pi_softc *sc;
static char button, lastbutton, deltay = 0;
static enum { DELY, BUTTON, DELX } state = DELY;
register mblk_t *bp;
/* read and wait for two reads to concur in case sample is changing */
samp = *(short *)pi;
while (--limit > 0) {
if (samp == *(short *)pi)
goto found;
samp = *(short *)pi;
}
return; /* hopeless confusion */
found:
pi = (struct pireg *)&samp;
if (pi->pi_kbd != pilast.pi_kbd) {
pilast.pi_kbd = pi->pi_kbd;
sc = &pisoftc[0];
if (sc->pi_isopen) {
if ((bp = allocb(1, BPRI_MED)) != NULL) {
if (!canput(sc->pi_readq->q_next))
freemsg(bp);
else {
*bp->b_wptr++ = pi->pi_kbd;
putnext(sc->pi_readq, bp);
}
}
}
}
if (pi->pi_mouse == pilast.pi_mouse) /* wait for sample to change */
return;
pilast.pi_mouse = pi->pi_mouse;
m = pi->pi_mouse;
sc = &pisoftc[1];
if (!(sc->pi_isopen)) /* mouse not active */
return;
if ((m & 0xf0) == 0x80) {
state = BUTTON;
button = 0x80 | ((m>>1)&7);
} else
m >>= 1;
switch (state) {
case DELY: /* delta Y until we see buttons */
deltay = m;
break;
case BUTTON: /* delta X immediately after we see buttons */
state = DELX;
break;
case DELX:
state = DELY;
if (button == lastbutton && m == 0 && deltay == 0)
break; /* no new info */
lastbutton = button;
if ((bp = allocb(5, BPRI_MED)) != NULL) {
if (!canput(sc->pi_readq->q_next))
freemsg(bp);
else {
*bp->b_wptr++ = button;
*bp->b_wptr++ = m;
*bp->b_wptr++ = deltay;
/*
* M3 mice used on Sun-3s transmit a five-byte
* packet: button, dx, dy, dx, dy. Let's make
* our mice look as much like an M3 as
* possible, so the mouse input routine
* upstream won't choke. See sundev/ms.c for
* details.
*/
*bp->b_wptr++ = '\0';
*bp->b_wptr++ = '\0';
putnext(sc->pi_readq, bp);
}
}
deltay = m; /* in case next delta y equals this delta x */
break;
}
}