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
1296 lines
30 KiB
C
1296 lines
30 KiB
C
/* $NetBSD: com.c,v 1.30 2002/10/23 09:12:41 jdolecek Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 1998 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Charles M. Hannum.
|
|
*
|
|
* 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) 1991 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.
|
|
*
|
|
* @(#)com.c 7.5 (Berkeley) 5/16/91
|
|
*/
|
|
|
|
/*
|
|
* COM driver, based on HP dca driver
|
|
* uses National Semiconductor NS16450/NS16550AF UART
|
|
*/
|
|
|
|
#include "opt_ddb.h"
|
|
#include "opt_kgdb.h"
|
|
#include "opt_com.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/select.h>
|
|
#include <sys/tty.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/user.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/file.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/types.h>
|
|
#include <sys/device.h>
|
|
|
|
#include <machine/cpu.h>
|
|
#if 0
|
|
#include <machine/pio.h>
|
|
#endif
|
|
|
|
#include <x68k/x68k/iodevice.h>
|
|
#if 0
|
|
#include <dev/isa/isavar.h>
|
|
#endif
|
|
#include <x68k/dev/comreg.h>
|
|
#include <dev/ic/ns16550reg.h>
|
|
#ifdef COM_HAYESP
|
|
#include <dev/ic/hayespreg.h>
|
|
#endif
|
|
#define com_lcr com_cfcr
|
|
|
|
#define COM_IBUFSIZE (2 * 512)
|
|
#define COM_IHIGHWATER ((3 * COM_IBUFSIZE) / 4)
|
|
|
|
struct com_softc {
|
|
struct device sc_dev;
|
|
void *sc_ih;
|
|
struct tty *sc_tty;
|
|
|
|
struct callout sc_diag_ch;
|
|
|
|
int sc_overflows;
|
|
int sc_floods;
|
|
int sc_errors;
|
|
|
|
int sc_halt;
|
|
|
|
int sc_iobase;
|
|
#ifdef COM_HAYESP
|
|
int sc_hayespbase;
|
|
#endif
|
|
u_char sc_hwflags;
|
|
#define COM_HW_NOIEN 0x01
|
|
#define COM_HW_FIFO 0x02
|
|
#define COM_HW_HAYESP 0x04
|
|
#define COM_HW_CONSOLE 0x40
|
|
u_char sc_swflags;
|
|
#define COM_SW_SOFTCAR 0x01
|
|
#define COM_SW_CLOCAL 0x02
|
|
#define COM_SW_CRTSCTS 0x04
|
|
#define COM_SW_MDMBUF 0x08
|
|
u_char sc_msr, sc_mcr, sc_lcr, sc_ier;
|
|
u_char sc_dtr;
|
|
|
|
u_char *sc_ibuf, *sc_ibufp, *sc_ibufhigh, *sc_ibufend;
|
|
u_char sc_ibufs[2][COM_IBUFSIZE];
|
|
};
|
|
|
|
struct callout com_poll_ch = CALLOUT_INITIALIZER;
|
|
|
|
int comprobe __P((struct device *, struct cfdata *, void *));
|
|
void comattach __P((struct device *, struct device *, void *));
|
|
|
|
static int comprobe1 __P((int));
|
|
static void comdiag __P((void *));
|
|
int comintr __P((void *));
|
|
static void compollin __P((void *));
|
|
static int comparam __P((struct tty *, struct termios *));
|
|
static void comstart __P((struct tty *));
|
|
static void cominit __P((int, int));
|
|
static int comspeed __P((long));
|
|
|
|
static u_char tiocm_xxx2mcr __P((int));
|
|
|
|
CFATTACH_DECL(xcom, sizeof(struct com_softc),
|
|
comprobe, comattach, NULL, NULL);
|
|
|
|
extern struct cfdriver xcom_cd;
|
|
|
|
dev_type_open(comopen);
|
|
dev_type_close(comclose);
|
|
dev_type_read(comread);
|
|
dev_type_write(comwrite);
|
|
dev_type_ioctl(comioctl);
|
|
dev_type_stop(comstop);
|
|
dev_type_tty(comtty);
|
|
dev_type_poll(compoll);
|
|
|
|
const struct cdevsw xcom_cdevsw = {
|
|
comopen, comclose, comread, comwrite, comioctl,
|
|
comstop, comtty, compoll, nommap, ttykqfilter, D_TTY
|
|
};
|
|
|
|
#define outb(addr, val) *(u_char *)(addr) = (val)
|
|
#define inb(addr) *(u_char *)(addr)
|
|
#define pio(addr,regno) ((addr)+1+(regno)*2)
|
|
|
|
#ifdef COMCONSOLE
|
|
int comdefaultrate = CONSPEED;
|
|
int comconsole = COMCONSOLE;
|
|
#else
|
|
int comdefaultrate = TTYDEF_SPEED;
|
|
int comconsole = -1;
|
|
#endif
|
|
int comconsinit;
|
|
int comsopen = 0;
|
|
int comevents = 0;
|
|
|
|
#ifdef KGDB
|
|
#include <machine/remote-sl.h>
|
|
extern int kgdb_dev;
|
|
extern int kgdb_rate;
|
|
extern int kgdb_debug_init;
|
|
#endif
|
|
|
|
#define COMUNIT(x) (minor(x) & 0x7F)
|
|
#define COMDIALOUT(x) (minor(x) & 0x80)
|
|
|
|
/* Macros to clear/set/test flags. */
|
|
#define SET(t, f) (t) |= (f)
|
|
#define CLR(t, f) (t) &= ~(f)
|
|
#define ISSET(t, f) ((t) & (f))
|
|
|
|
static int
|
|
comspeed(speed)
|
|
long speed;
|
|
{
|
|
#define divrnd(n, q) (((n)*2/(q)+1)/2) /* divide and round off */
|
|
|
|
int x, err;
|
|
|
|
if (speed == 0)
|
|
return 0;
|
|
if (speed < 0)
|
|
return -1;
|
|
x = divrnd((COM_FREQ / 16), speed);
|
|
if (x <= 0)
|
|
return -1;
|
|
err = divrnd((COM_FREQ / 16) * 1000, speed * x) - 1000;
|
|
if (err < 0)
|
|
err = -err;
|
|
if (err > COM_TOLERANCE)
|
|
return -1;
|
|
return x;
|
|
|
|
#undef divrnd
|
|
}
|
|
|
|
static int
|
|
comprobe1(iobase)
|
|
int iobase;
|
|
{
|
|
|
|
if (badbaddr((caddr_t)pio(iobase, com_lcr)))
|
|
return 0;
|
|
/* force access to id reg */
|
|
outb(pio(iobase , com_lcr), 0);
|
|
outb(pio(iobase , com_iir), 0);
|
|
if (inb(pio(iobase , com_iir)) & 0x38)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
#ifdef COM_HAYESP
|
|
int
|
|
comprobeHAYESP(iobase, sc)
|
|
int iobase;
|
|
struct com_softc *sc;
|
|
{
|
|
char val, dips;
|
|
int combaselist[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
|
|
|
|
/*
|
|
* Hayes ESP cards have two iobases. One is for compatibility with
|
|
* 16550 serial chips, and at the same ISA PC base addresses. The
|
|
* other is for ESP-specific enhanced features, and lies at a
|
|
* different addressing range entirely (0x140, 0x180, 0x280, or 0x300).
|
|
*/
|
|
|
|
/* Test for ESP signature */
|
|
if ((inb(iobase) & 0xf3) == 0)
|
|
return 0;
|
|
|
|
/*
|
|
* ESP is present at ESP enhanced base address; unknown com port
|
|
*/
|
|
|
|
/* Get the dip-switch configurations */
|
|
outb(iobase + HAYESP_CMD1, HAYESP_GETDIPS);
|
|
dips = inb(iobase + HAYESP_STATUS1);
|
|
|
|
/* Determine which com port this ESP card services: bits 0,1 of */
|
|
/* dips is the port # (0-3); combaselist[val] is the com_iobase */
|
|
if (sc->sc_iobase != combaselist[dips & 0x03])
|
|
return 0;
|
|
|
|
printf(": ESP");
|
|
|
|
/* Check ESP Self Test bits. */
|
|
/* Check for ESP version 2.0: bits 4,5,6 == 010 */
|
|
outb(iobase + HAYESP_CMD1, HAYESP_GETTEST);
|
|
val = inb(iobase + HAYESP_STATUS1); /* Clear reg 1 */
|
|
val = inb(iobase + HAYESP_STATUS2);
|
|
if ((val & 0x70) < 0x20) {
|
|
printf("-old (%o)", val & 0x70);
|
|
/* we do not support the necessary features */
|
|
return 0;
|
|
}
|
|
|
|
/* Check for ability to emulate 16550: bit 8 == 1 */
|
|
if ((dips & 0x80) == 0) {
|
|
printf(" slave");
|
|
/* XXX Does slave really mean no 16550 support?? */
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* If we made it this far, we are a full-featured ESP v2.0 (or
|
|
* better), at the correct com port address.
|
|
*/
|
|
|
|
SET(sc->sc_hwflags, COM_HW_HAYESP);
|
|
printf(", 1024 byte fifo\n");
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
comprobe(parent, cfp, aux)
|
|
struct device *parent;
|
|
struct cfdata *cfp;
|
|
void *aux;
|
|
{
|
|
#if 0
|
|
struct isa_attach_args *ia = aux;
|
|
#endif
|
|
int iobase = (int)&IODEVbase->psx16550;
|
|
|
|
if (strcmp(aux, "com") || cfp->cf_unit > 1)
|
|
return 0;
|
|
|
|
if (!comprobe1(iobase))
|
|
return 0;
|
|
|
|
#if 0
|
|
ia->ia_iosize = COM_NPORTS;
|
|
ia->ia_msize = 0;
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
comattach(parent, dev, aux)
|
|
struct device *parent;
|
|
struct device *dev;
|
|
void *aux;
|
|
{
|
|
struct com_softc *sc = (struct com_softc *)dev;
|
|
#if 0
|
|
struct isa_attach_args *ia = aux;
|
|
struct cfdata *cf = sc->sc_dev.dv_cfdata;
|
|
#endif
|
|
int iobase = (int)&IODEVbase->psx16550 + (COM_NPORTS * 2 * sc->sc_dev.dv_unit);
|
|
#ifdef COM_HAYESP
|
|
int hayesp_ports[] = { 0x140, 0x180, 0x280, 0x300, 0 };
|
|
int *hayespp;
|
|
#endif
|
|
|
|
callout_init(&sc->sc_diag_ch);
|
|
|
|
sc->sc_iobase = iobase;
|
|
sc->sc_hwflags = 0;
|
|
sc->sc_swflags = 0;
|
|
printf(": iobase %x", sc->sc_iobase);
|
|
|
|
#if 0
|
|
if (sc->sc_dev.dv_unit == comconsole)
|
|
delay(1000);
|
|
#endif
|
|
|
|
#ifdef COM_HAYESP
|
|
/* Look for a Hayes ESP board. */
|
|
for (hayespp = hayesp_ports; *hayespp != 0; hayespp++)
|
|
if (comprobeHAYESP(*hayespp, sc)) {
|
|
sc->sc_hayespbase = *hayespp;
|
|
break;
|
|
}
|
|
/* No ESP; look for other things. */
|
|
if (*hayespp == 0) {
|
|
#endif
|
|
|
|
/* look for a NS 16550AF UART with FIFOs */
|
|
outb(pio(iobase , com_fifo),
|
|
FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_14);
|
|
DELAY(100);
|
|
if (ISSET(inb(pio(iobase , com_iir)), IIR_FIFO_MASK) == IIR_FIFO_MASK)
|
|
if (ISSET(inb(pio(iobase , com_fifo)), FIFO_TRIGGER_14) == FIFO_TRIGGER_14) {
|
|
SET(sc->sc_hwflags, COM_HW_FIFO);
|
|
printf(": ns16550a, working fifo\n");
|
|
} else
|
|
printf(": ns16550, broken fifo\n");
|
|
else
|
|
printf(": ns8250 or ns16450, no fifo\n");
|
|
outb(pio(iobase , com_fifo), 0);
|
|
#ifdef COM_HAYESP
|
|
}
|
|
#endif
|
|
|
|
/* disable interrupts */
|
|
outb(pio(iobase , com_ier), 0);
|
|
outb(pio(iobase , com_mcr), 0);
|
|
|
|
outb(pio(iobase , com_scratch), ((u_char)240 + sc->sc_dev.dv_unit));
|
|
|
|
#if 0
|
|
if (ia->ia_irq != IRQUNK)
|
|
sc->sc_ih = isa_intr_establish(ia->ia_irq, IST_EDGE, IPL_TTY,
|
|
comintr, sc);
|
|
|
|
#ifdef KGDB
|
|
if (kgdb_dev == makedev(cdevsw_lookup_major(&xcom_cdevsw), unit)) {
|
|
if (comconsole == unit)
|
|
kgdb_dev = -1; /* can't debug over console port */
|
|
else {
|
|
cominit(unit, kgdb_rate);
|
|
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
|
|
#endif
|
|
|
|
if (sc->sc_dev.dv_unit == comconsole) {
|
|
/*
|
|
* Need to reset baud rate, etc. of next print so reset
|
|
* comconsinit. Also make sure console is always "hardwired".
|
|
*/
|
|
comconsinit = 0;
|
|
SET(sc->sc_hwflags, COM_HW_CONSOLE);
|
|
SET(sc->sc_swflags, COM_SW_SOFTCAR);
|
|
}
|
|
}
|
|
|
|
int
|
|
comopen(dev, flag, mode, p)
|
|
dev_t dev;
|
|
int flag, mode;
|
|
struct proc *p;
|
|
{
|
|
int unit = COMUNIT(dev);
|
|
struct com_softc *sc;
|
|
int iobase;
|
|
struct tty *tp;
|
|
int s;
|
|
int error = 0;
|
|
|
|
if (unit >= xcom_cd.cd_ndevs)
|
|
return ENXIO;
|
|
sc = xcom_cd.cd_devs[unit];
|
|
if (!sc)
|
|
return ENXIO;
|
|
|
|
if (!sc->sc_tty) {
|
|
tp = sc->sc_tty = ttymalloc();
|
|
tty_attach(tp);
|
|
} else
|
|
tp = sc->sc_tty;
|
|
|
|
tp->t_oproc = comstart;
|
|
tp->t_param = comparam;
|
|
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 (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
|
|
ttychars(tp);
|
|
tp->t_iflag = TTYDEF_IFLAG;
|
|
tp->t_oflag = TTYDEF_OFLAG;
|
|
tp->t_cflag = TTYDEF_CFLAG;
|
|
if (ISSET(sc->sc_swflags, COM_SW_CLOCAL))
|
|
SET(tp->t_cflag, CLOCAL);
|
|
if (ISSET(sc->sc_swflags, COM_SW_CRTSCTS))
|
|
SET(tp->t_cflag, CRTSCTS);
|
|
if (ISSET(sc->sc_swflags, COM_SW_MDMBUF))
|
|
SET(tp->t_cflag, MDMBUF);
|
|
tp->t_lflag = TTYDEF_LFLAG;
|
|
tp->t_ispeed = tp->t_ospeed = comdefaultrate;
|
|
|
|
comparam(tp, &tp->t_termios);
|
|
ttsetwater(tp);
|
|
|
|
if (comsopen++ == 0)
|
|
callout_reset(&com_poll_ch, 1, compollin, NULL);
|
|
|
|
sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0];
|
|
sc->sc_ibufhigh = sc->sc_ibuf + COM_IHIGHWATER;
|
|
sc->sc_ibufend = sc->sc_ibuf + COM_IBUFSIZE;
|
|
|
|
iobase = sc->sc_iobase;
|
|
#ifdef COM_HAYESP
|
|
/* Setup the ESP board */
|
|
if (ISSET(sc->sc_hwflags, COM_HW_HAYESP)) {
|
|
int hayespbase = sc->sc_hayespbase;
|
|
|
|
outb(iobase + com_fifo,
|
|
FIFO_DMA_MODE|FIFO_ENABLE|
|
|
FIFO_RCV_RST|FIFO_XMT_RST|FIFO_TRIGGER_8);
|
|
|
|
/* Set 16550 compatibility mode */
|
|
outb(hayespbase + HAYESP_CMD1, HAYESP_SETMODE);
|
|
outb(hayespbase + HAYESP_CMD2,
|
|
HAYESP_MODE_FIFO|HAYESP_MODE_RTS|
|
|
HAYESP_MODE_SCALE);
|
|
|
|
/* Set RTS/CTS flow control */
|
|
outb(hayespbase + HAYESP_CMD1, HAYESP_SETFLOWTYPE);
|
|
outb(hayespbase + HAYESP_CMD2, HAYESP_FLOW_RTS);
|
|
outb(hayespbase + HAYESP_CMD2, HAYESP_FLOW_CTS);
|
|
|
|
/* Set flow control levels */
|
|
outb(hayespbase + HAYESP_CMD1, HAYESP_SETRXFLOW);
|
|
outb(hayespbase + HAYESP_CMD2,
|
|
HAYESP_HIBYTE(HAYESP_RXHIWMARK));
|
|
outb(hayespbase + HAYESP_CMD2,
|
|
HAYESP_LOBYTE(HAYESP_RXHIWMARK));
|
|
outb(hayespbase + HAYESP_CMD2,
|
|
HAYESP_HIBYTE(HAYESP_RXLOWMARK));
|
|
outb(hayespbase + HAYESP_CMD2,
|
|
HAYESP_LOBYTE(HAYESP_RXLOWMARK));
|
|
} else
|
|
#endif
|
|
if (ISSET(sc->sc_hwflags, COM_HW_FIFO))
|
|
/* Set the FIFO threshold based on the receive speed. */
|
|
outb(pio(iobase , com_fifo),
|
|
FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST |
|
|
(tp->t_ispeed <= 1200 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_8));
|
|
/* flush any pending I/O */
|
|
while (ISSET(inb(pio(iobase , com_lsr)), LSR_RXRDY))
|
|
(void) inb(pio(iobase , com_data));
|
|
/* you turn me on, baby */
|
|
sc->sc_mcr = MCR_DTR | MCR_RTS;
|
|
if (!ISSET(sc->sc_hwflags, COM_HW_NOIEN))
|
|
SET(sc->sc_mcr, MCR_IENABLE | MCR_DRS); /* */
|
|
outb(pio(iobase , com_mcr), sc->sc_mcr);
|
|
sc->sc_ier = IER_ERXRDY | IER_ERLS | IER_EMSC;
|
|
outb(pio(iobase , com_ier), sc->sc_ier);
|
|
|
|
sc->sc_msr = inb(pio(iobase , com_msr));
|
|
if (ISSET(sc->sc_swflags, COM_SW_SOFTCAR) ||
|
|
ISSET(sc->sc_msr, MSR_DCD) || ISSET(tp->t_cflag, MDMBUF))
|
|
SET(tp->t_state, TS_CARR_ON);
|
|
else
|
|
CLR(tp->t_state, TS_CARR_ON);
|
|
}
|
|
splx(s);
|
|
|
|
error = ttyopen(tp, COMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
|
|
|
|
if (!error)
|
|
error = (*tp->t_linesw->l_open)(dev, tp);
|
|
|
|
/* XXX cleanup on error */
|
|
|
|
return error;
|
|
}
|
|
|
|
int
|
|
comclose(dev, flag, mode, p)
|
|
dev_t dev;
|
|
int flag, mode;
|
|
struct proc *p;
|
|
{
|
|
int unit = COMUNIT(dev);
|
|
struct com_softc *sc = xcom_cd.cd_devs[unit];
|
|
struct tty *tp = sc->sc_tty;
|
|
int iobase = sc->sc_iobase;
|
|
int s;
|
|
|
|
/* XXX This is for cons.c. */
|
|
if (!ISSET(tp->t_state, TS_ISOPEN))
|
|
return 0;
|
|
|
|
(*tp->t_linesw->l_close)(tp, flag);
|
|
s = spltty();
|
|
CLR(sc->sc_lcr, LCR_SBREAK);
|
|
outb(pio(iobase , com_lcr), sc->sc_lcr);
|
|
outb(pio(iobase , com_ier), 0);
|
|
if (ISSET(tp->t_cflag, HUPCL) &&
|
|
!ISSET(sc->sc_swflags, COM_SW_SOFTCAR)) {
|
|
/* XXX perhaps only clear DTR */
|
|
outb(pio(iobase , com_mcr), 0);
|
|
}
|
|
CLR(tp->t_state, TS_BUSY | TS_FLUSH);
|
|
if (--comsopen == 0)
|
|
callout_stop(&com_poll_ch);
|
|
splx(s);
|
|
ttyclose(tp);
|
|
#ifdef notyet /* XXXX */
|
|
if (unit != comconsole) {
|
|
ttyfree(tp);
|
|
sc->sc_tty = 0;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
comread(dev, uio, flag)
|
|
dev_t dev;
|
|
struct uio *uio;
|
|
int flag;
|
|
{
|
|
struct com_softc *sc = xcom_cd.cd_devs[COMUNIT(dev)];
|
|
struct tty *tp = sc->sc_tty;
|
|
|
|
return ((*tp->t_linesw->l_read)(tp, uio, flag));
|
|
}
|
|
|
|
int
|
|
comwrite(dev, uio, flag)
|
|
dev_t dev;
|
|
struct uio *uio;
|
|
int flag;
|
|
{
|
|
struct com_softc *sc = xcom_cd.cd_devs[COMUNIT(dev)];
|
|
struct tty *tp = sc->sc_tty;
|
|
|
|
return ((*tp->t_linesw->l_write)(tp, uio, flag));
|
|
}
|
|
|
|
int
|
|
compoll(dev, events, p)
|
|
dev_t dev;
|
|
int events;
|
|
struct proc *p;
|
|
{
|
|
struct com_softc *sc = xcom_cd.cd_devs[COMUNIT(dev)];
|
|
struct tty *tp = sc->sc_tty;
|
|
|
|
return ((*tp->t_linesw->l_poll)(tp, events, p));
|
|
}
|
|
|
|
struct tty *
|
|
comtty(dev)
|
|
dev_t dev;
|
|
{
|
|
struct com_softc *sc = xcom_cd.cd_devs[COMUNIT(dev)];
|
|
struct tty *tp = sc->sc_tty;
|
|
|
|
return (tp);
|
|
}
|
|
|
|
static u_char
|
|
tiocm_xxx2mcr(data)
|
|
int data;
|
|
{
|
|
u_char m = 0;
|
|
|
|
if (ISSET(data, TIOCM_DTR))
|
|
SET(m, MCR_DTR);
|
|
if (ISSET(data, TIOCM_RTS))
|
|
SET(m, MCR_RTS);
|
|
return m;
|
|
}
|
|
|
|
int
|
|
comioctl(dev, cmd, data, flag, p)
|
|
dev_t dev;
|
|
u_long cmd;
|
|
caddr_t data;
|
|
int flag;
|
|
struct proc *p;
|
|
{
|
|
int unit = COMUNIT(dev);
|
|
struct com_softc *sc = xcom_cd.cd_devs[unit];
|
|
struct tty *tp = sc->sc_tty;
|
|
int iobase = sc->sc_iobase;
|
|
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:
|
|
SET(sc->sc_lcr, LCR_SBREAK);
|
|
outb(pio(iobase , com_lcr), sc->sc_lcr);
|
|
break;
|
|
case TIOCCBRK:
|
|
CLR(sc->sc_lcr, LCR_SBREAK);
|
|
outb(pio(iobase , com_lcr), sc->sc_lcr);
|
|
break;
|
|
case TIOCSDTR:
|
|
SET(sc->sc_mcr, sc->sc_dtr);
|
|
outb(pio(iobase , com_mcr), sc->sc_mcr);
|
|
break;
|
|
case TIOCCDTR:
|
|
CLR(sc->sc_mcr, sc->sc_dtr);
|
|
outb(pio(iobase , com_mcr), sc->sc_mcr);
|
|
break;
|
|
case TIOCMSET:
|
|
CLR(sc->sc_mcr, MCR_DTR | MCR_RTS);
|
|
case TIOCMBIS:
|
|
SET(sc->sc_mcr, tiocm_xxx2mcr(*(int *)data));
|
|
outb(pio(iobase , com_mcr), sc->sc_mcr);
|
|
break;
|
|
case TIOCMBIC:
|
|
CLR(sc->sc_mcr, tiocm_xxx2mcr(*(int *)data));
|
|
outb(pio(iobase , com_mcr), sc->sc_mcr);
|
|
break;
|
|
case TIOCMGET: {
|
|
u_char m;
|
|
int bits = 0;
|
|
|
|
m = sc->sc_mcr;
|
|
if (ISSET(m, MCR_DTR))
|
|
SET(bits, TIOCM_DTR);
|
|
if (ISSET(m, MCR_RTS))
|
|
SET(bits, TIOCM_RTS);
|
|
m = sc->sc_msr;
|
|
if (ISSET(m, MSR_DCD))
|
|
SET(bits, TIOCM_CD);
|
|
if (ISSET(m, MSR_CTS))
|
|
SET(bits, TIOCM_CTS);
|
|
if (ISSET(m, MSR_DSR))
|
|
SET(bits, TIOCM_DSR);
|
|
if (ISSET(m, MSR_RI | MSR_TERI))
|
|
SET(bits, TIOCM_RI);
|
|
if (inb(pio(iobase , com_ier)))
|
|
SET(bits, TIOCM_LE);
|
|
*(int *)data = bits;
|
|
break;
|
|
}
|
|
case TIOCGFLAGS: {
|
|
int driverbits, userbits = 0;
|
|
|
|
driverbits = sc->sc_swflags;
|
|
if (ISSET(driverbits, COM_SW_SOFTCAR))
|
|
SET(userbits, TIOCFLAG_SOFTCAR);
|
|
if (ISSET(driverbits, COM_SW_CLOCAL))
|
|
SET(userbits, TIOCFLAG_CLOCAL);
|
|
if (ISSET(driverbits, COM_SW_CRTSCTS))
|
|
SET(userbits, TIOCFLAG_CRTSCTS);
|
|
if (ISSET(driverbits, COM_SW_MDMBUF))
|
|
SET(userbits, TIOCFLAG_MDMBUF);
|
|
|
|
*(int *)data = userbits;
|
|
break;
|
|
}
|
|
case TIOCSFLAGS: {
|
|
int userbits, driverbits = 0;
|
|
|
|
error = suser(p->p_ucred, &p->p_acflag);
|
|
if (error != 0)
|
|
return(EPERM);
|
|
|
|
userbits = *(int *)data;
|
|
if (ISSET(userbits, TIOCFLAG_SOFTCAR) ||
|
|
ISSET(sc->sc_hwflags, COM_HW_CONSOLE))
|
|
SET(driverbits, COM_SW_SOFTCAR);
|
|
if (ISSET(userbits, TIOCFLAG_CLOCAL))
|
|
SET(driverbits, COM_SW_CLOCAL);
|
|
if (ISSET(userbits, TIOCFLAG_CRTSCTS))
|
|
SET(driverbits, COM_SW_CRTSCTS);
|
|
if (ISSET(userbits, TIOCFLAG_MDMBUF))
|
|
SET(driverbits, COM_SW_MDMBUF);
|
|
|
|
sc->sc_swflags = driverbits;
|
|
break;
|
|
}
|
|
default:
|
|
return EPASSTHROUGH;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
comparam(tp, t)
|
|
struct tty *tp;
|
|
struct termios *t;
|
|
{
|
|
struct com_softc *sc = xcom_cd.cd_devs[COMUNIT(tp->t_dev)];
|
|
int iobase = sc->sc_iobase;
|
|
int ospeed = comspeed(t->c_ospeed);
|
|
u_char lcr;
|
|
tcflag_t oldcflag;
|
|
int s;
|
|
|
|
/* check requested parameters */
|
|
if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
|
|
return EINVAL;
|
|
|
|
lcr = ISSET(sc->sc_lcr, LCR_SBREAK);
|
|
|
|
switch (ISSET(t->c_cflag, CSIZE)) {
|
|
case CS5:
|
|
SET(lcr, LCR_5BITS);
|
|
break;
|
|
case CS6:
|
|
SET(lcr, LCR_6BITS);
|
|
break;
|
|
case CS7:
|
|
SET(lcr, LCR_7BITS);
|
|
break;
|
|
case CS8:
|
|
SET(lcr, LCR_8BITS);
|
|
break;
|
|
}
|
|
if (ISSET(t->c_cflag, PARENB)) {
|
|
SET(lcr, LCR_PENAB);
|
|
if (!ISSET(t->c_cflag, PARODD))
|
|
SET(lcr, LCR_PEVEN);
|
|
}
|
|
if (ISSET(t->c_cflag, CSTOPB))
|
|
SET(lcr, LCR_STOPB);
|
|
|
|
sc->sc_lcr = lcr;
|
|
|
|
s = spltty();
|
|
|
|
if (ospeed == 0) {
|
|
CLR(sc->sc_mcr, MCR_DTR);
|
|
outb(pio(iobase , com_mcr), sc->sc_mcr);
|
|
}
|
|
|
|
/*
|
|
* Set the FIFO threshold based on the receive speed, if we are
|
|
* changing it.
|
|
*/
|
|
if (tp->t_ispeed != t->c_ispeed) {
|
|
if (ISSET(sc->sc_hwflags, COM_HW_FIFO))
|
|
outb(pio(iobase , com_fifo),
|
|
FIFO_ENABLE |
|
|
(t->c_ispeed <= 1200 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_8));
|
|
}
|
|
|
|
if (ospeed != 0) {
|
|
outb(pio(iobase , com_lcr), lcr | LCR_DLAB);
|
|
outb(pio(iobase , com_dlbl), ospeed);
|
|
outb(pio(iobase , com_dlbh), ospeed >> 8);
|
|
outb(pio(iobase , com_lcr), lcr);
|
|
SET(sc->sc_mcr, MCR_DTR);
|
|
outb(pio(iobase , com_mcr), sc->sc_mcr);
|
|
} else
|
|
outb(pio(iobase , com_lcr), lcr);
|
|
|
|
/* When not using CRTSCTS, RTS follows DTR. */
|
|
if (!ISSET(t->c_cflag, CRTSCTS)) {
|
|
if (ISSET(sc->sc_mcr, MCR_DTR)) {
|
|
if (!ISSET(sc->sc_mcr, MCR_RTS)) {
|
|
SET(sc->sc_mcr, MCR_RTS);
|
|
outb(pio(iobase , com_mcr), sc->sc_mcr);
|
|
}
|
|
} else {
|
|
if (ISSET(sc->sc_mcr, MCR_RTS)) {
|
|
CLR(sc->sc_mcr, MCR_RTS);
|
|
outb(pio(iobase , com_mcr), sc->sc_mcr);
|
|
}
|
|
}
|
|
sc->sc_dtr = MCR_DTR | MCR_RTS;
|
|
} else
|
|
sc->sc_dtr = MCR_DTR;
|
|
|
|
/* and copy to tty */
|
|
tp->t_ispeed = t->c_ispeed;
|
|
tp->t_ospeed = t->c_ospeed;
|
|
oldcflag = tp->t_cflag;
|
|
tp->t_cflag = t->c_cflag;
|
|
|
|
/*
|
|
* If DCD is off and MDMBUF is changed, ask the tty layer if we should
|
|
* stop the device.
|
|
*/
|
|
if (!ISSET(sc->sc_msr, MSR_DCD) &&
|
|
!ISSET(sc->sc_swflags, COM_SW_SOFTCAR) &&
|
|
ISSET(oldcflag, MDMBUF) != ISSET(tp->t_cflag, MDMBUF) &&
|
|
(*tp->t_linesw->l_modem)(tp, 0) == 0) {
|
|
CLR(sc->sc_mcr, sc->sc_dtr);
|
|
outb(pio(iobase , com_mcr), sc->sc_mcr);
|
|
}
|
|
|
|
/* Just to be sure... */
|
|
splx(s);
|
|
comstart(tp);
|
|
return 0;
|
|
}
|
|
|
|
int comdebug = 0;
|
|
|
|
static void
|
|
comstart(tp)
|
|
struct tty *tp;
|
|
{
|
|
struct com_softc *sc = xcom_cd.cd_devs[COMUNIT(tp->t_dev)];
|
|
int iobase = sc->sc_iobase;
|
|
int s;
|
|
|
|
#ifdef DDB
|
|
if (comdebug)
|
|
Debugger();
|
|
#endif
|
|
s = spltty();
|
|
if (ISSET(tp->t_state, TS_BUSY))
|
|
goto out;
|
|
if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP) ||
|
|
sc->sc_halt > 0)
|
|
goto stopped;
|
|
if (ISSET(tp->t_cflag, CRTSCTS) && !ISSET(sc->sc_msr, MSR_CTS))
|
|
goto stopped;
|
|
if (tp->t_outq.c_cc <= tp->t_lowat) {
|
|
if (ISSET(tp->t_state, TS_ASLEEP)) {
|
|
CLR(tp->t_state, TS_ASLEEP);
|
|
wakeup(&tp->t_outq);
|
|
}
|
|
if (tp->t_outq.c_cc == 0)
|
|
goto stopped;
|
|
selwakeup(&tp->t_wsel);
|
|
}
|
|
SET(tp->t_state, TS_BUSY);
|
|
|
|
if (!ISSET(sc->sc_ier, IER_ETXRDY)) {
|
|
SET(sc->sc_ier, IER_ETXRDY);
|
|
outb(pio(iobase, com_ier), sc->sc_ier);
|
|
}
|
|
#ifdef COM_HAYESP
|
|
if (ISSET(sc->sc_hwflags, COM_HW_HAYESP)) {
|
|
u_char buffer[1024], *cp = buffer;
|
|
int n = q_to_b(&tp->t_outq, cp, sizeof buffer);
|
|
do
|
|
outb(iobase + com_data, *cp++);
|
|
while (--n);
|
|
}
|
|
else
|
|
#endif
|
|
if (ISSET(sc->sc_hwflags, COM_HW_FIFO)) {
|
|
u_char buffer[16], *cp = buffer;
|
|
int n = q_to_b(&tp->t_outq, cp, sizeof buffer);
|
|
do {
|
|
outb(pio(iobase , com_data), *cp++);
|
|
} while (--n);
|
|
} else
|
|
outb(pio(iobase , com_data), getc(&tp->t_outq));
|
|
out:
|
|
splx(s);
|
|
return;
|
|
stopped:
|
|
if (ISSET(sc->sc_ier, IER_ETXRDY)) {
|
|
CLR(sc->sc_ier, IER_ETXRDY);
|
|
outb(pio(iobase, com_ier), sc->sc_ier);
|
|
}
|
|
splx(s);
|
|
}
|
|
|
|
/*
|
|
* Stop output on a line.
|
|
*/
|
|
void
|
|
comstop(tp, flag)
|
|
struct tty *tp;
|
|
{
|
|
int s;
|
|
|
|
s = spltty();
|
|
if (ISSET(tp->t_state, TS_BUSY))
|
|
if (!ISSET(tp->t_state, TS_TTSTOP))
|
|
SET(tp->t_state, TS_FLUSH);
|
|
splx(s);
|
|
}
|
|
|
|
static void
|
|
comdiag(arg)
|
|
void *arg;
|
|
{
|
|
struct com_softc *sc = arg;
|
|
int overflows, floods;
|
|
int s;
|
|
|
|
s = spltty();
|
|
sc->sc_errors = 0;
|
|
overflows = sc->sc_overflows;
|
|
sc->sc_overflows = 0;
|
|
floods = sc->sc_floods;
|
|
sc->sc_floods = 0;
|
|
splx(s);
|
|
|
|
log(LOG_WARNING, "%s: %d silo overflow%s, %d ibuf overflow%s\n",
|
|
sc->sc_dev.dv_xname,
|
|
overflows, overflows == 1 ? "" : "s",
|
|
floods, floods == 1 ? "" : "s");
|
|
}
|
|
|
|
static void
|
|
compollin(arg)
|
|
void *arg;
|
|
{
|
|
int unit;
|
|
struct com_softc *sc;
|
|
struct tty *tp;
|
|
register u_char *ibufp;
|
|
u_char *ibufend;
|
|
register int c;
|
|
int s;
|
|
static int lsrmap[8] = {
|
|
0, TTY_PE,
|
|
TTY_FE, TTY_PE|TTY_FE,
|
|
TTY_FE, TTY_PE|TTY_FE,
|
|
TTY_FE, TTY_PE|TTY_FE
|
|
};
|
|
|
|
s = spltty();
|
|
if (comevents == 0) {
|
|
splx(s);
|
|
goto out;
|
|
}
|
|
comevents = 0;
|
|
splx(s);
|
|
|
|
for (unit = 0; unit < xcom_cd.cd_ndevs; unit++) {
|
|
sc = xcom_cd.cd_devs[unit];
|
|
if (sc == 0 || sc->sc_ibufp == sc->sc_ibuf)
|
|
continue;
|
|
|
|
tp = sc->sc_tty;
|
|
|
|
s = spltty();
|
|
|
|
ibufp = sc->sc_ibuf;
|
|
ibufend = sc->sc_ibufp;
|
|
|
|
if (ibufp == ibufend) {
|
|
splx(s);
|
|
continue;
|
|
}
|
|
|
|
sc->sc_ibufp = sc->sc_ibuf = (ibufp == sc->sc_ibufs[0]) ?
|
|
sc->sc_ibufs[1] : sc->sc_ibufs[0];
|
|
sc->sc_ibufhigh = sc->sc_ibuf + COM_IHIGHWATER;
|
|
sc->sc_ibufend = sc->sc_ibuf + COM_IBUFSIZE;
|
|
|
|
if (tp == 0 || !ISSET(tp->t_state, TS_ISOPEN)) {
|
|
splx(s);
|
|
continue;
|
|
}
|
|
|
|
if (ISSET(tp->t_cflag, CRTSCTS) &&
|
|
!ISSET(sc->sc_mcr, MCR_RTS)) {
|
|
/* XXX */
|
|
SET(sc->sc_mcr, MCR_RTS);
|
|
outb(pio(sc->sc_iobase , com_mcr), sc->sc_mcr);
|
|
}
|
|
|
|
splx(s);
|
|
|
|
while (ibufp < ibufend) {
|
|
c = *ibufp++;
|
|
if (*ibufp & LSR_OE) {
|
|
sc->sc_overflows++;
|
|
if (sc->sc_errors++ == 0)
|
|
callout_reset(&sc->sc_diag_ch, 60 * hz,
|
|
comdiag, sc);
|
|
}
|
|
/* This is ugly, but fast. */
|
|
c |= lsrmap[(*ibufp++ & (LSR_BI|LSR_FE|LSR_PE)) >> 2];
|
|
(*tp->t_linesw->l_rint)(c, tp);
|
|
}
|
|
}
|
|
|
|
out:
|
|
callout_reset(&com_poll_ch, 1, compollin, NULL);
|
|
}
|
|
|
|
int
|
|
comintr(arg)
|
|
void *arg;
|
|
{
|
|
struct com_softc *sc = xcom_cd.cd_devs[(int)arg];
|
|
int iobase = sc->sc_iobase;
|
|
struct tty *tp;
|
|
u_char lsr, data, msr, delta;
|
|
int iir;
|
|
|
|
#if 1
|
|
if ((iir = ISSET(inb(pio(iobase , com_iir)), IIR_NOPEND)))
|
|
return (0);
|
|
#endif
|
|
|
|
tp = sc->sc_tty;
|
|
|
|
for (;;) {
|
|
lsr = inb(pio(iobase , com_lsr));
|
|
|
|
if (ISSET(lsr, LSR_RXRDY)) {
|
|
register u_char *p = sc->sc_ibufp;
|
|
|
|
comevents = 1;
|
|
do {
|
|
data = inb(pio(iobase, com_data));
|
|
if (ISSET(lsr, LSR_BI)) {
|
|
#ifdef DDB
|
|
if (sc->sc_dev.dv_unit == comconsole) {
|
|
Debugger();
|
|
goto next;
|
|
}
|
|
#endif
|
|
}
|
|
if (p >= sc->sc_ibufend) {
|
|
sc->sc_floods++;
|
|
if (sc->sc_errors++ == 0)
|
|
callout_reset(&sc->sc_diag_ch,
|
|
60 * hz, comdiag, sc);
|
|
} else {
|
|
*p++ = data;
|
|
*p++ = lsr;
|
|
if (p == sc->sc_ibufhigh &&
|
|
ISSET(tp->t_cflag, CRTSCTS)) {
|
|
/* XXX */
|
|
CLR(sc->sc_mcr, MCR_RTS);
|
|
outb(pio(iobase , com_mcr),
|
|
sc->sc_mcr);
|
|
}
|
|
}
|
|
#ifdef DDB
|
|
next:
|
|
#endif
|
|
lsr = inb(pio(iobase , com_lsr));
|
|
} while (ISSET(lsr, LSR_RXRDY));
|
|
|
|
sc->sc_ibufp = p;
|
|
}
|
|
#ifdef COMDEBUG
|
|
else if (ISSET(lsr, LSR_BI|LSR_FE|LSR_PE|LSR_OE))
|
|
printf("weird lsr %02x\n", lsr);
|
|
#endif
|
|
|
|
msr = inb(pio(iobase , com_msr));
|
|
|
|
if (msr != sc->sc_msr) {
|
|
delta = msr ^ sc->sc_msr;
|
|
sc->sc_msr = msr;
|
|
if (ISSET(delta, MSR_DCD) &&
|
|
!ISSET(sc->sc_swflags, COM_SW_SOFTCAR) &&
|
|
(*tp->t_linesw->l_modem)(tp, ISSET(msr, MSR_DCD)) == 0) {
|
|
CLR(sc->sc_mcr, sc->sc_dtr);
|
|
outb(pio(iobase , com_mcr), sc->sc_mcr);
|
|
}
|
|
if (ISSET(delta & msr, MSR_CTS) &&
|
|
ISSET(tp->t_cflag, CRTSCTS)) {
|
|
/* the line is up and we want to do rts/cts flow control */
|
|
(*tp->t_linesw->l_start)(tp);
|
|
}
|
|
}
|
|
|
|
if (ISSET(lsr, LSR_TXRDY) && ISSET(tp->t_state, TS_BUSY)) {
|
|
CLR(tp->t_state, TS_BUSY | TS_FLUSH);
|
|
if (sc->sc_halt > 0)
|
|
wakeup(&tp->t_outq);
|
|
(*tp->t_linesw->l_start)(tp);
|
|
}
|
|
|
|
if ((iir = ISSET(inb(pio(iobase , com_iir)), IIR_NOPEND)))
|
|
return (1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Following are all routines needed for COM to act as console
|
|
*/
|
|
#include <dev/cons.h>
|
|
|
|
void comcnprobe __P((struct consdev *));
|
|
void comcninit __P((struct consdev *));
|
|
int comcngetc __P((dev_t));
|
|
void comcnputc __P((dev_t, int));
|
|
void comcnpollc __P((dev_t, int));
|
|
|
|
void
|
|
comcnprobe(cp)
|
|
struct consdev *cp;
|
|
{
|
|
int maj;
|
|
|
|
if (!comprobe1(CONADDR)) {
|
|
cp->cn_pri = CN_DEAD;
|
|
return;
|
|
}
|
|
|
|
/* locate the major number */
|
|
maj = cdevsw_lookup_major(&xcom_cdevsw);
|
|
|
|
/* initialize required fields */
|
|
cp->cn_dev = makedev(maj, CONUNIT);
|
|
#ifdef COMCONSOLE
|
|
cp->cn_pri = CN_REMOTE; /* Force a serial port console */
|
|
#else
|
|
cp->cn_pri = CN_NORMAL;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
comcninit(cp)
|
|
struct consdev *cp;
|
|
{
|
|
|
|
cominit(CONUNIT, comdefaultrate);
|
|
comconsole = CONUNIT;
|
|
comconsinit = 0;
|
|
}
|
|
|
|
static void
|
|
cominit(unit, rate)
|
|
int unit, rate;
|
|
{
|
|
int s = splhigh();
|
|
int iobase = CONADDR;
|
|
u_char stat;
|
|
|
|
outb(pio(iobase , com_lcr), LCR_DLAB);
|
|
rate = comspeed(comdefaultrate);
|
|
outb(pio(iobase , com_dlbl), rate);
|
|
outb(pio(iobase , com_dlbh), rate >> 8);
|
|
outb(pio(iobase , com_lcr), LCR_8BITS);
|
|
outb(pio(iobase , com_ier), IER_ERXRDY | IER_ETXRDY);
|
|
outb(pio(iobase , com_fifo), FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_4);
|
|
stat = inb(pio(iobase , com_iir));
|
|
splx(s);
|
|
}
|
|
|
|
int
|
|
comcngetc(dev)
|
|
dev_t dev;
|
|
{
|
|
int s = splhigh();
|
|
int iobase = CONADDR;
|
|
u_char stat, c;
|
|
|
|
while (!ISSET(stat = inb(pio(iobase , com_lsr)), LSR_RXRDY))
|
|
;
|
|
c = inb(pio(iobase , com_data));
|
|
stat = inb(pio(iobase , com_iir));
|
|
splx(s);
|
|
return c;
|
|
}
|
|
|
|
/*
|
|
* Console kernel output character routine.
|
|
*/
|
|
void
|
|
comcnputc(dev, c)
|
|
dev_t dev;
|
|
int c;
|
|
{
|
|
int s = splhigh();
|
|
int iobase = CONADDR;
|
|
u_char stat;
|
|
register int timo;
|
|
|
|
#ifdef KGDB
|
|
if (dev != kgdb_dev)
|
|
#endif
|
|
if (comconsinit == 0) {
|
|
(void) cominit(COMUNIT(dev), comdefaultrate);
|
|
comconsinit = 1;
|
|
}
|
|
/* wait for any pending transmission to finish */
|
|
timo = 50000;
|
|
while (!ISSET(stat = inb(pio(iobase , com_lsr)), LSR_TXRDY) && --timo)
|
|
;
|
|
outb(pio(iobase , com_data), c);
|
|
/* wait for this transmission to complete */
|
|
timo = 1500000;
|
|
while (!ISSET(stat = inb(pio(iobase , com_lsr)), LSR_TXRDY) && --timo)
|
|
;
|
|
/* clear any interrupts generated by this transmission */
|
|
stat = inb(pio(iobase , com_iir));
|
|
splx(s);
|
|
}
|
|
|
|
void
|
|
comcnpollc(dev, on)
|
|
dev_t dev;
|
|
int on;
|
|
{
|
|
|
|
}
|