|
|
Power 6/32 Unix version 1.21
#include "vx.h"
#if NVX > 0
/*
* VIOC-X driver
*/
#include "../h/param.h"
#include "../h/ioctl.h"
#include "../h/tty.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/map.h"
#include "../machine/pte.h"
#include "../h/buf.h"
#include "../vba/vbavar.h"
#include "../h/conf.h"
#include "../h/file.h"
#include "../h/uio.h"
#include "../vba/vioc.h"
#ifdef VXPERF
#include "../vba/scope.h"
#endif VXPERF
#include "vbsc.h"
#if NVBSC > 0
#include "../bsc/bscio.h"
#include "../bsc/bsc.h"
char bscport[NVXPORTS];
long bscno = 0;
extern struct bsc bsc[];
#endif
#define RSPquals 1
struct vcx vcx[NVIOCX];
struct tty vx_tty[NVXPORTS];
extern struct vcmds v_cmds[];
struct vba_device *vxinfo[NVIOCX];
int vxstart();
int ttrstrt();
caddr_t vtoph();
struct vxcmd *vobtain();
/*
* Driver information for auto-configuration stuff.
* (not tested and probably should be changed)
*/
int vxprobe(), vxattach(), vxrint();
long vxstd[] = { 0 };
struct vba_driver vxdriver =
{ vxprobe, 0, vxattach, 0, vxstd, "VIOC ", vxinfo };
char vxtype[NVIOCX]; /* 0: viox-x/vioc-b; 1: vioc-bop */
char vxbopno[NVIOCX]; /* BOP board no. if indicated by vxtype[] */
char vxbbno = -1;
short vbtbl = -1;
int vx_cnt = NVXPORTS; /* needed by vmstat/iostat */
extern vbrall();
vxprobe(reg)
caddr_t reg;
{
register int br, cvec;
register struct vblok *vp = (struct vblok *)reg;
#ifdef lint
br = 0; cvec = br; br = cvec;
#endif
if (badaddr(vp, 1))
return(0);
vp->v_fault = 0;
vp->v_vioc = V_BSY;
vp->v_hdwre = V_RESET; /* reset interrupt */
DELAY(4000000);
if (vp->v_fault != VREADY)
return(0);
return(1);
}
vxattach(ui)
register struct vba_device *ui;
{
VIOCBAS[ui->ui_unit] = ui->ui_addr;
vxinit(ui->ui_unit, 1);
}
/*
* Open a VX line.
*/
vxopen(dev, flag)
{
register struct tty *tp; /* pointer to tty struct for port */
register struct vcx *xp; /* pointer to VIOC-X info/cmd buffer */
register int d; /* minor device number */
register int error;
d = minor(dev); /* get minor device number */
if (d >= NVXPORTS) /* validate minor device number */
return(ENXIO); /* set errno to indicate bad port # */
tp = &vx_tty[d]; /* index the tty structure for port */
xp = &vcx[d>>4]; /* index VIOC-X info/cmd area */
d &= 017;
/*
* If we are resetting the VIOC, wait until the LIDENT has
* completed.
*/
while (xp->v_state&V_RESETTING)
sleep((caddr_t)&xp->v_state);
if (xp->v_hiport == xp->v_loport || /* no ports on this board */
d < xp->v_loport || d > xp->v_hiport)/* not in the range of ports */
return(ENXIO); /* bad minor device number */
if (isprinter(xp, d))
/*
* Printers have different semantics than tty ports.
* They have a fixed output transliteration, no read,
* only one process may open them at a time, they always
* have carrier and processes cannot be attached to them.
*/
if (tp->t_state&TS_ISOPEN)
return(EBUSY);
if ((tp->t_state&(TS_ISOPEN|TS_WOPEN))==0){/* is device already open? */
ttychars(tp); /* set default control chars */
tp->t_addr = (caddr_t)xp;/* store addr of VIOC-X info */
tp->t_oproc = vxstart; /* store address of startup routine */
tp->t_dev = dev; /* store major/minor device numbers */
if (tp->t_ispeed == 0) {/* if no default speeds set them */
tp->t_ispeed = SSPEED;/* default input baud */
tp->t_ospeed = SSPEED;/* default output baud */
tp->t_flags |= (ODDP|EVENP|ECHO); /* default modes */
}
vxparam(dev); /* set parameters for this port */
}
/*
* If already open for exclusive use open fails unless caller is root.
*/
if (tp->t_state&TS_XCLUDE && u.u_uid!=0)
return(EBUSY); /* device is busy, sorry */
d = spl8();
tp->t_state |= TS_WOPEN; /* mark device as waiting for open */
/*
* Check for carrier
*/
if (vcmodem(dev, VMOD_ON))
tp->t_state |= TS_CARR_ON;
else
tp->t_state &= ~TS_CARR_ON;
while ((tp->t_state&TS_CARR_ON) == 0) {
tp->t_state |= TS_WOPEN;
sleep(&tp->t_canq, TTIPRI);
}
error = (*linesw[tp->t_line].l_open)(dev, tp);/*let tty.c finish open */
splx(d);
return(error);
}
/*
* Close a VX line.
*/
vxclose(dev, flag)
dev_t dev;
int flag;
{
register int d = minor(dev);
register struct tty *tp = &vx_tty[d];
register int s = spl8();
register int error;
error = (*linesw[tp->t_line].l_close)(tp);
if ((tp->t_state&TS_ISOPEN) && (tp->t_state&TS_HUPCLS)) {
register int flags = tp->t_flags;
/*
** Turn off echo to prevent infinite flushing
** Flush output queue then turn off clear to send.
** Always restore flags so line disipline will not change.
*/
tp->t_flags &= ~ECHO;
ttywait(tp);
tp->t_flags = flags;
vcmodem(dev, VMOD_OFF);
}
while ((tp->t_state&TS_FLUSH) && (tp->t_state&TS_BUSY))
sleep((caddr_t)&tp->t_state, TTOPRI);
ttyclose(tp); /* let tty.c finish the close */
splx(s);
return(error);
}
/*
* Read from a VX line.
*/
vxread(dev, uio)
dev_t dev;
struct uio *uio;
{
register int d = minor(dev);
register struct tty *tp = &vx_tty[d];
register struct vcx *xp = &vcx[d>>4];
d &= 0xf;
if (isprinter(xp, d))
return(ENODEV);
return(*linesw[tp->t_line].l_read)(tp, uio);
}
/*
* Write on a VX line.
*/
vxwrite(dev, uio)
dev_t dev;
struct uio *uio;
{
register int d = minor(dev);
register struct tty *tp = &vx_tty[d];
return(*linesw[tp->t_line].l_write)(tp, uio);
}
/*
* VIOCX unsolicited interrupt.
*/
vxrint(n)
int n; /* mux number */
{
register struct tty *tp;
register struct vcx *xp;
register short *sp;
register int i;
register unsigned int c;
register int port;
struct tty *tp0;
struct vblok *kp;
short *savsilo;
struct silo {
char data;
unsigned char port;
};
kp = VBAS(n);
xp = &vcx[n];
#if VX_TESTING
c = kp->v_uqual&037;
if (vx_test&VX_PROC_ERR_DEBUG)
c = 2;
else if (vx_test&VX_UQUAL_ERR_DEBUG)
c = 5;
switch (c) {
#else
switch (kp->v_uqual&037) {
#endif
case 0:
break;
case 2:
printf(" ERR NBR %x\n", kp->v_ustat);
if (kp->v_ustat == 0x40) {
printf("VIOC %x lost some read data\n", n);
return(0);
}
vpanic("vc: VC PROC ERR");
vxstreset(n);
return(0);
case 3:
vcmintr(n);
return(1);
case 4:
return(1);
default:
printf(" ERR NBR %x\n", kp->v_uqual);
vpanic("vc: VC UQUAL ERR");
vxstreset(n);
return(0);
}
/*
* Get the address of the silo in VIOC common memory.
*/
sp = (short *)((char *)kp + *(short *)kp->v_usdata);
nextsilo:
/*
* See if the number of characters is reasonable.
*/
i = *(savsilo = sp);
if (i > xp->v_silosiz) {
printf("vx: %d exceeds silo size\n", i);
i = xp->v_silosiz;
}
/*
* Drain the silo.
*/
tp0 = &vx_tty[n<<4];
for (sp++; i > 0; i--, sp++) {
port = ((struct silo *)sp)->port&017;
if (port < xp->v_loport || port > xp->v_hiport)
/*
* Not a valid port.
*/
continue;
tp = tp0 + port;
if ((tp->t_state&TS_ISOPEN) == 0)
/*
* Nobody is listening.
*/
continue;
/*
* Fetch the character from vioc memory.
*/
c = ((struct silo *)sp)->data;
switch (((struct silo *)sp)->port&(PERROR|FERROR)) {
case PERROR:
case PERROR|FERROR:
if ((tp->t_flags&(EVENP|ODDP)) == EVENP ||
(tp->t_flags&(EVENP|ODDP)) == ODDP)
continue;
if (!(((struct silo *)sp)->port&FERROR))
break;
case FERROR:
if (tp->t_flags&RAW)
c = 0;
else
c = tp->t_intrc;
}
(*linesw[tp->t_line].l_rint)(c, tp);
}
*savsilo = 0;
return(1);
}
/*
* stty/gtty for VX.
*/
vxioctl(dev, cmd, data, flag)
int dev; /* major, minor device numbers */
int cmd; /* command */
caddr_t data;
int flag;
{
register int d = minor(dev);
register struct tty *tp = &vx_tty[d];
register int error;
switch (cmd) {
case VCMANUAL:
case VCGRS232:
case VCSRS232:
/*
* Intercept device specific commands.
*/
return(vcumodem(dev, cmd, data, flag));
default:
break;
}
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
if (error == 0)
return(error);
if ((error = ttioctl(tp, cmd, data, flag)) >= 0)
{
if (cmd==TIOCSETP||cmd==TIOCSETN)
vxparam(dev);
return(error);
} else
return(ENOTTY);
}
vxparam(dev)
dev_t dev;
{
vxcparam(dev, 1);
}
/*
* Set parameters from open or stty into the VX hardware
* registers.
*/
vxcparam(dev, wait)
dev_t dev; /* major, minor device numbers */
int wait; /* nonzero if we should wait for finish */
{
register struct tty *tp;
register struct vcx *xp;
register struct vxcmd *cp;
register int s = spl8();
tp = &vx_tty[minor(dev)]; /* pointer to tty structure for port */
xp = (struct vcx *)tp->t_addr; /* pointer to VIOCX info/cmd buffer */
cp = vobtain(xp);
cp->cmd = LPARAX; /* set command to "load parameters" */
cp->par[1] = minor(dev)&017; /* port number */
/*
* Check for XON/XOFF flow control.
*/
cp->par[2] = (tp->t_flags&RAW)? 0 : tp->t_startc; /* XON char */
cp->par[3] = (tp->t_flags&RAW)? 0 : tp->t_stopc; /* XOFF char */
if (tp->t_flags&(RAW|LITOUT) ||
(tp->t_flags&(EVENP|ODDP)) == (EVENP|ODDP)) {
cp->par[4] = 0xc0; /* 8 bits of data */
cp->par[7] = 0; /* no parity */
} else {
cp->par[4] = 0x40; /* 7 bits of data */
if ((tp->t_flags&(EVENP|ODDP)) == ODDP)
cp->par[7] = 1; /* odd parity */
else if ((tp->t_flags&(EVENP|ODDP)) == EVENP)
cp->par[7] = 3; /* even parity */
else
cp->par[7] = 0; /* no parity */
}
cp->par[5] = (tp->t_stopbits == 0) ? 4 : tp->t_stopbits;
if (tp->t_ospeed == EXTA || tp->t_ospeed == EXTB)
cp->par[6] = 19; /* 19200 BAUD */
else
cp->par[6] = tp->t_ospeed;
if (vcmd(xp->v_nbr, &cp->cmd) && wait)
sleep(cp, TTIPRI);
splx(s);
}
/*
* VIOCX command response interrupt.
* For transmission, restart output to any active port.
* For all other commands, just clean up.
* This routine is called with spl8 from vcmdrsp().
*/
vxxint(n, cp)
register int n; /* VIOC number */
register struct vxcmd *cp; /* command structure */
{
register struct vxmit *vp, *pvp;
register struct tty *tp;
register struct vcx *xp;
register struct tty *hp;
register int port;
xp = &vcx[n];
cp = (struct vxcmd *)((long *)cp-1);
#if NVBSC > 0
switch (cp->cmd) {
case MDMCTL1: case HUNTMD1: case LPARAX1:
vrelease(xp, cp);
wakeup(cp);
return;
}
#endif
switch (cp->cmd&0xff00) {
case LIDENT: /* initialization complete */
if (xp->v_state&V_RESETTING)
vxfnreset(n, cp);
cp->cmd++;
return;
case XMITDTA: case XMITIMM:
break;
case LPARAX:
wakeup(cp);
case FDTATOX:
default: /* MDMCTL or FDTATOX */
vrelease(xp, cp);
return;
}
for (vp = (struct vxmit *)(cp->par + (cp->cmd&07)*sizvxmit);
vp >= (struct vxmit *)cp->par;
vp = (struct vxmit *) ((char *)vp - sizvxmit)) {
tp = &vx_tty[(vp->line&017)+(n<<4)];
#if NVBSC > 0
port = vp->line;
if (tp->t_line == LDISP) {
vrelease(xp, cp);
/*
* Call bsctxd with logical bscport no; 0,1..4,5.
* v_xmtcnt was set to BISYNC ctlr no in vxinit().
*/
bsctxd((xp->v_xmtcnt*NBSC)+port);
return;
}
if (bscport[(n<<4)+port] == BISYNC) {
printf("\nvxxint: int. after line closed. (cmd:%lx)",
cp->cmd&0xff);
vrelease(xp, cp);
return;
}
#endif
pvp = vp;
tp->t_state &= ~TS_BUSY;
if (tp->t_state&TS_FLUSH) {
tp->t_state &= ~TS_FLUSH;
wakeup((caddr_t)&tp->t_state);
}
else
ndflush(&tp->t_outq, vp->bcount+1);
}
xp->v_xmtcnt--;
vrelease(xp, cp);
vp = pvp;
vxstart(tp);
return;
}
/*
* Start (restart) transmission on the given VX line.
*/
vxstart(tp)
register struct tty *tp;
{
register short nch;
register struct vcx *xp;
register char *outb;
register full = 0;
int s, port;
s = spl8();
port = minor(tp->t_dev)&017;
xp = (struct vcx *)tp->t_addr;
if (!(tp->t_state&(TS_TIMEOUT|TS_BUSY|TS_TTSTOP))) {
if (tp->t_outq.c_cc <= TTLOWAT(tp)) {
if (tp->t_state&TS_ASLEEP) {
tp->t_state &= ~TS_ASLEEP;
wakeup((caddr_t)&tp->t_outq);
}
if (tp->t_wsel) {
selwakeup(tp->t_wsel, tp->t_state&TS_WCOLL);
tp->t_wsel = 0;
tp->t_state &= ~TS_WCOLL;
}
}
if (tp->t_outq.c_cc == 0) {
splx(s);
return(0);
}
#ifdef VXPERF
scope_out(3);
#endif VXPERF
if (!(tp->t_flags&(RAW|LITOUT)))
full = 0200;
if ((nch = ndqb(&tp->t_outq, full)) == 0) {
if (full) {
nch = getc(&tp->t_outq);
timeout(ttrstrt, (caddr_t)tp, (nch&0177) +6);
tp->t_state |= TS_TIMEOUT;
full = 0;
}
} else {
outb = (char *)tp->t_outq.c_cf;
tp->t_state |= TS_BUSY;
vsend(xp, port, outb, nch);
}
}
splx(s);
return;
}
/*
* Stop output on a line.
*/
vxstop(tp)
register struct tty *tp;
{
register int s;
s = spl8();
if (tp->t_state&TS_BUSY) {
if ((tp->t_state&TS_TTSTOP)==0) {
tp->t_state |= TS_FLUSH;
}
}
splx(s);
}
/*
* VIOCX Initialization. Makes free lists of command buffers.
* Resets all viocx's. Issues a LIDENT command to each
* viocx which establishes interrupt vectors and logical
* port numbers.
*/
vxinit(i, wait)
register int i;
int wait;
{
register struct vcx *xp; /* ptr to VIOC-X info/cmd buffer */
register struct vblok *kp; /* pointer to VIOC-X control block */
register struct vxcmd *cp; /* pointer to a command buffer */
register char *resp; /* pointer to response buffer */
register int j, ix;
register struct vcmds *cpp;
char type;
#if NVBSC > 0
register struct bsc *bp; /* bsc change */
#endif
/*
* Only initialize once.
*/
if (vbtbl) {
for (j = 0;j < NVIOCX; j++)
vxbopno[j] = (char)-1;
vbtbl = 0;
}
kp = VBAS(i); /* get base adr of cntl blok for VIOC */
xp = &vcx[i]; /* index info/command buffers */
cpp = &v_cmds[i];
type = kp->v_ident;
vxtype[i] = 0; /* Type is VIOC-X */
switch (type) {
case PVIOCX:
case VIOCX:
printf("WARNING: Old vioc no longer supported.\n");
xp->v_loport = xp->v_hiport = 0;
return;
case NWVIOCX:
case NPVIOCX:
xp->v_vers = V_NEW;
xp->v_silosiz = kp->v_maxsilo;
/*
* Initialize the port types.
*/
for (j = 0; j < 16; j++)
xp->vx_type[j] = kp->v_portyp[j];
break;
case VIOCB: /* old f/w, Bisync board */
if (wait) { /* Just boot up */
if (bscno >= NVBSC) {
printf("\nVIOCB no%ld is not configured in software", bscno);
break;
}
if (bscno >= MXVIOC_B) {
printf("\nvxinit: exceed max no of VIOC-B:%ld",
MXVIOC_B);
break;
}
}
printf("(kp:%lx)(xp:%lx): %lx---%x OLD VIOC-B, ",
(long)kp, (long)xp,(int)kp->v_ident,
(int)kp->v_fault);
xp->v_vers = V_OLD;
if (wait) /* Use v_xmtcnt to */
xp->v_xmtcnt = (char)bscno; /* save VIOC-B ctlrno */
else
bscno = (long)xp->v_xmtcnt&0x7f;
bp = &bsc[0+(bscno*4)];
for (ix = 0; ix < 4; ix++, bp++)
bp->b_devregs = (caddr_t)xp;
bscno++;
bscport[(i<<4)+0] = BISYNC;
bscport[(i<<4)+1] = BISYNC;
bscport[(i<<4)+2] = BISYNC;
bscport[(i<<4)+3] = BISYNC;
printf(" Bisync initialized\n");
break;
case NWVIOCB: /* new f/w, Bisync board */
if (wait) { /* Just boot up */
if (bscno >= NVBSC) {
printf("\nVIOCB no%ld is not configured in software", bscno);
break;
}
if (bscno >= MXVIOC_B) {
printf("\nvxinit: exceed max no of VIOC-B:%ld",
MXVIOC_B);
break;
}
}
xp->v_vers = V_NEW;
xp->v_silosiz = kp->v_maxsilo;
if (wait) /* Use v_xmtcnt to */
xp->v_xmtcnt = (char)bscno; /* save VIOC-B ctlrno */
else
bscno = (long)xp->v_xmtcnt&0x7f;
bp = &bsc[bscno*4];
for (ix = 0; ix < 4; ix++, bp++)
bp->b_devregs = (caddr_t)xp;
bscno++;
bscport[(i<<4)+0] = BISYNC;
bscport[(i<<4)+1] = BISYNC;
bscport[(i<<4)+2] = BISYNC;
bscport[(i<<4)+3] = BISYNC;
printf(" Bisync initialized\n");
break;
case VBOPID: /* VIOC-BOP */
if (vxtype[i] == 0) { /* don't increment if already seen */
vxbbno++;
vxtype[i] = 1;
vxbopno[i] = vxbbno;
}
printf(" VIOC-BOP initialized\n");
default:
return; /* Not a viocx type */
}
xp->v_nbr = -1; /* no number for it yet */
xp->v_maxcmd = xp->v_vers == V_NEW ? 23 : 3;
for (j = 0; j < NVCXBUFS; j++) {/* init all cmd buffers */
cp = &xp->vx_lst[j]; /* index a buffer */
cp->c_fwd = &xp->vx_lst[j+1]; /* point to next buf */
}
xp->vx_avail = &xp->vx_lst[0]; /* set idx to 1st free buf */
cp->c_fwd = (struct vxcmd *)0; /* mark last buf in free list */
/*
* Assures proper state when reinit. in case of INT. ERR
*/
cpp->v_cmdsem = cpp->v_curcnt = cpp->v_fill = 0;
cpp->v_empty = cpp->v_itrfill = cpp->v_itrempt = 0;
cp = vobtain(xp); /* grap the control block */
cp->cmd = LIDENT; /* set command type */
cp->par[0] = i * 4 + VCVECT; /* ack vector */
cp->par[1] = cp->par[0] + 1; /* cmd resp vector */
cp->par[3] = cp->par[0] + 2; /* unsol intr vector */
cp->par[4] = 15; /* max ports, no longer used */
cp->par[5] = 0; /* set 1st port number */
vcmd(i, &cp->cmd); /* initialize the VIOC-X */
if (!wait)
return;
while (cp->cmd == LIDENT) /* wait for command completion */
;
/*
* Calculate address of response buffer.
*/
resp = (char *)kp;
resp += kp->v_rspoff&0x3FFF;
if (resp[0] != 0 && (resp[0]&0177) != 3) { /* did init work? */
vrelease(xp, cp); /* init failed */
return; /* try next VIOC-X */
}
xp->v_loport = cp->par[5]; /* save low port number */
xp->v_hiport = cp->par[7]; /* VIOC knows high port numbr */
vrelease(xp, cp); /* done with this control block */
xp->v_nbr = i; /* assign VIOC-X board number */
}
/*
* Obtain a command buffer.
*/
struct vxcmd *
vobtain(xp)
register struct vcx *xp;
{
register struct vxcmd *p;
register int s;
s = spl8();
p = xp->vx_avail;
if (p == (struct vxcmd *)0) {
vpanic("vx: no buffs");
vxstreset(xp - vcx);
splx(s);
return(vobtain(xp)); /* guaranteed to work this time */
}
xp->vx_avail = (xp->vx_avail)->c_fwd;
splx(s);
return((struct vxcmd *)p);
}
/*
* Release a command buffer.
*/
vrelease(xp, cp)
register struct vcx *xp;
register struct vxcmd *cp;
{
register int s = spl8();
cp->c_fwd = xp->vx_avail;
xp->vx_avail = cp;
splx(s);
}
/*
* Arrange for "cnt" bytes from "addr" to be written to port number
* "port" on VIOC described by "xp".
*/
vsend(xp, port, addr, cnt)
register struct vcx *xp;/* points to vioc control structure for this port */
int port; /* port number to get the data */
caddr_t addr; /* beginning address of the block */
register int cnt; /* size of the block of characters */
{
register struct vxcmd *cp;
register struct vxmit *mp;
register int i;
register char *src, *dst;
/*
* Get a command.
*/
cp = vobtain(xp);
/*
* Fill out the port information.
*/
mp = (struct vxmit *)cp->par;
mp->bcount = cnt-1;
mp->line = port;
if (cnt <= 6) {
/*
* If small enough, we can give the data directly to
* the vioc, eliminating the need for DMA.
*/
cp->cmd = XMITIMM;
src = addr;
i = cnt;
} else {
/*
* Get the physical address of the block to DMA.
*/
cp->cmd = XMITDTA;
addr = vtoph(0, (caddr_t)addr);
src = (char *)&addr;
i = sizeof(addr);
}
/*
* Copy the data or address into the command.
*/
dst = mp->ostream;
while (--i >= 0)
*dst++ = *src++;
xp->v_xmtcnt++;
vcmd(xp->v_nbr, (char *)&cp->cmd);
return;
}
#endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.