NetBSD/sys/arch/hp300/dev/hil.c
1994-02-10 14:55:29 +00:00

1609 lines
38 KiB
C

/*
* Copyright (c) 1988 University of Utah.
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* Science Department.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE REGENTS OR CONTRIBUTORS 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.
*
* from: Utah Hdr: hil.c 1.33 89/12/22
* from: @(#)hil.c 7.8.1.1 (Berkeley) 6/28/91
* $Id: hil.c,v 1.10 1994/02/10 14:57:57 mycroft Exp $
*/
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/select.h>
#include <sys/tty.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <hp300/dev/hilreg.h>
#include <hp300/dev/hilioctl.h>
#include <hp300/dev/hilvar.h>
#include <hp300/dev/kbdmap.h>
#include <machine/cpu.h>
#include <vm/vm_param.h>
#include <vm/vm_map.h>
#include <vm/vm_kern.h>
#include <vm/vm_page.h>
#include <vm/vm_pager.h>
struct hilloop hil0;
struct _hilbell default_bell = { BELLDUR, BELLFREQ };
#ifdef DEBUG
int hildebug = 0;
#define HDB_FOLLOW 0x01
#define HDB_MMAP 0x02
#define HDB_MASK 0x04
#define HDB_CONFIG 0x08
#define HDB_KEYBOARD 0x10
#define HDB_IDMODULE 0x20
#define HDB_EVENTS 0x80
#endif
/* symbolic sleep message strings */
char hilin[] = "hilin";
/* XXX see function definition below */
#define hilq_to_b q_to_b
hilinit()
{
register struct hilloop *hilp = &hil0; /* XXX */
register int i;
/*
* Initialize loop information
*/
hilp->hl_addr = HILADDR;
hilp->hl_cmdending = FALSE;
hilp->hl_actdev = hilp->hl_cmddev = 0;
hilp->hl_cmddone = FALSE;
hilp->hl_cmdbp = hilp->hl_cmdbuf;
hilp->hl_pollbp = hilp->hl_pollbuf;
hilp->hl_kbddev = 0;
hilp->hl_kbdlang = KBD_DEFAULT;
hilp->hl_kbdflags = 0;
/*
* Clear all queues and device associations with queues
*/
for (i = 0; i < NHILQ; i++) {
hilp->hl_queue[i].hq_eventqueue = NULL;
hilp->hl_queue[i].hq_procp = NULL;
hilp->hl_queue[i].hq_devmask = 0;
}
for (i = 0; i < NHILD; i++)
hilp->hl_device[i].hd_qmask = 0;
hilp->hl_device[HILLOOPDEV].hd_flags = (HIL_ALIVE|HIL_PSEUDO);
/*
* Reset the loop hardware, and collect keyboard/id info
*/
hilreset(hilp);
hilinfo(hilp);
kbdenable();
}
/* ARGSUSED */
hilopen(dev, flags, mode, p)
dev_t dev;
int flags, mode;
struct proc *p;
{
register struct hilloop *hilp = &hil0; /* XXX */
register struct hilloopdev *dptr;
u_char device = HILUNIT(dev);
#ifdef DEBUG
if (hildebug & HDB_FOLLOW)
printf("hilopen(%d): device %x\n", p->p_pid, device);
#endif
if ((hilp->hl_device[HILLOOPDEV].hd_flags & HIL_ALIVE) == 0)
return(ENXIO);
dptr = &hilp->hl_device[device];
if ((dptr->hd_flags & HIL_ALIVE) == 0)
return(ENODEV);
/*
* Pseudo-devices cannot be read, nothing more to do.
*/
if (dptr->hd_flags & HIL_PSEUDO)
return(0);
/*
* Open semantics:
* 1. Open devices have only one of HIL_READIN/HIL_QUEUEIN.
* 2. HPUX processes always get read syscall interface and
* must have exclusive use of the device.
* 3. BSD processes default to shared queue interface.
* Multiple processes can open the device.
*/
if (p->p_flag & SHPUX) {
if (dptr->hd_flags & (HIL_READIN|HIL_QUEUEIN))
return(EBUSY);
dptr->hd_flags |= HIL_READIN;
} else {
if (dptr->hd_flags & HIL_READIN)
return(EBUSY);
dptr->hd_flags |= HIL_QUEUEIN;
}
if (flags & FNONBLOCK)
dptr->hd_flags |= HIL_NOBLOCK;
/*
* It is safe to flush the read buffer as we are guarenteed
* that no one else is using it.
*/
ndflush(&dptr->hd_queue, dptr->hd_queue.c_cc);
send_hil_cmd(hilp->hl_addr, HIL_INTON, NULL, 0, NULL);
/*
* Opened the keyboard, put in raw mode.
*/
(void) splhil();
if (device == hilp->hl_kbddev) {
u_char mask = 0;
send_hil_cmd(hilp->hl_addr, HIL_WRITEKBDSADR, &mask, 1, NULL);
hilp->hl_kbdflags |= KBD_RAW;
#ifdef DEBUG
if (hildebug & HDB_KEYBOARD)
printf("hilopen: keyboard %d raw\n", hilp->hl_kbddev);
#endif
}
(void) spl0();
return (0);
}
/* ARGSUSED */
hilclose(dev, flags)
dev_t dev;
{
struct proc *p = curproc; /* XXX */
register struct hilloop *hilp = &hil0; /* XXX */
register struct hilloopdev *dptr;
register int i;
u_char device = HILUNIT(dev);
char mask, lpctrl;
#ifdef DEBUG
if (hildebug & HDB_FOLLOW)
printf("hilclose(%d): device %x\n", p->p_pid, device);
#endif
dptr = &hilp->hl_device[device];
if (device && (dptr->hd_flags & HIL_PSEUDO))
return (0);
if ((p->p_flag & SHPUX) == 0) {
/*
* If this is the loop device,
* free up all queues belonging to this process.
*/
if (device == 0) {
for (i = 0; i < NHILQ; i++)
if (hilp->hl_queue[i].hq_procp == p)
(void) hilqfree(i);
} else {
mask = ~hildevmask(device);
(void) splhil();
for (i = 0; i < NHILQ; i++)
if (hilp->hl_queue[i].hq_procp == p) {
dptr->hd_qmask &= ~hilqmask(i);
hilp->hl_queue[i].hq_devmask &= mask;
}
(void) spl0();
}
}
/*
* Always flush the read buffer
*/
dptr->hd_flags &= ~(HIL_QUEUEIN|HIL_READIN|HIL_NOBLOCK);
ndflush(&dptr->hd_queue, dptr->hd_queue.c_cc);
/*
* Set keyboard back to cooked mode when closed.
*/
(void) splhil();
if (device && device == hilp->hl_kbddev) {
mask = 1 << (hilp->hl_kbddev - 1);
send_hil_cmd(hilp->hl_addr, HIL_WRITEKBDSADR, &mask, 1, NULL);
hilp->hl_kbdflags &= ~(KBD_RAW|KBD_AR1|KBD_AR2);
/*
* XXX: We have had trouble with keyboards remaining raw
* after close due to the LPC_KBDCOOK bit getting cleared
* somewhere along the line. Hence we check and reset
* LPCTRL if necessary.
*/
send_hil_cmd(hilp->hl_addr, HIL_READLPCTRL, NULL, 0, &lpctrl);
if ((lpctrl & LPC_KBDCOOK) == 0) {
printf("hilclose: bad LPCTRL %x, reset to %x\n",
lpctrl, lpctrl|LPC_KBDCOOK);
lpctrl |= LPC_KBDCOOK;
send_hil_cmd(hilp->hl_addr, HIL_WRITELPCTRL,
&lpctrl, 1, NULL);
}
#ifdef DEBUG
if (hildebug & HDB_KEYBOARD)
printf("hilclose: keyboard %d cooked\n",
hilp->hl_kbddev);
#endif
kbdenable();
}
(void) spl0();
return (0);
}
/*
* Read interface to HIL device.
*/
hilread(dev, uio)
dev_t dev;
register struct uio *uio;
{
struct hilloop *hilp = &hil0; /* XXX */
register struct hilloopdev *dptr;
register int cc;
u_char device = HILUNIT(dev);
char buf[HILBUFSIZE];
int error;
#if 0
/*
* XXX: Don't do this since HP-UX doesn't.
*
* Check device number.
* This check is necessary since loop can reconfigure.
*/
if (device > hilp->hl_maxdev)
return(ENODEV);
#endif
dptr = &hilp->hl_device[device];
if ((dptr->hd_flags & HIL_READIN) == 0)
return(ENODEV);
(void) splhil();
while (dptr->hd_queue.c_cc == 0) {
if (dptr->hd_flags & HIL_NOBLOCK) {
spl0();
return(EWOULDBLOCK);
}
dptr->hd_flags |= HIL_ASLEEP;
if (error = tsleep((caddr_t)dptr, TTIPRI | PCATCH, hilin, 0)) {
(void) spl0();
return (error);
}
}
(void) spl0();
error = 0;
while (uio->uio_resid > 0 && error == 0) {
cc = hilq_to_b(&dptr->hd_queue, buf,
MIN(uio->uio_resid, HILBUFSIZE));
if (cc <= 0)
break;
error = uiomove(buf, cc, uio);
}
return(error);
}
hilioctl(dev, cmd, data, flag, p)
dev_t dev;
int cmd;
caddr_t data;
int flag;
struct proc *p;
{
register struct hilloop *hilp = &hil0; /* XXX */
char device = HILUNIT(dev);
struct hilloopdev *dptr;
register int i;
u_char hold;
int error;
#ifdef DEBUG
if (hildebug & HDB_FOLLOW)
printf("hilioctl(%d): dev %x cmd %x\n",
p->p_pid, device, cmd);
#endif
dptr = &hilp->hl_device[device];
if ((dptr->hd_flags & HIL_ALIVE) == 0)
return (ENODEV);
/*
* Don't allow hardware ioctls on virtual devices.
* Note that though these are the BSD names, they have the same
* values as the HP-UX equivalents so we catch them as well.
*/
if (dptr->hd_flags & HIL_PSEUDO) {
switch (cmd) {
case HILIOCSC:
case HILIOCID:
case HILIOCRN:
case HILIOCRS:
case HILIOCED:
return(ENODEV);
/*
* XXX: should also return ENODEV but HP-UX compat
* breaks if we do. They work ok right now because
* we only recognize one keyboard on the loop. This
* will have to change if we remove that restriction.
*/
case HILIOCAROFF:
case HILIOCAR1:
case HILIOCAR2:
break;
default:
break;
}
}
#ifdef HPUXCOMPAT
if (p->p_flag & SHPUX)
return(hpuxhilioctl(dev, cmd, data, flag));
#endif
hilp->hl_cmdbp = hilp->hl_cmdbuf;
bzero((caddr_t)hilp->hl_cmdbuf, HILBUFSIZE);
hilp->hl_cmddev = device;
error = 0;
switch (cmd) {
case HILIOCSBP:
/* Send four data bytes to the tone gererator. */
send_hil_cmd(hilp->hl_addr, HIL_STARTCMD, data, 4, NULL);
/* Send the trigger beeper command to the 8042. */
send_hil_cmd(hilp->hl_addr, (cmd & 0xFF), NULL, 0, NULL);
break;
case HILIOCRRT:
/* Transfer the real time to the 8042 data buffer */
send_hil_cmd(hilp->hl_addr, (cmd & 0xFF), NULL, 0, NULL);
/* Read each byte of the real time */
for (i = 0; i < 5; i++) {
send_hil_cmd(hilp->hl_addr, HIL_READTIME + i, NULL,
0, &hold);
data[4-i] = hold;
}
break;
case HILIOCRT:
for (i = 0; i < 4; i++) {
send_hil_cmd(hilp->hl_addr, (cmd & 0xFF) + i,
NULL, 0, &hold);
data[i] = hold;
}
break;
case HILIOCID:
case HILIOCSC:
case HILIOCRN:
case HILIOCRS:
case HILIOCED:
send_hildev_cmd(hilp, device, (cmd & 0xFF));
bcopy(hilp->hl_cmdbuf, data, hilp->hl_cmdbp-hilp->hl_cmdbuf);
break;
case HILIOCAROFF:
case HILIOCAR1:
case HILIOCAR2:
if (hilp->hl_kbddev) {
hilp->hl_cmddev = hilp->hl_kbddev;
send_hildev_cmd(hilp, hilp->hl_kbddev, (cmd & 0xFF));
hilp->hl_kbdflags &= ~(KBD_AR1|KBD_AR2);
if (cmd == HILIOCAR1)
hilp->hl_kbdflags |= KBD_AR1;
else if (cmd == HILIOCAR2)
hilp->hl_kbdflags |= KBD_AR2;
}
break;
case HILIOCBEEP:
hilbeep(hilp, (struct _hilbell *)data);
break;
case FIONBIO:
dptr = &hilp->hl_device[device];
if (*(int *)data)
dptr->hd_flags |= HIL_NOBLOCK;
else
dptr->hd_flags &= ~HIL_NOBLOCK;
break;
/*
* FIOASYNC must be present for FIONBIO above to work!
* (See fcntl in kern_descrip.c).
*/
case FIOASYNC:
break;
case HILIOCALLOCQ:
error = hilqalloc((struct hilqinfo *)data);
break;
case HILIOCFREEQ:
error = hilqfree(((struct hilqinfo *)data)->qid);
break;
case HILIOCMAPQ:
error = hilqmap(*(int *)data, device);
break;
case HILIOCUNMAPQ:
error = hilqunmap(*(int *)data, device);
break;
case HILIOCHPUX:
dptr = &hilp->hl_device[device];
dptr->hd_flags |= HIL_READIN;
dptr->hd_flags &= ~HIL_QUEUEIN;
break;
case HILIOCRESET:
hilreset(hilp);
break;
#ifdef DEBUG
case HILIOCTEST:
hildebug = *(int *) data;
break;
#endif
default:
error = EINVAL;
break;
}
hilp->hl_cmddev = 0;
return(error);
}
#ifdef HPUXCOMPAT
/* ARGSUSED */
hpuxhilioctl(dev, cmd, data, flag)
dev_t dev;
caddr_t data;
{
register struct hilloop *hilp = &hil0; /* XXX */
char device = HILUNIT(dev);
struct hilloopdev *dptr;
register int i;
u_char hold;
hilp->hl_cmdbp = hilp->hl_cmdbuf;
bzero((caddr_t)hilp->hl_cmdbuf, HILBUFSIZE);
hilp->hl_cmddev = device;
switch (cmd) {
case HILSC:
case HILID:
case HILRN:
case HILRS:
case HILED:
case HILP1:
case HILP2:
case HILP3:
case HILP4:
case HILP5:
case HILP6:
case HILP7:
case HILP:
case HILA1:
case HILA2:
case HILA3:
case HILA4:
case HILA5:
case HILA6:
case HILA7:
case HILA:
send_hildev_cmd(hilp, device, (cmd & 0xFF));
bcopy(hilp->hl_cmdbuf, data, hilp->hl_cmdbp-hilp->hl_cmdbuf);
break;
case HILDKR:
case HILER1:
case HILER2:
if (hilp->hl_kbddev) {
hilp->hl_cmddev = hilp->hl_kbddev;
send_hildev_cmd(hilp, hilp->hl_kbddev, (cmd & 0xFF));
hilp->hl_kbdflags &= ~(KBD_AR1|KBD_AR2);
if (cmd == HILIOCAR1)
hilp->hl_kbdflags |= KBD_AR1;
else if (cmd == HILIOCAR2)
hilp->hl_kbdflags |= KBD_AR2;
}
break;
case EFTSBP:
/* Send four data bytes to the tone gererator. */
send_hil_cmd(hilp->hl_addr, HIL_STARTCMD, data, 4, NULL);
/* Send the trigger beeper command to the 8042. */
send_hil_cmd(hilp->hl_addr, (cmd & 0xFF), NULL, 0, NULL);
break;
case EFTRRT:
/* Transfer the real time to the 8042 data buffer */
send_hil_cmd(hilp->hl_addr, (cmd & 0xFF), NULL, 0, NULL);
/* Read each byte of the real time */
for (i = 0; i < 5; i++) {
send_hil_cmd(hilp->hl_addr, HIL_READTIME + i, NULL,
0, &hold);
data[4-i] = hold;
}
break;
case EFTRT:
for (i = 0; i < 4; i++) {
send_hil_cmd(hilp->hl_addr, (cmd & 0xFF) + i,
NULL, 0, &hold);
data[i] = hold;
}
break;
case EFTRLC:
case EFTRCC:
send_hil_cmd(hilp->hl_addr, (cmd & 0xFF), NULL, 0, &hold);
*data = hold;
break;
case EFTSRPG:
case EFTSRD:
case EFTSRR:
send_hil_cmd(hilp->hl_addr, (cmd & 0xFF), data, 1, NULL);
break;
case EFTSBI:
hilbeep(hilp, (struct _hilbell *)data);
break;
case FIONBIO:
dptr = &hilp->hl_device[device];
if (*(int *)data)
dptr->hd_flags |= HIL_NOBLOCK;
else
dptr->hd_flags &= ~HIL_NOBLOCK;
break;
case FIOASYNC:
break;
default:
hilp->hl_cmddev = 0;
return(EINVAL);
}
hilp->hl_cmddev = 0;
return(0);
}
#endif
/*
* XXX: the mmap interface for HIL devices should be rethought.
* We used it only briefly in conjuntion with shared queues
* (instead of HILIOCMAPQ ioctl). Perhaps mmap()ing a device
* should give a single queue per process.
*/
/* ARGSUSED */
hilmap(dev, off, prot)
dev_t dev;
register int off;
{
#ifdef MMAP
struct proc *p = curproc; /* XXX */
register struct hilloop *hilp = &hil0; /* XXX */
register struct hiliqueue *qp;
register int qnum;
/*
* Only allow mmap() on loop device
*/
if (HILUNIT(dev) != 0 || off >= NHILQ*sizeof(HILQ))
return(-1);
/*
* Determine which queue we want based on the offset.
* Queue must belong to calling process.
*/
qp = &hilp->hl_queue[off / sizeof(HILQ)];
if (qp->hq_procp != p)
return(-1);
off %= sizeof(HILQ);
return(kvtop((u_int)qp->hq_eventqueue + off) >> PGSHIFT);
#endif
}
/*ARGSUSED*/
hilselect(dev, rw, p)
dev_t dev;
struct proc *p;
{
register struct hilloop *hilp = &hil0; /* XXX */
register struct hilloopdev *dptr;
register struct hiliqueue *qp;
register int mask;
int s, device;
if (rw == FWRITE)
return (1);
device = HILUNIT(dev);
/*
* Read interface.
* Return 1 if there is something in the queue, 0 ow.
*/
dptr = &hilp->hl_device[device];
if (dptr->hd_flags & HIL_READIN) {
s = splhil();
if (dptr->hd_queue.c_cc) {
splx(s);
return (1);
}
selrecord(p, &dptr->hd_selr);
splx(s);
return (0);
}
/*
* Make sure device is alive and real (or the loop device).
* Note that we do not do this for the read interface.
* This is primarily to be consistant with HP-UX.
*/
if (device && (dptr->hd_flags & (HIL_ALIVE|HIL_PSEUDO)) != HIL_ALIVE)
return (1);
/*
* Select on loop device is special.
* Check to see if there are any data for any loop device
* provided it is associated with a queue belonging to this user.
*/
if (device == 0)
mask = -1;
else
mask = hildevmask(device);
/*
* Must check everybody with interrupts blocked to prevent races.
*/
s = splhil();
for (qp = hilp->hl_queue; qp < &hilp->hl_queue[NHILQ]; qp++)
if (qp->hq_procp == p && (mask & qp->hq_devmask) &&
qp->hq_eventqueue->hil_evqueue.head !=
qp->hq_eventqueue->hil_evqueue.tail) {
splx(s);
return (1);
}
selrecord(p, &dptr->hd_selr);
splx(s);
return (0);
}
hilint()
{
struct hilloop *hilp = &hil0; /* XXX */
register struct hil_dev *hildevice = hilp->hl_addr;
u_char c, stat;
stat = hildevice->hil_stat;
c = hildevice->hil_data; /* clears interrupt */
hil_process_int(stat, c);
}
#include "ite.h"
hil_process_int(stat, c)
register u_char stat, c;
{
register struct hilloop *hilp;
#ifdef DEBUG
if (hildebug & HDB_EVENTS)
printf("hilint: %x %x\n", stat, c);
#endif
/* the shift enables the compiler to generate a jump table */
switch ((stat>>HIL_SSHIFT) & HIL_SMASK) {
#if NITE > 0
case HIL_KEY:
case HIL_SHIFT:
case HIL_CTRL:
case HIL_CTRLSHIFT:
itefilter(stat, c);
return;
#endif
case HIL_STATUS: /* The status info. */
hilp = &hil0; /* XXX */
if (c & HIL_ERROR) {
hilp->hl_cmddone = TRUE;
if (c == HIL_RECONFIG)
hilconfig(hilp);
break;
}
if (c & HIL_COMMAND) {
if (c & HIL_POLLDATA) /* End of data */
hilevent(hilp);
else /* End of command */
hilp->hl_cmdending = TRUE;
hilp->hl_actdev = 0;
} else {
if (c & HIL_POLLDATA) { /* Start of polled data */
if (hilp->hl_actdev != 0)
hilevent(hilp);
hilp->hl_actdev = (c & HIL_DEVMASK);
hilp->hl_pollbp = hilp->hl_pollbuf;
} else { /* Start of command */
if (hilp->hl_cmddev == (c & HIL_DEVMASK)) {
hilp->hl_cmdbp = hilp->hl_cmdbuf;
hilp->hl_actdev = 0;
}
}
}
return;
case HIL_DATA:
hilp = &hil0; /* XXX */
if (hilp->hl_actdev != 0) /* Collecting poll data */
*hilp->hl_pollbp++ = c;
else if (hilp->hl_cmddev != 0) /* Collecting cmd data */
if (hilp->hl_cmdending) {
hilp->hl_cmddone = TRUE;
hilp->hl_cmdending = FALSE;
} else
*hilp->hl_cmdbp++ = c;
return;
case 0: /* force full jump table */
default:
return;
}
}
#if (defined(DDB) || defined(DEBUG)) && !defined(PANICBUTTON)
#define PANICBUTTON
#endif
/*
* Optimized macro to compute:
* eq->head == (eq->tail + 1) % eq->size
* i.e. has tail caught up with head. We do this because 32 bit long
* remaidering is expensive (a function call with our compiler).
*/
#define HQFULL(eq) (((eq)->head?(eq)->head:(eq)->size) == (eq)->tail+1)
#define HQVALID(eq) \
((eq)->size == HEVQSIZE && (eq)->tail >= 0 && (eq)->tail < HEVQSIZE)
hilevent(hilp)
struct hilloop *hilp;
{
register struct hilloopdev *dptr = &hilp->hl_device[hilp->hl_actdev];
register int len, mask, qnum;
register u_char *cp, *pp;
register HILQ *hq;
struct timeval ourtime;
hil_packet *proto;
int s, len0;
long tenths;
#ifdef PANICBUTTON
static int first;
extern int panicbutton;
cp = hilp->hl_pollbuf;
if (panicbutton && (*cp & HIL_KBDDATA)) {
if (*++cp == 0x4E)
first = 1;
else if (first && *cp == 0x46 && !panicstr)
panic("are we having fun yet?");
else
first = 0;
}
#endif
#ifdef DEBUG
if (hildebug & HDB_EVENTS) {
printf("hilevent: dev %d pollbuf: ", hilp->hl_actdev);
printhilpollbuf(hilp);
printf("\n");
}
#endif
/*
* Note that HIL_READIN effectively "shuts off" any queues
* that may have been in use at the time of an HILIOCHPUX call.
*/
if (dptr->hd_flags & HIL_READIN) {
hpuxhilevent(hilp, dptr);
return;
}
/*
* If this device isn't on any queue or there are no data
* in the packet (can this happen?) do nothing.
*/
if (dptr->hd_qmask == 0 ||
(len0 = hilp->hl_pollbp - hilp->hl_pollbuf) <= 0)
return;
/*
* Everybody gets the same time stamp
*/
s = splclock();
ourtime = time;
splx(s);
tenths = (ourtime.tv_sec * 100) + (ourtime.tv_usec / 10000);
proto = NULL;
mask = dptr->hd_qmask;
for (qnum = 0; mask; qnum++) {
if ((mask & hilqmask(qnum)) == 0)
continue;
mask &= ~hilqmask(qnum);
hq = hilp->hl_queue[qnum].hq_eventqueue;
/*
* Ensure that queue fields that we rely on are valid
* and that there is space in the queue. If either
* test fails, we just skip this queue.
*/
if (!HQVALID(&hq->hil_evqueue) || HQFULL(&hq->hil_evqueue))
continue;
/*
* Copy data to queue.
* If this is the first queue we construct the packet
* with length, timestamp and poll buffer data.
* For second and sucessive packets we just duplicate
* the first packet.
*/
pp = (u_char *) &hq->hil_event[hq->hil_evqueue.tail];
if (proto == NULL) {
proto = (hil_packet *)pp;
cp = hilp->hl_pollbuf;
len = len0;
*pp++ = len + 6;
*pp++ = hilp->hl_actdev;
*(long *)pp = tenths;
pp += sizeof(long);
do *pp++ = *cp++; while (--len);
} else
*(hil_packet *)pp = *proto;
if (++hq->hil_evqueue.tail == hq->hil_evqueue.size)
hq->hil_evqueue.tail = 0;
}
/*
* Wake up anyone selecting on this device or the loop itself
*/
selwakeup(&dptr->hd_selr);
dptr = &hilp->hl_device[HILLOOPDEV];
selwakeup(&dptr->hd_selr);
}
#undef HQFULL
hpuxhilevent(hilp, dptr)
register struct hilloop *hilp;
register struct hilloopdev *dptr;
{
register int len;
struct timeval ourtime;
long tstamp;
int s;
/*
* Everybody gets the same time stamp
*/
s = splclock();
ourtime = time;
splx(s);
tstamp = (ourtime.tv_sec * 100) + (ourtime.tv_usec / 10000);
/*
* Each packet that goes into the buffer must be preceded by the
* number of bytes in the packet, and the timestamp of the packet.
* This adds 5 bytes to the packet size. Make sure there is enough
* room in the buffer for it, and if not, toss the packet.
*/
len = hilp->hl_pollbp - hilp->hl_pollbuf;
if (dptr->hd_queue.c_cc <= (HILMAXCLIST - (len+5))) {
putc(len+5, &dptr->hd_queue);
(void) b_to_q((char *)&tstamp, sizeof tstamp, &dptr->hd_queue);
(void) b_to_q((char *)hilp->hl_pollbuf, len, &dptr->hd_queue);
}
/*
* Wake up any one blocked on a read or select
*/
if (dptr->hd_flags & HIL_ASLEEP) {
dptr->hd_flags &= ~HIL_ASLEEP;
wakeup((caddr_t)dptr);
}
selwakeup(&dptr->hd_selr);
}
/*
* Shared queue manipulation routines
*/
hilqalloc(qip)
struct hilqinfo *qip;
{
struct proc *p = curproc; /* XXX */
#ifdef DEBUG
if (hildebug & HDB_FOLLOW)
printf("hilqalloc(%d): addr %x\n", p->p_pid, qip->addr);
#endif
return(EINVAL);
}
hilqfree(qnum)
register int qnum;
{
struct proc *p = curproc; /* XXX */
#ifdef DEBUG
if (hildebug & HDB_FOLLOW)
printf("hilqfree(%d): qnum %d\n", p->p_pid, qnum);
#endif
return(EINVAL);
}
hilqmap(qnum, device)
register int qnum, device;
{
struct proc *p = curproc; /* XXX */
register struct hilloop *hilp = &hil0; /* XXX */
register struct hilloopdev *dptr = &hilp->hl_device[device];
int s;
#ifdef DEBUG
if (hildebug & HDB_FOLLOW)
printf("hilqmap(%d): qnum %d device %x\n",
p->p_pid, qnum, device);
#endif
if (qnum >= NHILQ || hilp->hl_queue[qnum].hq_procp != p)
return(EINVAL);
if ((dptr->hd_flags & HIL_QUEUEIN) == 0)
return(EINVAL);
if (dptr->hd_qmask && p->p_ucred->cr_uid &&
p->p_ucred->cr_uid != dptr->hd_uid)
return(EPERM);
hilp->hl_queue[qnum].hq_devmask |= hildevmask(device);
if (dptr->hd_qmask == 0)
dptr->hd_uid = p->p_ucred->cr_uid;
s = splhil();
dptr->hd_qmask |= hilqmask(qnum);
splx(s);
#ifdef DEBUG
if (hildebug & HDB_MASK)
printf("hilqmap(%d): devmask %x qmask %x\n",
p->p_pid, hilp->hl_queue[qnum].hq_devmask,
dptr->hd_qmask);
#endif
return(0);
}
hilqunmap(qnum, device)
register int qnum, device;
{
struct proc *p = curproc; /* XXX */
register struct hilloop *hilp = &hil0; /* XXX */
int s;
#ifdef DEBUG
if (hildebug & HDB_FOLLOW)
printf("hilqunmap(%d): qnum %d device %x\n",
p->p_pid, qnum, device);
#endif
if (qnum >= NHILQ || hilp->hl_queue[qnum].hq_procp != p)
return(EINVAL);
hilp->hl_queue[qnum].hq_devmask &= ~hildevmask(device);
s = splhil();
hilp->hl_device[device].hd_qmask &= ~hilqmask(qnum);
splx(s);
#ifdef DEBUG
if (hildebug & HDB_MASK)
printf("hilqunmap(%d): devmask %x qmask %x\n",
p->p_pid, hilp->hl_queue[qnum].hq_devmask,
hilp->hl_device[device].hd_qmask);
#endif
return(0);
}
#if 0
#include "sys/clist.h"
/*
* This is just a copy of the virgin q_to_b routine with minor
* optimizations for HIL use. It is used because we don't have
* to raise the priority to spltty() for most of the clist manipulations.
*/
hilq_to_b(q, cp, cc)
register struct clist *q;
register char *cp;
{
panic("hilq_to_b: missing body");
}
#endif
/*
* Cooked keyboard functions for ite driver.
* There is only one "cooked" ITE keyboard (the first keyboard found)
* per loop. There may be other keyboards, but they will always be "raw".
*/
kbdbell()
{
struct hilloop *hilp = &hil0; /* XXX */
hilbeep(hilp, &default_bell);
}
kbdenable()
{
struct hilloop *hilp = &hil0; /* XXX */
register struct hil_dev *hildevice = hilp->hl_addr;
char db;
/* Set the autorepeat rate register */
db = ar_format(KBD_ARR);
send_hil_cmd(hildevice, HIL_SETARR, &db, 1, NULL);
/* Set the autorepeat delay register */
db = ar_format(KBD_ARD);
send_hil_cmd(hildevice, HIL_SETARD, &db, 1, NULL);
/* Enable interrupts */
send_hil_cmd(hildevice, HIL_INTON, NULL, 0, NULL);
}
kbddisable()
{
}
/*
* XXX: read keyboard directly and return code.
* Used by console getchar routine. Could really screw up anybody
* reading from the keyboard in the normal, interrupt driven fashion.
*/
kbdgetc(statp)
int *statp;
{
struct hilloop *hilp = &hil0; /* XXX */
register struct hil_dev *hildevice = hilp->hl_addr;
register int c, stat;
int s;
s = splhil();
while (((stat = hildevice->hil_stat) & HIL_DATA_RDY) == 0)
;
c = hildevice->hil_data;
splx(s);
*statp = stat;
return(c);
}
/*
* Recoginize and clear keyboard generated NMIs.
* Returns 1 if it was ours, 0 otherwise. Note that we cannot use
* send_hil_cmd() to issue the clear NMI command as that would actually
* lower the priority to splimp() and it doesn't wait for the completion
* of the command. Either of these conditions could result in the
* interrupt reoccuring. Note that we issue the CNMT command twice.
* This seems to be needed, once is not always enough!?!
*/
kbdnmi()
{
register struct hilloop *hilp = &hil0; /* XXX */
if ((*KBDNMISTAT & KBDNMI) == 0)
return(0);
HILWAIT(hilp->hl_addr);
hilp->hl_addr->hil_cmd = HIL_CNMT;
HILWAIT(hilp->hl_addr);
hilp->hl_addr->hil_cmd = HIL_CNMT;
HILWAIT(hilp->hl_addr);
return(1);
}
#define HILSECURITY 0x33
#define HILIDENTIFY 0x03
#define HILSCBIT 0x04
/*
* Called at boot time to print out info about interesting devices
*/
hilinfo(hilp)
register struct hilloop *hilp;
{
register int id, len;
register struct kbdmap *km;
/*
* Keyboard info.
*/
if (hilp->hl_kbddev) {
printf("hil%d: ", hilp->hl_kbddev);
for (km = kbd_map; km->kbd_code; km++)
if (km->kbd_code == hilp->hl_kbdlang) {
printf("%s ", km->kbd_desc);
break;
}
printf("keyboard\n");
}
/*
* ID module.
* Attempt to locate the first ID module and print out its
* security code. Is this a good idea??
*/
id = hiliddev(hilp);
if (id) {
hilp->hl_cmdbp = hilp->hl_cmdbuf;
hilp->hl_cmddev = id;
send_hildev_cmd(hilp, id, HILSECURITY);
len = hilp->hl_cmdbp - hilp->hl_cmdbuf;
hilp->hl_cmdbp = hilp->hl_cmdbuf;
hilp->hl_cmddev = 0;
printf("hil%d: security code", id);
for (id = 0; id < len; id++)
printf(" %x", hilp->hl_cmdbuf[id]);
while (id++ < 16)
printf(" 0");
printf("\n");
}
}
#define HILAR1 0x3E
#define HILAR2 0x3F
/*
* Called after the loop has reconfigured. Here we need to:
* - determine how many devices are on the loop
* (some may have been added or removed)
* - locate the ITE keyboard (if any) and ensure
* that it is in the proper state (raw or cooked)
* and is set to use the proper language mapping table
* - ensure all other keyboards are raw
* Note that our device state is now potentially invalid as
* devices may no longer be where they were. What we should
* do here is either track where the devices went and move
* state around accordingly or, more simply, just mark all
* devices as HIL_DERROR and don't allow any further use until
* they are closed. This is a little too brutal for my tastes,
* we prefer to just assume people won't move things around.
*/
hilconfig(hilp)
register struct hilloop *hilp;
{
u_char db;
int s;
s = splhil();
#ifdef DEBUG
if (hildebug & HDB_CONFIG) {
printf("hilconfig: reconfigured: ");
send_hil_cmd(hilp->hl_addr, HIL_READLPSTAT, NULL, 0, &db);
printf("LPSTAT %x, ", db);
send_hil_cmd(hilp->hl_addr, HIL_READLPCTRL, NULL, 0, &db);
printf("LPCTRL %x, ", db);
send_hil_cmd(hilp->hl_addr, HIL_READKBDSADR, NULL, 0, &db);
printf("KBDSADR %x\n", db);
hilreport(hilp);
}
#endif
/*
* Determine how many devices are on the loop.
* Mark those as alive and real, all others as dead.
*/
db = 0;
send_hil_cmd(hilp->hl_addr, HIL_READLPSTAT, NULL, 0, &db);
hilp->hl_maxdev = db & LPS_DEVMASK;
for (db = 1; db < NHILD; db++) {
if (db <= hilp->hl_maxdev)
hilp->hl_device[db].hd_flags |= HIL_ALIVE;
else
hilp->hl_device[db].hd_flags &= ~HIL_ALIVE;
hilp->hl_device[db].hd_flags &= ~HIL_PSEUDO;
}
#ifdef DEBUG
if (hildebug & (HDB_CONFIG|HDB_KEYBOARD))
printf("hilconfig: max device %d\n", hilp->hl_maxdev);
#endif
if (hilp->hl_maxdev == 0) {
hilp->hl_kbddev = 0;
splx(s);
return;
}
/*
* Find out where the keyboards are and record the ITE keyboard
* (first one found). If no keyboards found, we are all done.
*/
db = 0;
send_hil_cmd(hilp->hl_addr, HIL_READKBDSADR, NULL, 0, &db);
#ifdef DEBUG
if (hildebug & HDB_KEYBOARD)
printf("hilconfig: keyboard: KBDSADR %x, old %d, new %d\n",
db, hilp->hl_kbddev, ffs((int)db));
#endif
hilp->hl_kbddev = ffs((int)db);
if (hilp->hl_kbddev == 0) {
splx(s);
return;
}
/*
* Determine if the keyboard should be cooked or raw and configure it.
*/
db = (hilp->hl_kbdflags & KBD_RAW) ? 0 : 1 << (hilp->hl_kbddev - 1);
send_hil_cmd(hilp->hl_addr, HIL_WRITEKBDSADR, &db, 1, NULL);
/*
* Re-enable autorepeat in raw mode, cooked mode AR is not affected.
*/
if (hilp->hl_kbdflags & (KBD_AR1|KBD_AR2)) {
db = (hilp->hl_kbdflags & KBD_AR1) ? HILAR1 : HILAR2;
hilp->hl_cmddev = hilp->hl_kbddev;
send_hildev_cmd(hilp, hilp->hl_kbddev, db);
hilp->hl_cmddev = 0;
}
/*
* Determine the keyboard language configuration, but don't
* override a user-specified setting.
*/
db = 0;
send_hil_cmd(hilp->hl_addr, HIL_READKBDLANG, NULL, 0, &db);
#ifdef DEBUG
if (hildebug & HDB_KEYBOARD)
printf("hilconfig: language: old %x new %x\n",
hilp->hl_kbdlang, db);
#endif
if (hilp->hl_kbdlang != KBD_SPECIAL) {
struct kbdmap *km;
for (km = kbd_map; km->kbd_code; km++)
if (km->kbd_code == db) {
hilp->hl_kbdlang = db;
/* XXX */
kbd_keymap = km->kbd_keymap;
kbd_shiftmap = km->kbd_shiftmap;
kbd_ctrlmap = km->kbd_ctrlmap;
kbd_ctrlshiftmap = km->kbd_ctrlshiftmap;
kbd_stringmap = km->kbd_stringmap;
}
}
splx(s);
}
hilreset(hilp)
struct hilloop *hilp;
{
register struct hil_dev *hildevice = hilp->hl_addr;
u_char db;
/*
* Initialize the loop: reconfigure, don't report errors,
* cook keyboards, and enable autopolling.
*/
db = LPC_RECONF | LPC_KBDCOOK | LPC_NOERROR | LPC_AUTOPOLL;
send_hil_cmd(hildevice, HIL_WRITELPCTRL, &db, 1, NULL);
/*
* Delay one second for reconfiguration and then read the the
* data register to clear the interrupt (if the loop reconfigured).
*/
DELAY(1000000);
if (hildevice->hil_stat & HIL_DATA_RDY)
db = hildevice->hil_data;
/*
* The HIL loop may have reconfigured. If so we proceed on,
* if not we loop until a successful reconfiguration is reported
* back to us. The HIL loop will continue to attempt forever.
* Probably not very smart.
*/
do {
send_hil_cmd(hildevice, HIL_READLPSTAT, NULL, 0, &db);
} while ((db & (LPS_CONFFAIL|LPS_CONFGOOD)) == 0);
/*
* At this point, the loop should have reconfigured.
* The reconfiguration interrupt has already called hilconfig()
* so the keyboard has been determined.
*/
send_hil_cmd(hildevice, HIL_INTON, NULL, 0, NULL);
}
hilbeep(hilp, bp)
struct hilloop *hilp;
register struct _hilbell *bp;
{
u_char buf[2];
buf[0] = ~((bp->duration - 10) / 10);
buf[1] = bp->frequency;
send_hil_cmd(hilp->hl_addr, HIL_SETTONE, buf, 2, NULL);
}
/*
* Locate and return the address of the first ID module, 0 if none present.
*/
hiliddev(hilp)
register struct hilloop *hilp;
{
register int i, len;
#ifdef DEBUG
if (hildebug & HDB_IDMODULE)
printf("hiliddev(%x): looking for idmodule...", hilp);
#endif
for (i = 1; i <= hilp->hl_maxdev; i++) {
hilp->hl_cmdbp = hilp->hl_cmdbuf;
hilp->hl_cmddev = i;
send_hildev_cmd(hilp, i, HILIDENTIFY);
/*
* XXX: the final condition checks to ensure that the
* device ID byte is in the range of the ID module (0x30-0x3F)
*/
len = hilp->hl_cmdbp - hilp->hl_cmdbuf;
if (len > 1 && (hilp->hl_cmdbuf[1] & HILSCBIT) &&
(hilp->hl_cmdbuf[0] & 0xF0) == 0x30) {
hilp->hl_cmdbp = hilp->hl_cmdbuf;
hilp->hl_cmddev = i;
send_hildev_cmd(hilp, i, HILSECURITY);
break;
}
}
hilp->hl_cmdbp = hilp->hl_cmdbuf;
hilp->hl_cmddev = 0;
#ifdef DEBUG
if (hildebug & HDB_IDMODULE)
if (i <= hilp->hl_maxdev)
printf("found at %d\n", i);
else
printf("not found\n");
#endif
return(i <= hilp->hl_maxdev ? i : 0);
}
/*
* Low level routines which actually talk to the 8042 chip.
*/
/*
* Send a command to the 8042 with zero or more bytes of data.
* If rdata is non-null, wait for and return a byte of data.
* We run at splimp() to make the transaction as atomic as
* possible without blocking the clock (is this necessary?)
*/
send_hil_cmd(hildevice, cmd, data, dlen, rdata)
register struct hil_dev *hildevice;
u_char cmd, *data, dlen;
u_char *rdata;
{
u_char status;
int s = splimp();
HILWAIT(hildevice);
hildevice->hil_cmd = cmd;
while (dlen--) {
HILWAIT(hildevice);
hildevice->hil_data = *data++;
}
if (rdata) {
do {
HILDATAWAIT(hildevice);
status = hildevice->hil_stat;
*rdata = hildevice->hil_data;
} while (((status >> HIL_SSHIFT) & HIL_SMASK) != HIL_68K);
}
splx(s);
}
/*
* Send a command to a device on the loop.
* Since only one command can be active on the loop at any time,
* we must ensure that we are not interrupted during this process.
* Hence we mask interrupts to prevent potential access from most
* interrupt routines and turn off auto-polling to disable the
* internally generated poll commands.
*
* splhigh is extremely conservative but insures atomic operation,
* splimp (clock only interrupts) seems to be good enough in practice.
*/
send_hildev_cmd(hilp, device, cmd)
register struct hilloop *hilp;
char device, cmd;
{
register struct hil_dev *hildevice = hilp->hl_addr;
u_char status, c;
int s = splimp();
polloff(hildevice);
/*
* Transfer the command and device info to the chip
*/
HILWAIT(hildevice);
hildevice->hil_cmd = HIL_STARTCMD;
HILWAIT(hildevice);
hildevice->hil_data = 8 + device;
HILWAIT(hildevice);
hildevice->hil_data = cmd;
HILWAIT(hildevice);
hildevice->hil_data = HIL_TIMEOUT;
/*
* Trigger the command and wait for completion
*/
HILWAIT(hildevice);
hildevice->hil_cmd = HIL_TRIGGER;
hilp->hl_cmddone = FALSE;
do {
HILDATAWAIT(hildevice);
status = hildevice->hil_stat;
c = hildevice->hil_data;
hil_process_int(status, c);
} while (!hilp->hl_cmddone);
pollon(hildevice);
splx(s);
}
/*
* Turn auto-polling off and on.
* Also disables and enable auto-repeat. Why?
*/
polloff(hildevice)
register struct hil_dev *hildevice;
{
register char db;
/*
* Turn off auto repeat
*/
HILWAIT(hildevice);
hildevice->hil_cmd = HIL_SETARR;
HILWAIT(hildevice);
hildevice->hil_data = 0;
/*
* Turn off auto-polling
*/
HILWAIT(hildevice);
hildevice->hil_cmd = HIL_READLPCTRL;
HILDATAWAIT(hildevice);
db = hildevice->hil_data;
db &= ~LPC_AUTOPOLL;
HILWAIT(hildevice);
hildevice->hil_cmd = HIL_WRITELPCTRL;
HILWAIT(hildevice);
hildevice->hil_data = db;
/*
* Must wait til polling is really stopped
*/
do {
HILWAIT(hildevice);
hildevice->hil_cmd = HIL_READBUSY;
HILDATAWAIT(hildevice);
db = hildevice->hil_data;
} while (db & BSY_LOOPBUSY);
}
pollon(hildevice)
register struct hil_dev *hildevice;
{
register char db;
/*
* Turn on auto polling
*/
HILWAIT(hildevice);
hildevice->hil_cmd = HIL_READLPCTRL;
HILDATAWAIT(hildevice);
db = hildevice->hil_data;
db |= LPC_AUTOPOLL;
HILWAIT(hildevice);
hildevice->hil_cmd = HIL_WRITELPCTRL;
HILWAIT(hildevice);
hildevice->hil_data = db;
/*
* Turn on auto repeat
*/
HILWAIT(hildevice);
hildevice->hil_cmd = HIL_SETARR;
HILWAIT(hildevice);
hildevice->hil_data = ar_format(KBD_ARR);
}
#ifdef DEBUG
printhilpollbuf(hilp)
register struct hilloop *hilp;
{
register u_char *cp;
register int i, len;
cp = hilp->hl_pollbuf;
len = hilp->hl_pollbp - cp;
for (i = 0; i < len; i++)
printf("%x ", hilp->hl_pollbuf[i]);
printf("\n");
}
printhilcmdbuf(hilp)
register struct hilloop *hilp;
{
register u_char *cp;
register int i, len;
cp = hilp->hl_cmdbuf;
len = hilp->hl_cmdbp - cp;
for (i = 0; i < len; i++)
printf("%x ", hilp->hl_cmdbuf[i]);
printf("\n");
}
hilreport(hilp)
register struct hilloop *hilp;
{
register int i, len;
int s = splhil();
for (i = 1; i <= hilp->hl_maxdev; i++) {
hilp->hl_cmdbp = hilp->hl_cmdbuf;
hilp->hl_cmddev = i;
send_hildev_cmd(hilp, i, HILIDENTIFY);
printf("hil%d: id: ", i);
printhilcmdbuf(hilp);
len = hilp->hl_cmdbp - hilp->hl_cmdbuf;
if (len > 1 && (hilp->hl_cmdbuf[1] & HILSCBIT)) {
hilp->hl_cmdbp = hilp->hl_cmdbuf;
hilp->hl_cmddev = i;
send_hildev_cmd(hilp, i, HILSECURITY);
printf("hil%d: sc: ", i);
printhilcmdbuf(hilp);
}
}
hilp->hl_cmdbp = hilp->hl_cmdbuf;
hilp->hl_cmddev = 0;
splx(s);
}
#endif