freebsd-dev/sys/dev/rc/rc.c

1465 lines
38 KiB
C
Raw Normal View History

1995-05-12 15:09:29 +00:00
/*
* Copyright (C) 1995 by Pavel Antonov, Moscow, Russia.
* Copyright (C) 1995 by Andrey A. Chernov, Moscow, Russia.
* 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$
*
1995-05-12 15:09:29 +00:00
*/
/*
* SDL Communications Riscom/8 (based on Cirrus Logic CL-CD180) driver
*
*/
#include "rc.h"
1995-06-14 19:37:02 +00:00
/*#define RCDEBUG*/
1995-05-12 15:09:29 +00:00
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
1995-05-12 15:09:29 +00:00
#include <sys/tty.h>
#include <sys/conf.h>
#include <sys/dkstat.h>
#include <sys/fcntl.h>
#include <sys/bus.h>
#include <sys/interrupt.h>
1995-05-12 15:09:29 +00:00
#include <i386/isa/isa_device.h>
#include <i386/isa/ic/cd180.h>
#include <i386/isa/rcreg.h>
/* Prototypes */
2002-03-20 07:51:46 +00:00
static int rcprobe(struct isa_device *);
static int rcattach(struct isa_device *);
1995-05-12 15:09:29 +00:00
#define rcin(port) RC_IN (nec, port)
#define rcout(port,v) RC_OUT (nec, port, v)
1995-06-14 19:37:02 +00:00
#define WAITFORCCR(u,c) rc_wait0(nec, (u), (c), __LINE__)
#define CCRCMD(u,c,cmd) WAITFORCCR((u), (c)); rcout(CD180_CCR, (cmd))
1995-05-12 15:09:29 +00:00
1995-06-14 19:37:02 +00:00
#define RC_IBUFSIZE 256
#define RB_I_HIGH_WATER (TTYHOG - 2 * RC_IBUFSIZE)
#define RC_OBUFSIZE 512
1995-05-12 15:09:29 +00:00
#define RC_IHIGHWATER (3 * RC_IBUFSIZE / 4)
#define INPUT_FLAGS_SHIFT (2 * RC_IBUFSIZE)
#define LOTS_OF_EVENTS 64
#define RC_FAKEID 0x10
1995-06-14 19:37:02 +00:00
#define RC_PROBED 1
#define RC_ATTACHED 2
1995-05-12 15:09:29 +00:00
#define GET_UNIT(dev) (minor(dev) & 0x3F)
#define CALLOUT(dev) (minor(dev) & 0x80)
/* For isa routines */
struct isa_driver rcdriver = {
INTR_TYPE_TTY,
rcprobe,
rcattach,
"rc"
1995-05-12 15:09:29 +00:00
};
COMPAT_ISA_DRIVER(rc, rcdriver);
1995-05-12 15:09:29 +00:00
static d_open_t rcopen;
static d_close_t rcclose;
static d_ioctl_t rcioctl;
#define CDEV_MAJOR 63
static struct cdevsw rc_cdevsw = {
/* open */ rcopen,
/* close */ rcclose,
/* read */ ttyread,
/* write */ ttywrite,
/* ioctl */ rcioctl,
/* poll */ ttypoll,
/* mmap */ nommap,
/* strategy */ nostrategy,
/* name */ "rc",
/* maj */ CDEV_MAJOR,
/* dump */ nodump,
/* psize */ nopsize,
/* flags */ D_TTY | D_KQFILTER,
/* kqfilter */ ttykqfilter,
};
1995-05-12 15:09:29 +00:00
/* Per-board structure */
static struct rc_softc {
1995-06-14 19:37:02 +00:00
u_int rcb_probed; /* 1 - probed, 2 - attached */
1995-05-12 15:09:29 +00:00
u_int rcb_addr; /* Base I/O addr */
u_int rcb_unit; /* unit # */
u_char rcb_dtr; /* DTR status */
struct rc_chans *rcb_baserc; /* base rc ptr */
} rc_softc[NRC];
/* Per-channel structure */
static struct rc_chans {
struct rc_softc *rc_rcb; /* back ptr */
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 */
} rc_chans[NRC * CD180_NCHAN];
static int rc_scheduled_event = 0;
/* for pstat -t */
1995-12-10 13:40:44 +00:00
static struct tty rc_tty[NRC * CD180_NCHAN];
static const int nrc_tty = NRC * CD180_NCHAN;
1995-05-12 15:09:29 +00:00
/* Flags */
1995-06-14 19:37:02 +00:00
#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 */
1995-05-12 15:09:29 +00:00
/* 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
};
2001-02-09 18:35:53 +00:00
static void *rc_ih;
1995-05-12 15:09:29 +00:00
/* Static prototypes */
static ointhand2_t rcintr;
2002-03-20 07:51:46 +00:00
static void rc_hwreset(int, int, unsigned int);
static int rc_test(int, int);
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 rcpoll(void *);
2002-03-20 07:51:46 +00:00
static void rc_reinit(struct rc_softc *);
1995-06-14 19:37:02 +00:00
#ifdef RCDEBUG
static void printrcflags();
#endif
1995-05-12 15:09:29 +00:00
static timeout_t rc_dtrwakeup;
static timeout_t rc_wakeup;
static void disc_optim(struct tty *tp, struct termios *t, struct rc_chans *);
2002-03-20 07:51:46 +00:00
static void rc_wait0(int nec, int unit, int chan, int line);
1995-05-12 15:09:29 +00:00
/**********************************************/
/* Quick device probing */
1995-12-10 13:40:44 +00:00
static int
rcprobe(dvp)
1995-05-12 15:09:29 +00:00
struct isa_device *dvp;
{
int irq = ffs(dvp->id_irq) - 1;
register int nec = dvp->id_iobase;
if (dvp->id_unit > NRC)
return 0;
if (!RC_VALIDADDR(nec)) {
printf("rc%d: illegal base address %x\n", dvp->id_unit, nec);
1995-05-12 15:09:29 +00:00
return 0;
}
if (!RC_VALIDIRQ(irq)) {
printf("rc%d: illegal IRQ value %d\n", dvp->id_unit, irq);
1995-05-12 15:09:29 +00:00
return 0;
}
rcout(CD180_PPRL, 0x22); /* Random values to Prescale reg. */
rcout(CD180_PPRH, 0x11);
if (rcin(CD180_PPRL) != 0x22 || rcin(CD180_PPRH) != 0x11)
return 0;
/* Now, test the board more thoroughly, with diagnostic */
if (rc_test(nec, dvp->id_unit))
return 0;
1995-06-14 19:37:02 +00:00
rc_softc[dvp->id_unit].rcb_probed = RC_PROBED;
return 0xF;
1995-05-12 15:09:29 +00:00
}
1995-12-10 13:40:44 +00:00
static int
rcattach(dvp)
1995-05-12 15:09:29 +00:00
struct isa_device *dvp;
{
register int chan, nec = dvp->id_iobase;
1995-05-12 15:09:29 +00:00
struct rc_softc *rcb = &rc_softc[dvp->id_unit];
struct rc_chans *rc = &rc_chans[dvp->id_unit * CD180_NCHAN];
static int rc_started = 0;
1995-06-14 19:37:02 +00:00
struct tty *tp;
1995-05-12 15:09:29 +00:00
dvp->id_ointr = rcintr;
1995-05-12 15:09:29 +00:00
/* Thorooughly test the device */
1995-06-14 19:37:02 +00:00
if (rcb->rcb_probed != RC_PROBED)
1995-05-12 15:09:29 +00:00
return 0;
rcb->rcb_addr = nec;
rcb->rcb_dtr = 0;
rcb->rcb_baserc = rc;
rcb->rcb_unit = dvp->id_unit;
1995-05-12 15:09:29 +00:00
/*rcb->rcb_chipid = 0x10 + dvp->id_unit;*/
printf("rc%d: %d chans, firmware rev. %c\n", rcb->rcb_unit,
1995-05-12 15:09:29 +00:00
CD180_NCHAN, (rcin(CD180_GFRCR) & 0xF) + 'A');
for (chan = 0; chan < CD180_NCHAN; chan++, rc++) {
rc->rc_rcb = rcb;
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_flags = rc->rc_ier = rc->rc_msvr = 0;
rc->rc_cor2 = rc->rc_pendcmd = 0;
rc->rc_optr = rc->rc_obufend = rc->rc_obuf;
rc->rc_dtrwait = 3 * hz;
rc->rc_dcdwaits= 0;
rc->rc_hotchar = 0;
tp = rc->rc_tp = &rc_tty[chan + (dvp->id_unit * CD180_NCHAN)];
1995-06-14 19:37:02 +00:00
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;
make_dev(&rc_cdevsw, chan + CD180_NCHAN * rcb->rcb_unit,
UID_ROOT, GID_WHEEL, 0600, "ttym%d", chan + CD180_NCHAN *
rcb->rcb_unit);
make_dev(&rc_cdevsw, chan + CD180_NCHAN * rcb->rcb_unit + 128,
UID_UUCP, GID_DIALER, 0660, "cuam%d", chan + CD180_NCHAN *
rcb->rcb_unit);
1995-05-12 15:09:29 +00:00
}
1995-06-14 19:37:02 +00:00
rcb->rcb_probed = RC_ATTACHED;
if (!rc_started) {
cdevsw_add(&rc_cdevsw);
2001-02-09 18:35:53 +00:00
swi_add(&tty_ithd, "tty:rc", rcpoll, NULL, SWI_TTY, 0, &rc_ih);
1995-05-12 15:09:29 +00:00
rc_wakeup((void *)NULL);
rc_started = 1;
1995-05-12 15:09:29 +00:00
}
return 1;
}
/* RC interrupt handling */
static void
rcintr(unit)
1995-05-12 15:09:29 +00:00
int unit;
{
register struct rc_softc *rcb = &rc_softc[unit];
register struct rc_chans *rc;
1995-06-14 19:37:02 +00:00
register int nec, resid;
register u_char val, iack, bsr, ucnt, *optr;
int good_data, t_state;
1995-05-12 15:09:29 +00:00
1995-06-14 19:37:02 +00:00
if (rcb->rcb_probed != RC_ATTACHED) {
printf("rc%d: bogus interrupt\n", unit);
return;
}
1995-05-12 15:09:29 +00:00
nec = rcb->rcb_addr;
bsr = ~(rcin(RC_BSR));
1995-06-14 19:37:02 +00:00
if (!(bsr & (RC_BSR_TOUT|RC_BSR_RXINT|RC_BSR_TXINT|RC_BSR_MOINT))) {
printf("rc%d: extra interrupt\n", unit);
rcout(CD180_EOIR, 0);
return;
}
while (bsr & (RC_BSR_TOUT|RC_BSR_RXINT|RC_BSR_TXINT|RC_BSR_MOINT)) {
#ifdef RCDEBUG_DETAILED
printf("rc%d: intr (%02x) %s%s%s%s\n", unit, bsr,
(bsr & RC_BSR_TOUT)?"TOUT ":"",
(bsr & RC_BSR_RXINT)?"RXINT ":"",
(bsr & RC_BSR_TXINT)?"TXINT ":"",
(bsr & RC_BSR_MOINT)?"MOINT":"");
1995-05-12 15:09:29 +00:00
#endif
1995-06-14 19:37:02 +00:00
if (bsr & RC_BSR_TOUT) {
printf("rc%d: hardware failure, reset board\n", unit);
rcout(RC_CTOUT, 0);
rc_reinit(rcb);
return;
1995-05-12 15:09:29 +00:00
}
1995-06-14 19:37:02 +00:00
if (bsr & RC_BSR_RXINT) {
iack = rcin(RC_PILR_RX);
good_data = (iack == (GIVR_IT_RGDI | RC_FAKEID));
if (!good_data && iack != (GIVR_IT_REI | RC_FAKEID)) {
printf("rc%d: fake rxint: %02x\n", unit, iack);
goto more_intrs;
1995-05-12 15:09:29 +00:00
}
1995-06-14 19:37:02 +00:00
rc = rcb->rcb_baserc + ((rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH);
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(CD180_MSVR,
rc->rc_msvr &= ~MSVR_RTS);
else if (!(rc->rc_msvr & MSVR_RTS))
rcout(CD180_MSVR,
rc->rc_msvr |= MSVR_RTS);
}
ucnt = rcin(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;
1995-05-12 15:09:29 +00:00
rc_scheduled_event++;
}
1995-06-14 19:37:02 +00:00
}
optr = rc->rc_iptr;
/* check foor good data */
if (good_data) {
while (ucnt-- > 0) {
val = rcin(CD180_RDR);
optr[0] = val;
optr[INPUT_FLAGS_SHIFT] = 0;
optr++;
1995-05-12 15:09:29 +00:00
rc_scheduled_event++;
1995-06-14 19:37:02 +00:00
if (val != 0 && val == rc->rc_hotchar)
Change the preemption code for software interrupt thread schedules and mutex releases to not require flags for the cases when preemption is not allowed: The purpose of the MTX_NOSWITCH and SWI_NOSWITCH flags is to prevent switching to a higher priority thread on mutex releease and swi schedule, respectively when that switch is not safe. Now that the critical section API maintains a per-thread nesting count, the kernel can easily check whether or not it should switch without relying on flags from the programmer. This fixes a few bugs in that all current callers of swi_sched() used SWI_NOSWITCH, when in fact, only the ones called from fast interrupt handlers and the swi_sched of softclock needed this flag. Note that to ensure that swi_sched()'s in clock and fast interrupt handlers do not switch, these handlers have to be explicitly wrapped in critical_enter/exit pairs. Presently, just wrapping the handlers is sufficient, but in the future with the fully preemptive kernel, the interrupt must be EOI'd before critical_exit() is called. (critical_exit() can switch due to a deferred preemption in a fully preemptive kernel.) I've tested the changes to the interrupt code on i386 and alpha. I have not tested ia64, but the interrupt code is almost identical to the alpha code, so I expect it will work fine. PowerPC and ARM do not yet have interrupt code in the tree so they shouldn't be broken. Sparc64 is broken, but that's been ok'd by jake and tmm who will be fixing the interrupt code for sparc64 shortly. Reviewed by: peter Tested on: i386, alpha
2002-01-05 08:47:13 +00:00
swi_sched(rc_ih, 0);
1995-06-14 19:37:02 +00:00
}
} else {
/* Store also status data */
while (ucnt-- > 0) {
iack = rcin(CD180_RCSR);
if (iack & RCSR_Timeout)
break;
if ( (iack & RCSR_OE)
&& !(rc->rc_flags & RC_WAS_SILOVFL)) {
rc->rc_flags |= RC_WAS_SILOVFL;
rc_scheduled_event++;
}
val = rcin(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))
1995-06-14 19:37:02 +00:00
|| !(rc->rc_tp->t_iflag & IGNPAR))
&& (!(iack & RCSR_Break)
|| !(rc->rc_tp->t_iflag & IGNBRK)))) {
1995-06-14 19:37:02 +00:00
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))))
1995-06-14 19:37:02 +00:00
val = 0;
else if (val != 0 && val == rc->rc_hotchar)
Change the preemption code for software interrupt thread schedules and mutex releases to not require flags for the cases when preemption is not allowed: The purpose of the MTX_NOSWITCH and SWI_NOSWITCH flags is to prevent switching to a higher priority thread on mutex releease and swi schedule, respectively when that switch is not safe. Now that the critical section API maintains a per-thread nesting count, the kernel can easily check whether or not it should switch without relying on flags from the programmer. This fixes a few bugs in that all current callers of swi_sched() used SWI_NOSWITCH, when in fact, only the ones called from fast interrupt handlers and the swi_sched of softclock needed this flag. Note that to ensure that swi_sched()'s in clock and fast interrupt handlers do not switch, these handlers have to be explicitly wrapped in critical_enter/exit pairs. Presently, just wrapping the handlers is sufficient, but in the future with the fully preemptive kernel, the interrupt must be EOI'd before critical_exit() is called. (critical_exit() can switch due to a deferred preemption in a fully preemptive kernel.) I've tested the changes to the interrupt code on i386 and alpha. I have not tested ia64, but the interrupt code is almost identical to the alpha code, so I expect it will work fine. PowerPC and ARM do not yet have interrupt code in the tree so they shouldn't be broken. Sparc64 is broken, but that's been ok'd by jake and tmm who will be fixing the interrupt code for sparc64 shortly. Reviewed by: peter Tested on: i386, alpha
2002-01-05 08:47:13 +00:00
swi_sched(rc_ih, 0);
1995-06-14 19:37:02 +00:00
optr[0] = val;
optr[INPUT_FLAGS_SHIFT] = iack;
optr++;
rc_scheduled_event++;
}
1995-05-12 15:09:29 +00:00
}
}
1995-06-14 19:37:02 +00:00
rc->rc_iptr = optr;
rc->rc_flags |= RC_DORXFER;
} else
resid = ucnt;
/* Clear FIFO if necessary */
while (resid-- > 0) {
if (!good_data)
iack = rcin(CD180_RCSR);
else
iack = 0;
if (iack & RCSR_Timeout)
break;
(void) rcin(CD180_RDR);
1995-05-12 15:09:29 +00:00
}
1995-06-14 19:37:02 +00:00
goto more_intrs;
1995-05-12 15:09:29 +00:00
}
1995-06-14 19:37:02 +00:00
if (bsr & RC_BSR_MOINT) {
iack = rcin(RC_PILR_MODEM);
if (iack != (GIVR_IT_MSCI | RC_FAKEID)) {
printf("rc%d: fake moint: %02x\n", unit, iack);
goto more_intrs;
}
rc = rcb->rcb_baserc + ((rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH);
iack = rcin(CD180_MCR);
rc->rc_msvr = rcin(CD180_MSVR);
rcout(CD180_MCR, 0);
1995-05-12 15:09:29 +00:00
#ifdef RCDEBUG
1995-06-14 19:37:02 +00:00
printrcflags(rc, "moint");
1995-05-12 15:09:29 +00:00
#endif
1995-06-14 19:37:02 +00:00
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
1995-05-12 15:09:29 +00:00
rc->rc_flags |= RC_SEND_RDY;
1995-06-14 19:37:02 +00:00
if ((iack & MCR_CDchg) && !(rc->rc_flags & RC_MODCHG)) {
rc_scheduled_event += LOTS_OF_EVENTS;
rc->rc_flags |= RC_MODCHG;
Change the preemption code for software interrupt thread schedules and mutex releases to not require flags for the cases when preemption is not allowed: The purpose of the MTX_NOSWITCH and SWI_NOSWITCH flags is to prevent switching to a higher priority thread on mutex releease and swi schedule, respectively when that switch is not safe. Now that the critical section API maintains a per-thread nesting count, the kernel can easily check whether or not it should switch without relying on flags from the programmer. This fixes a few bugs in that all current callers of swi_sched() used SWI_NOSWITCH, when in fact, only the ones called from fast interrupt handlers and the swi_sched of softclock needed this flag. Note that to ensure that swi_sched()'s in clock and fast interrupt handlers do not switch, these handlers have to be explicitly wrapped in critical_enter/exit pairs. Presently, just wrapping the handlers is sufficient, but in the future with the fully preemptive kernel, the interrupt must be EOI'd before critical_exit() is called. (critical_exit() can switch due to a deferred preemption in a fully preemptive kernel.) I've tested the changes to the interrupt code on i386 and alpha. I have not tested ia64, but the interrupt code is almost identical to the alpha code, so I expect it will work fine. PowerPC and ARM do not yet have interrupt code in the tree so they shouldn't be broken. Sparc64 is broken, but that's been ok'd by jake and tmm who will be fixing the interrupt code for sparc64 shortly. Reviewed by: peter Tested on: i386, alpha
2002-01-05 08:47:13 +00:00
swi_sched(rc_ih, 0);
1995-06-14 19:37:02 +00:00
}
goto more_intrs;
1995-05-12 15:09:29 +00:00
}
1995-06-14 19:37:02 +00:00
if (bsr & RC_BSR_TXINT) {
iack = rcin(RC_PILR_TX);
if (iack != (GIVR_IT_TDI | RC_FAKEID)) {
printf("rc%d: fake txint: %02x\n", unit, iack);
goto more_intrs;
}
rc = rcb->rcb_baserc + ((rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH);
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(CD180_COR2, rc->rc_cor2 |= COR2_ETC);
rcout(CD180_TDR, CD180_C_ESC);
rcout(CD180_TDR, rc->rc_pendcmd);
rcout(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(CD180_TDR, *optr++);
rc->rc_optr = optr;
/* output completed? */
if (optr >= rc->rc_obufend) {
rcout(CD180_IER, rc->rc_ier &= ~IER_TxRdy);
1995-05-12 15:09:29 +00:00
#ifdef RCDEBUG
1995-06-14 19:37:02 +00:00
printf("rc%d/%d: output completed\n", unit, rc->rc_chan);
1995-05-12 15:09:29 +00:00
#endif
1995-06-14 19:37:02 +00:00
if (!(rc->rc_flags & RC_DOXXFER)) {
rc_scheduled_event += LOTS_OF_EVENTS;
rc->rc_flags |= RC_DOXXFER;
Change the preemption code for software interrupt thread schedules and mutex releases to not require flags for the cases when preemption is not allowed: The purpose of the MTX_NOSWITCH and SWI_NOSWITCH flags is to prevent switching to a higher priority thread on mutex releease and swi schedule, respectively when that switch is not safe. Now that the critical section API maintains a per-thread nesting count, the kernel can easily check whether or not it should switch without relying on flags from the programmer. This fixes a few bugs in that all current callers of swi_sched() used SWI_NOSWITCH, when in fact, only the ones called from fast interrupt handlers and the swi_sched of softclock needed this flag. Note that to ensure that swi_sched()'s in clock and fast interrupt handlers do not switch, these handlers have to be explicitly wrapped in critical_enter/exit pairs. Presently, just wrapping the handlers is sufficient, but in the future with the fully preemptive kernel, the interrupt must be EOI'd before critical_exit() is called. (critical_exit() can switch due to a deferred preemption in a fully preemptive kernel.) I've tested the changes to the interrupt code on i386 and alpha. I have not tested ia64, but the interrupt code is almost identical to the alpha code, so I expect it will work fine. PowerPC and ARM do not yet have interrupt code in the tree so they shouldn't be broken. Sparc64 is broken, but that's been ok'd by jake and tmm who will be fixing the interrupt code for sparc64 shortly. Reviewed by: peter Tested on: i386, alpha
2002-01-05 08:47:13 +00:00
swi_sched(rc_ih, 0);
1995-06-14 19:37:02 +00:00
}
}
1995-05-12 15:09:29 +00:00
}
1995-06-14 19:37:02 +00:00
more_intrs:
rcout(CD180_EOIR, 0); /* end of interrupt */
rcout(RC_CTOUT, 0);
bsr = ~(rcin(RC_BSR));
1995-05-12 15:09:29 +00:00
}
}
/* Feed characters to output buffer */
static void rc_start(tp)
register struct tty *tp;
{
register struct rc_chans *rc = &rc_chans[GET_UNIT(tp->t_dev)];
register int nec = rc->rc_rcb->rcb_addr, s;
if (rc->rc_flags & RC_OSBUSY)
return;
s = spltty();
rc->rc_flags |= RC_OSBUSY;
disable_intr();
if (tp->t_state & TS_TTSTOP)
rc->rc_flags |= RC_OSUSP;
else
rc->rc_flags &= ~RC_OSUSP;
/* Do RTS flow control stuff */
1995-06-14 19:37:02 +00:00
if ( (rc->rc_flags & RC_RTSFLOW)
&& (tp->t_state & TS_TBLOCK)
&& (rc->rc_msvr & MSVR_RTS)
) {
rcout(CD180_CAR, rc->rc_chan);
rcout(CD180_MSVR, rc->rc_msvr &= ~MSVR_RTS);
} else if (!(rc->rc_msvr & MSVR_RTS)) {
rcout(CD180_CAR, rc->rc_chan);
rcout(CD180_MSVR, rc->rc_msvr |= MSVR_RTS);
1995-05-12 15:09:29 +00:00
}
enable_intr();
if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP))
goto out;
#ifdef RCDEBUG
printrcflags(rc, "rcstart");
#endif
ttwwakeup(tp);
1995-05-12 15:09:29 +00:00
#ifdef RCDEBUG
1995-06-14 19:37:02 +00:00
printf("rcstart: outq = %d obuf = %d\n",
1995-05-12 15:09:29 +00:00
tp->t_outq.c_cc, rc->rc_obufend - rc->rc_optr);
#endif
1995-06-14 19:37:02 +00:00
if (tp->t_state & TS_BUSY)
1995-05-12 15:09:29 +00:00
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);
disable_intr();
rc->rc_optr = rc->rc_obuf;
1995-06-14 19:37:02 +00:00
rc->rc_obufend = rc->rc_optr + ocnt;
1995-05-12 15:09:29 +00:00
enable_intr();
1995-06-14 19:37:02 +00:00
if (!(rc->rc_ier & IER_TxRdy)) {
1995-05-12 15:09:29 +00:00
#ifdef RCDEBUG
1995-06-14 19:37:02 +00:00
printf("rc%d/%d: rcstart enable txint\n", rc->rc_rcb->rcb_unit, rc->rc_chan);
1995-05-12 15:09:29 +00:00
#endif
rcout(CD180_CAR, rc->rc_chan);
1995-06-14 19:37:02 +00:00
rcout(CD180_IER, rc->rc_ier |= IER_TxRdy);
1995-05-12 15:09:29 +00:00
}
}
out:
rc->rc_flags &= ~RC_OSBUSY;
(void) splx(s);
}
/* Handle delayed events. */
void rcpoll(void *arg)
1995-05-12 15:09:29 +00:00
{
register struct rc_chans *rc;
register struct rc_softc *rcb;
register u_char *tptr, *eptr;
register struct tty *tp;
register int chan, icnt, nec, unit;
1995-05-12 15:09:29 +00:00
if (rc_scheduled_event == 0)
return;
repeat:
for (unit = 0; unit < NRC; unit++) {
rcb = &rc_softc[unit];
rc = rcb->rcb_baserc;
nec = rc->rc_rcb->rcb_addr;
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) {
1995-06-14 19:37:02 +00:00
disable_intr();
1995-05-12 15:09:29 +00:00
rc->rc_flags &= ~RC_WAS_BUFOVFL;
rc_scheduled_event--;
1995-06-14 19:37:02 +00:00
enable_intr();
1995-05-12 15:09:29 +00:00
printf("rc%d/%d: interrupt-level buffer overflow\n",
unit, chan);
}
if (rc->rc_flags & RC_WAS_SILOVFL) {
1995-06-14 19:37:02 +00:00
disable_intr();
1995-05-12 15:09:29 +00:00
rc->rc_flags &= ~RC_WAS_SILOVFL;
rc_scheduled_event--;
1995-06-14 19:37:02 +00:00
enable_intr();
1995-05-12 15:09:29 +00:00
printf("rc%d/%d: silo overflow\n",
unit, chan);
}
if (rc->rc_flags & RC_MODCHG) {
1995-06-14 19:37:02 +00:00
disable_intr();
1995-05-12 15:09:29 +00:00
rc->rc_flags &= ~RC_MODCHG;
rc_scheduled_event -= LOTS_OF_EVENTS;
1995-06-14 19:37:02 +00:00
enable_intr();
(*linesw[tp->t_line].l_modem)(tp, !!(rc->rc_msvr & MSVR_CD));
1995-05-12 15:09:29 +00:00
}
if (rc->rc_flags & RC_DORXFER) {
disable_intr();
1995-06-14 19:37:02 +00:00
rc->rc_flags &= ~RC_DORXFER;
1995-05-12 15:09:29 +00:00
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];
}
1995-06-14 19:37:02 +00:00
if ( (rc->rc_flags & RC_RTSFLOW)
1995-05-12 15:09:29 +00:00
&& (tp->t_state & TS_ISOPEN)
1995-06-14 19:37:02 +00:00
&& !(tp->t_state & TS_TBLOCK)
&& !(rc->rc_msvr & MSVR_RTS)
) {
1995-05-12 15:09:29 +00:00
rcout(CD180_CAR, chan);
rcout(CD180_MSVR,
rc->rc_msvr |= MSVR_RTS);
}
rc_scheduled_event -= icnt;
}
enable_intr();
1995-06-14 19:37:02 +00:00
if (icnt <= 0 || !(tp->t_state & TS_ISOPEN))
1995-05-12 15:09:29 +00:00
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);
1995-05-12 15:09:29 +00:00
tk_nin += icnt;
tk_rawcc += icnt;
tp->t_rawcc += icnt;
if (b_to_q(tptr, icnt, &tp->t_rawq))
printf("rc%d/%d: tty-level buffer overflow\n",
unit, 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);
1995-05-12 15:09:29 +00:00
}
} else {
for (; tptr < eptr; tptr++)
(*linesw[tp->t_line].l_rint)
(tptr[0] |
rc_rcsrt[tptr[INPUT_FLAGS_SHIFT] & 0xF], tp);
}
done1: ;
1995-05-12 15:09:29 +00:00
}
if (rc->rc_flags & RC_DOXXFER) {
1995-06-14 19:37:02 +00:00
disable_intr();
rc_scheduled_event -= LOTS_OF_EVENTS;
rc->rc_flags &= ~RC_DOXXFER;
rc->rc_tp->t_state &= ~TS_BUSY;
enable_intr();
1995-05-12 15:09:29 +00:00
(*linesw[tp->t_line].l_start)(tp);
}
}
if (rc_scheduled_event == 0)
break;
}
if (rc_scheduled_event >= LOTS_OF_EVENTS)
goto repeat;
}
static void
rc_stop(tp, rw)
1995-05-12 15:09:29 +00:00
register struct tty *tp;
int rw;
{
register struct rc_chans *rc = &rc_chans[GET_UNIT(tp->t_dev)];
u_char *tptr, *eptr;
#ifdef RCDEBUG
printf("rc%d/%d: rc_stop %s%s\n", rc->rc_rcb->rcb_unit, rc->rc_chan,
1995-05-12 15:09:29 +00:00
(rw & FWRITE)?"FWRITE ":"", (rw & FREAD)?"FREAD":"");
#endif
if (rw & FWRITE)
rc_discard_output(rc);
disable_intr();
if (rw & FREAD) {
1995-06-14 19:37:02 +00:00
rc->rc_flags &= ~RC_DORXFER;
1995-05-12 15:09:29 +00:00
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;
}
rc_scheduled_event -= eptr - tptr;
}
if (tp->t_state & TS_TTSTOP)
rc->rc_flags |= RC_OSUSP;
else
rc->rc_flags &= ~RC_OSUSP;
enable_intr();
}
static int
rcopen(dev, flag, mode, td)
1995-05-12 15:09:29 +00:00
dev_t dev;
int flag, mode;
struct thread *td;
1995-05-12 15:09:29 +00:00
{
register struct rc_chans *rc;
register struct tty *tp;
int unit, nec, s, error = 0;
unit = GET_UNIT(dev);
if (unit >= NRC * CD180_NCHAN)
return ENXIO;
1995-06-14 19:37:02 +00:00
if (rc_softc[unit / CD180_NCHAN].rcb_probed != RC_ATTACHED)
return ENXIO;
1995-05-12 15:09:29 +00:00
rc = &rc_chans[unit];
1995-06-14 19:37:02 +00:00
tp = rc->rc_tp;
dev->si_tty = tp;
1995-05-12 15:09:29 +00:00
nec = rc->rc_rcb->rcb_addr;
#ifdef RCDEBUG
1995-06-14 19:37:02 +00:00
printf("rc%d/%d: rcopen: dev %x\n", rc->rc_rcb->rcb_unit, unit, dev);
1995-05-12 15:09:29 +00:00
#endif
s = spltty();
again:
while (rc->rc_flags & RC_DTR_OFF) {
1995-06-14 19:37:02 +00:00
error = tsleep(&(rc->rc_dtrwait), TTIPRI | PCATCH, "rcdtr", 0);
1995-05-12 15:09:29 +00:00
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)
1995-05-12 15:09:29 +00:00
goto out;
goto again;
}
}
if (tp->t_state & TS_XCLUDE &&
suser(td)) {
1995-05-12 15:09:29 +00:00
error = EBUSY;
goto out;
}
} else {
tp->t_oproc = rc_start;
tp->t_param = rc_param;
tp->t_stop = rc_stop;
1995-05-12 15:09:29 +00:00
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;
1995-06-14 19:37:02 +00:00
(void) rc_modctl(rc, TIOCM_RTS|TIOCM_DTR, DMSET);
1995-05-12 15:09:29 +00:00
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);
1995-05-12 15:09:29 +00:00
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);
1995-05-12 15:09:29 +00:00
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, flag, mode, td)
1995-05-12 15:09:29 +00:00
dev_t dev;
int flag, mode;
struct thread *td;
1995-05-12 15:09:29 +00:00
{
register struct rc_chans *rc;
register struct tty *tp;
int s, unit = GET_UNIT(dev);
if (unit >= NRC * CD180_NCHAN)
return ENXIO;
rc = &rc_chans[unit];
tp = rc->rc_tp;
1995-06-14 19:37:02 +00:00
#ifdef RCDEBUG
printf("rc%d/%d: rcclose dev %x\n", rc->rc_rcb->rcb_unit, unit, dev);
#endif
1995-05-12 15:09:29 +00:00
s = spltty();
(*linesw[tp->t_line].l_close)(tp, flag);
disc_optim(tp, &tp->t_termios, rc);
rc_stop(tp, FREAD | FWRITE);
1995-05-12 15:09:29 +00:00
rc_hardclose(rc);
ttyclose(tp);
splx(s);
return 0;
}
static void rc_hardclose(rc)
register struct rc_chans *rc;
{
register int s, nec = rc->rc_rcb->rcb_addr;
register struct tty *tp = rc->rc_tp;
s = spltty();
rcout(CD180_CAR, rc->rc_chan);
1995-06-14 19:37:02 +00:00
/* Disable rx/tx intrs */
1995-05-12 15:09:29 +00:00
rcout(CD180_IER, rc->rc_ier = 0);
1995-06-14 19:37:02 +00:00
if ( (tp->t_cflag & HUPCL)
|| (!(rc->rc_flags & RC_ACTOUT)
1995-05-12 15:09:29 +00:00
&& !(rc->rc_msvr & MSVR_CD)
&& !(tp->t_cflag & CLOCAL))
1995-06-14 19:37:02 +00:00
|| !(tp->t_state & TS_ISOPEN)
) {
CCRCMD(rc->rc_rcb->rcb_unit, rc->rc_chan, CCR_ResetChan);
WAITFORCCR(rc->rc_rcb->rcb_unit, rc->rc_chan);
1995-05-12 15:09:29 +00:00
(void) rc_modctl(rc, TIOCM_RTS, DMSET);
if (rc->rc_dtrwait) {
timeout(rc_dtrwakeup, rc, rc->rc_dtrwait);
rc->rc_flags |= RC_DTR_OFF;
}
}
rc->rc_flags &= ~RC_ACTOUT;
wakeup((caddr_t) &rc->rc_rcb); /* wake bi */
wakeup(TSA_CARR_ON(tp));
1995-05-12 15:09:29 +00:00
(void) splx(s);
}
/* Reset the bastard */
1995-06-14 19:37:02 +00:00
static void rc_hwreset(unit, nec, chipid)
register int unit, nec;
1995-05-12 15:09:29 +00:00
unsigned int chipid;
{
1995-06-14 19:37:02 +00:00
CCRCMD(unit, -1, CCR_HWRESET); /* Hardware reset */
1995-05-12 15:09:29 +00:00
DELAY(20000);
1995-06-14 19:37:02 +00:00
WAITFORCCR(unit, -1);
rcout(RC_CTOUT, 0); /* Clear timeout */
1995-05-12 15:09:29 +00:00
rcout(CD180_GIVR, chipid);
rcout(CD180_GICR, 0);
/* Set Prescaler Registers (1 msec) */
1995-06-14 19:37:02 +00:00
rcout(CD180_PPRL, ((RC_OSCFREQ + 999) / 1000) & 0xFF);
rcout(CD180_PPRH, ((RC_OSCFREQ + 999) / 1000) >> 8);
1995-05-12 15:09:29 +00:00
/* Initialize Priority Interrupt Level Registers */
rcout(CD180_PILR1, RC_PILR_MODEM);
rcout(CD180_PILR2, RC_PILR_TX);
rcout(CD180_PILR3, RC_PILR_RX);
/* Reset DTR */
1995-06-14 19:37:02 +00:00
rcout(RC_DTREG, ~0);
1995-05-12 15:09:29 +00:00
}
/* Set channel parameters */
static int rc_param(tp, ts)
register struct tty *tp;
struct termios *ts;
{
1995-06-14 19:37:02 +00:00
register struct rc_chans *rc = &rc_chans[GET_UNIT(tp->t_dev)];
1995-05-12 15:09:29 +00:00
register int nec = rc->rc_rcb->rcb_addr;
1995-06-14 19:37:02 +00:00
int idivs, odivs, s, val, cflag, iflag, lflag, inpflow;
1995-05-12 15:09:29 +00:00
if ( ts->c_ospeed < 0 || ts->c_ospeed > 76800
|| ts->c_ispeed < 0 || ts->c_ispeed > 76800
)
return (EINVAL);
1995-05-12 15:09:29 +00:00
if (ts->c_ispeed == 0)
ts->c_ispeed = ts->c_ospeed;
odivs = RC_BRD(ts->c_ospeed);
idivs = RC_BRD(ts->c_ispeed);
1995-05-12 15:09:29 +00:00
s = spltty();
1995-06-14 19:37:02 +00:00
/* Select channel */
rcout(CD180_CAR, rc->rc_chan);
1995-05-12 15:09:29 +00:00
/* If speed == 0, hangup line */
1995-06-14 19:37:02 +00:00
if (ts->c_ospeed == 0) {
CCRCMD(rc->rc_rcb->rcb_unit, rc->rc_chan, CCR_ResetChan);
WAITFORCCR(rc->rc_rcb->rcb_unit, rc->rc_chan);
(void) rc_modctl(rc, TIOCM_DTR, DMBIC);
}
1995-05-12 15:09:29 +00:00
tp->t_state &= ~TS_CAN_BYPASS_L_RINT;
cflag = ts->c_cflag;
iflag = ts->c_iflag;
lflag = ts->c_lflag;
if (idivs > 0) {
rcout(CD180_RBPRL, idivs & 0xFF);
rcout(CD180_RBPRH, idivs >> 8);
}
if (odivs > 0) {
rcout(CD180_TBPRL, odivs & 0xFF);
rcout(CD180_TBPRH, odivs >> 8);
}
/* set timeout value */
1995-06-14 19:37:02 +00:00
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(CD180_RTPR, itm <= 255 ? itm : 255);
}
1995-05-12 15:09:29 +00:00
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;
1995-06-14 19:37:02 +00:00
if (!(cflag & INPCK))
val |= COR1_Ignore;
1995-05-12 15:09:29 +00:00
} else
1995-06-14 19:37:02 +00:00
val |= COR1_Ignore;
1995-05-12 15:09:29 +00:00
if (cflag & CSTOPB)
val |= COR1_2SB;
rcout(CD180_COR1, val);
/* Set FIFO threshold */
1995-06-14 19:37:02 +00:00
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(CD180_COR3, val);
1995-05-12 15:09:29 +00:00
/* Initialize on-chip automatic flow control */
val = 0;
1995-06-14 19:37:02 +00:00
rc->rc_flags &= ~(RC_CTSFLOW|RC_SEND_RDY);
1995-05-12 15:09:29 +00:00
if (cflag & CCTS_OFLOW) {
rc->rc_flags |= RC_CTSFLOW;
1995-06-14 19:37:02 +00:00
val |= COR2_CtsAE;
} else
1995-05-12 15:09:29 +00:00
rc->rc_flags |= RC_SEND_RDY;
1995-06-14 19:37:02 +00:00
if (tp->t_state & TS_TTSTOP)
rc->rc_flags |= RC_OSUSP;
else
rc->rc_flags &= ~RC_OSUSP;
1995-05-12 15:09:29 +00:00
if (cflag & CRTS_IFLOW)
rc->rc_flags |= RC_RTSFLOW;
1995-06-14 19:37:02 +00:00
else
rc->rc_flags &= ~RC_RTSFLOW;
if (inpflow) {
if (ts->c_cc[VSTART] != _POSIX_VDISABLE)
rcout(CD180_SCHR1, ts->c_cc[VSTART]);
rcout(CD180_SCHR2, ts->c_cc[VSTOP]);
val |= COR2_TxIBE;
if (iflag & IXANY)
val |= COR2_IXM;
1995-05-12 15:09:29 +00:00
}
1995-06-14 19:37:02 +00:00
rcout(CD180_COR2, rc->rc_cor2 = val);
CCRCMD(rc->rc_rcb->rcb_unit, rc->rc_chan,
CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1995-05-12 15:09:29 +00:00
disc_optim(tp, ts, rc);
/* modem ctl */
1995-06-14 19:37:02 +00:00
val = cflag & CLOCAL ? 0 : MCOR1_CDzd;
if (cflag & CCTS_OFLOW)
val |= MCOR1_CTSzd;
rcout(CD180_MCOR1, val);
1995-05-12 15:09:29 +00:00
1995-06-14 19:37:02 +00:00
val = cflag & CLOCAL ? 0 : MCOR2_CDod;
if (cflag & CCTS_OFLOW)
val |= MCOR2_CTSod;
rcout(CD180_MCOR2, val);
1995-05-12 15:09:29 +00:00
1995-06-14 19:37:02 +00:00
/* enable i/o and interrupts */
CCRCMD(rc->rc_rcb->rcb_unit, rc->rc_chan,
CCR_XMTREN | ((cflag & CREAD) ? CCR_RCVREN : CCR_RCVRDIS));
WAITFORCCR(rc->rc_rcb->rcb_unit, 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(CD180_IER, rc->rc_ier);
1995-05-12 15:09:29 +00:00
(void) splx(s);
return 0;
}
1995-06-14 19:37:02 +00:00
/* Re-initialize board after bogus interrupts */
static void rc_reinit(rcb)
struct rc_softc *rcb;
{
register struct rc_chans *rc, *rce;
register int nec;
1995-06-14 19:37:02 +00:00
nec = rcb->rcb_addr;
rc_hwreset(rcb->rcb_unit, nec, RC_FAKEID);
rc = &rc_chans[rcb->rcb_unit * CD180_NCHAN];
rce = rc + CD180_NCHAN;
for (; rc < rce; rc++)
(void) rc_param(rc->rc_tp, &rc->rc_tp->t_termios);
}
static int
rcioctl(dev, cmd, data, flag, td)
1995-05-12 15:09:29 +00:00
dev_t dev;
u_long cmd;
int flag;
1995-05-12 15:09:29 +00:00
caddr_t data;
struct thread *td;
1995-05-12 15:09:29 +00:00
{
register struct rc_chans *rc = &rc_chans[GET_UNIT(dev)];
register int s, error;
struct tty *tp = rc->rc_tp;
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td);
if (error != ENOIOCTL)
1995-05-12 15:09:29 +00:00
return (error);
error = ttioctl(tp, cmd, data, flag);
disc_optim(tp, &tp->t_termios, rc);
if (error != ENOIOCTL)
1995-05-12 15:09:29 +00:00
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:
1995-06-14 19:37:02 +00:00
(void) rc_modctl(rc, TIOCM_DTR, DMBIS);
1995-05-12 15:09:29 +00:00
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);
1995-05-12 15:09:29 +00:00
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(rc, bits, cmd)
register struct rc_chans *rc;
int bits, cmd;
{
register int nec = rc->rc_rcb->rcb_addr;
1995-06-14 19:37:02 +00:00
u_char *dtr = &rc->rc_rcb->rcb_dtr, msvr;
1995-05-12 15:09:29 +00:00
rcout(CD180_CAR, rc->rc_chan);
switch (cmd) {
case DMSET:
1995-06-14 19:37:02 +00:00
rcout(RC_DTREG, (bits & TIOCM_DTR) ?
~(*dtr |= 1 << rc->rc_chan) :
~(*dtr &= ~(1 << rc->rc_chan)));
msvr = rcin(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(CD180_MSVR, msvr);
break;
1995-05-12 15:09:29 +00:00
case DMBIS:
1995-06-14 19:37:02 +00:00
if (bits & TIOCM_DTR)
rcout(RC_DTREG, ~(*dtr |= 1 << rc->rc_chan));
msvr = rcin(CD180_MSVR);
1995-05-12 15:09:29 +00:00
if (bits & TIOCM_RTS)
1995-06-14 19:37:02 +00:00
msvr |= MSVR_RTS;
1995-05-12 15:09:29 +00:00
if (bits & TIOCM_DTR)
1995-06-14 19:37:02 +00:00
msvr |= MSVR_DTR;
rcout(CD180_MSVR, msvr);
1995-05-12 15:09:29 +00:00
break;
case DMGET:
bits = TIOCM_LE;
1995-06-14 19:37:02 +00:00
msvr = rc->rc_msvr = rcin(CD180_MSVR);
1995-05-12 15:09:29 +00:00
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;
1995-06-14 19:37:02 +00:00
if (msvr & MSVR_CD)
bits |= TIOCM_CD;
if (~rcin(RC_RIREG) & (1 << rc->rc_chan))
bits |= TIOCM_RI;
1995-05-12 15:09:29 +00:00
return bits;
case DMBIC:
if (bits & TIOCM_DTR)
1995-06-14 19:37:02 +00:00
rcout(RC_DTREG, ~(*dtr &= ~(1 << rc->rc_chan)));
msvr = rcin(CD180_MSVR);
1995-05-12 15:09:29 +00:00
if (bits & TIOCM_RTS)
1995-06-14 19:37:02 +00:00
msvr &= ~MSVR_RTS;
if (bits & TIOCM_DTR)
msvr &= ~MSVR_DTR;
rcout(CD180_MSVR, msvr);
1995-05-12 15:09:29 +00:00
break;
}
1995-06-14 19:37:02 +00:00
rc->rc_msvr = rcin(CD180_MSVR);
1995-05-12 15:09:29 +00:00
return 0;
}
/* Test the board. */
int rc_test(nec, unit)
register int nec;
int unit;
{
int chan = 0;
1995-05-12 15:09:29 +00:00
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
#define ERR(s) { \
printf("rc%d: ", unit); printf s ; printf("\n"); \
(void) splx(old_level); return 1; }
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];
1995-06-14 19:37:02 +00:00
old_level = spltty();
1995-05-12 15:09:29 +00:00
chipid = RC_FAKEID;
/* First, reset board to inital state */
1995-06-14 19:37:02 +00:00
rc_hwreset(unit, nec, chipid);
divs = RC_BRD(19200);
1995-05-12 15:09:29 +00:00
/* Initialize channels */
for (chan = 0; chan < CD180_NCHAN; chan++) {
/* Select and reset channel */
rcout(CD180_CAR, chan);
1995-06-14 19:37:02 +00:00
CCRCMD(unit, chan, CCR_ResetChan);
WAITFORCCR(unit, chan);
1995-05-12 15:09:29 +00:00
/* Set speed */
rcout(CD180_RBPRL, divs & 0xFF);
rcout(CD180_RBPRH, divs >> 8);
rcout(CD180_TBPRL, divs & 0xFF);
rcout(CD180_TBPRH, divs >> 8);
/* set timeout value */
rcout(CD180_RTPR, 0);
/* Establish local loopback */
rcout(CD180_COR1, COR1_NOPAR | COR1_8BITS | COR1_1SB);
rcout(CD180_COR2, COR2_LLM);
rcout(CD180_COR3, CD180_NFIFO);
1995-06-14 19:37:02 +00:00
CCRCMD(unit, chan, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
CCRCMD(unit, chan, CCR_RCVREN | CCR_XMTREN);
WAITFORCCR(unit, chan);
1995-05-12 15:09:29 +00:00
rcout(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 */
1995-06-14 19:37:02 +00:00
rcout(CD180_IER, IER_TxMpty|IER_RxData);
1995-05-12 15:09:29 +00:00
}
/* Pseudo-interrupt poll stuff */
for (rcnt = 10000; rcnt-- > 0; rcnt--) {
1995-06-14 19:37:02 +00:00
i = ~(rcin(RC_BSR));
1995-05-12 15:09:29 +00:00
if (i & RC_BSR_TOUT)
ERR(("BSR timeout bit set\n"))
1995-06-14 19:37:02 +00:00
else if (i & RC_BSR_TXINT) {
1995-05-12 15:09:29 +00:00
iack = rcin(RC_PILR_TX);
if (iack != (GIVR_IT_TDI | chipid))
ERR(("Bad TX intr ack (%02x != %02x)\n",
iack, GIVR_IT_TDI | chipid));
1995-06-14 19:37:02 +00:00
chan = (rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH;
1995-05-12 15:09:29 +00:00
/* If no more data to transmit, disable TX intr */
if (tchans[chan].txptr >= CD180_NFIFO) {
iack = rcin(CD180_IER);
1995-06-14 19:37:02 +00:00
rcout(CD180_IER, iack & ~IER_TxMpty);
1995-05-12 15:09:29 +00:00
} else {
for (iack = tchans[chan].txptr;
iack < CD180_NFIFO; iack++)
rcout(CD180_TDR,
tchans[chan].txbuf[iack]);
tchans[chan].txptr = iack;
}
1995-06-14 19:37:02 +00:00
rcout(CD180_EOIR, 0);
} else if (i & RC_BSR_RXINT) {
u_char ucnt;
1995-05-12 15:09:29 +00:00
iack = rcin(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))
1995-06-14 19:37:02 +00:00
chan = (rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH;
1995-05-12 15:09:29 +00:00
ucnt = rcin(CD180_RDCR) & 0xF;
while (ucnt-- > 0) {
iack = rcin(CD180_RCSR);
1995-06-14 19:37:02 +00:00
if (iack & RCSR_Timeout)
1995-05-12 15:09:29 +00:00
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(CD180_RDR);
}
rcout(CD180_EOIR, 0);
}
1995-06-14 19:37:02 +00:00
rcout(RC_CTOUT, 0);
1995-05-12 15:09:29 +00:00
for (iack = chan = 0; chan < CD180_NCHAN; chan++)
if (tchans[chan].rxptr >= CD180_NFIFO)
iack++;
if (iack == CD180_NCHAN)
break;
}
1995-06-14 19:37:02 +00:00
for (chan = 0; chan < CD180_NCHAN; chan++) {
/* Select and reset channel */
rcout(CD180_CAR, chan);
CCRCMD(unit, chan, CCR_ResetChan);
}
1995-05-12 15:09:29 +00:00
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
1995-06-14 19:37:02 +00:00
static void printrcflags(rc, comment)
1995-05-12 15:09:29 +00:00
struct rc_chans *rc;
char *comment;
{
u_short f = rc->rc_flags;
1995-06-14 19:37:02 +00:00
register int nec = rc->rc_rcb->rcb_addr;
1995-05-12 15:09:29 +00:00
1995-06-14 19:37:02 +00:00
printf("rc%d/%d: %s flags: %s%s%s%s%s%s%s%s%s%s%s%s\n",
1995-05-12 15:09:29 +00:00
rc->rc_rcb->rcb_unit, rc->rc_chan, comment,
(f & RC_DTR_OFF)?"DTR_OFF " :"",
1995-06-14 19:37:02 +00:00
(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(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(CD180_MSVR),
rcin(CD180_IER),
rcin(CD180_CCSR));
1995-05-12 15:09:29 +00:00
}
#endif /* RCDEBUG */
static void
rc_dtrwakeup(chan)
void *chan;
{
struct rc_chans *rc;
rc = (struct rc_chans *)chan;
rc->rc_flags &= ~RC_DTR_OFF;
wakeup(&rc->rc_dtrwait);
}
static void
rc_discard_output(rc)
struct rc_chans *rc;
{
disable_intr();
if (rc->rc_flags & RC_DOXXFER) {
rc_scheduled_event -= LOTS_OF_EVENTS;
rc->rc_flags &= ~RC_DOXXFER;
}
rc->rc_optr = rc->rc_obufend;
rc->rc_tp->t_state &= ~TS_BUSY;
1995-06-14 19:37:02 +00:00
enable_intr();
ttwwakeup(rc->rc_tp);
1995-05-12 15:09:29 +00:00
}
static void
rc_wakeup(chan)
void *chan;
{
timeout(rc_wakeup, (caddr_t)NULL, 1);
if (rc_scheduled_event != 0) {
int s;
s = splsofttty();
rcpoll(NULL);
1995-05-12 15:09:29 +00:00
splx(s);
}
}
static void
disc_optim(tp, t, rc)
struct tty *tp;
struct termios *t;
struct rc_chans *rc;
{
if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON))
1995-05-12 15:09:29 +00:00
&& (!(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))
1995-05-12 15:09:29 +00:00
&& 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;
1995-05-12 15:09:29 +00:00
}
1995-06-14 19:37:02 +00:00
static void
rc_wait0(nec, unit, chan, line)
int nec, unit, chan, line;
{
int rcnt;
for (rcnt = 50; rcnt && rcin(CD180_CCR); rcnt--)
DELAY(30);
1995-06-14 19:37:02 +00:00
if (rcnt == 0)
printf("rc%d/%d: channel command timeout, rc.c line: %d\n",
unit, chan, line);
}