7ac40f5f59
branches: Initialize struct cdevsw using C99 sparse initializtion and remove all initializations to default values. This patch is automatically generated and has been tested by compiling LINT with all the fields in struct cdevsw in reverse order on alpha, sparc64 and i386. Approved by: re(scottl)
1594 lines
41 KiB
C
1594 lines
41 KiB
C
/*
|
|
* Copyright (C) 1995 by Pavel Antonov, Moscow, Russia.
|
|
* Copyright (C) 1995 by Andrey A. Chernov, Moscow, Russia.
|
|
* Copyright (C) 2002 by John Baldwin <jhb@FreeBSD.org>
|
|
* 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
/*
|
|
* SDL Communications Riscom/8 (based on Cirrus Logic CL-CD180) driver
|
|
*
|
|
*/
|
|
|
|
/*#define RCDEBUG*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/interrupt.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/tty.h>
|
|
#include <machine/bus.h>
|
|
#include <machine/resource.h>
|
|
#include <sys/rman.h>
|
|
|
|
#include <dev/ic/cd180.h>
|
|
#include <dev/rc/rcreg.h>
|
|
#include <isa/isavar.h>
|
|
|
|
#define IOBASE_ADDRS 14
|
|
|
|
#define DEV_TO_RC(dev) (struct rc_chans *)((dev)->si_drv1)
|
|
#define TTY_TO_RC(tty) DEV_TO_RC((tty)->t_dev)
|
|
|
|
#define rcin(sc, port) RC_IN(sc, port)
|
|
#define rcout(sc, port, v) RC_OUT(sc, port, v)
|
|
|
|
#define WAITFORCCR(sc, chan) rc_wait0((sc), (chan), __LINE__)
|
|
|
|
#define CCRCMD(sc, chan, cmd) do { \
|
|
WAITFORCCR((sc), (chan)); \
|
|
rcout((sc), CD180_CCR, (cmd)); \
|
|
} while (0)
|
|
|
|
#define RC_IBUFSIZE 256
|
|
#define RB_I_HIGH_WATER (TTYHOG - 2 * RC_IBUFSIZE)
|
|
#define RC_OBUFSIZE 512
|
|
#define RC_IHIGHWATER (3 * RC_IBUFSIZE / 4)
|
|
#define INPUT_FLAGS_SHIFT (2 * RC_IBUFSIZE)
|
|
#define LOTS_OF_EVENTS 64
|
|
|
|
#define RC_FAKEID 0x10
|
|
|
|
#define CALLOUT(dev) (((intptr_t)(dev)->si_drv2) != 0)
|
|
|
|
/* Per-channel structure */
|
|
struct rc_chans {
|
|
struct rc_softc *rc_rcb; /* back ptr */
|
|
dev_t rc_dev; /* non-callout device */
|
|
dev_t rc_cdev; /* callout device */
|
|
u_short rc_flags; /* Misc. flags */
|
|
int rc_chan; /* Channel # */
|
|
u_char rc_ier; /* intr. enable reg */
|
|
u_char rc_msvr; /* modem sig. status */
|
|
u_char rc_cor2; /* options reg */
|
|
u_char rc_pendcmd; /* special cmd pending */
|
|
u_int rc_dtrwait; /* dtr timeout */
|
|
u_int rc_dcdwaits; /* how many waits DCD in open */
|
|
u_char rc_hotchar; /* end packed optimize */
|
|
struct tty rc_tp; /* tty struct */
|
|
u_char *rc_iptr; /* Chars input buffer */
|
|
u_char *rc_hiwat; /* hi-water mark */
|
|
u_char *rc_bufend; /* end of buffer */
|
|
u_char *rc_optr; /* ptr in output buf */
|
|
u_char *rc_obufend; /* end of output buf */
|
|
u_char rc_ibuf[4 * RC_IBUFSIZE]; /* input buffer */
|
|
u_char rc_obuf[RC_OBUFSIZE]; /* output buffer */
|
|
struct callout rc_dtrcallout;
|
|
};
|
|
|
|
/* Per-board structure */
|
|
struct rc_softc {
|
|
device_t sc_dev;
|
|
struct resource *sc_irq;
|
|
struct resource *sc_port[IOBASE_ADDRS];
|
|
int sc_irqrid;
|
|
void *sc_hwicookie;
|
|
bus_space_tag_t sc_bt;
|
|
bus_space_handle_t sc_bh;
|
|
u_int sc_unit; /* unit # */
|
|
u_char sc_dtr; /* DTR status */
|
|
int sc_opencount;
|
|
int sc_scheduled_event;
|
|
void *sc_swicookie;
|
|
struct rc_chans sc_channels[CD180_NCHAN]; /* channels */
|
|
};
|
|
|
|
/* Static prototypes */
|
|
static void rc_release_resources(device_t dev);
|
|
static void rc_intr(void *);
|
|
static void rc_hwreset(struct rc_softc *, unsigned int);
|
|
static int rc_test(struct rc_softc *);
|
|
static void rc_discard_output(struct rc_chans *);
|
|
static void rc_hardclose(struct rc_chans *);
|
|
static int rc_modctl(struct rc_chans *, int, int);
|
|
static void rc_start(struct tty *);
|
|
static void rc_stop(struct tty *, int rw);
|
|
static int rc_param(struct tty *, struct termios *);
|
|
static void rc_pollcard(void *);
|
|
static void rc_reinit(struct rc_softc *);
|
|
#ifdef RCDEBUG
|
|
static void printrcflags();
|
|
#endif
|
|
static void rc_dtrwakeup(void *);
|
|
static void disc_optim(struct tty *tp, struct termios *t, struct rc_chans *);
|
|
static void rc_wait0(struct rc_softc *sc, int chan, int line);
|
|
|
|
static d_open_t rcopen;
|
|
static d_close_t rcclose;
|
|
static d_ioctl_t rcioctl;
|
|
|
|
#define CDEV_MAJOR 63
|
|
static struct cdevsw rc_cdevsw = {
|
|
.d_open = rcopen,
|
|
.d_close = rcclose,
|
|
.d_read = ttyread,
|
|
.d_write = ttywrite,
|
|
.d_ioctl = rcioctl,
|
|
.d_poll = ttypoll,
|
|
.d_name = "rc",
|
|
.d_maj = CDEV_MAJOR,
|
|
.d_flags = D_TTY | D_KQFILTER,
|
|
.d_kqfilter = ttykqfilter,
|
|
};
|
|
|
|
static devclass_t rc_devclass;
|
|
|
|
/* Flags */
|
|
#define RC_DTR_OFF 0x0001 /* DTR wait, for close/open */
|
|
#define RC_ACTOUT 0x0002 /* Dial-out port active */
|
|
#define RC_RTSFLOW 0x0004 /* RTS flow ctl enabled */
|
|
#define RC_CTSFLOW 0x0008 /* CTS flow ctl enabled */
|
|
#define RC_DORXFER 0x0010 /* RXFER event planned */
|
|
#define RC_DOXXFER 0x0020 /* XXFER event planned */
|
|
#define RC_MODCHG 0x0040 /* Modem status changed */
|
|
#define RC_OSUSP 0x0080 /* Output suspended */
|
|
#define RC_OSBUSY 0x0100 /* start() routine in progress */
|
|
#define RC_WAS_BUFOVFL 0x0200 /* low-level buffer ovferflow */
|
|
#define RC_WAS_SILOVFL 0x0400 /* silo buffer overflow */
|
|
#define RC_SEND_RDY 0x0800 /* ready to send */
|
|
|
|
/* Table for translation of RCSR status bits to internal form */
|
|
static int rc_rcsrt[16] = {
|
|
0, TTY_OE, TTY_FE,
|
|
TTY_FE|TTY_OE, TTY_PE, TTY_PE|TTY_OE,
|
|
TTY_PE|TTY_FE, TTY_PE|TTY_FE|TTY_OE, TTY_BI,
|
|
TTY_BI|TTY_OE, TTY_BI|TTY_FE, TTY_BI|TTY_FE|TTY_OE,
|
|
TTY_BI|TTY_PE, TTY_BI|TTY_PE|TTY_OE, TTY_BI|TTY_PE|TTY_FE,
|
|
TTY_BI|TTY_PE|TTY_FE|TTY_OE
|
|
};
|
|
|
|
static int rc_ports[] =
|
|
{ 0x220, 0x240, 0x250, 0x260, 0x2a0, 0x2b0, 0x300, 0x320 };
|
|
static int iobase_addrs[IOBASE_ADDRS] =
|
|
{ 0, 0x400, 0x800, 0xc00, 0x1400, 0x1800, 0x1c00, 0x2000,
|
|
0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x8000 };
|
|
|
|
/**********************************************/
|
|
|
|
static int
|
|
rc_probe(device_t dev)
|
|
{
|
|
u_int port;
|
|
int i, found;
|
|
|
|
/*
|
|
* We don't know of any PnP ID's for these cards.
|
|
*/
|
|
if (isa_get_logicalid(dev) != 0)
|
|
return (ENXIO);
|
|
|
|
/*
|
|
* We have to have an IO port hint that is valid.
|
|
*/
|
|
port = isa_get_port(dev);
|
|
if (port == -1)
|
|
return (ENXIO);
|
|
found = 0;
|
|
for (i = 0; i < sizeof(rc_ports) / sizeof(int); i++)
|
|
if (rc_ports[i] == port) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
if (!found)
|
|
return (ENXIO);
|
|
|
|
/*
|
|
* We have to have an IRQ hint.
|
|
*/
|
|
if (isa_get_irq(dev) == -1)
|
|
return (ENXIO);
|
|
|
|
device_set_desc(dev, "SDL Riscom/8");
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
rc_attach(device_t dev)
|
|
{
|
|
struct rc_chans *rc;
|
|
struct tty *tp;
|
|
struct rc_softc *sc;
|
|
u_int port;
|
|
int base, chan, error, i, x;
|
|
dev_t cdev;
|
|
|
|
sc = device_get_softc(dev);
|
|
sc->sc_dev = dev;
|
|
|
|
/*
|
|
* We need to have IO ports. Lots of them. We need
|
|
* the following ranges relative to the base port:
|
|
* 0x0 - 0x10
|
|
* 0x400 - 0x410
|
|
* 0x800 - 0x810
|
|
* 0xc00 - 0xc10
|
|
* 0x1400 - 0x1410
|
|
* 0x1800 - 0x1810
|
|
* 0x1c00 - 0x1c10
|
|
* 0x2000 - 0x2010
|
|
* 0x3000 - 0x3010
|
|
* 0x3400 - 0x3410
|
|
* 0x3800 - 0x3810
|
|
* 0x3c00 - 0x3c10
|
|
* 0x4000 - 0x4010
|
|
* 0x8000 - 0x8010
|
|
*/
|
|
port = isa_get_port(dev);
|
|
for (i = 0; i < IOBASE_ADDRS; i++)
|
|
if (bus_set_resource(dev, SYS_RES_IOPORT, i,
|
|
port + iobase_addrs[i], 0x10) != 0)
|
|
return (ENXIO);
|
|
error = ENOMEM;
|
|
for (i = 0; i < IOBASE_ADDRS; i++) {
|
|
x = i;
|
|
sc->sc_port[i] = bus_alloc_resource(dev, SYS_RES_IOPORT, &x,
|
|
0ul, ~0ul, 0x10, RF_ACTIVE);
|
|
if (x != i) {
|
|
device_printf(dev, "ioport %d was rid %d\n", i, x);
|
|
goto fail;
|
|
}
|
|
if (sc->sc_port[i] == NULL) {
|
|
device_printf(dev, "failed to alloc ioports %x-%x\n",
|
|
port + iobase_addrs[i],
|
|
port + iobase_addrs[i] + 0x10);
|
|
goto fail;
|
|
}
|
|
}
|
|
sc->sc_bt = rman_get_bustag(sc->sc_port[0]);
|
|
sc->sc_bh = rman_get_bushandle(sc->sc_port[0]);
|
|
|
|
sc->sc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->sc_irqrid,
|
|
0ul, ~0ul, 1, RF_ACTIVE);
|
|
if (sc->sc_irq == NULL) {
|
|
device_printf(dev, "failed to alloc IRQ\n");
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* Now do some actual tests to make sure it works.
|
|
*/
|
|
error = ENXIO;
|
|
rcout(sc, CD180_PPRL, 0x22); /* Random values to Prescale reg. */
|
|
rcout(sc, CD180_PPRH, 0x11);
|
|
if (rcin(sc, CD180_PPRL) != 0x22 || rcin(sc, CD180_PPRH) != 0x11)
|
|
goto fail;
|
|
if (rc_test(sc))
|
|
goto fail;
|
|
|
|
/*
|
|
* Ok, start actually hooking things up.
|
|
*/
|
|
sc->sc_unit = device_get_unit(dev);
|
|
/*sc->sc_chipid = 0x10 + device_get_unit(dev);*/
|
|
device_printf(dev, "%d chans, firmware rev. %c\n",
|
|
CD180_NCHAN, (rcin(sc, CD180_GFRCR) & 0xF) + 'A');
|
|
rc = sc->sc_channels;
|
|
base = CD180_NCHAN * sc->sc_unit;
|
|
for (chan = 0; chan < CD180_NCHAN; chan++, rc++) {
|
|
rc->rc_rcb = sc;
|
|
rc->rc_chan = chan;
|
|
rc->rc_iptr = rc->rc_ibuf;
|
|
rc->rc_bufend = &rc->rc_ibuf[RC_IBUFSIZE];
|
|
rc->rc_hiwat = &rc->rc_ibuf[RC_IHIGHWATER];
|
|
rc->rc_optr = rc->rc_obufend = rc->rc_obuf;
|
|
rc->rc_dtrwait = 3 * hz;
|
|
callout_init(&rc->rc_dtrcallout, 0);
|
|
tp = &rc->rc_tp;
|
|
ttychars(tp);
|
|
tp->t_lflag = tp->t_iflag = tp->t_oflag = 0;
|
|
tp->t_cflag = TTYDEF_CFLAG;
|
|
tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
|
|
cdev = make_dev(&rc_cdevsw, chan + base,
|
|
UID_ROOT, GID_WHEEL, 0600, "ttym%d", chan + base);
|
|
cdev->si_drv1 = rc;
|
|
cdev->si_drv2 = 0;
|
|
cdev->si_tty = tp;
|
|
rc->rc_dev = cdev;
|
|
cdev = make_dev(&rc_cdevsw, chan + base + 128,
|
|
UID_UUCP, GID_DIALER, 0660, "cuam%d", chan + base);
|
|
cdev->si_drv1 = rc;
|
|
cdev->si_drv2 = (void *)1;
|
|
cdev->si_tty = tp;
|
|
rc->rc_cdev = cdev;
|
|
}
|
|
|
|
error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_TTY, rc_intr, sc,
|
|
&sc->sc_hwicookie);
|
|
if (error) {
|
|
device_printf(dev, "failed to register interrupt handler\n");
|
|
goto fail;
|
|
}
|
|
|
|
swi_add(&tty_ithd, "tty:rc", rc_pollcard, sc, SWI_TTY, 0,
|
|
&sc->sc_swicookie);
|
|
return (0);
|
|
|
|
fail:
|
|
rc_release_resources(dev);
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
rc_detach(device_t dev)
|
|
{
|
|
struct rc_softc *sc;
|
|
struct rc_chans *rc;
|
|
int error, i, s;
|
|
|
|
sc = device_get_softc(dev);
|
|
if (sc->sc_opencount > 0)
|
|
return (EBUSY);
|
|
sc->sc_opencount = -1;
|
|
|
|
rc = sc->sc_channels;
|
|
for (i = 0; i < CD180_NCHAN; i++, rc++) {
|
|
destroy_dev(rc->rc_dev);
|
|
destroy_dev(rc->rc_cdev);
|
|
}
|
|
|
|
rc = sc->sc_channels;
|
|
s = splsoftclock();
|
|
for (i = 0; i < CD180_NCHAN; i++) {
|
|
if ((rc->rc_flags & RC_DTR_OFF) &&
|
|
!callout_stop(&rc->rc_dtrcallout))
|
|
tsleep(&rc->rc_dtrwait, TTIPRI, "rcdtrdet", 0);
|
|
}
|
|
|
|
error = bus_teardown_intr(dev, sc->sc_irq, sc->sc_hwicookie);
|
|
if (error)
|
|
device_printf(dev, "failed to deregister interrupt handler\n");
|
|
ithread_remove_handler(sc->sc_swicookie);
|
|
rc_release_resources(dev);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
rc_release_resources(device_t dev)
|
|
{
|
|
struct rc_softc *sc;
|
|
int i;
|
|
|
|
sc = device_get_softc(dev);
|
|
if (sc->sc_irq != NULL) {
|
|
bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqrid,
|
|
sc->sc_irq);
|
|
sc->sc_irq = NULL;
|
|
}
|
|
for (i = 0; i < IOBASE_ADDRS; i++) {
|
|
if (sc->sc_port[i] == NULL)
|
|
break;
|
|
bus_release_resource(dev, SYS_RES_IOPORT, i, sc->sc_port[i]);
|
|
sc->sc_port[i] = NULL;
|
|
}
|
|
}
|
|
|
|
/* RC interrupt handling */
|
|
static void
|
|
rc_intr(void *arg)
|
|
{
|
|
struct rc_softc *sc;
|
|
struct rc_chans *rc;
|
|
int resid, chan;
|
|
u_char val, iack, bsr, ucnt, *optr;
|
|
int good_data, t_state;
|
|
|
|
sc = (struct rc_softc *)arg;
|
|
bsr = ~(rcin(sc, RC_BSR));
|
|
if (!(bsr & (RC_BSR_TOUT|RC_BSR_RXINT|RC_BSR_TXINT|RC_BSR_MOINT))) {
|
|
device_printf(sc->sc_dev, "extra interrupt\n");
|
|
rcout(sc, CD180_EOIR, 0);
|
|
return;
|
|
}
|
|
|
|
while (bsr & (RC_BSR_TOUT|RC_BSR_RXINT|RC_BSR_TXINT|RC_BSR_MOINT)) {
|
|
#ifdef RCDEBUG_DETAILED
|
|
device_printf(sc->sc_dev, "intr (%p) %s%s%s%s\n", arg, bsr,
|
|
(bsr & RC_BSR_TOUT)?"TOUT ":"",
|
|
(bsr & RC_BSR_RXINT)?"RXINT ":"",
|
|
(bsr & RC_BSR_TXINT)?"TXINT ":"",
|
|
(bsr & RC_BSR_MOINT)?"MOINT":"");
|
|
#endif
|
|
if (bsr & RC_BSR_TOUT) {
|
|
device_printf(sc->sc_dev,
|
|
"hardware failure, reset board\n");
|
|
rcout(sc, RC_CTOUT, 0);
|
|
rc_reinit(sc);
|
|
return;
|
|
}
|
|
if (bsr & RC_BSR_RXINT) {
|
|
iack = rcin(sc, RC_PILR_RX);
|
|
good_data = (iack == (GIVR_IT_RGDI | RC_FAKEID));
|
|
if (!good_data && iack != (GIVR_IT_REI | RC_FAKEID)) {
|
|
device_printf(sc->sc_dev,
|
|
"fake rxint: %02x\n", iack);
|
|
goto more_intrs;
|
|
}
|
|
chan = ((rcin(sc, CD180_GICR) & GICR_CHAN) >> GICR_LSH);
|
|
rc = &sc->sc_channels[chan];
|
|
t_state = rc->rc_tp.t_state;
|
|
/* Do RTS flow control stuff */
|
|
if ( (rc->rc_flags & RC_RTSFLOW)
|
|
|| !(t_state & TS_ISOPEN)
|
|
) {
|
|
if ( ( !(t_state & TS_ISOPEN)
|
|
|| (t_state & TS_TBLOCK)
|
|
)
|
|
&& (rc->rc_msvr & MSVR_RTS)
|
|
)
|
|
rcout(sc, CD180_MSVR,
|
|
rc->rc_msvr &= ~MSVR_RTS);
|
|
else if (!(rc->rc_msvr & MSVR_RTS))
|
|
rcout(sc, CD180_MSVR,
|
|
rc->rc_msvr |= MSVR_RTS);
|
|
}
|
|
ucnt = rcin(sc, CD180_RDCR) & 0xF;
|
|
resid = 0;
|
|
|
|
if (t_state & TS_ISOPEN) {
|
|
/* check for input buffer overflow */
|
|
if ((rc->rc_iptr + ucnt) >= rc->rc_bufend) {
|
|
resid = ucnt;
|
|
ucnt = rc->rc_bufend - rc->rc_iptr;
|
|
resid -= ucnt;
|
|
if (!(rc->rc_flags & RC_WAS_BUFOVFL)) {
|
|
rc->rc_flags |= RC_WAS_BUFOVFL;
|
|
sc->sc_scheduled_event++;
|
|
}
|
|
}
|
|
optr = rc->rc_iptr;
|
|
/* check foor good data */
|
|
if (good_data) {
|
|
while (ucnt-- > 0) {
|
|
val = rcin(sc, CD180_RDR);
|
|
optr[0] = val;
|
|
optr[INPUT_FLAGS_SHIFT] = 0;
|
|
optr++;
|
|
sc->sc_scheduled_event++;
|
|
if (val != 0 && val == rc->rc_hotchar)
|
|
swi_sched(sc->sc_swicookie, 0);
|
|
}
|
|
} else {
|
|
/* Store also status data */
|
|
while (ucnt-- > 0) {
|
|
iack = rcin(sc, CD180_RCSR);
|
|
if (iack & RCSR_Timeout)
|
|
break;
|
|
if ( (iack & RCSR_OE)
|
|
&& !(rc->rc_flags & RC_WAS_SILOVFL)) {
|
|
rc->rc_flags |= RC_WAS_SILOVFL;
|
|
sc->sc_scheduled_event++;
|
|
}
|
|
val = rcin(sc, CD180_RDR);
|
|
/*
|
|
Don't store PE if IGNPAR and BREAK if IGNBRK,
|
|
this hack allows "raw" tty optimization
|
|
works even if IGN* is set.
|
|
*/
|
|
if ( !(iack & (RCSR_PE|RCSR_FE|RCSR_Break))
|
|
|| ((!(iack & (RCSR_PE|RCSR_FE))
|
|
|| !(rc->rc_tp.t_iflag & IGNPAR))
|
|
&& (!(iack & RCSR_Break)
|
|
|| !(rc->rc_tp.t_iflag & IGNBRK)))) {
|
|
if ( (iack & (RCSR_PE|RCSR_FE))
|
|
&& (t_state & TS_CAN_BYPASS_L_RINT)
|
|
&& ((iack & RCSR_FE)
|
|
|| ((iack & RCSR_PE)
|
|
&& (rc->rc_tp.t_iflag & INPCK))))
|
|
val = 0;
|
|
else if (val != 0 && val == rc->rc_hotchar)
|
|
swi_sched(sc->sc_swicookie, 0);
|
|
optr[0] = val;
|
|
optr[INPUT_FLAGS_SHIFT] = iack;
|
|
optr++;
|
|
sc->sc_scheduled_event++;
|
|
}
|
|
}
|
|
}
|
|
rc->rc_iptr = optr;
|
|
rc->rc_flags |= RC_DORXFER;
|
|
} else
|
|
resid = ucnt;
|
|
/* Clear FIFO if necessary */
|
|
while (resid-- > 0) {
|
|
if (!good_data)
|
|
iack = rcin(sc, CD180_RCSR);
|
|
else
|
|
iack = 0;
|
|
if (iack & RCSR_Timeout)
|
|
break;
|
|
(void) rcin(sc, CD180_RDR);
|
|
}
|
|
goto more_intrs;
|
|
}
|
|
if (bsr & RC_BSR_MOINT) {
|
|
iack = rcin(sc, RC_PILR_MODEM);
|
|
if (iack != (GIVR_IT_MSCI | RC_FAKEID)) {
|
|
device_printf(sc->sc_dev, "fake moint: %02x\n",
|
|
iack);
|
|
goto more_intrs;
|
|
}
|
|
chan = ((rcin(sc, CD180_GICR) & GICR_CHAN) >> GICR_LSH);
|
|
rc = &sc->sc_channels[chan];
|
|
iack = rcin(sc, CD180_MCR);
|
|
rc->rc_msvr = rcin(sc, CD180_MSVR);
|
|
rcout(sc, CD180_MCR, 0);
|
|
#ifdef RCDEBUG
|
|
printrcflags(rc, "moint");
|
|
#endif
|
|
if (rc->rc_flags & RC_CTSFLOW) {
|
|
if (rc->rc_msvr & MSVR_CTS)
|
|
rc->rc_flags |= RC_SEND_RDY;
|
|
else
|
|
rc->rc_flags &= ~RC_SEND_RDY;
|
|
} else
|
|
rc->rc_flags |= RC_SEND_RDY;
|
|
if ((iack & MCR_CDchg) && !(rc->rc_flags & RC_MODCHG)) {
|
|
sc->sc_scheduled_event += LOTS_OF_EVENTS;
|
|
rc->rc_flags |= RC_MODCHG;
|
|
swi_sched(sc->sc_swicookie, 0);
|
|
}
|
|
goto more_intrs;
|
|
}
|
|
if (bsr & RC_BSR_TXINT) {
|
|
iack = rcin(sc, RC_PILR_TX);
|
|
if (iack != (GIVR_IT_TDI | RC_FAKEID)) {
|
|
device_printf(sc->sc_dev, "fake txint: %02x\n",
|
|
iack);
|
|
goto more_intrs;
|
|
}
|
|
chan = ((rcin(sc, CD180_GICR) & GICR_CHAN) >> GICR_LSH);
|
|
rc = &sc->sc_channels[chan];
|
|
if ( (rc->rc_flags & RC_OSUSP)
|
|
|| !(rc->rc_flags & RC_SEND_RDY)
|
|
)
|
|
goto more_intrs;
|
|
/* Handle breaks and other stuff */
|
|
if (rc->rc_pendcmd) {
|
|
rcout(sc, CD180_COR2, rc->rc_cor2 |= COR2_ETC);
|
|
rcout(sc, CD180_TDR, CD180_C_ESC);
|
|
rcout(sc, CD180_TDR, rc->rc_pendcmd);
|
|
rcout(sc, CD180_COR2, rc->rc_cor2 &= ~COR2_ETC);
|
|
rc->rc_pendcmd = 0;
|
|
goto more_intrs;
|
|
}
|
|
optr = rc->rc_optr;
|
|
resid = rc->rc_obufend - optr;
|
|
if (resid > CD180_NFIFO)
|
|
resid = CD180_NFIFO;
|
|
while (resid-- > 0)
|
|
rcout(sc, CD180_TDR, *optr++);
|
|
rc->rc_optr = optr;
|
|
|
|
/* output completed? */
|
|
if (optr >= rc->rc_obufend) {
|
|
rcout(sc, CD180_IER, rc->rc_ier &= ~IER_TxRdy);
|
|
#ifdef RCDEBUG
|
|
device_printf(sc->sc_dev,
|
|
"channel %d: output completed\n",
|
|
rc->rc_chan);
|
|
#endif
|
|
if (!(rc->rc_flags & RC_DOXXFER)) {
|
|
sc->sc_scheduled_event += LOTS_OF_EVENTS;
|
|
rc->rc_flags |= RC_DOXXFER;
|
|
swi_sched(sc->sc_swicookie, 0);
|
|
}
|
|
}
|
|
}
|
|
more_intrs:
|
|
rcout(sc, CD180_EOIR, 0); /* end of interrupt */
|
|
rcout(sc, RC_CTOUT, 0);
|
|
bsr = ~(rcin(sc, RC_BSR));
|
|
}
|
|
}
|
|
|
|
/* Feed characters to output buffer */
|
|
static void
|
|
rc_start(struct tty *tp)
|
|
{
|
|
struct rc_softc *sc;
|
|
struct rc_chans *rc;
|
|
int s;
|
|
|
|
rc = TTY_TO_RC(tp);
|
|
if (rc->rc_flags & RC_OSBUSY)
|
|
return;
|
|
sc = rc->rc_rcb;
|
|
s = spltty();
|
|
rc->rc_flags |= RC_OSBUSY;
|
|
critical_enter();
|
|
if (tp->t_state & TS_TTSTOP)
|
|
rc->rc_flags |= RC_OSUSP;
|
|
else
|
|
rc->rc_flags &= ~RC_OSUSP;
|
|
/* Do RTS flow control stuff */
|
|
if ( (rc->rc_flags & RC_RTSFLOW)
|
|
&& (tp->t_state & TS_TBLOCK)
|
|
&& (rc->rc_msvr & MSVR_RTS)
|
|
) {
|
|
rcout(sc, CD180_CAR, rc->rc_chan);
|
|
rcout(sc, CD180_MSVR, rc->rc_msvr &= ~MSVR_RTS);
|
|
} else if (!(rc->rc_msvr & MSVR_RTS)) {
|
|
rcout(sc, CD180_CAR, rc->rc_chan);
|
|
rcout(sc, CD180_MSVR, rc->rc_msvr |= MSVR_RTS);
|
|
}
|
|
critical_exit();
|
|
if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP))
|
|
goto out;
|
|
#ifdef RCDEBUG
|
|
printrcflags(rc, "rcstart");
|
|
#endif
|
|
ttwwakeup(tp);
|
|
#ifdef RCDEBUG
|
|
printf("rcstart: outq = %d obuf = %d\n",
|
|
tp->t_outq.c_cc, rc->rc_obufend - rc->rc_optr);
|
|
#endif
|
|
if (tp->t_state & TS_BUSY)
|
|
goto out; /* output still in progress ... */
|
|
|
|
if (tp->t_outq.c_cc > 0) {
|
|
u_int ocnt;
|
|
|
|
tp->t_state |= TS_BUSY;
|
|
ocnt = q_to_b(&tp->t_outq, rc->rc_obuf, sizeof rc->rc_obuf);
|
|
critical_enter();
|
|
rc->rc_optr = rc->rc_obuf;
|
|
rc->rc_obufend = rc->rc_optr + ocnt;
|
|
critical_exit();
|
|
if (!(rc->rc_ier & IER_TxRdy)) {
|
|
#ifdef RCDEBUG
|
|
device_printf(sc->sc_dev,
|
|
"channel %d: rcstart enable txint\n", rc->rc_chan);
|
|
#endif
|
|
rcout(sc, CD180_CAR, rc->rc_chan);
|
|
rcout(sc, CD180_IER, rc->rc_ier |= IER_TxRdy);
|
|
}
|
|
}
|
|
out:
|
|
rc->rc_flags &= ~RC_OSBUSY;
|
|
(void) splx(s);
|
|
}
|
|
|
|
/* Handle delayed events. */
|
|
void
|
|
rc_pollcard(void *arg)
|
|
{
|
|
struct rc_softc *sc;
|
|
struct rc_chans *rc;
|
|
struct tty *tp;
|
|
u_char *tptr, *eptr;
|
|
int chan, icnt;
|
|
|
|
sc = (struct rc_softc *)arg;
|
|
if (sc->sc_scheduled_event == 0)
|
|
return;
|
|
do {
|
|
rc = sc->sc_channels;
|
|
for (chan = 0; chan < CD180_NCHAN; rc++, chan++) {
|
|
tp = &rc->rc_tp;
|
|
#ifdef RCDEBUG
|
|
if (rc->rc_flags & (RC_DORXFER|RC_DOXXFER|RC_MODCHG|
|
|
RC_WAS_BUFOVFL|RC_WAS_SILOVFL))
|
|
printrcflags(rc, "rcevent");
|
|
#endif
|
|
if (rc->rc_flags & RC_WAS_BUFOVFL) {
|
|
critical_enter();
|
|
rc->rc_flags &= ~RC_WAS_BUFOVFL;
|
|
sc->sc_scheduled_event--;
|
|
critical_exit();
|
|
device_printf(sc->sc_dev,
|
|
"channel %d: interrupt-level buffer overflow\n",
|
|
chan);
|
|
}
|
|
if (rc->rc_flags & RC_WAS_SILOVFL) {
|
|
critical_enter();
|
|
rc->rc_flags &= ~RC_WAS_SILOVFL;
|
|
sc->sc_scheduled_event--;
|
|
critical_exit();
|
|
device_printf(sc->sc_dev,
|
|
"channel %d: silo overflow\n", chan);
|
|
}
|
|
if (rc->rc_flags & RC_MODCHG) {
|
|
critical_enter();
|
|
rc->rc_flags &= ~RC_MODCHG;
|
|
sc->sc_scheduled_event -= LOTS_OF_EVENTS;
|
|
critical_exit();
|
|
(*linesw[tp->t_line].l_modem)(tp, !!(rc->rc_msvr & MSVR_CD));
|
|
}
|
|
if (rc->rc_flags & RC_DORXFER) {
|
|
critical_enter();
|
|
rc->rc_flags &= ~RC_DORXFER;
|
|
eptr = rc->rc_iptr;
|
|
if (rc->rc_bufend == &rc->rc_ibuf[2 * RC_IBUFSIZE])
|
|
tptr = &rc->rc_ibuf[RC_IBUFSIZE];
|
|
else
|
|
tptr = rc->rc_ibuf;
|
|
icnt = eptr - tptr;
|
|
if (icnt > 0) {
|
|
if (rc->rc_bufend == &rc->rc_ibuf[2 * RC_IBUFSIZE]) {
|
|
rc->rc_iptr = rc->rc_ibuf;
|
|
rc->rc_bufend = &rc->rc_ibuf[RC_IBUFSIZE];
|
|
rc->rc_hiwat = &rc->rc_ibuf[RC_IHIGHWATER];
|
|
} else {
|
|
rc->rc_iptr = &rc->rc_ibuf[RC_IBUFSIZE];
|
|
rc->rc_bufend = &rc->rc_ibuf[2 * RC_IBUFSIZE];
|
|
rc->rc_hiwat =
|
|
&rc->rc_ibuf[RC_IBUFSIZE + RC_IHIGHWATER];
|
|
}
|
|
if ( (rc->rc_flags & RC_RTSFLOW)
|
|
&& (tp->t_state & TS_ISOPEN)
|
|
&& !(tp->t_state & TS_TBLOCK)
|
|
&& !(rc->rc_msvr & MSVR_RTS)
|
|
) {
|
|
rcout(sc, CD180_CAR, chan);
|
|
rcout(sc, CD180_MSVR,
|
|
rc->rc_msvr |= MSVR_RTS);
|
|
}
|
|
sc->sc_scheduled_event -= icnt;
|
|
}
|
|
critical_exit();
|
|
|
|
if (icnt <= 0 || !(tp->t_state & TS_ISOPEN))
|
|
goto done1;
|
|
|
|
if ( (tp->t_state & TS_CAN_BYPASS_L_RINT)
|
|
&& !(tp->t_state & TS_LOCAL)) {
|
|
if ((tp->t_rawq.c_cc + icnt) >= RB_I_HIGH_WATER
|
|
&& ((rc->rc_flags & RC_RTSFLOW) || (tp->t_iflag & IXOFF))
|
|
&& !(tp->t_state & TS_TBLOCK))
|
|
ttyblock(tp);
|
|
tk_nin += icnt;
|
|
tk_rawcc += icnt;
|
|
tp->t_rawcc += icnt;
|
|
if (b_to_q(tptr, icnt, &tp->t_rawq))
|
|
device_printf(sc->sc_dev,
|
|
"channel %d: tty-level buffer overflow\n",
|
|
chan);
|
|
ttwakeup(tp);
|
|
if ((tp->t_state & TS_TTSTOP) && ((tp->t_iflag & IXANY)
|
|
|| (tp->t_cc[VSTART] == tp->t_cc[VSTOP]))) {
|
|
tp->t_state &= ~TS_TTSTOP;
|
|
tp->t_lflag &= ~FLUSHO;
|
|
rc_start(tp);
|
|
}
|
|
} else {
|
|
for (; tptr < eptr; tptr++)
|
|
(*linesw[tp->t_line].l_rint)
|
|
(tptr[0] |
|
|
rc_rcsrt[tptr[INPUT_FLAGS_SHIFT] & 0xF], tp);
|
|
}
|
|
done1: ;
|
|
}
|
|
if (rc->rc_flags & RC_DOXXFER) {
|
|
critical_enter();
|
|
sc->sc_scheduled_event -= LOTS_OF_EVENTS;
|
|
rc->rc_flags &= ~RC_DOXXFER;
|
|
rc->rc_tp.t_state &= ~TS_BUSY;
|
|
critical_exit();
|
|
(*linesw[tp->t_line].l_start)(tp);
|
|
}
|
|
if (sc->sc_scheduled_event == 0)
|
|
break;
|
|
}
|
|
} while (sc->sc_scheduled_event >= LOTS_OF_EVENTS);
|
|
}
|
|
|
|
static void
|
|
rc_stop(struct tty *tp, int rw)
|
|
{
|
|
struct rc_softc *sc;
|
|
struct rc_chans *rc;
|
|
u_char *tptr, *eptr;
|
|
|
|
rc = TTY_TO_RC(tp);
|
|
sc = rc->rc_rcb;
|
|
#ifdef RCDEBUG
|
|
device_printf(sc->sc_dev, "channel %d: rc_stop %s%s\n",
|
|
rc->rc_chan, (rw & FWRITE)?"FWRITE ":"", (rw & FREAD)?"FREAD":"");
|
|
#endif
|
|
if (rw & FWRITE)
|
|
rc_discard_output(rc);
|
|
critical_enter();
|
|
if (rw & FREAD) {
|
|
rc->rc_flags &= ~RC_DORXFER;
|
|
eptr = rc->rc_iptr;
|
|
if (rc->rc_bufend == &rc->rc_ibuf[2 * RC_IBUFSIZE]) {
|
|
tptr = &rc->rc_ibuf[RC_IBUFSIZE];
|
|
rc->rc_iptr = &rc->rc_ibuf[RC_IBUFSIZE];
|
|
} else {
|
|
tptr = rc->rc_ibuf;
|
|
rc->rc_iptr = rc->rc_ibuf;
|
|
}
|
|
sc->sc_scheduled_event -= eptr - tptr;
|
|
}
|
|
if (tp->t_state & TS_TTSTOP)
|
|
rc->rc_flags |= RC_OSUSP;
|
|
else
|
|
rc->rc_flags &= ~RC_OSUSP;
|
|
critical_exit();
|
|
}
|
|
|
|
static int
|
|
rcopen(dev_t dev, int flag, int mode, d_thread_t *td)
|
|
{
|
|
struct rc_softc *sc;
|
|
struct rc_chans *rc;
|
|
struct tty *tp;
|
|
int s, error = 0;
|
|
|
|
rc = DEV_TO_RC(dev);
|
|
sc = rc->rc_rcb;
|
|
tp = &rc->rc_tp;
|
|
if (sc->sc_opencount < 0)
|
|
return (ENXIO);
|
|
sc->sc_opencount++;
|
|
#ifdef RCDEBUG
|
|
device_printf(sc->sc_dev, "channel %d: rcopen: dev %p\n",
|
|
rc->rc_chan, dev);
|
|
#endif
|
|
s = spltty();
|
|
|
|
again:
|
|
while (rc->rc_flags & RC_DTR_OFF) {
|
|
error = tsleep(&(rc->rc_dtrwait), TTIPRI | PCATCH, "rcdtr", 0);
|
|
if (error != 0)
|
|
goto out;
|
|
}
|
|
if (tp->t_state & TS_ISOPEN) {
|
|
if (CALLOUT(dev)) {
|
|
if (!(rc->rc_flags & RC_ACTOUT)) {
|
|
error = EBUSY;
|
|
goto out;
|
|
}
|
|
} else {
|
|
if (rc->rc_flags & RC_ACTOUT) {
|
|
if (flag & O_NONBLOCK) {
|
|
error = EBUSY;
|
|
goto out;
|
|
}
|
|
error = tsleep(&rc->rc_rcb,
|
|
TTIPRI|PCATCH, "rcbi", 0);
|
|
if (error)
|
|
goto out;
|
|
goto again;
|
|
}
|
|
}
|
|
if (tp->t_state & TS_XCLUDE &&
|
|
suser(td)) {
|
|
error = EBUSY;
|
|
goto out;
|
|
}
|
|
} else {
|
|
tp->t_oproc = rc_start;
|
|
tp->t_param = rc_param;
|
|
tp->t_stop = rc_stop;
|
|
tp->t_dev = dev;
|
|
|
|
if (CALLOUT(dev))
|
|
tp->t_cflag |= CLOCAL;
|
|
else
|
|
tp->t_cflag &= ~CLOCAL;
|
|
|
|
error = rc_param(tp, &tp->t_termios);
|
|
if (error)
|
|
goto out;
|
|
(void) rc_modctl(rc, TIOCM_RTS|TIOCM_DTR, DMSET);
|
|
|
|
if ((rc->rc_msvr & MSVR_CD) || CALLOUT(dev))
|
|
(*linesw[tp->t_line].l_modem)(tp, 1);
|
|
}
|
|
if (!(tp->t_state & TS_CARR_ON) && !CALLOUT(dev)
|
|
&& !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) {
|
|
rc->rc_dcdwaits++;
|
|
error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "rcdcd", 0);
|
|
rc->rc_dcdwaits--;
|
|
if (error != 0)
|
|
goto out;
|
|
goto again;
|
|
}
|
|
error = (*linesw[tp->t_line].l_open)(dev, tp);
|
|
disc_optim(tp, &tp->t_termios, rc);
|
|
if ((tp->t_state & TS_ISOPEN) && CALLOUT(dev))
|
|
rc->rc_flags |= RC_ACTOUT;
|
|
out:
|
|
(void) splx(s);
|
|
|
|
if(rc->rc_dcdwaits == 0 && !(tp->t_state & TS_ISOPEN))
|
|
rc_hardclose(rc);
|
|
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
rcclose(dev_t dev, int flag, int mode, d_thread_t *td)
|
|
{
|
|
struct rc_softc *sc;
|
|
struct rc_chans *rc;
|
|
struct tty *tp;
|
|
int s;
|
|
|
|
rc = DEV_TO_RC(dev);
|
|
sc = rc->rc_rcb;
|
|
tp = &rc->rc_tp;
|
|
#ifdef RCDEBUG
|
|
device_printf(sc->sc_dev, "channel %d: rcclose dev %p\n",
|
|
rc->rc_chan, dev);
|
|
#endif
|
|
s = spltty();
|
|
(*linesw[tp->t_line].l_close)(tp, flag);
|
|
disc_optim(tp, &tp->t_termios, rc);
|
|
rc_stop(tp, FREAD | FWRITE);
|
|
rc_hardclose(rc);
|
|
ttyclose(tp);
|
|
splx(s);
|
|
KASSERT(sc->sc_opencount > 0, ("rcclose: non-positive open count"));
|
|
sc->sc_opencount--;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
rc_hardclose(struct rc_chans *rc)
|
|
{
|
|
struct rc_softc *sc;
|
|
struct tty *tp;
|
|
int s;
|
|
|
|
tp = &rc->rc_tp;
|
|
sc = rc->rc_rcb;
|
|
s = spltty();
|
|
rcout(sc, CD180_CAR, rc->rc_chan);
|
|
|
|
/* Disable rx/tx intrs */
|
|
rcout(sc, CD180_IER, rc->rc_ier = 0);
|
|
if ( (tp->t_cflag & HUPCL)
|
|
|| (!(rc->rc_flags & RC_ACTOUT)
|
|
&& !(rc->rc_msvr & MSVR_CD)
|
|
&& !(tp->t_cflag & CLOCAL))
|
|
|| !(tp->t_state & TS_ISOPEN)
|
|
) {
|
|
CCRCMD(sc, rc->rc_chan, CCR_ResetChan);
|
|
WAITFORCCR(sc, rc->rc_chan);
|
|
(void) rc_modctl(rc, TIOCM_RTS, DMSET);
|
|
if (rc->rc_dtrwait) {
|
|
callout_reset(&rc->rc_dtrcallout, rc->rc_dtrwait,
|
|
rc_dtrwakeup, rc);
|
|
rc->rc_flags |= RC_DTR_OFF;
|
|
}
|
|
}
|
|
rc->rc_flags &= ~RC_ACTOUT;
|
|
wakeup( &rc->rc_rcb); /* wake bi */
|
|
wakeup(TSA_CARR_ON(tp));
|
|
(void) splx(s);
|
|
}
|
|
|
|
/* Reset the bastard */
|
|
static void
|
|
rc_hwreset(struct rc_softc *sc, uint chipid)
|
|
{
|
|
CCRCMD(sc, -1, CCR_HWRESET); /* Hardware reset */
|
|
DELAY(20000);
|
|
WAITFORCCR(sc, -1);
|
|
|
|
rcout(sc, RC_CTOUT, 0); /* Clear timeout */
|
|
rcout(sc, CD180_GIVR, chipid);
|
|
rcout(sc, CD180_GICR, 0);
|
|
|
|
/* Set Prescaler Registers (1 msec) */
|
|
rcout(sc, CD180_PPRL, ((RC_OSCFREQ + 999) / 1000) & 0xFF);
|
|
rcout(sc, CD180_PPRH, ((RC_OSCFREQ + 999) / 1000) >> 8);
|
|
|
|
/* Initialize Priority Interrupt Level Registers */
|
|
rcout(sc, CD180_PILR1, RC_PILR_MODEM);
|
|
rcout(sc, CD180_PILR2, RC_PILR_TX);
|
|
rcout(sc, CD180_PILR3, RC_PILR_RX);
|
|
|
|
/* Reset DTR */
|
|
rcout(sc, RC_DTREG, ~0);
|
|
}
|
|
|
|
/* Set channel parameters */
|
|
static int
|
|
rc_param(struct tty *tp, struct termios *ts)
|
|
{
|
|
struct rc_softc *sc;
|
|
struct rc_chans *rc;
|
|
int idivs, odivs, s, val, cflag, iflag, lflag, inpflow;
|
|
|
|
if ( ts->c_ospeed < 0 || ts->c_ospeed > 76800
|
|
|| ts->c_ispeed < 0 || ts->c_ispeed > 76800
|
|
)
|
|
return (EINVAL);
|
|
if (ts->c_ispeed == 0)
|
|
ts->c_ispeed = ts->c_ospeed;
|
|
odivs = RC_BRD(ts->c_ospeed);
|
|
idivs = RC_BRD(ts->c_ispeed);
|
|
|
|
rc = TTY_TO_RC(tp);
|
|
sc = rc->rc_rcb;
|
|
s = spltty();
|
|
|
|
/* Select channel */
|
|
rcout(sc, CD180_CAR, rc->rc_chan);
|
|
|
|
/* If speed == 0, hangup line */
|
|
if (ts->c_ospeed == 0) {
|
|
CCRCMD(sc, rc->rc_chan, CCR_ResetChan);
|
|
WAITFORCCR(sc, rc->rc_chan);
|
|
(void) rc_modctl(rc, TIOCM_DTR, DMBIC);
|
|
}
|
|
|
|
tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
|
|
cflag = ts->c_cflag;
|
|
iflag = ts->c_iflag;
|
|
lflag = ts->c_lflag;
|
|
|
|
if (idivs > 0) {
|
|
rcout(sc, CD180_RBPRL, idivs & 0xFF);
|
|
rcout(sc, CD180_RBPRH, idivs >> 8);
|
|
}
|
|
if (odivs > 0) {
|
|
rcout(sc, CD180_TBPRL, odivs & 0xFF);
|
|
rcout(sc, CD180_TBPRH, odivs >> 8);
|
|
}
|
|
|
|
/* set timeout value */
|
|
if (ts->c_ispeed > 0) {
|
|
int itm = ts->c_ispeed > 2400 ? 5 : 10000 / ts->c_ispeed + 1;
|
|
|
|
if ( !(lflag & ICANON)
|
|
&& ts->c_cc[VMIN] != 0 && ts->c_cc[VTIME] != 0
|
|
&& ts->c_cc[VTIME] * 10 > itm)
|
|
itm = ts->c_cc[VTIME] * 10;
|
|
|
|
rcout(sc, CD180_RTPR, itm <= 255 ? itm : 255);
|
|
}
|
|
|
|
switch (cflag & CSIZE) {
|
|
case CS5: val = COR1_5BITS; break;
|
|
case CS6: val = COR1_6BITS; break;
|
|
case CS7: val = COR1_7BITS; break;
|
|
default:
|
|
case CS8: val = COR1_8BITS; break;
|
|
}
|
|
if (cflag & PARENB) {
|
|
val |= COR1_NORMPAR;
|
|
if (cflag & PARODD)
|
|
val |= COR1_ODDP;
|
|
if (!(cflag & INPCK))
|
|
val |= COR1_Ignore;
|
|
} else
|
|
val |= COR1_Ignore;
|
|
if (cflag & CSTOPB)
|
|
val |= COR1_2SB;
|
|
rcout(sc, CD180_COR1, val);
|
|
|
|
/* Set FIFO threshold */
|
|
val = ts->c_ospeed <= 4800 ? 1 : CD180_NFIFO / 2;
|
|
inpflow = 0;
|
|
if ( (iflag & IXOFF)
|
|
&& ( ts->c_cc[VSTOP] != _POSIX_VDISABLE
|
|
&& ( ts->c_cc[VSTART] != _POSIX_VDISABLE
|
|
|| (iflag & IXANY)
|
|
)
|
|
)
|
|
) {
|
|
inpflow = 1;
|
|
val |= COR3_SCDE|COR3_FCT;
|
|
}
|
|
rcout(sc, CD180_COR3, val);
|
|
|
|
/* Initialize on-chip automatic flow control */
|
|
val = 0;
|
|
rc->rc_flags &= ~(RC_CTSFLOW|RC_SEND_RDY);
|
|
if (cflag & CCTS_OFLOW) {
|
|
rc->rc_flags |= RC_CTSFLOW;
|
|
val |= COR2_CtsAE;
|
|
} else
|
|
rc->rc_flags |= RC_SEND_RDY;
|
|
if (tp->t_state & TS_TTSTOP)
|
|
rc->rc_flags |= RC_OSUSP;
|
|
else
|
|
rc->rc_flags &= ~RC_OSUSP;
|
|
if (cflag & CRTS_IFLOW)
|
|
rc->rc_flags |= RC_RTSFLOW;
|
|
else
|
|
rc->rc_flags &= ~RC_RTSFLOW;
|
|
|
|
if (inpflow) {
|
|
if (ts->c_cc[VSTART] != _POSIX_VDISABLE)
|
|
rcout(sc, CD180_SCHR1, ts->c_cc[VSTART]);
|
|
rcout(sc, CD180_SCHR2, ts->c_cc[VSTOP]);
|
|
val |= COR2_TxIBE;
|
|
if (iflag & IXANY)
|
|
val |= COR2_IXM;
|
|
}
|
|
|
|
rcout(sc, CD180_COR2, rc->rc_cor2 = val);
|
|
|
|
CCRCMD(sc, rc->rc_chan, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
|
|
|
|
disc_optim(tp, ts, rc);
|
|
|
|
/* modem ctl */
|
|
val = cflag & CLOCAL ? 0 : MCOR1_CDzd;
|
|
if (cflag & CCTS_OFLOW)
|
|
val |= MCOR1_CTSzd;
|
|
rcout(sc, CD180_MCOR1, val);
|
|
|
|
val = cflag & CLOCAL ? 0 : MCOR2_CDod;
|
|
if (cflag & CCTS_OFLOW)
|
|
val |= MCOR2_CTSod;
|
|
rcout(sc, CD180_MCOR2, val);
|
|
|
|
/* enable i/o and interrupts */
|
|
CCRCMD(sc, rc->rc_chan,
|
|
CCR_XMTREN | ((cflag & CREAD) ? CCR_RCVREN : CCR_RCVRDIS));
|
|
WAITFORCCR(sc, rc->rc_chan);
|
|
|
|
rc->rc_ier = cflag & CLOCAL ? 0 : IER_CD;
|
|
if (cflag & CCTS_OFLOW)
|
|
rc->rc_ier |= IER_CTS;
|
|
if (cflag & CREAD)
|
|
rc->rc_ier |= IER_RxData;
|
|
if (tp->t_state & TS_BUSY)
|
|
rc->rc_ier |= IER_TxRdy;
|
|
if (ts->c_ospeed != 0)
|
|
rc_modctl(rc, TIOCM_DTR, DMBIS);
|
|
if ((cflag & CCTS_OFLOW) && (rc->rc_msvr & MSVR_CTS))
|
|
rc->rc_flags |= RC_SEND_RDY;
|
|
rcout(sc, CD180_IER, rc->rc_ier);
|
|
(void) splx(s);
|
|
return 0;
|
|
}
|
|
|
|
/* Re-initialize board after bogus interrupts */
|
|
static void
|
|
rc_reinit(struct rc_softc *sc)
|
|
{
|
|
struct rc_chans *rc;
|
|
int i;
|
|
|
|
rc_hwreset(sc, RC_FAKEID);
|
|
rc = sc->sc_channels;
|
|
for (i = 0; i < CD180_NCHAN; i++, rc++)
|
|
(void) rc_param(&rc->rc_tp, &rc->rc_tp.t_termios);
|
|
}
|
|
|
|
static int
|
|
rcioctl(dev_t dev, u_long cmd, caddr_t data, int flag, d_thread_t *td)
|
|
{
|
|
struct rc_chans *rc;
|
|
struct tty *tp;
|
|
int s, error;
|
|
|
|
rc = DEV_TO_RC(dev);
|
|
tp = &rc->rc_tp;
|
|
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td);
|
|
if (error != ENOIOCTL)
|
|
return (error);
|
|
error = ttioctl(tp, cmd, data, flag);
|
|
disc_optim(tp, &tp->t_termios, rc);
|
|
if (error != ENOIOCTL)
|
|
return (error);
|
|
s = spltty();
|
|
|
|
switch (cmd) {
|
|
case TIOCSBRK:
|
|
rc->rc_pendcmd = CD180_C_SBRK;
|
|
break;
|
|
|
|
case TIOCCBRK:
|
|
rc->rc_pendcmd = CD180_C_EBRK;
|
|
break;
|
|
|
|
case TIOCSDTR:
|
|
(void) rc_modctl(rc, TIOCM_DTR, DMBIS);
|
|
break;
|
|
|
|
case TIOCCDTR:
|
|
(void) rc_modctl(rc, TIOCM_DTR, DMBIC);
|
|
break;
|
|
|
|
case TIOCMGET:
|
|
*(int *) data = rc_modctl(rc, 0, DMGET);
|
|
break;
|
|
|
|
case TIOCMSET:
|
|
(void) rc_modctl(rc, *(int *) data, DMSET);
|
|
break;
|
|
|
|
case TIOCMBIC:
|
|
(void) rc_modctl(rc, *(int *) data, DMBIC);
|
|
break;
|
|
|
|
case TIOCMBIS:
|
|
(void) rc_modctl(rc, *(int *) data, DMBIS);
|
|
break;
|
|
|
|
case TIOCMSDTRWAIT:
|
|
error = suser(td);
|
|
if (error != 0) {
|
|
splx(s);
|
|
return (error);
|
|
}
|
|
rc->rc_dtrwait = *(int *)data * hz / 100;
|
|
break;
|
|
|
|
case TIOCMGDTRWAIT:
|
|
*(int *)data = rc->rc_dtrwait * 100 / hz;
|
|
break;
|
|
|
|
default:
|
|
(void) splx(s);
|
|
return ENOTTY;
|
|
}
|
|
(void) splx(s);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Modem control routines */
|
|
|
|
static int
|
|
rc_modctl(struct rc_chans *rc, int bits, int cmd)
|
|
{
|
|
struct rc_softc *sc;
|
|
u_char *dtr;
|
|
u_char msvr;
|
|
|
|
sc = rc->rc_rcb;
|
|
dtr = &sc->sc_dtr;
|
|
rcout(sc, CD180_CAR, rc->rc_chan);
|
|
|
|
switch (cmd) {
|
|
case DMSET:
|
|
rcout(sc, RC_DTREG, (bits & TIOCM_DTR) ?
|
|
~(*dtr |= 1 << rc->rc_chan) :
|
|
~(*dtr &= ~(1 << rc->rc_chan)));
|
|
msvr = rcin(sc, CD180_MSVR);
|
|
if (bits & TIOCM_RTS)
|
|
msvr |= MSVR_RTS;
|
|
else
|
|
msvr &= ~MSVR_RTS;
|
|
if (bits & TIOCM_DTR)
|
|
msvr |= MSVR_DTR;
|
|
else
|
|
msvr &= ~MSVR_DTR;
|
|
rcout(sc, CD180_MSVR, msvr);
|
|
break;
|
|
|
|
case DMBIS:
|
|
if (bits & TIOCM_DTR)
|
|
rcout(sc, RC_DTREG, ~(*dtr |= 1 << rc->rc_chan));
|
|
msvr = rcin(sc, CD180_MSVR);
|
|
if (bits & TIOCM_RTS)
|
|
msvr |= MSVR_RTS;
|
|
if (bits & TIOCM_DTR)
|
|
msvr |= MSVR_DTR;
|
|
rcout(sc, CD180_MSVR, msvr);
|
|
break;
|
|
|
|
case DMGET:
|
|
bits = TIOCM_LE;
|
|
msvr = rc->rc_msvr = rcin(sc, CD180_MSVR);
|
|
|
|
if (msvr & MSVR_RTS)
|
|
bits |= TIOCM_RTS;
|
|
if (msvr & MSVR_CTS)
|
|
bits |= TIOCM_CTS;
|
|
if (msvr & MSVR_DSR)
|
|
bits |= TIOCM_DSR;
|
|
if (msvr & MSVR_DTR)
|
|
bits |= TIOCM_DTR;
|
|
if (msvr & MSVR_CD)
|
|
bits |= TIOCM_CD;
|
|
if (~rcin(sc, RC_RIREG) & (1 << rc->rc_chan))
|
|
bits |= TIOCM_RI;
|
|
return bits;
|
|
|
|
case DMBIC:
|
|
if (bits & TIOCM_DTR)
|
|
rcout(sc, RC_DTREG, ~(*dtr &= ~(1 << rc->rc_chan)));
|
|
msvr = rcin(sc, CD180_MSVR);
|
|
if (bits & TIOCM_RTS)
|
|
msvr &= ~MSVR_RTS;
|
|
if (bits & TIOCM_DTR)
|
|
msvr &= ~MSVR_DTR;
|
|
rcout(sc, CD180_MSVR, msvr);
|
|
break;
|
|
}
|
|
rc->rc_msvr = rcin(sc, CD180_MSVR);
|
|
return 0;
|
|
}
|
|
|
|
#define ERR(s) do { \
|
|
device_printf(sc->sc_dev, "%s", ""); \
|
|
printf s ; \
|
|
printf("\n"); \
|
|
(void) splx(old_level); \
|
|
return 1; \
|
|
} while (0)
|
|
|
|
/* Test the board. */
|
|
int
|
|
rc_test(struct rc_softc *sc)
|
|
{
|
|
int chan = 0;
|
|
int i = 0, rcnt, old_level;
|
|
unsigned int iack, chipid;
|
|
unsigned short divs;
|
|
static u_char ctest[] = "\377\125\252\045\244\0\377";
|
|
#define CTLEN 8
|
|
|
|
struct rtest {
|
|
u_char txbuf[CD180_NFIFO]; /* TX buffer */
|
|
u_char rxbuf[CD180_NFIFO]; /* RX buffer */
|
|
int rxptr; /* RX pointer */
|
|
int txptr; /* TX pointer */
|
|
} tchans[CD180_NCHAN];
|
|
|
|
old_level = spltty();
|
|
|
|
chipid = RC_FAKEID;
|
|
|
|
/* First, reset board to inital state */
|
|
rc_hwreset(sc, chipid);
|
|
|
|
divs = RC_BRD(19200);
|
|
|
|
/* Initialize channels */
|
|
for (chan = 0; chan < CD180_NCHAN; chan++) {
|
|
|
|
/* Select and reset channel */
|
|
rcout(sc, CD180_CAR, chan);
|
|
CCRCMD(sc, chan, CCR_ResetChan);
|
|
WAITFORCCR(sc, chan);
|
|
|
|
/* Set speed */
|
|
rcout(sc, CD180_RBPRL, divs & 0xFF);
|
|
rcout(sc, CD180_RBPRH, divs >> 8);
|
|
rcout(sc, CD180_TBPRL, divs & 0xFF);
|
|
rcout(sc, CD180_TBPRH, divs >> 8);
|
|
|
|
/* set timeout value */
|
|
rcout(sc, CD180_RTPR, 0);
|
|
|
|
/* Establish local loopback */
|
|
rcout(sc, CD180_COR1, COR1_NOPAR | COR1_8BITS | COR1_1SB);
|
|
rcout(sc, CD180_COR2, COR2_LLM);
|
|
rcout(sc, CD180_COR3, CD180_NFIFO);
|
|
CCRCMD(sc, chan, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
|
|
CCRCMD(sc, chan, CCR_RCVREN | CCR_XMTREN);
|
|
WAITFORCCR(sc, chan);
|
|
rcout(sc, CD180_MSVR, MSVR_RTS);
|
|
|
|
/* Fill TXBUF with test data */
|
|
for (i = 0; i < CD180_NFIFO; i++) {
|
|
tchans[chan].txbuf[i] = ctest[i];
|
|
tchans[chan].rxbuf[i] = 0;
|
|
}
|
|
tchans[chan].txptr = tchans[chan].rxptr = 0;
|
|
|
|
/* Now, start transmit */
|
|
rcout(sc, CD180_IER, IER_TxMpty|IER_RxData);
|
|
}
|
|
/* Pseudo-interrupt poll stuff */
|
|
for (rcnt = 10000; rcnt-- > 0; rcnt--) {
|
|
i = ~(rcin(sc, RC_BSR));
|
|
if (i & RC_BSR_TOUT)
|
|
ERR(("BSR timeout bit set\n"));
|
|
else if (i & RC_BSR_TXINT) {
|
|
iack = rcin(sc, RC_PILR_TX);
|
|
if (iack != (GIVR_IT_TDI | chipid))
|
|
ERR(("Bad TX intr ack (%02x != %02x)\n",
|
|
iack, GIVR_IT_TDI | chipid));
|
|
chan = (rcin(sc, CD180_GICR) & GICR_CHAN) >> GICR_LSH;
|
|
/* If no more data to transmit, disable TX intr */
|
|
if (tchans[chan].txptr >= CD180_NFIFO) {
|
|
iack = rcin(sc, CD180_IER);
|
|
rcout(sc, CD180_IER, iack & ~IER_TxMpty);
|
|
} else {
|
|
for (iack = tchans[chan].txptr;
|
|
iack < CD180_NFIFO; iack++)
|
|
rcout(sc, CD180_TDR,
|
|
tchans[chan].txbuf[iack]);
|
|
tchans[chan].txptr = iack;
|
|
}
|
|
rcout(sc, CD180_EOIR, 0);
|
|
} else if (i & RC_BSR_RXINT) {
|
|
u_char ucnt;
|
|
|
|
iack = rcin(sc, RC_PILR_RX);
|
|
if (iack != (GIVR_IT_RGDI | chipid) &&
|
|
iack != (GIVR_IT_REI | chipid))
|
|
ERR(("Bad RX intr ack (%02x != %02x)\n",
|
|
iack, GIVR_IT_RGDI | chipid));
|
|
chan = (rcin(sc, CD180_GICR) & GICR_CHAN) >> GICR_LSH;
|
|
ucnt = rcin(sc, CD180_RDCR) & 0xF;
|
|
while (ucnt-- > 0) {
|
|
iack = rcin(sc, CD180_RCSR);
|
|
if (iack & RCSR_Timeout)
|
|
break;
|
|
if (iack & 0xF)
|
|
ERR(("Bad char chan %d (RCSR = %02X)\n",
|
|
chan, iack));
|
|
if (tchans[chan].rxptr > CD180_NFIFO)
|
|
ERR(("Got extra chars chan %d\n",
|
|
chan));
|
|
tchans[chan].rxbuf[tchans[chan].rxptr++] =
|
|
rcin(sc, CD180_RDR);
|
|
}
|
|
rcout(sc, CD180_EOIR, 0);
|
|
}
|
|
rcout(sc, RC_CTOUT, 0);
|
|
for (iack = chan = 0; chan < CD180_NCHAN; chan++)
|
|
if (tchans[chan].rxptr >= CD180_NFIFO)
|
|
iack++;
|
|
if (iack == CD180_NCHAN)
|
|
break;
|
|
}
|
|
for (chan = 0; chan < CD180_NCHAN; chan++) {
|
|
/* Select and reset channel */
|
|
rcout(sc, CD180_CAR, chan);
|
|
CCRCMD(sc, chan, CCR_ResetChan);
|
|
}
|
|
|
|
if (!rcnt)
|
|
ERR(("looses characters during local loopback\n"));
|
|
/* Now, check data */
|
|
for (chan = 0; chan < CD180_NCHAN; chan++)
|
|
for (i = 0; i < CD180_NFIFO; i++)
|
|
if (ctest[i] != tchans[chan].rxbuf[i])
|
|
ERR(("data mismatch chan %d ptr %d (%d != %d)\n",
|
|
chan, i, ctest[i], tchans[chan].rxbuf[i]));
|
|
(void) splx(old_level);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef RCDEBUG
|
|
static void
|
|
printrcflags(struct rc_chans *rc, char *comment)
|
|
{
|
|
struct rc_softc *sc;
|
|
u_short f = rc->rc_flags;
|
|
|
|
sc = rc->rc_rcb;
|
|
printf("rc%d/%d: %s flags: %s%s%s%s%s%s%s%s%s%s%s%s\n",
|
|
rc->rc_rcb->rcb_unit, rc->rc_chan, comment,
|
|
(f & RC_DTR_OFF)?"DTR_OFF " :"",
|
|
(f & RC_ACTOUT) ?"ACTOUT " :"",
|
|
(f & RC_RTSFLOW)?"RTSFLOW " :"",
|
|
(f & RC_CTSFLOW)?"CTSFLOW " :"",
|
|
(f & RC_DORXFER)?"DORXFER " :"",
|
|
(f & RC_DOXXFER)?"DOXXFER " :"",
|
|
(f & RC_MODCHG) ?"MODCHG " :"",
|
|
(f & RC_OSUSP) ?"OSUSP " :"",
|
|
(f & RC_OSBUSY) ?"OSBUSY " :"",
|
|
(f & RC_WAS_BUFOVFL) ?"BUFOVFL " :"",
|
|
(f & RC_WAS_SILOVFL) ?"SILOVFL " :"",
|
|
(f & RC_SEND_RDY) ?"SEND_RDY":"");
|
|
|
|
rcout(sc, CD180_CAR, rc->rc_chan);
|
|
|
|
printf("rc%d/%d: msvr %02x ier %02x ccsr %02x\n",
|
|
rc->rc_rcb->rcb_unit, rc->rc_chan,
|
|
rcin(sc, CD180_MSVR),
|
|
rcin(sc, CD180_IER),
|
|
rcin(sc, CD180_CCSR));
|
|
}
|
|
#endif /* RCDEBUG */
|
|
|
|
static void
|
|
rc_dtrwakeup(void *arg)
|
|
{
|
|
struct rc_chans *rc;
|
|
|
|
rc = (struct rc_chans *)arg;
|
|
rc->rc_flags &= ~RC_DTR_OFF;
|
|
wakeup(&rc->rc_dtrwait);
|
|
}
|
|
|
|
static void
|
|
rc_discard_output(struct rc_chans *rc)
|
|
{
|
|
critical_enter();
|
|
if (rc->rc_flags & RC_DOXXFER) {
|
|
rc->rc_rcb->sc_scheduled_event -= LOTS_OF_EVENTS;
|
|
rc->rc_flags &= ~RC_DOXXFER;
|
|
}
|
|
rc->rc_optr = rc->rc_obufend;
|
|
rc->rc_tp.t_state &= ~TS_BUSY;
|
|
critical_exit();
|
|
ttwwakeup(&rc->rc_tp);
|
|
}
|
|
|
|
static void
|
|
disc_optim(struct tty *tp, struct termios *t, struct rc_chans *rc)
|
|
{
|
|
|
|
if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
|
|
&& (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK))
|
|
&& (!(t->c_iflag & PARMRK)
|
|
|| (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
|
|
&& !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN))
|
|
&& linesw[tp->t_line].l_rint == ttyinput)
|
|
tp->t_state |= TS_CAN_BYPASS_L_RINT;
|
|
else
|
|
tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
|
|
rc->rc_hotchar = linesw[tp->t_line].l_hotchar;
|
|
}
|
|
|
|
static void
|
|
rc_wait0(struct rc_softc *sc, int chan, int line)
|
|
{
|
|
int rcnt;
|
|
|
|
for (rcnt = 50; rcnt && rcin(sc, CD180_CCR); rcnt--)
|
|
DELAY(30);
|
|
if (rcnt == 0)
|
|
device_printf(sc->sc_dev,
|
|
"channel %d command timeout, rc.c line: %d\n", chan, line);
|
|
}
|
|
|
|
static device_method_t rc_methods[] = {
|
|
/* Device interface */
|
|
DEVMETHOD(device_probe, rc_probe),
|
|
DEVMETHOD(device_attach, rc_attach),
|
|
DEVMETHOD(device_detach, rc_detach),
|
|
{ 0, 0 }
|
|
};
|
|
|
|
static driver_t rc_driver = {
|
|
"rc",
|
|
rc_methods, sizeof(struct rc_softc),
|
|
};
|
|
|
|
DRIVER_MODULE(rc, isa, rc_driver, rc_devclass, 0, 0);
|