freebsd-skq/sys/i386/isa/rc.c

1529 lines
40 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.
*/
/*
* SDL Communications Riscom/8 (based on Cirrus Logic CL-CD180) driver
*
*/
#include "rc.h"
#if NRC > 0
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/ioctl.h>
#include <sys/tty.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/conf.h>
#include <sys/dkstat.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/devconf.h>
#include <machine/clock.h>
#include <i386/isa/isa.h>
#include <i386/isa/isa_device.h>
#include <i386/isa/sioreg.h>
#include <i386/isa/ic/cd180.h>
#include <i386/isa/rcreg.h>
the second set of changes in a move towards getting devices to be totally dynamic. this is only the devices in i386/isa I'll do more tomorrow. they're completely masked by #ifdef JREMOD at this stage... the eventual aim is that every driver will do a SYSINIT at startup BEFORE the probes, which will effectively link it into the devsw tables etc. If I'd thought about it more I'd have put that in in this set (damn) The ioconf lines generated by config will also end up in the device's own scope as well, so ioconf.c will eventually be gutted the SYSINIT call to the driver will include a phase where the driver links it's ioconf line into a chain of such. when this phase is done then the user can modify them with the boot: -c config menu if he wants, just like now.. config will put the config lines out in the .h file (e.g. in aha.h will be the addresses for the aha driver to look.) as I said this is a very small first step.. the aim of THIS set of edits is to not have to edit conf.c at all when adding a new device.. the tabe will be a simple skeleton.. when this is done, it will allow other changes to be made, all teh time still having a fully working kernel tree, but the logical outcome is the complete REMOVAL of the devsw tables. By the end of this, linked in drivers will be exactly the same as run-time loaded drivers, except they JUST HAPPEN to already be linked and present at startup.. the SYSINIT calls will be the equivalent of the "init" call made to a newly loaded driver in every respect. For this edit, each of the files has the following code inserted into it: obviously, tailored to suit.. ----------------------somewhere at the top: #ifdef JREMOD #include <sys/conf.h> #define CDEV_MAJOR 13 #define BDEV_MAJOR 4 static void sd_devsw_install(); #endif /*JREMOD */ ---------------------somewhere that's run during bootup: EVENTUALLY a SYSINIT #ifdef JREMOD sd_devsw_install(); #endif /*JREMOD*/ -----------------------at the bottom: #ifdef JREMOD struct bdevsw sd_bdevsw = { sdopen, sdclose, sdstrategy, sdioctl, /*4*/ sddump, sdsize, 0 }; struct cdevsw sd_cdevsw = { sdopen, sdclose, rawread, rawwrite, /*13*/ sdioctl, nostop, nullreset, nodevtotty,/* sd */ seltrue, nommap, sdstrategy }; static sd_devsw_installed = 0; static void sd_devsw_install() { dev_t descript; if( ! sd_devsw_installed ) { descript = makedev(CDEV_MAJOR,0); cdevsw_add(&descript,&sd_cdevsw,NULL); #if defined(BDEV_MAJOR) descript = makedev(BDEV_MAJOR,0); bdevsw_add(&descript,&sd_bdevsw,NULL); #endif /*BDEV_MAJOR*/ sd_devsw_installed = 1; } } #endif /* JREMOD */
1995-11-28 09:42:06 +00:00
#ifdef JREMOD
#include <sys/conf.h>
#define CDEV_MAJOR 63
static void rc_devsw_install();
#endif /*JREMOD*/
1995-05-12 15:09:29 +00:00
/* Prototypes */
int rcprobe __P((struct isa_device *));
int rcattach __P((struct isa_device *));
/*-
* This space intentionally left blank to stop __LINE__ from screwing up
* regression tests :-(.
*
*
*
*/
1995-05-12 15:09:29 +00:00
void rcpoll __P((void));
#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 = {
rcprobe, rcattach, "rc"
};
/* 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 */
struct tty rc_tty[NRC * CD180_NCHAN];
int nrc_tty = NRC * CD180_NCHAN;
/* 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
};
/* Static prototypes */
1995-06-14 19:37:02 +00:00
static void rc_hwreset __P((int, int, unsigned int));
1995-05-12 15:09:29 +00:00
static int rc_test __P((int, int));
static void rc_discard_output __P((struct rc_chans *));
static void rc_hardclose __P((struct rc_chans *));
static int rc_modctl __P((struct rc_chans *, int, int));
static void rc_start __P((struct tty *));
static int rc_param __P((struct tty *, struct termios *));
static void rc_registerdev __P((struct isa_device *id));
1995-06-14 19:37:02 +00:00
static void rc_reinit __P((struct rc_softc *));
#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 __P((struct tty *tp, struct termios *t, struct rc_chans *));
1995-06-14 19:37:02 +00:00
static void rc_wait0 __P((int nec, int unit, int chan, int line));
1995-05-12 15:09:29 +00:00
/**********************************************/
/* Quick device probing */
int rcprobe(dvp)
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", nec);
return 0;
}
if (!RC_VALIDIRQ(irq)) {
printf("rc%d: illegal IRQ value %d\n", irq);
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
}
static struct kern_devconf kdc_rc[NRC] = { {
0, 0, 0, /* filled in by dev_attach */
"rc", 0, { MDDT_ISA, 0, "tty" },
isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
&kdc_isa0, /* parent */
0, /* parentdata */
DC_UNCONFIGURED, /* state */
"RISCom/8 multiport card",
DC_CLS_SERIAL /* class */
} };
static void
rc_registerdev(id)
struct isa_device *id;
{
int unit;
unit = id->id_unit;
if (unit != 0)
kdc_rc[unit] = kdc_rc[0];
kdc_rc[unit].kdc_unit = unit;
kdc_rc[unit].kdc_isa = id;
kdc_rc[unit].kdc_state = DC_UNKNOWN;
dev_attach(&kdc_rc[unit]);
}
int rcattach(dvp)
struct isa_device *dvp;
{
register int i, chan, nec = dvp->id_iobase;
struct rc_softc *rcb = &rc_softc[dvp->id_unit];
struct rc_chans *rc = &rc_chans[dvp->id_unit * CD180_NCHAN];
static int rc_wakeup_started = 0;
1995-06-14 19:37:02 +00:00
struct tty *tp;
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_chipid = 0x10 + dvp->id_unit;*/
printf("rc%d: %d chans, firmware rev. %c\n", dvp->id_unit,
CD180_NCHAN, (rcin(CD180_GFRCR) & 0xF) + 'A');
rc_registerdev(dvp);
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;
1995-06-14 19:37:02 +00:00
tp = rc->rc_tp = &rc_tty[chan];
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;
1995-05-12 15:09:29 +00:00
}
1995-06-14 19:37:02 +00:00
rcb->rcb_probed = RC_ATTACHED;
1995-05-12 15:09:29 +00:00
if (!rc_wakeup_started) {
rc_wakeup((void *)NULL);
rc_wakeup_started = 0;
}
the second set of changes in a move towards getting devices to be totally dynamic. this is only the devices in i386/isa I'll do more tomorrow. they're completely masked by #ifdef JREMOD at this stage... the eventual aim is that every driver will do a SYSINIT at startup BEFORE the probes, which will effectively link it into the devsw tables etc. If I'd thought about it more I'd have put that in in this set (damn) The ioconf lines generated by config will also end up in the device's own scope as well, so ioconf.c will eventually be gutted the SYSINIT call to the driver will include a phase where the driver links it's ioconf line into a chain of such. when this phase is done then the user can modify them with the boot: -c config menu if he wants, just like now.. config will put the config lines out in the .h file (e.g. in aha.h will be the addresses for the aha driver to look.) as I said this is a very small first step.. the aim of THIS set of edits is to not have to edit conf.c at all when adding a new device.. the tabe will be a simple skeleton.. when this is done, it will allow other changes to be made, all teh time still having a fully working kernel tree, but the logical outcome is the complete REMOVAL of the devsw tables. By the end of this, linked in drivers will be exactly the same as run-time loaded drivers, except they JUST HAPPEN to already be linked and present at startup.. the SYSINIT calls will be the equivalent of the "init" call made to a newly loaded driver in every respect. For this edit, each of the files has the following code inserted into it: obviously, tailored to suit.. ----------------------somewhere at the top: #ifdef JREMOD #include <sys/conf.h> #define CDEV_MAJOR 13 #define BDEV_MAJOR 4 static void sd_devsw_install(); #endif /*JREMOD */ ---------------------somewhere that's run during bootup: EVENTUALLY a SYSINIT #ifdef JREMOD sd_devsw_install(); #endif /*JREMOD*/ -----------------------at the bottom: #ifdef JREMOD struct bdevsw sd_bdevsw = { sdopen, sdclose, sdstrategy, sdioctl, /*4*/ sddump, sdsize, 0 }; struct cdevsw sd_cdevsw = { sdopen, sdclose, rawread, rawwrite, /*13*/ sdioctl, nostop, nullreset, nodevtotty,/* sd */ seltrue, nommap, sdstrategy }; static sd_devsw_installed = 0; static void sd_devsw_install() { dev_t descript; if( ! sd_devsw_installed ) { descript = makedev(CDEV_MAJOR,0); cdevsw_add(&descript,&sd_cdevsw,NULL); #if defined(BDEV_MAJOR) descript = makedev(BDEV_MAJOR,0); bdevsw_add(&descript,&sd_bdevsw,NULL); #endif /*BDEV_MAJOR*/ sd_devsw_installed = 1; } } #endif /* JREMOD */
1995-11-28 09:42:06 +00:00
#ifdef JREMOD
rc_devsw_install();
#endif /*JREMOD*/
1995-05-12 15:09:29 +00:00
return 1;
}
/* RC interrupt handling */
void rcintr(unit)
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)
setsofttty();
}
} 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))
|| !(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)
setsofttty();
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;
setsofttty();
}
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;
setsofttty();
}
}
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()
{
register struct rc_chans *rc;
register struct rc_softc *rcb;
register u_char *tptr, *eptr;
register int s;
register struct tty *tp;
register int chan, icnt, c, nec, unit;
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:
}
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;
}
void rcstop(tp, rw)
register struct tty *tp;
int rw;
{
register struct rc_chans *rc = &rc_chans[GET_UNIT(tp->t_dev)];
u_char *tptr, *eptr;
#ifdef RCDEBUG
1995-06-14 19:37:02 +00:00
printf("rc%d/%d: rcstop %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();
}
int rcopen(dev, flag, mode, p)
dev_t dev;
int flag, mode;
struct proc *p;
{
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;
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;
}
if (error = tsleep(&rc->rc_rcb,
TTIPRI|PCATCH, "rcbi", 0))
goto out;
goto again;
}
}
if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) {
error = EBUSY;
goto out;
}
} else {
tp->t_oproc = rc_start;
tp->t_param = rc_param;
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
ttsetwater(tp);
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);
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;
}
int rcclose(dev, flag, mode, p)
dev_t dev;
int flag, mode;
struct proc *p;
{
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);
rcstop(tp, FREAD | FWRITE);
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)
1995-05-12 15:09:29 +00:00
|| !(rc->rc_flags & RC_ACTOUT)
&& !(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);
}
/* Read from line */
int rcread(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
struct tty *tp = rc_chans[GET_UNIT(dev)].rc_tp;
1995-06-14 19:37:02 +00:00
1995-05-12 15:09:29 +00:00
return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
}
/* Write to line */
int rcwrite(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
struct tty *tp = rc_chans[GET_UNIT(dev)].rc_tp;
1995-06-14 19:37:02 +00:00
1995-05-12 15:09:29 +00:00
return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
}
/* 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 i, nec;
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);
}
1995-05-12 15:09:29 +00:00
int rcioctl(dev, cmd, data, flag, p)
dev_t dev;
int cmd, flag;
caddr_t data;
struct proc *p;
{
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, p);
if (error >= 0)
return (error);
error = ttioctl(tp, cmd, data, flag);
if (error >= 0)
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(p->p_ucred, &p->p_acflag);
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, nopt = 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
#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 */
struct tty *
rcdevtotty(dev)
dev_t dev;
{
int unit;
unit = GET_UNIT(dev);
if (unit >= NRC * CD180_NCHAN)
return NULL;
return (&rc_tty[unit]);
}
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;
{
int unit;
timeout(rc_wakeup, (caddr_t)NULL, 1);
if (rc_scheduled_event != 0) {
int s;
s = splsofttty();
rcpoll();
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;
if (tp->t_line == SLIPDISC)
rc->rc_hotchar = 0xc0;
else if (tp->t_line == PPPDISC)
rc->rc_hotchar = 0x7e;
else
rc->rc_hotchar = 0;
}
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 = 100; rcnt && rcin(CD180_CCR); rcnt--)
DELAY(15);
if (rcnt == 0)
printf("rc%d/%d: channel command timeout, rc.c line: %d\n",
unit, chan, line);
}
the second set of changes in a move towards getting devices to be totally dynamic. this is only the devices in i386/isa I'll do more tomorrow. they're completely masked by #ifdef JREMOD at this stage... the eventual aim is that every driver will do a SYSINIT at startup BEFORE the probes, which will effectively link it into the devsw tables etc. If I'd thought about it more I'd have put that in in this set (damn) The ioconf lines generated by config will also end up in the device's own scope as well, so ioconf.c will eventually be gutted the SYSINIT call to the driver will include a phase where the driver links it's ioconf line into a chain of such. when this phase is done then the user can modify them with the boot: -c config menu if he wants, just like now.. config will put the config lines out in the .h file (e.g. in aha.h will be the addresses for the aha driver to look.) as I said this is a very small first step.. the aim of THIS set of edits is to not have to edit conf.c at all when adding a new device.. the tabe will be a simple skeleton.. when this is done, it will allow other changes to be made, all teh time still having a fully working kernel tree, but the logical outcome is the complete REMOVAL of the devsw tables. By the end of this, linked in drivers will be exactly the same as run-time loaded drivers, except they JUST HAPPEN to already be linked and present at startup.. the SYSINIT calls will be the equivalent of the "init" call made to a newly loaded driver in every respect. For this edit, each of the files has the following code inserted into it: obviously, tailored to suit.. ----------------------somewhere at the top: #ifdef JREMOD #include <sys/conf.h> #define CDEV_MAJOR 13 #define BDEV_MAJOR 4 static void sd_devsw_install(); #endif /*JREMOD */ ---------------------somewhere that's run during bootup: EVENTUALLY a SYSINIT #ifdef JREMOD sd_devsw_install(); #endif /*JREMOD*/ -----------------------at the bottom: #ifdef JREMOD struct bdevsw sd_bdevsw = { sdopen, sdclose, sdstrategy, sdioctl, /*4*/ sddump, sdsize, 0 }; struct cdevsw sd_cdevsw = { sdopen, sdclose, rawread, rawwrite, /*13*/ sdioctl, nostop, nullreset, nodevtotty,/* sd */ seltrue, nommap, sdstrategy }; static sd_devsw_installed = 0; static void sd_devsw_install() { dev_t descript; if( ! sd_devsw_installed ) { descript = makedev(CDEV_MAJOR,0); cdevsw_add(&descript,&sd_cdevsw,NULL); #if defined(BDEV_MAJOR) descript = makedev(BDEV_MAJOR,0); bdevsw_add(&descript,&sd_bdevsw,NULL); #endif /*BDEV_MAJOR*/ sd_devsw_installed = 1; } } #endif /* JREMOD */
1995-11-28 09:42:06 +00:00
#ifdef JREMOD
struct cdevsw rc_cdevsw =
{ rcopen, rcclose, rcread, rcwrite, /*63*/
rcioctl, rcstop, nxreset, rcdevtotty,/* rc */
ttselect, nommap, NULL };
static rc_devsw_installed = 0;
static void rc_devsw_install()
{
dev_t descript;
if( ! rc_devsw_installed ) {
descript = makedev(CDEV_MAJOR,0);
cdevsw_add(&descript,&rc_cdevsw,NULL);
#if defined(BDEV_MAJOR)
descript = makedev(BDEV_MAJOR,0);
bdevsw_add(&descript,&rc_bdevsw,NULL);
#endif /*BDEV_MAJOR*/
rc_devsw_installed = 1;
}
}
#endif /* JREMOD */
1995-05-12 15:09:29 +00:00
#endif /* NRC */