NetBSD/sys/arch/hp300/dev/hil.c

1774 lines
43 KiB
C

/* $NetBSD: hil.c,v 1.82 2008/06/13 09:41:15 cegger Exp $ */
/*
* Copyright (c) 1990, 1993
* 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. 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.38 92/01/21$
*
* @(#)hil.c 8.2 (Berkeley) 1/12/94
*/
/*
* Copyright (c) 1988 University of Utah.
*
* 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.38 92/01/21$
*
* @(#)hil.c 8.2 (Berkeley) 1/12/94
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: hil.c,v 1.82 2008/06/13 09:41:15 cegger Exp $");
#include "ite.h"
#include "rnd.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/kernel.h>
#include <sys/poll.h>
#include <sys/proc.h>
#include <sys/tty.h>
#include <sys/uio.h>
#include <sys/user.h>
#include <sys/kauth.h>
#include <uvm/uvm_extern.h>
#if NRND > 0
#include <sys/rnd.h>
#endif
#include <hp300/dev/intiovar.h>
#include <hp300/dev/hilreg.h>
#include <hp300/dev/hilioctl.h>
#include <hp300/dev/hilvar.h>
#include <hp300/dev/itevar.h>
#include <hp300/dev/kbdmap.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include "ioconf.h"
static int hilmatch(device_t, cfdata_t, void *);
static void hilattach(device_t, device_t, void *);
CFATTACH_DECL_NEW(hil, sizeof(struct hil_softc),
hilmatch, hilattach, NULL, NULL);
static 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
extern struct kbdmap kbd_map[];
/* symbolic sleep message strings */
static const char hilin[] = "hilin";
static dev_type_open(hilopen);
static dev_type_close(hilclose);
static dev_type_read(hilread);
static dev_type_ioctl(hilioctl);
static dev_type_poll(hilpoll);
static dev_type_kqfilter(hilkqfilter);
const struct cdevsw hil_cdevsw = {
hilopen, hilclose, hilread, nullwrite, hilioctl,
nostop, notty, hilpoll, nommap, hilkqfilter,
};
static void hilattach_deferred(device_t);
static void hilinfo(struct hil_softc *);
static void hilconfig(struct hil_softc *);
static void hilreset(struct hil_softc *);
static void hilbeep(struct hil_softc *, const struct _hilbell *);
static int hiliddev(struct hil_softc *);
static int hilint(void *);
static void hil_process_int(struct hil_softc *, uint8_t, uint8_t);
static void hilevent(struct hil_softc *);
static void hpuxhilevent(struct hil_softc *, struct hilloopdev *);
static int hilqalloc(struct hil_softc *, struct hilqinfo *, struct proc *);
static int hilqfree(struct hil_softc *, int, struct proc *);
static int hilqmap(struct hil_softc *, int, int, struct lwp *);
static int hilqunmap(struct hil_softc *, int, int, struct proc *);
#ifdef DEBUG
static void printhilpollbuf(struct hil_softc *);
static void printhilcmdbuf(struct hil_softc *);
static void hilreport(struct hil_softc *);
#endif /* DEBUG */
static int
hilmatch(device_t parent, cfdata_t cf, void *aux)
{
struct intio_attach_args *ia = aux;
if (strcmp("hil", ia->ia_modname) != 0)
return 0;
return 1;
}
static void
hilattach(device_t parent, device_t self, void *aux)
{
struct hil_softc *sc = device_private(self);
struct intio_attach_args *ia = aux;
int i;
sc->sc_dev = self;
aprint_normal("\n");
#ifdef DEBUG
if (hildebug & HDB_FOLLOW)
aprint_debug("hilsoftinit(%p, %p)\n", sc,
(void *)ia->ia_addr);
#endif
/*
* Initialize loop information
*/
sc->sc_addr = (struct hil_dev *)ia->ia_addr;
sc->sc_cmdending = false;
sc->sc_actdev = sc->sc_cmddev = 0;
sc->sc_cmddone = false;
sc->sc_cmdbp = sc->sc_cmdbuf;
sc->sc_pollbp = sc->sc_pollbuf;
sc->sc_kbddev = 0;
sc->sc_kbdflags = 0;
/*
* Clear all queues and device associations with queues
*/
for (i = 0; i < NHILQ; i++) {
sc->sc_queue[i].hq_eventqueue = NULL;
sc->sc_queue[i].hq_procp = NULL;
sc->sc_queue[i].hq_devmask = 0;
}
for (i = 0; i < NHILD; i++) {
selinit(&sc->sc_device[i].hd_selr);
sc->sc_device[i].hd_qmask = 0;
}
sc->sc_device[HILLOOPDEV].hd_flags = (HIL_ALIVE|HIL_PSEUDO);
/*
* Set up default keyboard language. We always default
* to US ASCII - it seems to work OK for non-recognized
* keyboards.
*/
sc->sc_kbdlang = KBD_DEFAULT;
#if NITE > 0
{
struct kbdmap *km;
for (km = kbd_map; km->kbd_code; km++) {
if (km->kbd_code == KBD_US)
iteinstallkeymap(km);
}
}
#endif
(void)intio_intr_establish(hilint, sc, ia->ia_ipl, IPL_TTY);
config_interrupts(self, hilattach_deferred);
}
static void
hilattach_deferred(device_t self)
{
struct hil_softc *sc = device_private(self);
#ifdef DEBUG
if (hildebug & HDB_FOLLOW)
aprint_debug("hilinit(%p, %p)\n", sc, sc->sc_addr);
#endif
/*
* Initialize hardware.
* Reset the loop hardware, and collect keyboard/id info
*/
hilreset(sc);
hilinfo(sc);
hilkbdenable(sc);
}
/* ARGSUSED */
static int
hilopen(dev_t dev, int flags, int mode, struct lwp *l)
{
struct hil_softc *sc;
struct hilloopdev *dptr;
int s;
#ifdef DEBUG
struct proc *p = l->l_proc;
#endif
sc = device_lookup_private(&hil_cd, HILLOOP(dev));
#ifdef DEBUG
if (hildebug & HDB_FOLLOW)
printf("hilopen(%d): loop %x device %x\n",
p->p_pid, HILLOOP(dev), HILUNIT(dev));
#endif
if ((sc->sc_device[HILLOOPDEV].hd_flags & HIL_ALIVE) == 0)
return ENXIO;
dptr = &sc->sc_device[HILUNIT(dev)];
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 (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 guaranteed
* that no one else is using it.
*/
if ((dptr->hd_flags & HIL_OPENED) == 0) {
dptr->hd_flags |= HIL_OPENED;
clalloc(&dptr->hd_queue, HILMAXCLIST, 0);
}
send_hil_cmd(sc->sc_addr, HIL_INTON, NULL, 0, NULL);
/*
* Opened the keyboard, put in raw mode.
*/
s = splhil();
if (HILUNIT(dev) == sc->sc_kbddev) {
uint8_t mask = 0;
send_hil_cmd(sc->sc_addr, HIL_WRITEKBDSADR, &mask, 1, NULL);
sc->sc_kbdflags |= KBD_RAW;
#ifdef DEBUG
if (hildebug & HDB_KEYBOARD)
printf("hilopen: keyboard %d raw\n", sc->sc_kbddev);
#endif
}
splx(s);
return 0;
}
/* ARGSUSED */
static int
hilclose(dev_t dev, int flags, int mode, struct lwp *l)
{
struct hil_softc *sc;
struct hilloopdev *dptr;
int i;
char mask, lpctrl;
int s;
extern struct emul emul_netbsd;
#ifdef DEBUG
struct proc *p = l->l_proc;
#endif
sc = device_lookup_private(&hil_cd, HILLOOP(dev));
#ifdef DEBUG
if (hildebug & HDB_FOLLOW)
printf("hilclose(%d): device %x\n", p->p_pid, HILUNIT(dev));
#endif
dptr = &sc->sc_device[HILUNIT(dev)];
if (HILUNIT(dev) && (dptr->hd_flags & HIL_PSEUDO))
return 0;
if (l && l->l_proc->p_emul == &emul_netbsd) {
/*
* If this is the loop device,
* free up all queues belonging to this process.
*/
if (HILUNIT(dev) == 0) {
for (i = 0; i < NHILQ; i++)
if (sc->sc_queue[i].hq_procp == l->l_proc)
(void) hilqfree(sc, i, l->l_proc);
} else {
mask = ~hildevmask(HILUNIT(dev));
s = splhil();
for (i = 0; i < NHILQ; i++)
if (sc->sc_queue[i].hq_procp == l->l_proc) {
dptr->hd_qmask &= ~hilqmask(i);
sc->sc_queue[i].hq_devmask &= mask;
}
splx(s);
}
}
/*
* The read buffer can go away.
*/
dptr->hd_flags &= ~(HIL_QUEUEIN|HIL_READIN|HIL_NOBLOCK|HIL_OPENED);
clfree(&dptr->hd_queue);
/*
* Set keyboard back to cooked mode when closed.
*/
s = splhil();
if (HILUNIT(dev) && HILUNIT(dev) == sc->sc_kbddev) {
mask = 1 << (sc->sc_kbddev - 1);
send_hil_cmd(sc->sc_addr, HIL_WRITEKBDSADR, &mask, 1, NULL);
sc->sc_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(sc->sc_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(sc->sc_addr, HIL_WRITELPCTRL,
&lpctrl, 1, NULL);
}
#ifdef DEBUG
if (hildebug & HDB_KEYBOARD)
printf("hilclose: keyboard %d cooked\n",
sc->sc_kbddev);
#endif
hilkbdenable(sc);
}
splx(s);
return 0;
}
/*
* Read interface to HIL device.
*/
/* ARGSUSED */
static int
hilread(dev_t dev, struct uio *uio, int flag)
{
struct hil_softc *sc;
struct hilloopdev *dptr;
int cc;
uint8_t buf[HILBUFSIZE];
int error, s;
sc = device_lookup_private(&hil_cd, HILLOOP(dev));
#if 0
/*
* XXX: Don't do this since HP-UX doesn't.
*
* Check device number.
* This check is necessary since loop can reconfigure.
*/
if (HILUNIT(dev) > sc->sc_maxdev)
return ENODEV;
#endif
dptr = &sc->sc_device[HILUNIT(dev)];
if ((dptr->hd_flags & HIL_READIN) == 0)
return ENODEV;
s = 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((void *)dptr,
TTIPRI | PCATCH, hilin, 0))) {
(void)spl0();
return error;
}
}
splx(s);
error = 0;
while (uio->uio_resid > 0 && error == 0) {
cc = q_to_b(&dptr->hd_queue, buf,
min(uio->uio_resid, HILBUFSIZE));
if (cc <= 0)
break;
error = uiomove(buf, cc, uio);
}
return error;
}
static int
hilioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
{
struct hil_softc *sc;
struct hilloopdev *dptr;
uint8_t *buf;
int i;
uint8_t hold;
int error;
sc = device_lookup_private(&hil_cd, HILLOOP(dev));
#ifdef DEBUG
if (hildebug & HDB_FOLLOW)
printf("hilioctl(%d): dev %x cmd %lx\n",
l->l_proc->p_pid, HILUNIT(dev), cmd);
#endif
dptr = &sc->sc_device[HILUNIT(dev)];
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 OHILIOCID:
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;
}
}
sc->sc_cmdbp = sc->sc_cmdbuf;
memset((void *)sc->sc_cmdbuf, 0, HILBUFSIZE);
sc->sc_cmddev = HILUNIT(dev);
error = 0;
switch (cmd) {
case HILIOCSBP:
/* Send four data bytes to the tone gererator. */
send_hil_cmd(sc->sc_addr, HIL_STARTCMD, data, 4, NULL);
/* Send the trigger beeper command to the 8042. */
send_hil_cmd(sc->sc_addr, (cmd & 0xFF), NULL, 0, NULL);
break;
case OHILIOCRRT:
case HILIOCRRT:
/* Transfer the real time to the 8042 data buffer */
send_hil_cmd(sc->sc_addr, (cmd & 0xFF), NULL, 0, NULL);
/* Read each byte of the real time */
buf = data;
for (i = 0; i < 5; i++) {
send_hil_cmd(sc->sc_addr, HIL_READTIME + i, NULL,
0, &hold);
buf[4 - i] = hold;
}
break;
case HILIOCRT:
buf = data;
for (i = 0; i < 4; i++) {
send_hil_cmd(sc->sc_addr, (cmd & 0xFF) + i,
NULL, 0, &hold);
buf[i] = hold;
}
break;
case HILIOCID:
case OHILIOCID:
case HILIOCSC:
case HILIOCRN:
case HILIOCRS:
case HILIOCED:
send_hildev_cmd(sc, HILUNIT(dev), (cmd & 0xFF));
memcpy(data, sc->sc_cmdbuf, sc->sc_cmdbp - sc->sc_cmdbuf);
break;
case HILIOCAROFF:
case HILIOCAR1:
case HILIOCAR2:
if (sc->sc_kbddev) {
sc->sc_cmddev = sc->sc_kbddev;
send_hildev_cmd(sc, sc->sc_kbddev, (cmd & 0xFF));
sc->sc_kbdflags &= ~(KBD_AR1|KBD_AR2);
if (cmd == HILIOCAR1)
sc->sc_kbdflags |= KBD_AR1;
else if (cmd == HILIOCAR2)
sc->sc_kbdflags |= KBD_AR2;
}
break;
case HILIOCBEEP:
hilbeep(sc, (struct _hilbell *)data);
break;
case FIONBIO:
dptr = &sc->sc_device[HILUNIT(dev)];
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(sc, (struct hilqinfo *)data, l->l_proc);
break;
case HILIOCFREEQ:
error = hilqfree(sc, ((struct hilqinfo *)data)->qid, l->l_proc);
break;
case HILIOCMAPQ:
error = hilqmap(sc, *(int *)data, HILUNIT(dev), l);
break;
case HILIOCUNMAPQ:
error = hilqunmap(sc, *(int *)data, HILUNIT(dev), l->l_proc);
break;
case HILIOCHPUX:
dptr = &sc->sc_device[HILUNIT(dev)];
dptr->hd_flags |= HIL_READIN;
dptr->hd_flags &= ~HIL_QUEUEIN;
break;
case HILIOCRESET:
hilreset(sc);
break;
#ifdef DEBUG
case HILIOCTEST:
hildebug = *(int *)data;
break;
#endif
default:
error = EINVAL;
break;
}
sc->sc_cmddev = 0;
return error;
}
/*ARGSUSED*/
static int
hilpoll(dev_t dev, int events, struct lwp *l)
{
struct hil_softc *sc;
struct hilloopdev *dptr;
struct hiliqueue *qp;
int mask;
int s, revents;
sc = device_lookup_private(&hil_cd, HILLOOP(dev));
revents = events & (POLLOUT | POLLWRNORM);
/* Attempt to save some work. */
if ((events & (POLLIN | POLLRDNORM)) == 0)
return revents;
/*
* Read interface.
* Return 1 if there is something in the queue, 0 ow.
*/
dptr = &sc->sc_device[HILUNIT(dev)];
if (dptr->hd_flags & HIL_READIN) {
s = splhil();
if (dptr->hd_queue.c_cc > 0)
revents |= events & (POLLIN | POLLRDNORM);
else
selrecord(l, &dptr->hd_selr);
splx(s);
return revents;
}
/*
* 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 (HILUNIT(dev) &&
(dptr->hd_flags & (HIL_ALIVE|HIL_PSEUDO)) != HIL_ALIVE)
return revents | (events & (POLLIN | POLLRDNORM));
/*
* 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 (HILUNIT(dev) == 0)
mask = -1;
else
mask = hildevmask(HILUNIT(dev));
/*
* Must check everybody with interrupts blocked to prevent races.
*/
s = splhil();
for (qp = sc->sc_queue; qp < &sc->sc_queue[NHILQ]; qp++)
if (qp->hq_procp == l->l_proc && (mask & qp->hq_devmask) &&
qp->hq_eventqueue->hil_evqueue.head !=
qp->hq_eventqueue->hil_evqueue.tail) {
splx(s);
return revents | (events & (POLLIN | POLLRDNORM));
}
selrecord(l, &dptr->hd_selr);
splx(s);
return revents;
}
static void
filt_hilrdetach(struct knote *kn)
{
dev_t dev = (intptr_t) kn->kn_hook;
struct hil_softc *sc = device_lookup_private(&hil_cd,HILLOOP(dev));
struct hilloopdev *dptr = &sc->sc_device[HILUNIT(dev)];
int s;
s = splhil();
SLIST_REMOVE(&dptr->hd_selr.sel_klist, kn, knote, kn_selnext);
splx(s);
}
static int
filt_hilread(struct knote *kn, long hint)
{
dev_t dev = (intptr_t) kn->kn_hook;
int device = HILUNIT(dev);
struct hil_softc *sc = device_lookup_private(&hil_cd,HILLOOP(dev));
struct hilloopdev *dptr = &sc->sc_device[device];
struct hiliqueue *qp;
int mask;
if (dptr->hd_flags & HIL_READIN) {
kn->kn_data = dptr->hd_queue.c_cc;
return kn->kn_data > 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) {
kn->kn_data = 0; /* XXXLUKEM (thorpej): what to put here? */
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.
* (Interrupts are already blocked.)
*/
for (qp = sc->sc_queue; qp < &sc->sc_queue[NHILQ]; qp++) {
/* XXXLUKEM (thorpej): PROCESS CHECK! */
if (/*qp->hq_procp == l->l_proc &&*/ (mask & qp->hq_devmask) &&
qp->hq_eventqueue->hil_evqueue.head !=
qp->hq_eventqueue->hil_evqueue.tail) {
/* XXXLUKEM (thorpej): what to put here? */
kn->kn_data = 0;
return 1;
}
}
return 0;
}
static const struct filterops hilread_filtops =
{ 1, NULL, filt_hilrdetach, filt_hilread };
static const struct filterops hil_seltrue_filtops =
{ 1, NULL, filt_hilrdetach, filt_seltrue };
static int
hilkqfilter(dev_t dev, struct knote *kn)
{
struct hil_softc *sc = device_lookup_private(&hil_cd,HILLOOP(dev));
struct hilloopdev *dptr = &sc->sc_device[HILUNIT(dev)];
struct klist *klist;
int s;
switch (kn->kn_filter) {
case EVFILT_READ:
klist = &dptr->hd_selr.sel_klist;
kn->kn_fop = &hilread_filtops;
break;
case EVFILT_WRITE:
klist = &dptr->hd_selr.sel_klist;
kn->kn_fop = &hil_seltrue_filtops;
break;
default:
return 1;
}
kn->kn_hook = (void *)(intptr_t) dev; /* XXX yuck */
s = splhil();
SLIST_INSERT_HEAD(klist, kn, kn_selnext);
splx(s);
return 0;
}
/*ARGSUSED*/
static int
hilint(void *v)
{
struct hil_softc *sc = v;
struct hil_dev *hildevice = sc->sc_addr;
uint8_t c, stat;
stat = READHILSTAT(hildevice);
c = READHILDATA(hildevice); /* clears interrupt */
hil_process_int(sc, stat, c);
#if NRND > 0
rnd_add_uint32(&sc->rnd_source, (stat<<8)|c);
#endif
return 1;
}
static void
hil_process_int(struct hil_softc *sc, uint8_t stat, uint8_t c)
{
#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. */
if (c & HIL_ERROR) {
sc->sc_cmddone = true;
if (c == HIL_RECONFIG)
hilconfig(sc);
break;
}
if (c & HIL_COMMAND) {
if (c & HIL_POLLDATA) /* End of data */
hilevent(sc);
else /* End of command */
sc->sc_cmdending = true;
sc->sc_actdev = 0;
} else {
if (c & HIL_POLLDATA) { /* Start of polled data */
if (sc->sc_actdev != 0)
hilevent(sc);
sc->sc_actdev = (c & HIL_DEVMASK);
sc->sc_pollbp = sc->sc_pollbuf;
} else { /* Start of command */
if (sc->sc_cmddev == (c & HIL_DEVMASK)) {
sc->sc_cmdbp = sc->sc_cmdbuf;
sc->sc_actdev = 0;
}
}
}
return;
case HIL_DATA:
if (sc->sc_actdev != 0) /* Collecting poll data */
*sc->sc_pollbp++ = c;
else {
if (sc->sc_cmddev != 0) { /* Collecting cmd data */
if (sc->sc_cmdending) {
sc->sc_cmddone = true;
sc->sc_cmdending = false;
} else
*sc->sc_cmdbp++ = c;
}
}
return;
case 0: /* force full jump table */
default:
return;
}
}
/*
* 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)
static void
hilevent(struct hil_softc *sc)
{
struct hilloopdev *dptr = &sc->sc_device[sc->sc_actdev];
int len, mask, qnum;
uint8_t *cp, *pp;
HILQ *hq;
struct timeval ourtime;
hil_packet *proto;
int len0;
long tenths;
#ifdef DEBUG
if (hildebug & HDB_EVENTS) {
printf("hilevent: dev %d pollbuf: ", sc->sc_actdev);
printhilpollbuf(sc);
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(sc, 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 = sc->sc_pollbp - sc->sc_pollbuf) <= 0)
return;
/*
* Everybody gets the same time stamp
*/
microtime(&ourtime);
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 = sc->sc_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 successive packets we just duplicate
* the first packet.
*/
pp = (uint8_t *)&hq->hil_event[hq->hil_evqueue.tail];
if (proto == NULL) {
proto = (hil_packet *)pp;
cp = sc->sc_pollbuf;
len = len0;
*pp++ = len + 6;
*pp++ = sc->sc_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
*/
selnotify(&dptr->hd_selr, 0, 0);
dptr = &sc->sc_device[HILLOOPDEV];
selnotify(&dptr->hd_selr, 0, 0);
}
#undef HQFULL
static void
hpuxhilevent(struct hil_softc *sc, struct hilloopdev *dptr)
{
int len;
struct timeval ourtime;
long tstamp;
/*
* Everybody gets the same time stamp
*/
microtime(&ourtime);
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 = sc->sc_pollbp - sc->sc_pollbuf;
if (dptr->hd_queue.c_cc <= (HILMAXCLIST - (len + 5))) {
putc(len+5, &dptr->hd_queue);
(void)b_to_q((uint8_t *)&tstamp, sizeof tstamp,
&dptr->hd_queue);
(void)b_to_q((uint8_t *)sc->sc_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((void *)dptr);
}
selnotify(&dptr->hd_selr, 0, 0);
}
/*
* Shared queue manipulation routines
*/
static int
hilqalloc(struct hil_softc *sc, struct hilqinfo *qip, struct proc *p)
{
#ifdef DEBUG
if (hildebug & HDB_FOLLOW)
printf("hilqalloc(%d): addr %p\n", p->p_pid, qip->addr);
#endif
return EINVAL;
}
static int
hilqfree(struct hil_softc *sc, int qnum, struct proc *p)
{
#ifdef DEBUG
if (hildebug & HDB_FOLLOW)
printf("hilqfree(%d): qnum %d\n", p->p_pid, qnum);
#endif
return EINVAL;
}
static int
hilqmap(struct hil_softc *sc, int qnum, int device, struct lwp *l)
{
struct hilloopdev *dptr = &sc->sc_device[device];
int s;
#ifdef DEBUG
if (hildebug & HDB_FOLLOW)
printf("hilqmap(%d): qnum %d device %x\n",
l->l_proc->p_pid, qnum, device);
#endif
if (qnum >= NHILQ || sc->sc_queue[qnum].hq_procp != l->l_proc)
return EINVAL;
if ((dptr->hd_flags & HIL_QUEUEIN) == 0)
return EINVAL;
if (dptr->hd_qmask && kauth_cred_geteuid(l->l_cred) &&
kauth_cred_geteuid(l->l_cred) != dptr->hd_uid)
return EPERM;
sc->sc_queue[qnum].hq_devmask |= hildevmask(device);
if (dptr->hd_qmask == 0)
dptr->hd_uid = kauth_cred_geteuid(l->l_cred);
s = splhil();
dptr->hd_qmask |= hilqmask(qnum);
splx(s);
#ifdef DEBUG
if (hildebug & HDB_MASK)
printf("hilqmap(%d): devmask %x qmask %x\n",
l->l_proc->p_pid, sc->sc_queue[qnum].hq_devmask,
dptr->hd_qmask);
#endif
return 0;
}
static int
hilqunmap(struct hil_softc *sc, int qnum, int device, struct proc *p)
{
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 || sc->sc_queue[qnum].hq_procp != p)
return EINVAL;
sc->sc_queue[qnum].hq_devmask &= ~hildevmask(device);
s = splhil();
sc->sc_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, sc->sc_queue[qnum].hq_devmask,
sc->sc_device[device].hd_qmask);
#endif
return 0;
}
/*
* 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".
*/
void
hilkbdbell(void *v)
{
hilbeep(v, &default_bell);
}
void
hilkbdenable(void *v)
{
struct hil_softc *sc = v;
struct hil_dev *hildevice = HILADDR;
char db;
if (sc != NULL)
hildevice = sc->sc_addr;
/* Set the autorepeat rate */
db = ar_format(KBD_ARR);
send_hil_cmd(hildevice, HIL_SETARR, &db, 1, NULL);
/* Set the autorepeat delay */
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);
}
void
hilkbddisable(void *v)
{
}
#if NITE > 0
/*
* The following chunk of code implements HIL console keyboard
* support.
*/
static struct hil_dev *hilkbd_cn_device;
static struct ite_kbdmap hilkbd_cn_map;
static struct ite_kbdops hilkbd_cn_ops = {
hilkbdcngetc,
hilkbdenable,
hilkbdbell,
NULL,
};
extern char us_keymap[], us_shiftmap[], us_ctrlmap[];
/*
* 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.
*/
int
hilkbdcngetc(int *statp)
{
int c, stat;
int s;
if (hilkbd_cn_device == NULL)
return 0;
/*
* XXX needs to be splraise because we could be called
* XXX at splhigh, e.g. in DDB.
*/
s = splhil();
while (((stat = READHILSTAT(hilkbd_cn_device)) & HIL_DATA_RDY) == 0)
;
c = READHILDATA(hilkbd_cn_device);
splx(s);
*statp = stat;
return c;
}
/*
* Perform basic initialization of the HIL keyboard, suitable
* for early console use.
*/
int
hilkbdcnattach(bus_space_tag_t bst, bus_addr_t addr)
{
void *va;
struct kbdmap *km;
bus_space_handle_t bsh;
u_char lang;
if (bus_space_map(bst, addr, PAGE_SIZE, 0, &bsh))
return 1;
va = bus_space_vaddr(bst, bsh);
hilkbd_cn_device = (struct hil_dev *)va;
/* Default to US-ASCII keyboard. */
hilkbd_cn_map.keymap = us_keymap;
hilkbd_cn_map.shiftmap = us_shiftmap;
hilkbd_cn_map.ctrlmap = us_ctrlmap;
HILWAIT(hilkbd_cn_device);
WRITEHILCMD(hilkbd_cn_device, HIL_SETARR);
HILWAIT(hilkbd_cn_device);
WRITEHILDATA(hilkbd_cn_device, ar_format(KBD_ARR));
HILWAIT(hilkbd_cn_device);
WRITEHILCMD(hilkbd_cn_device, HIL_READKBDLANG);
HILDATAWAIT(hilkbd_cn_device);
lang = READHILDATA(hilkbd_cn_device);
for (km = kbd_map; km->kbd_code; km++) {
if (km->kbd_code == lang) {
hilkbd_cn_map.keymap = km->kbd_keymap;
hilkbd_cn_map.shiftmap = km->kbd_shiftmap;
hilkbd_cn_map.ctrlmap = km->kbd_ctrlmap;
}
}
HILWAIT(hilkbd_cn_device);
WRITEHILCMD(hilkbd_cn_device, HIL_INTON);
hilkbd_cn_ops.arg = NULL;
itekbdcnattach(&hilkbd_cn_ops, &hilkbd_cn_map);
return 0;
}
#endif /* End of HIL console keyboard code. */
/*
* Recognize 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 splvm() 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!?!
*/
int
kbdnmi(void)
{
struct hil_dev *hl_addr = HILADDR;
if ((*KBDNMISTAT & KBDNMI) == 0)
return 0;
HILWAIT(hl_addr);
WRITEHILCMD(hl_addr, HIL_CNMT);
HILWAIT(hl_addr);
WRITEHILCMD(hl_addr, HIL_CNMT);
HILWAIT(hl_addr);
return 1;
}
#define HILSECURITY 0x33
#define HILIDENTIFY 0x03
#define HILSCBIT 0x04
/*
* Called at boot time to print out info about interesting devices
*/
void
hilinfo(struct hil_softc *sc)
{
int id, len;
struct kbdmap *km;
/*
* Keyboard info.
*/
if (sc->sc_kbddev) {
aprint_normal("%s device %d: ", device_xname(sc->sc_dev),
sc->sc_kbddev);
for (km = kbd_map; km->kbd_code; km++)
if (km->kbd_code == sc->sc_kbdlang) {
aprint_normal("%s ", km->kbd_desc);
break;
}
aprint_normal("keyboard\n");
}
/*
* ID module.
* Attempt to locate the first ID module and print out its
* security code. Is this a good idea??
*/
id = hiliddev(sc);
if (id) {
sc->sc_cmdbp = sc->sc_cmdbuf;
sc->sc_cmddev = id;
send_hildev_cmd(sc, id, HILSECURITY);
len = sc->sc_cmdbp - sc->sc_cmdbuf;
sc->sc_cmdbp = sc->sc_cmdbuf;
sc->sc_cmddev = 0;
aprint_normal_dev(sc->sc_dev, "security code");
for (id = 0; id < len; id++)
aprint_normal(" %x", sc->sc_cmdbuf[id]);
while (id++ < 16)
aprint_normal(" 0");
aprint_normal("\n");
}
#if NRND > 0
/*
* attach the device into the random source list
* except from ID module (no point)
*/
if (!id) {
char buf[10];
sprintf(buf, "%s", device_xname(sc->sc_dev));
rnd_attach_source(&sc->rnd_source, buf, RND_TYPE_TTY, 0);
}
#endif
}
#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.
*/
void
hilconfig(struct hil_softc *sc)
{
u_char db;
int s;
s = splhil();
#ifdef DEBUG
if (hildebug & HDB_CONFIG) {
printf("hilconfig: reconfigured: ");
send_hil_cmd(sc->sc_addr, HIL_READLPSTAT, NULL, 0, &db);
printf("LPSTAT %x, ", db);
send_hil_cmd(sc->sc_addr, HIL_READLPCTRL, NULL, 0, &db);
printf("LPCTRL %x, ", db);
send_hil_cmd(sc->sc_addr, HIL_READKBDSADR, NULL, 0, &db);
printf("KBDSADR %x\n", db);
hilreport(sc);
}
#endif
/*
* Determine how many devices are on the loop.
* Mark those as alive and real, all others as dead.
*/
db = 0;
send_hil_cmd(sc->sc_addr, HIL_READLPSTAT, NULL, 0, &db);
sc->sc_maxdev = db & LPS_DEVMASK;
#ifdef DEBUG
if (hildebug & HDB_CONFIG)
printf("hilconfig: %d devices found\n", sc->sc_maxdev);
#endif
for (db = 1; db < NHILD; db++) {
if (db <= sc->sc_maxdev)
sc->sc_device[db].hd_flags |= HIL_ALIVE;
else
sc->sc_device[db].hd_flags &= ~HIL_ALIVE;
sc->sc_device[db].hd_flags &= ~HIL_PSEUDO;
}
#ifdef DEBUG
if (hildebug & (HDB_CONFIG|HDB_KEYBOARD))
printf("hilconfig: max device %d\n", sc->sc_maxdev);
#endif
if (sc->sc_maxdev == 0) {
sc->sc_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(sc->sc_addr, HIL_READKBDSADR, NULL, 0, &db);
#ifdef DEBUG
if (hildebug & HDB_KEYBOARD)
printf("hilconfig: keyboard: KBDSADR %x, old %d, new %d\n",
db, sc->sc_kbddev, ffs((int)db));
#endif
sc->sc_kbddev = ffs((int)db);
if (sc->sc_kbddev == 0) {
splx(s);
return;
}
/*
* Determine if the keyboard should be cooked or raw and configure it.
*/
db = (sc->sc_kbdflags & KBD_RAW) ? 0 : 1 << (sc->sc_kbddev - 1);
send_hil_cmd(sc->sc_addr, HIL_WRITEKBDSADR, &db, 1, NULL);
/*
* Re-enable autorepeat in raw mode, cooked mode AR is not affected.
*/
if (sc->sc_kbdflags & (KBD_AR1|KBD_AR2)) {
db = (sc->sc_kbdflags & KBD_AR1) ? HILAR1 : HILAR2;
sc->sc_cmddev = sc->sc_kbddev;
send_hildev_cmd(sc, sc->sc_kbddev, db);
sc->sc_cmddev = 0;
}
/*
* Determine the keyboard language configuration, but don't
* override a user-specified setting.
*/
db = 0;
send_hil_cmd(sc->sc_addr, HIL_READKBDLANG, NULL, 0, &db);
#ifdef DEBUG
if (hildebug & HDB_KEYBOARD)
printf("hilconfig: language: old %x new %x\n",
sc->sc_kbdlang, db);
#endif
if (sc->sc_kbdlang != KBD_SPECIAL) {
struct kbdmap *km;
#if NITE > 0
for (km = kbd_map; km->kbd_code; km++) {
if (km->kbd_code == db) {
sc->sc_kbdlang = db;
iteinstallkeymap(km);
break;
}
}
#endif
if (km->kbd_code == 0) {
printf("hilconfig: unknown keyboard type 0x%x, "
"using default\n", db);
}
}
splx(s);
}
void
hilreset(struct hil_softc *sc)
{
struct hil_dev *hildevice = sc->sc_addr;
u_char db;
#ifdef DEBUG
if (hildebug & HDB_FOLLOW)
printf("hilreset(%p)\n", sc);
#endif
/*
* 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
* data to clear the interrupt (if the loop reconfigured).
*/
DELAY(1000000);
if (READHILSTAT(hildevice) & HIL_DATA_RDY)
db = READHILDATA(hildevice);
/*
* 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);
}
void
hilbeep(struct hil_softc *sc, const struct _hilbell *bp)
{
struct hil_dev *hl_addr = HILADDR;
u_char buf[2];
if (sc != NULL)
hl_addr = sc->sc_addr;
buf[0] = ~((bp->duration - 10) / 10);
buf[1] = bp->frequency;
send_hil_cmd(hl_addr, HIL_SETTONE, buf, 2, NULL);
}
/*
* Locate and return the address of the first ID module, 0 if none present.
*/
int
hiliddev(struct hil_softc *sc)
{
int i, len;
#ifdef DEBUG
if (hildebug & HDB_IDMODULE)
printf("hiliddev(%p): max %d, looking for idmodule...",
sc, sc->sc_maxdev);
#endif
for (i = 1; i <= sc->sc_maxdev; i++) {
sc->sc_cmdbp = sc->sc_cmdbuf;
sc->sc_cmddev = i;
send_hildev_cmd(sc, 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 = sc->sc_cmdbp - sc->sc_cmdbuf;
if (len > 1 && (sc->sc_cmdbuf[1] & HILSCBIT) &&
(sc->sc_cmdbuf[0] & 0xF0) == 0x30) {
sc->sc_cmdbp = sc->sc_cmdbuf;
sc->sc_cmddev = i;
send_hildev_cmd(sc, i, HILSECURITY);
break;
}
}
sc->sc_cmdbp = sc->sc_cmdbuf;
sc->sc_cmddev = 0;
#ifdef DEBUG
if (hildebug & HDB_IDMODULE) {
if (i <= sc->sc_maxdev)
printf("found at %d\n", i);
else
printf("not found\n");
}
#endif
return i <= sc->sc_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 splvm() to make the transaction as atomic as
* possible without blocking the clock (is this necessary?)
*/
void
send_hil_cmd(struct hil_dev *hildevice, u_char cmd, u_char *data, u_char dlen,
u_char *rdata)
{
u_char status;
int s = splvm();
HILWAIT(hildevice);
WRITEHILCMD(hildevice, cmd);
while (dlen--) {
HILWAIT(hildevice);
WRITEHILDATA(hildevice, *data++);
}
if (rdata) {
do {
HILDATAWAIT(hildevice);
status = READHILSTAT(hildevice);
*rdata = READHILDATA(hildevice);
} 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,
* splvm (clock only interrupts) seems to be good enough in practice.
*/
void
send_hildev_cmd(struct hil_softc *sc, char device, char cmd)
{
struct hil_dev *hildevice = sc->sc_addr;
uint8_t status, c;
int s = splvm();
polloff(hildevice);
/*
* Transfer the command and device info to the chip
*/
HILWAIT(hildevice);
WRITEHILCMD(hildevice, HIL_STARTCMD);
HILWAIT(hildevice);
WRITEHILDATA(hildevice, 8 + device);
HILWAIT(hildevice);
WRITEHILDATA(hildevice, cmd);
HILWAIT(hildevice);
WRITEHILDATA(hildevice, HIL_TIMEOUT);
/*
* Trigger the command and wait for completion
*/
HILWAIT(hildevice);
WRITEHILCMD(hildevice, HIL_TRIGGER);
sc->sc_cmddone = false;
do {
HILDATAWAIT(hildevice);
status = READHILSTAT(hildevice);
c = READHILDATA(hildevice);
hil_process_int(sc, status, c);
} while (!sc->sc_cmddone);
pollon(hildevice);
splx(s);
}
/*
* Turn auto-polling off and on.
* Also disables and enable auto-repeat. Why?
*/
void
polloff(struct hil_dev *hildevice)
{
char db;
/*
* Turn off auto repeat
*/
HILWAIT(hildevice);
WRITEHILCMD(hildevice, HIL_SETARR);
HILWAIT(hildevice);
WRITEHILDATA(hildevice, 0);
/*
* Turn off auto-polling
*/
HILWAIT(hildevice);
WRITEHILCMD(hildevice, HIL_READLPCTRL);
HILDATAWAIT(hildevice);
db = READHILDATA(hildevice);
db &= ~LPC_AUTOPOLL;
HILWAIT(hildevice);
WRITEHILCMD(hildevice, HIL_WRITELPCTRL);
HILWAIT(hildevice);
WRITEHILDATA(hildevice, db);
/*
* Must wait til polling is really stopped
*/
do {
HILWAIT(hildevice);
WRITEHILCMD(hildevice, HIL_READBUSY);
HILDATAWAIT(hildevice);
db = READHILDATA(hildevice);
} while (db & BSY_LOOPBUSY);
}
void
pollon(struct hil_dev *hildevice)
{
char db;
/*
* Turn on auto polling
*/
HILWAIT(hildevice);
WRITEHILCMD(hildevice, HIL_READLPCTRL);
HILDATAWAIT(hildevice);
db = READHILDATA(hildevice);
db |= LPC_AUTOPOLL;
HILWAIT(hildevice);
WRITEHILCMD(hildevice, HIL_WRITELPCTRL);
HILWAIT(hildevice);
WRITEHILDATA(hildevice, db);
/*
* Turn on auto repeat
*/
HILWAIT(hildevice);
WRITEHILCMD(hildevice, HIL_SETARR);
HILWAIT(hildevice);
WRITEHILDATA(hildevice, ar_format(KBD_ARR));
}
#ifdef DEBUG
static void
printhilpollbuf(struct hil_softc *sc)
{
u_char *cp;
int i, len;
cp = sc->sc_pollbuf;
len = sc->sc_pollbp - cp;
for (i = 0; i < len; i++)
printf("%x ", sc->sc_pollbuf[i]);
printf("\n");
}
static void
printhilcmdbuf(struct hil_softc *sc)
{
u_char *cp;
int i, len;
cp = sc->sc_cmdbuf;
len = sc->sc_cmdbp - cp;
for (i = 0; i < len; i++)
printf("%x ", sc->sc_cmdbuf[i]);
printf("\n");
}
static void
hilreport(struct hil_softc *sc)
{
int i, len;
int s = splhil();
for (i = 1; i <= sc->sc_maxdev; i++) {
sc->sc_cmdbp = sc->sc_cmdbuf;
sc->sc_cmddev = i;
send_hildev_cmd(sc, i, HILIDENTIFY);
printf("hil%d: id: ", i);
printhilcmdbuf(sc);
len = sc->sc_cmdbp - sc->sc_cmdbuf;
if (len > 1 && (sc->sc_cmdbuf[1] & HILSCBIT)) {
sc->sc_cmdbp = sc->sc_cmdbuf;
sc->sc_cmddev = i;
send_hildev_cmd(sc, i, HILSECURITY);
printf("hil%d: sc: ", i);
printhilcmdbuf(sc);
}
}
sc->sc_cmdbp = sc->sc_cmdbuf;
sc->sc_cmddev = 0;
splx(s);
}
#endif