e0cc03a09b
kqueue provides a stateful and efficient event notification framework currently supported events include socket, file, directory, fifo, pipe, tty and device changes, and monitoring of processes and signals kqueue is supported by all writable filesystems in NetBSD tree (with exception of Coda) and all device drivers supporting poll(2) based on work done by Jonathan Lemon for FreeBSD initial NetBSD port done by Luke Mewburn and Jason Thorpe
1107 lines
24 KiB
C
1107 lines
24 KiB
C
/* $NetBSD: dca.c,v 1.53 2002/10/23 09:11:03 jdolecek Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 1996, 1997 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Jason R. Thorpe.
|
|
*
|
|
* 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 NetBSD
|
|
* Foundation, Inc. and its contributors.
|
|
* 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 1982, 1986, 1990, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* 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.
|
|
*
|
|
* @(#)dca.c 8.2 (Berkeley) 1/12/94
|
|
*/
|
|
|
|
/*
|
|
* Driver for the 98626/98644/internal serial interface on hp300/hp400,
|
|
* based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs.
|
|
*
|
|
* N.B. On the hp700 and some hp300s, there is a "secret bit" with
|
|
* undocumented behavior. The third bit of the Modem Control Register
|
|
* (MCR_IEN == 0x08) must be set to enable interrupts. Failure to do
|
|
* so can result in deadlock on those machines, whereas the don't seem to
|
|
* be any harmful side-effects from setting this bit on non-affected
|
|
* machines.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: dca.c,v 1.53 2002/10/23 09:11:03 jdolecek Exp $");
|
|
|
|
#include "opt_ddb.h"
|
|
#include "opt_kgdb.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/tty.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/file.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/device.h>
|
|
|
|
#include <machine/autoconf.h>
|
|
#include <machine/bus.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/intr.h>
|
|
|
|
#include <dev/cons.h>
|
|
|
|
#include <hp300/dev/dioreg.h>
|
|
#include <hp300/dev/diovar.h>
|
|
#include <hp300/dev/diodevs.h>
|
|
#include <hp300/dev/dcareg.h>
|
|
|
|
struct dca_softc {
|
|
struct device sc_dev; /* generic device glue */
|
|
struct dcadevice *sc_dca; /* pointer to hardware */
|
|
struct tty *sc_tty; /* our tty instance */
|
|
int sc_oflows; /* overflow counter */
|
|
short sc_flags; /* state flags */
|
|
|
|
/*
|
|
* Bits for sc_flags.
|
|
*/
|
|
#define DCA_ACTIVE 0x0001 /* indicates live unit */
|
|
#define DCA_SOFTCAR 0x0002 /* indicates soft-carrier */
|
|
#define DCA_HASFIFO 0x0004 /* indicates unit has FIFO */
|
|
#define DCA_ISCONSOLE 0x0008 /* indicates unit is console */
|
|
|
|
};
|
|
|
|
int dcamatch __P((struct device *, struct cfdata *, void *));
|
|
void dcaattach __P((struct device *, struct device *, void *));
|
|
|
|
CFATTACH_DECL(dca, sizeof(struct dca_softc),
|
|
dcamatch, dcaattach, NULL, NULL);
|
|
|
|
extern struct cfdriver dca_cd;
|
|
|
|
dev_type_open(dcaopen);
|
|
dev_type_close(dcaclose);
|
|
dev_type_read(dcaread);
|
|
dev_type_write(dcawrite);
|
|
dev_type_ioctl(dcaioctl);
|
|
dev_type_stop(dcastop);
|
|
dev_type_tty(dcatty);
|
|
dev_type_poll(dcapoll);
|
|
|
|
const struct cdevsw dca_cdevsw = {
|
|
dcaopen, dcaclose, dcaread, dcawrite, dcaioctl,
|
|
dcastop, dcatty, dcapoll, nommap, ttykqfilter, D_TTY
|
|
};
|
|
|
|
int dcaintr __P((void *));
|
|
void dcaeint __P((struct dca_softc *, int));
|
|
void dcamint __P((struct dca_softc *));
|
|
|
|
int dcaparam __P((struct tty *, struct termios *));
|
|
void dcastart __P((struct tty *));
|
|
int dcamctl __P((struct dca_softc *, int, int));
|
|
void dcainit __P((struct dcadevice *, int));
|
|
|
|
int dcacnattach __P((bus_space_tag_t, bus_addr_t, int));
|
|
int dcacngetc __P((dev_t));
|
|
void dcacnputc __P((dev_t, int));
|
|
|
|
/*
|
|
* Stuff for DCA console support.
|
|
*/
|
|
|
|
static int dcadefaultrate = TTYDEF_SPEED;
|
|
static struct consdev dca_cons = {
|
|
NULL, NULL, dcacngetc, dcacnputc, nullcnpollc, NULL, NODEV, CN_REMOTE
|
|
};
|
|
static struct dcadevice *dca_cn = NULL; /* pointer to hardware */
|
|
static int dcaconsinit; /* has been initialized */
|
|
static int dcaconscode;
|
|
|
|
struct speedtab dcaspeedtab[] = {
|
|
{ 0, 0 },
|
|
{ 50, DCABRD(50) },
|
|
{ 75, DCABRD(75) },
|
|
{ 110, DCABRD(110) },
|
|
{ 134, DCABRD(134) },
|
|
{ 150, DCABRD(150) },
|
|
{ 200, DCABRD(200) },
|
|
{ 300, DCABRD(300) },
|
|
{ 600, DCABRD(600) },
|
|
{ 1200, DCABRD(1200) },
|
|
{ 1800, DCABRD(1800) },
|
|
{ 2400, DCABRD(2400) },
|
|
{ 4800, DCABRD(4800) },
|
|
{ 9600, DCABRD(9600) },
|
|
{ 19200, DCABRD(19200) },
|
|
{ 38400, DCABRD(38400) },
|
|
{ -1, -1 },
|
|
};
|
|
|
|
#ifdef KGDB
|
|
#include <machine/remote-sl.h>
|
|
|
|
extern dev_t kgdb_dev;
|
|
extern int kgdb_rate;
|
|
extern int kgdb_debug_init;
|
|
#endif
|
|
|
|
#define DCAUNIT(x) (minor(x) & 0x7ffff)
|
|
#define DCADIALOUT(x) (minor(x) & 0x80000)
|
|
|
|
#ifdef DEBUG
|
|
long fifoin[17];
|
|
long fifoout[17];
|
|
long dcaintrcount[16];
|
|
long dcamintcount[16];
|
|
#endif
|
|
|
|
void dcainit __P((struct dcadevice *, int));
|
|
|
|
int
|
|
dcamatch(parent, match, aux)
|
|
struct device *parent;
|
|
struct cfdata *match;
|
|
void *aux;
|
|
{
|
|
struct dio_attach_args *da = aux;
|
|
|
|
switch (da->da_id) {
|
|
case DIO_DEVICE_ID_DCA0:
|
|
case DIO_DEVICE_ID_DCA0REM:
|
|
case DIO_DEVICE_ID_DCA1:
|
|
case DIO_DEVICE_ID_DCA1REM:
|
|
return (1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
dcaattach(parent, self, aux)
|
|
struct device *parent, *self;
|
|
void *aux;
|
|
{
|
|
struct dca_softc *sc = (struct dca_softc *)self;
|
|
struct dio_attach_args *da = aux;
|
|
struct dcadevice *dca;
|
|
int unit = self->dv_unit;
|
|
int scode = da->da_scode;
|
|
int ipl;
|
|
|
|
if (scode == dcaconscode) {
|
|
dca = dca_cn;
|
|
sc->sc_flags |= DCA_ISCONSOLE;
|
|
DELAY(100000);
|
|
|
|
/*
|
|
* We didn't know which unit this would be during
|
|
* the console probe, so we have to fixup cn_dev here.
|
|
*/
|
|
cn_tab->cn_dev = makedev(cdevsw_lookup_major(&dca_cdevsw),
|
|
unit);
|
|
} else {
|
|
dca = (struct dcadevice *)iomap(dio_scodetopa(da->da_scode),
|
|
da->da_size);
|
|
if (dca == NULL) {
|
|
printf("\n%s: can't map registers\n",
|
|
sc->sc_dev.dv_xname);
|
|
return;
|
|
}
|
|
}
|
|
|
|
sc->sc_dca = dca;
|
|
|
|
ipl = DIO_IPL(dca);
|
|
printf(" ipl %d", ipl);
|
|
|
|
DELAY(1000);
|
|
dca->dca_reset = 0xFF;
|
|
DELAY(1000);
|
|
|
|
/* look for a NS 16550AF UART with FIFOs */
|
|
dca->dca_fifo = FIFO_ENABLE|FIFO_RCV_RST|FIFO_XMT_RST|FIFO_TRIGGER_14;
|
|
DELAY(100);
|
|
if ((dca->dca_iir & IIR_FIFO_MASK) == IIR_FIFO_MASK)
|
|
sc->sc_flags |= DCA_HASFIFO;
|
|
|
|
/* Establish interrupt handler. */
|
|
(void) dio_intr_establish(dcaintr, sc, ipl,
|
|
(sc->sc_flags & DCA_HASFIFO) ? IPL_TTY : IPL_TTYNOBUF);
|
|
|
|
sc->sc_flags |= DCA_ACTIVE;
|
|
if (self->dv_cfdata->cf_flags)
|
|
sc->sc_flags |= DCA_SOFTCAR;
|
|
|
|
/* Enable interrupts. */
|
|
dca->dca_ic = IC_IE;
|
|
|
|
/*
|
|
* Need to reset baud rate, etc. of next print so reset dcaconsinit.
|
|
* Also make sure console is always "hardwired."
|
|
*/
|
|
if (sc->sc_flags & DCA_ISCONSOLE) {
|
|
dcaconsinit = 0;
|
|
sc->sc_flags |= DCA_SOFTCAR;
|
|
printf(": console, ");
|
|
} else
|
|
printf(": ");
|
|
|
|
if (sc->sc_flags & DCA_HASFIFO)
|
|
printf("working fifo\n");
|
|
else
|
|
printf("no fifo\n");
|
|
|
|
#ifdef KGDB
|
|
if (kgdb_dev == makedev(cdevsw_lookup_major(&dca_cdevsw), unit)) {
|
|
if (sc->sc_flags & DCA_ISCONSOLE)
|
|
kgdb_dev = NODEV; /* can't debug over console port */
|
|
else {
|
|
dcainit(dca, kgdb_rate);
|
|
dcaconsinit = 1; /* don't re-init in dcaputc */
|
|
if (kgdb_debug_init) {
|
|
/*
|
|
* Print prefix of device name,
|
|
* let kgdb_connect print the rest.
|
|
*/
|
|
printf("%s: ", sc->sc_dev.dv_xname);
|
|
kgdb_connect(1);
|
|
} else
|
|
printf("%s: kgdb enabled\n",
|
|
sc->sc_dev.dv_xname);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
dcaopen(dev, flag, mode, p)
|
|
dev_t dev;
|
|
int flag, mode;
|
|
struct proc *p;
|
|
{
|
|
int unit = DCAUNIT(dev);
|
|
struct dca_softc *sc;
|
|
struct tty *tp;
|
|
struct dcadevice *dca;
|
|
u_char code;
|
|
int s, error = 0;
|
|
|
|
if (unit >= dca_cd.cd_ndevs ||
|
|
(sc = dca_cd.cd_devs[unit]) == NULL)
|
|
return (ENXIO);
|
|
|
|
if ((sc->sc_flags & DCA_ACTIVE) == 0)
|
|
return (ENXIO);
|
|
|
|
dca = sc->sc_dca;
|
|
|
|
if (sc->sc_tty == NULL) {
|
|
tp = sc->sc_tty = ttymalloc();
|
|
tty_attach(tp);
|
|
} else
|
|
tp = sc->sc_tty;
|
|
tp->t_oproc = dcastart;
|
|
tp->t_param = dcaparam;
|
|
tp->t_dev = dev;
|
|
|
|
if ((tp->t_state & TS_ISOPEN) &&
|
|
(tp->t_state & TS_XCLUDE) &&
|
|
p->p_ucred->cr_uid != 0)
|
|
return (EBUSY);
|
|
|
|
s = spltty();
|
|
|
|
if ((tp->t_state & TS_ISOPEN) == 0 && tp->t_wopen == 0) {
|
|
/*
|
|
* Sanity clause: reset the card on first open.
|
|
* The card might be left in an inconsistent state
|
|
* if card memory is read inadvertently.
|
|
*/
|
|
dcainit(dca, dcadefaultrate);
|
|
|
|
ttychars(tp);
|
|
tp->t_iflag = TTYDEF_IFLAG;
|
|
tp->t_oflag = TTYDEF_OFLAG;
|
|
tp->t_cflag = TTYDEF_CFLAG;
|
|
tp->t_lflag = TTYDEF_LFLAG;
|
|
tp->t_ispeed = tp->t_ospeed = dcadefaultrate;
|
|
|
|
dcaparam(tp, &tp->t_termios);
|
|
ttsetwater(tp);
|
|
|
|
/* Set the FIFO threshold based on the receive speed. */
|
|
if (sc->sc_flags & DCA_HASFIFO)
|
|
dca->dca_fifo = FIFO_ENABLE | FIFO_RCV_RST |
|
|
FIFO_XMT_RST |
|
|
(tp->t_ispeed <= 1200 ? FIFO_TRIGGER_1 :
|
|
FIFO_TRIGGER_14);
|
|
|
|
/* Flush any pending I/O */
|
|
while ((dca->dca_iir & IIR_IMASK) == IIR_RXRDY)
|
|
code = dca->dca_data;
|
|
|
|
/* Set modem control state. */
|
|
(void) dcamctl(sc, MCR_DTR | MCR_RTS, DMSET);
|
|
|
|
/* Set soft-carrier if so configured. */
|
|
if ((sc->sc_flags & DCA_SOFTCAR) ||
|
|
(dcamctl(sc, 0, DMGET) & MSR_DCD))
|
|
tp->t_state |= TS_CARR_ON;
|
|
}
|
|
|
|
splx(s);
|
|
|
|
error = ttyopen(tp, DCADIALOUT(dev), (flag & O_NONBLOCK));
|
|
if (error)
|
|
goto bad;
|
|
|
|
error = (*tp->t_linesw->l_open)(dev, tp);
|
|
|
|
bad:
|
|
return (error);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
dcaclose(dev, flag, mode, p)
|
|
dev_t dev;
|
|
int flag, mode;
|
|
struct proc *p;
|
|
{
|
|
struct dca_softc *sc;
|
|
struct tty *tp;
|
|
struct dcadevice *dca;
|
|
int unit;
|
|
int s;
|
|
|
|
unit = DCAUNIT(dev);
|
|
|
|
sc = dca_cd.cd_devs[unit];
|
|
|
|
dca = sc->sc_dca;
|
|
tp = sc->sc_tty;
|
|
(*tp->t_linesw->l_close)(tp, flag);
|
|
|
|
s = spltty();
|
|
|
|
dca->dca_cfcr &= ~CFCR_SBREAK;
|
|
#ifdef KGDB
|
|
/* do not disable interrupts if debugging */
|
|
if (dev != kgdb_dev)
|
|
#endif
|
|
dca->dca_ier = 0;
|
|
if (tp->t_cflag & HUPCL && (sc->sc_flags & DCA_SOFTCAR) == 0) {
|
|
/* XXX perhaps only clear DTR */
|
|
(void) dcamctl(sc, 0, DMSET);
|
|
}
|
|
tp->t_state &= ~(TS_BUSY | TS_FLUSH);
|
|
splx(s);
|
|
ttyclose(tp);
|
|
#if 0
|
|
tty_detach(tp);
|
|
ttyfree(tp);
|
|
sc->sc_tty = NULL;
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
dcaread(dev, uio, flag)
|
|
dev_t dev;
|
|
struct uio *uio;
|
|
int flag;
|
|
{
|
|
int unit = DCAUNIT(dev);
|
|
struct dca_softc *sc;
|
|
struct tty *tp;
|
|
int error, of;
|
|
|
|
sc = dca_cd.cd_devs[unit];
|
|
|
|
tp = sc->sc_tty;
|
|
of = sc->sc_oflows;
|
|
error = (*tp->t_linesw->l_read)(tp, uio, flag);
|
|
/*
|
|
* XXX hardly a reasonable thing to do, but reporting overflows
|
|
* at interrupt time just exacerbates the problem.
|
|
*/
|
|
if (sc->sc_oflows != of)
|
|
log(LOG_WARNING, "%s: silo overflow\n", sc->sc_dev.dv_xname);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
dcawrite(dev, uio, flag)
|
|
dev_t dev;
|
|
struct uio *uio;
|
|
int flag;
|
|
{
|
|
struct dca_softc *sc = dca_cd.cd_devs[DCAUNIT(dev)];
|
|
struct tty *tp = sc->sc_tty;
|
|
|
|
return ((*tp->t_linesw->l_write)(tp, uio, flag));
|
|
}
|
|
|
|
int
|
|
dcapoll(dev, events, p)
|
|
dev_t dev;
|
|
int events;
|
|
struct proc *p;
|
|
{
|
|
struct dca_softc *sc = dca_cd.cd_devs[DCAUNIT(dev)];
|
|
struct tty *tp = sc->sc_tty;
|
|
|
|
return ((*tp->t_linesw->l_poll)(tp, events, p));
|
|
}
|
|
|
|
struct tty *
|
|
dcatty(dev)
|
|
dev_t dev;
|
|
{
|
|
struct dca_softc *sc = dca_cd.cd_devs[DCAUNIT(dev)];
|
|
|
|
return (sc->sc_tty);
|
|
}
|
|
|
|
int
|
|
dcaintr(arg)
|
|
void *arg;
|
|
{
|
|
struct dca_softc *sc = arg;
|
|
#ifdef KGDB
|
|
int unit = sc->sc_dev.dv_unit;
|
|
#endif
|
|
struct dcadevice *dca = sc->sc_dca;
|
|
struct tty *tp = sc->sc_tty;
|
|
u_char code;
|
|
int iflowdone = 0;
|
|
|
|
/*
|
|
* If interrupts aren't enabled, then the interrupt can't
|
|
* be for us.
|
|
*/
|
|
if ((dca->dca_ic & (IC_IR|IC_IE)) != (IC_IR|IC_IE))
|
|
return (0);
|
|
|
|
for (;;) {
|
|
code = dca->dca_iir;
|
|
#ifdef DEBUG
|
|
dcaintrcount[code & IIR_IMASK]++;
|
|
#endif
|
|
switch (code & IIR_IMASK) {
|
|
case IIR_NOPEND:
|
|
return (1);
|
|
case IIR_RXTOUT:
|
|
case IIR_RXRDY:
|
|
/* do time-critical read in-line */
|
|
/*
|
|
* Process a received byte. Inline for speed...
|
|
*/
|
|
#ifdef KGDB
|
|
#define RCVBYTE() \
|
|
code = dca->dca_data; \
|
|
if (tp == NULL || (tp->t_state & TS_ISOPEN) == 0) { \
|
|
int maj; \
|
|
maj = cdevsw_lookup_major(&dca_cdevsw); \
|
|
if (code == FRAME_END && \
|
|
kgdb_dev == makedev(maj, unit)) \
|
|
kgdb_connect(0); /* trap into kgdb */ \
|
|
} else \
|
|
(*tp->t_linesw->l_rint)(code, tp)
|
|
#else
|
|
#define RCVBYTE() \
|
|
code = dca->dca_data; \
|
|
if (tp != NULL && (tp->t_state & TS_ISOPEN) != 0) \
|
|
(*tp->t_linesw->l_rint)(code, tp)
|
|
#endif
|
|
RCVBYTE();
|
|
if (sc->sc_flags & DCA_HASFIFO) {
|
|
#ifdef DEBUG
|
|
int fifocnt = 1;
|
|
#endif
|
|
while ((code = dca->dca_lsr) & LSR_RCV_MASK) {
|
|
if (code == LSR_RXRDY) {
|
|
RCVBYTE();
|
|
} else
|
|
dcaeint(sc, code);
|
|
#ifdef DEBUG
|
|
fifocnt++;
|
|
#endif
|
|
}
|
|
#ifdef DEBUG
|
|
if (fifocnt > 16)
|
|
fifoin[0]++;
|
|
else
|
|
fifoin[fifocnt]++;
|
|
#endif
|
|
}
|
|
if (!iflowdone && tp != NULL &&
|
|
(tp->t_cflag&CRTS_IFLOW) &&
|
|
tp->t_rawq.c_cc > TTYHOG/2) {
|
|
dca->dca_mcr &= ~MCR_RTS;
|
|
iflowdone = 1;
|
|
}
|
|
break;
|
|
case IIR_TXRDY:
|
|
if (tp == NULL)
|
|
break;
|
|
tp->t_state &=~ (TS_BUSY|TS_FLUSH);
|
|
(*tp->t_linesw->l_start)(tp);
|
|
break;
|
|
case IIR_RLS:
|
|
dcaeint(sc, dca->dca_lsr);
|
|
break;
|
|
default:
|
|
if (code & IIR_NOPEND)
|
|
return (1);
|
|
log(LOG_WARNING, "%s: weird interrupt: 0x%x\n",
|
|
sc->sc_dev.dv_xname, code);
|
|
/* fall through */
|
|
case IIR_MLSC:
|
|
dcamint(sc);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
dcaeint(sc, stat)
|
|
struct dca_softc *sc;
|
|
int stat;
|
|
{
|
|
struct tty *tp = sc->sc_tty;
|
|
struct dcadevice *dca = sc->sc_dca;
|
|
int c;
|
|
|
|
c = dca->dca_data;
|
|
if (tp == NULL || (tp->t_state & TS_ISOPEN) == 0) {
|
|
#ifdef KGDB
|
|
/* we don't care about parity errors */
|
|
if (((stat & (LSR_BI|LSR_FE|LSR_PE)) == LSR_PE) &&
|
|
kgdb_dev == makedev(cdevsw_lookup_major(&dca_cdevsw),
|
|
sc->sc_hd->hp_unit)
|
|
&& c == FRAME_END)
|
|
kgdb_connect(0); /* trap into kgdb */
|
|
#endif
|
|
return;
|
|
}
|
|
#ifdef DDB
|
|
if ((sc->sc_flags & DCA_ISCONSOLE) && (stat & LSR_BI)) {
|
|
Debugger();
|
|
return;
|
|
}
|
|
#endif
|
|
if (stat & (LSR_BI | LSR_FE))
|
|
c |= TTY_FE;
|
|
else if (stat & LSR_PE)
|
|
c |= TTY_PE;
|
|
else if (stat & LSR_OE)
|
|
sc->sc_oflows++;
|
|
(*tp->t_linesw->l_rint)(c, tp);
|
|
}
|
|
|
|
void
|
|
dcamint(sc)
|
|
struct dca_softc *sc;
|
|
{
|
|
struct tty *tp = sc->sc_tty;
|
|
struct dcadevice *dca = sc->sc_dca;
|
|
u_char stat;
|
|
|
|
stat = dca->dca_msr;
|
|
#ifdef DEBUG
|
|
dcamintcount[stat & 0xf]++;
|
|
#endif
|
|
if (tp == NULL)
|
|
return;
|
|
if ((stat & MSR_DDCD) &&
|
|
(sc->sc_flags & DCA_SOFTCAR) == 0) {
|
|
if (stat & MSR_DCD)
|
|
(void)(*tp->t_linesw->l_modem)(tp, 1);
|
|
else if ((*tp->t_linesw->l_modem)(tp, 0) == 0)
|
|
dca->dca_mcr &= ~(MCR_DTR | MCR_RTS);
|
|
}
|
|
/*
|
|
* CTS change.
|
|
* If doing HW output flow control start/stop output as appropriate.
|
|
*/
|
|
if ((stat & MSR_DCTS) &&
|
|
(tp->t_state & TS_ISOPEN) && (tp->t_cflag & CCTS_OFLOW)) {
|
|
if (stat & MSR_CTS) {
|
|
tp->t_state &=~ TS_TTSTOP;
|
|
dcastart(tp);
|
|
} else {
|
|
tp->t_state |= TS_TTSTOP;
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
dcaioctl(dev, cmd, data, flag, p)
|
|
dev_t dev;
|
|
u_long cmd;
|
|
caddr_t data;
|
|
int flag;
|
|
struct proc *p;
|
|
{
|
|
int unit = DCAUNIT(dev);
|
|
struct dca_softc *sc = dca_cd.cd_devs[unit];
|
|
struct tty *tp = sc->sc_tty;
|
|
struct dcadevice *dca = sc->sc_dca;
|
|
int error;
|
|
|
|
error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, p);
|
|
if (error != EPASSTHROUGH)
|
|
return (error);
|
|
|
|
error = ttioctl(tp, cmd, data, flag, p);
|
|
if (error != EPASSTHROUGH)
|
|
return (error);
|
|
|
|
switch (cmd) {
|
|
case TIOCSBRK:
|
|
dca->dca_cfcr |= CFCR_SBREAK;
|
|
break;
|
|
|
|
case TIOCCBRK:
|
|
dca->dca_cfcr &= ~CFCR_SBREAK;
|
|
break;
|
|
|
|
case TIOCSDTR:
|
|
(void) dcamctl(sc, MCR_DTR | MCR_RTS, DMBIS);
|
|
break;
|
|
|
|
case TIOCCDTR:
|
|
(void) dcamctl(sc, MCR_DTR | MCR_RTS, DMBIC);
|
|
break;
|
|
|
|
case TIOCMSET:
|
|
(void) dcamctl(sc, *(int *)data, DMSET);
|
|
break;
|
|
|
|
case TIOCMBIS:
|
|
(void) dcamctl(sc, *(int *)data, DMBIS);
|
|
break;
|
|
|
|
case TIOCMBIC:
|
|
(void) dcamctl(sc, *(int *)data, DMBIC);
|
|
break;
|
|
|
|
case TIOCMGET:
|
|
*(int *)data = dcamctl(sc, 0, DMGET);
|
|
break;
|
|
|
|
case TIOCGFLAGS: {
|
|
int bits = 0;
|
|
|
|
if (sc->sc_flags & DCA_SOFTCAR)
|
|
bits |= TIOCFLAG_SOFTCAR;
|
|
|
|
if (tp->t_cflag & CLOCAL)
|
|
bits |= TIOCFLAG_CLOCAL;
|
|
|
|
*(int *)data = bits;
|
|
break;
|
|
}
|
|
|
|
case TIOCSFLAGS: {
|
|
int userbits;
|
|
|
|
error = suser(p->p_ucred, &p->p_acflag);
|
|
if (error)
|
|
return (EPERM);
|
|
|
|
userbits = *(int *)data;
|
|
|
|
if ((userbits & TIOCFLAG_SOFTCAR) ||
|
|
(sc->sc_flags & DCA_ISCONSOLE))
|
|
sc->sc_flags |= DCA_SOFTCAR;
|
|
|
|
if (userbits & TIOCFLAG_CLOCAL)
|
|
tp->t_cflag |= CLOCAL;
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return (EPASSTHROUGH);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
dcaparam(tp, t)
|
|
struct tty *tp;
|
|
struct termios *t;
|
|
{
|
|
int unit = DCAUNIT(tp->t_dev);
|
|
struct dca_softc *sc = dca_cd.cd_devs[unit];
|
|
struct dcadevice *dca = sc->sc_dca;
|
|
int cfcr, cflag = t->c_cflag;
|
|
int ospeed = ttspeedtab(t->c_ospeed, dcaspeedtab);
|
|
int s;
|
|
|
|
/* check requested parameters */
|
|
if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
|
|
return (EINVAL);
|
|
|
|
switch (cflag & CSIZE) {
|
|
case CS5:
|
|
cfcr = CFCR_5BITS;
|
|
break;
|
|
|
|
case CS6:
|
|
cfcr = CFCR_6BITS;
|
|
break;
|
|
|
|
case CS7:
|
|
cfcr = CFCR_7BITS;
|
|
break;
|
|
|
|
case CS8:
|
|
default: /* XXX gcc whines about cfcr being unitialized... */
|
|
cfcr = CFCR_8BITS;
|
|
break;
|
|
}
|
|
if (cflag & PARENB) {
|
|
cfcr |= CFCR_PENAB;
|
|
if ((cflag & PARODD) == 0)
|
|
cfcr |= CFCR_PEVEN;
|
|
}
|
|
if (cflag & CSTOPB)
|
|
cfcr |= CFCR_STOPB;
|
|
|
|
s = spltty();
|
|
|
|
if (ospeed == 0)
|
|
(void) dcamctl(sc, 0, DMSET); /* hang up line */
|
|
|
|
/*
|
|
* Set the FIFO threshold based on the receive speed, if we
|
|
* are changing it.
|
|
*/
|
|
if (tp->t_ispeed != t->c_ispeed) {
|
|
if (sc->sc_flags & DCA_HASFIFO)
|
|
dca->dca_fifo = FIFO_ENABLE |
|
|
(t->c_ispeed <= 1200 ? FIFO_TRIGGER_1 :
|
|
FIFO_TRIGGER_14);
|
|
}
|
|
|
|
if (ospeed != 0) {
|
|
dca->dca_cfcr |= CFCR_DLAB;
|
|
dca->dca_data = ospeed & 0xFF;
|
|
dca->dca_ier = ospeed >> 8;
|
|
dca->dca_cfcr = cfcr;
|
|
} else
|
|
dca->dca_cfcr = cfcr;
|
|
|
|
/* and copy to tty */
|
|
tp->t_ispeed = t->c_ispeed;
|
|
tp->t_ospeed = t->c_ospeed;
|
|
tp->t_cflag = cflag;
|
|
|
|
dca->dca_ier = IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC;
|
|
dca->dca_mcr |= MCR_IEN;
|
|
|
|
splx(s);
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
dcastart(tp)
|
|
struct tty *tp;
|
|
{
|
|
int s, c, unit = DCAUNIT(tp->t_dev);
|
|
struct dca_softc *sc = dca_cd.cd_devs[unit];
|
|
struct dcadevice *dca = sc->sc_dca;
|
|
|
|
s = spltty();
|
|
|
|
if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP))
|
|
goto out;
|
|
if (tp->t_outq.c_cc <= tp->t_lowat) {
|
|
if (tp->t_state & TS_ASLEEP) {
|
|
tp->t_state &= ~TS_ASLEEP;
|
|
wakeup((caddr_t)&tp->t_outq);
|
|
}
|
|
if (tp->t_outq.c_cc == 0)
|
|
goto out;
|
|
selwakeup(&tp->t_wsel);
|
|
}
|
|
if (dca->dca_lsr & LSR_TXRDY) {
|
|
tp->t_state |= TS_BUSY;
|
|
if (sc->sc_flags & DCA_HASFIFO) {
|
|
for (c = 0; c < 16 && tp->t_outq.c_cc; ++c)
|
|
dca->dca_data = getc(&tp->t_outq);
|
|
#ifdef DEBUG
|
|
if (c > 16)
|
|
fifoout[0]++;
|
|
else
|
|
fifoout[c]++;
|
|
#endif
|
|
} else
|
|
dca->dca_data = getc(&tp->t_outq);
|
|
}
|
|
|
|
out:
|
|
splx(s);
|
|
}
|
|
|
|
/*
|
|
* Stop output on a line.
|
|
*/
|
|
/*ARGSUSED*/
|
|
void
|
|
dcastop(tp, flag)
|
|
struct tty *tp;
|
|
int flag;
|
|
{
|
|
int s;
|
|
|
|
s = spltty();
|
|
if (tp->t_state & TS_BUSY)
|
|
if ((tp->t_state & TS_TTSTOP) == 0)
|
|
tp->t_state |= TS_FLUSH;
|
|
splx(s);
|
|
}
|
|
|
|
int
|
|
dcamctl(sc, bits, how)
|
|
struct dca_softc *sc;
|
|
int bits, how;
|
|
{
|
|
struct dcadevice *dca = sc->sc_dca;
|
|
int s;
|
|
|
|
/*
|
|
* Always make sure MCR_IEN is set (unless setting to 0)
|
|
*/
|
|
#ifdef KGDB
|
|
if (how == DMSET &&
|
|
kgdb_dev == makedev(cdevsw_lookup_major(&dca_cdevsw),
|
|
sc->sc_hd->hp_unit))
|
|
bits |= MCR_IEN;
|
|
else
|
|
#endif
|
|
if (how == DMBIS || (how == DMSET && bits))
|
|
bits |= MCR_IEN;
|
|
else if (how == DMBIC)
|
|
bits &= ~MCR_IEN;
|
|
s = spltty();
|
|
|
|
switch (how) {
|
|
case DMSET:
|
|
dca->dca_mcr = bits;
|
|
break;
|
|
|
|
case DMBIS:
|
|
dca->dca_mcr |= bits;
|
|
break;
|
|
|
|
case DMBIC:
|
|
dca->dca_mcr &= ~bits;
|
|
break;
|
|
|
|
case DMGET:
|
|
bits = dca->dca_msr;
|
|
break;
|
|
}
|
|
splx(s);
|
|
return (bits);
|
|
}
|
|
|
|
void
|
|
dcainit(dca, rate)
|
|
struct dcadevice *dca;
|
|
int rate;
|
|
{
|
|
int s;
|
|
short stat;
|
|
|
|
s = splhigh();
|
|
|
|
dca->dca_reset = 0xFF;
|
|
DELAY(100);
|
|
dca->dca_ic = IC_IE;
|
|
|
|
dca->dca_cfcr = CFCR_DLAB;
|
|
rate = ttspeedtab(rate, dcaspeedtab);
|
|
dca->dca_data = rate & 0xFF;
|
|
dca->dca_ier = rate >> 8;
|
|
dca->dca_cfcr = CFCR_8BITS;
|
|
dca->dca_ier = IER_ERXRDY | IER_ETXRDY;
|
|
dca->dca_fifo =
|
|
FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_1;
|
|
dca->dca_mcr = MCR_DTR | MCR_RTS;
|
|
DELAY(100);
|
|
stat = dca->dca_iir;
|
|
splx(s);
|
|
}
|
|
|
|
/*
|
|
* Following are all routines needed for DCA to act as console
|
|
*/
|
|
|
|
int
|
|
dcacnattach(bus_space_tag_t bst, bus_addr_t addr, int scode)
|
|
{
|
|
bus_space_handle_t bsh;
|
|
caddr_t va;
|
|
struct dcadevice *dca;
|
|
#ifdef KGDB
|
|
extern const struct cdevsw ctty_cdevsw;
|
|
#endif
|
|
|
|
if (bus_space_map(bst, addr, DIOCSIZE, 0, &bsh))
|
|
return (1);
|
|
|
|
va = bus_space_vaddr(bst, bsh);
|
|
dca = (struct dcadevice *)va;
|
|
|
|
switch (dca->dca_id) {
|
|
#ifdef CONSCODE
|
|
case DCAID0:
|
|
case DCAID1:
|
|
#endif
|
|
case DCAREMID0:
|
|
case DCAREMID1:
|
|
break;
|
|
default:
|
|
bus_space_unmap(bst, bsh, DIOCSIZE);
|
|
return (1);
|
|
}
|
|
|
|
dcainit(dca, dcadefaultrate);
|
|
dcaconsinit = 1;
|
|
dcaconscode = scode;
|
|
dca_cn = dca;
|
|
|
|
/* initialize required fields */
|
|
cn_tab = &dca_cons;
|
|
cn_tab->cn_dev = makedev(cdevsw_lookup_major(&dca_cdevsw), 0);
|
|
|
|
#ifdef KGDB
|
|
if (cdevsw_lookup(kgdb_dev) == &ctty_cdevsw)
|
|
kgdb_dev = makedev(maj, minor(kgdb_dev));
|
|
#endif
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
dcacngetc(dev)
|
|
dev_t dev;
|
|
{
|
|
u_char stat;
|
|
int c, s;
|
|
|
|
#ifdef lint
|
|
stat = dev; if (stat) return (0);
|
|
#endif
|
|
|
|
s = splhigh();
|
|
while (((stat = dca_cn->dca_lsr) & LSR_RXRDY) == 0)
|
|
;
|
|
c = dca_cn->dca_data;
|
|
stat = dca_cn->dca_iir;
|
|
splx(s);
|
|
return (c);
|
|
}
|
|
|
|
/*
|
|
* Console kernel output character routine.
|
|
*/
|
|
/* ARGSUSED */
|
|
void
|
|
dcacnputc(dev, c)
|
|
dev_t dev;
|
|
int c;
|
|
{
|
|
int timo;
|
|
u_char stat;
|
|
int s = splhigh();
|
|
|
|
#ifdef lint
|
|
stat = dev; if (stat) return;
|
|
#endif
|
|
|
|
if (dcaconsinit == 0) {
|
|
dcainit(dca_cn, dcadefaultrate);
|
|
dcaconsinit = 1;
|
|
}
|
|
/* wait for any pending transmission to finish */
|
|
timo = 50000;
|
|
while (((stat = dca_cn->dca_lsr) & LSR_TXRDY) == 0 && --timo)
|
|
;
|
|
dca_cn->dca_data = c;
|
|
/* wait for this transmission to complete */
|
|
timo = 1500000;
|
|
while (((stat = dca_cn->dca_lsr) & LSR_TXRDY) == 0 && --timo)
|
|
;
|
|
/* clear any interrupts generated by this transmission */
|
|
stat = dca_cn->dca_iir;
|
|
splx(s);
|
|
}
|