|
|
Power 6/32 Unix version 1.21
#include "vx.h"
#if NVX > 0
/*
* VIOC driver
*/
#include "../h/param.h"
#include "../h/file.h"
#include "../h/ioctl.h"
#include "../h/tty.h"
#include "../h/errno.h"
#include "../h/time.h"
#include "../h/kernel.h"
#include "../vba/vioc.h"
#ifdef VXPERF
#include "../vba/scope.h"
#endif VXPERF
#define CMDquals 0
#define RSPquals 1
#define UNSquals 2
extern struct vcx vcx[];
extern struct tty vx_tty[];
struct vcmds v_cmds[NVIOCX];
extern char vxtype[];
extern char vxbbno;
extern char vxbopno[];
#if defined(NVSNA) | defined(NSD)
extern vbrall();
#else
vbrall() {};
#endif
/*
* Write a command out to the VIOC
*/
vcmd(n, cmdad)
register int n;
register caddr_t cmdad; /* command address */
{
register struct vcmds *cp;
register struct vcx *xp;
int s;
s = spl8();
cp = &v_cmds[n];
xp = &vcx[n];
if (xp->v_state&V_RESETTING && cmdad != NULL) {
/*
* When the vioc is resetting, don't process
* anything other than LIDENT commands.
*/
register struct vxcmd *cp = (struct vxcmd *)
((char *)cmdad - sizeof(cp->c_fwd));
if (cp->cmd != LIDENT) {
vrelease(xp, cp);
return(0);
}
}
if (cmdad != (caddr_t) 0) {
cp->cmdbuf[cp->v_fill] = cmdad;
if (++cp->v_fill >= VC_CMDBUFL)
cp->v_fill = 0;
if (cp->v_fill == cp->v_empty) {
vpanic("vc: CMD Q OVFLO");
vxstreset(n);
splx(s);
return(0);
}
cp->v_cmdsem++;
}
if (cp->v_cmdsem && cp->v_curcnt < vcx[n].v_maxcmd) {
cp->v_cmdsem--;
cp->v_curcnt++;
vinthandl(n, ((V_BSY | CMDquals) << 8) | V_INTR);
}
splx(s);
return(1);
}
/*
* VIOC acknowledge interrupt. The VIOC has received the new
* command. If no errors, the new command becomes one of 16 (max)
* current commands being executed.
*/
vackint(n)
register int n; /* VIOC number */
{
register struct vblok *vp;
register struct vcmds *cp;
register int s;
#ifdef VXPERF
scope_out(5);
#endif VXPERF
if (vxtype[n]) { /* It is a BOP */
vbrall(n); /* Int. from BOP, port 0 */
return;
}
s = spl8();
vp = VBAS(n);
cp = &v_cmds[n];
if (vp->v_vcid&V_ERR) {
register char *resp;
register i;
printf("INTR ERR type = %x VIOC = %x\n", vp->v_vcid&07, n);
resp = (char *)vp + (vp->v_rspoff&0x7FFF);
for (i = 0; i < 16; i++)
printf("%x ", resp[i]&0xff);
vpanic("\nvcc: vackint");
vxstreset(n);
splx(s);
return;
}
if ((vp->v_hdwre&017) == CMDquals) {
cp->v_curcmd[vp->v_vcid&VCMDLEN-1] = cp->cmdbuf[cp->v_empty];
if (++cp->v_empty >= VC_CMDBUFL)
cp->v_empty = 0;
}
if (++cp->v_itrempt >= VC_IQLEN)
cp->v_itrempt = 0;
vintempt(n);
splx(s);
vcmd(n, 0); /* queue next cmd, if any */
}
/*
* Command Response interrupt. The Vioc has completed
* a command. The command may now be returned to
* the appropriate device driver .
*/
vcmdrsp(n)
register int n;
{
register struct vblok *vp;
register struct vcmds *cp;
register caddr_t cmd;
register char *resp;
register int k, s;
#ifdef VXPERF
scope_out(6);
#endif VXPERF
if (vxtype[n]) { /* Its a BOP */
printf("vcmdrsp: stray interrupt from BOP at VIOC%d...\n", n);
return;
}
s = spl8();
vp = VBAS(n);
cp = &v_cmds[n];
resp = (char *)vp;
resp += vp->v_rspoff&0x7FFF;
if (
#if VX_TESTING
(vx_test&VX_CMDRESP_DEBUG) == 0 &&
#endif
(k=resp[1])&V_UNBSY) {
k &= VCMDLEN-1;
cmd = cp->v_curcmd[k];
cp->v_curcmd[k] = (caddr_t)0;
cp->v_curcnt--;
k = *((short *)&resp[4]); /* cmd operation code */
if ((k&0xFF00) == LIDENT) { /* want hiport number */
for (k = 0; k < VRESPLEN; k++)
cmd[k] = resp[k+4];
}
resp[1] = 0;
vxxint(n, cmd);
}
else {
vpanic("vc, cmdresp debug");
vxstreset(n);
splx(s);
return;
}
vinthandl(n, ((V_BSY | RSPquals) << 8) | V_INTR);
splx(s);
}
/*
* Unsolicited interrupt.
*/
vunsol(n)
register int n;
{
register struct vblok *vp;
register int s;
#ifdef VXPERF
scope_out(1);
#endif VXPERF
if (vxtype[n]) { /* Its a BOP */
printf("vunsol: stray interrupt from BOP at VIOC%d...\n", n);
return;
}
s = spl8();
vp = VBAS(n);
if (vp == 0) {
printf("vunsol: stray interrupt from VIOC%d...\n", n);
splx(s);
return;
}
if (
#if VX_TESTING
(vx_test&VX_UNSOL_INT_DEBUG) == 0 &&
#endif
vp->v_uqual&V_UNBSY) {
if (vxrint(n))
vinthandl(n, ((V_BSY | UNSquals) << 8) | V_INTR);
splx(s);
}
else {
vpanic("vc: UNSOL INT ERR");
vxstreset(n);
splx(s);
}
}
/*
* Enqueue an interrupt
*/
vinthandl(n, item)
register int n, item;
{
register struct vcmds *cp;
register int empflag = 0;
cp = &v_cmds[n];
if (cp->v_itrfill == cp->v_itrempt)
empflag++;
cp->v_itrqueu[cp->v_itrfill] = item;
if (++cp->v_itrfill >= VC_IQLEN)
cp->v_itrfill = 0;
if (cp->v_itrfill == cp->v_itrempt) {
vpanic("vc: INT Q OVFLO");
vxstreset(n);
} else if (empflag)
vintempt(n);
}
vintempt(n)
register int n;
{
register struct vcmds *cp;
register struct vblok *vp;
register short item, *intr;
vp = VBAS(n);
if (vp->v_vioc&V_BSY)
return;
cp = &v_cmds[n];
if (cp->v_itrempt == cp->v_itrfill)
return;
item = cp->v_itrqueu[cp->v_itrempt];
intr = (short *)&vp->v_vioc;
switch ((item >> 8)&03) {
case CMDquals: /* command */
{
int phys;
if (cp->v_empty == cp->v_fill || vp->v_vcbsy&V_BSY)
break;
phys = vtoph(0, cp->cmdbuf[cp->v_empty]); /* should be a sys address */
vp->v_vcp[0] = ((short *)&phys)[0];
vp->v_vcp[1] = ((short *)&phys)[1];
vp->v_vcbsy = V_BSY;
*intr = item;
}
#ifdef VXPERF
scope_out(4);
#endif VXPERF
break;
case RSPquals: /* command response */
*intr = item;
#ifdef VXPERF
scope_out(7);
#endif VXPERF
break;
case UNSquals: /* unsolicited interrupt */
vp->v_uqual = 0;
*intr = item;
#ifdef VXPERF
scope_out(2);
#endif VXPERF
break;
}
}
struct viocdump viocdump; /* contents of VIOC memory prior to reset */
/* start a reset on a vioc after error (hopefully) */
vxstreset(n)
register int n;
{
register struct vcx *xp;
register struct vblok *vp;
register struct vxcmd *cp;
register short *src, *dst, *end;
register int j;
extern int vxinreset();
int s;
s = spl8();
vp = VBAS(n);
xp = &vcx[n];
#if VX_TESTING
vx_test = 0;
#endif
if (xp->v_state&V_RESETTING) {
/*
* Avoid infinite recursion.
*/
splx(s);
return;
}
/*
* Dump the VIOC memory -- important we do it as shorts.
*/
src = (short *)vp;
dst = (short *)viocdump.vxcore;
end = (short *)((char *)viocdump.vxcore + sizeof(viocdump.vxcore));
while (dst < end)
*dst++ = *src++;
/*
* Note the time so we may correlate console messages
* with the vioc dump.
*/
viocdump.vxtime = time.tv_sec;
viocdump.vxn = n;
viocdump.vxmagic = VIOCDMAGIC;
/*
* Zero out the vioc structures, mark the vioc as being
* reset, reinitialize the free command list, reset the vioc
* and start a timer to check on the progress of the reset.
*/
bzero(&v_cmds[n], sizeof(struct vcmds));
bzero(xp, sizeof(struct vcx));
/*
* Setting V_RESETTING prevents others from issuing
* commands while allowing currently queued commands to
* be passed to the VIOC.
*/
xp->v_state |= V_RESETTING;
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 */
printf("resetting VIOC %x at %x .. ", n, viocdump.vxtime);
vp->v_fault = 0;
vp->v_vioc = V_BSY;
vp->v_hdwre = V_RESET; /* reset interrupt */
timeout(vxinreset, (caddr_t)n, hz*5);
splx(s);
return;
}
/* continue processing a reset on a vioc after an error (hopefully) */
vxinreset(vioc)
caddr_t vioc;
{
register struct vcx *xp;
register struct vblok *vp;
register int n = (int)vioc;
int s = spl8();
vp = VBAS(n);
xp = &vcx[n];
/*
* See if the vioc has reset.
*/
if (vp->v_fault != VREADY) {
printf("failed\n");
splx(s);
return;
}
/*
* Send a LIDENT to the vioc and mess with carrier flags
* on parallel printer ports.
*/
vxinit(n, 0);
splx(s);
}
/*
* Restore modem control, parameters and restart output.
*/
/* finish the reset on the vioc after an error (hopefully) */
vxfnreset(n, cp)
register int n;
register struct vxcmd *cp;
{
register struct vcx *xp;
register struct vblok *vp;
register struct tty *tp, *tp0;
register int i;
register int on;
int s = spl8();
vp = VBAS(n);
xp = &vcx[n];
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 = n; /* assign VIOC-X board number */
xp->v_state &= ~V_RESETTING;
vp->v_vcid = 0;
/*
* Wakeup up anyone waiting for the reset (LIDENT) to complete.
*/
wakeup((caddr_t)&xp->v_state);
/*
* Restore modem information and control.
*/
tp0 = &vx_tty[n<<4];
for (i = xp->v_loport; i <= xp->v_hiport; i++) {
tp = tp0 + i;
if ((tp->t_state&(TS_ISOPEN|TS_WOPEN)) == 0)
/*
* Nobody cares about this line.
*/
continue;
/*
* Reset modem signals and set parameters
*/
tp->t_state &= ~TS_CARR_ON;
vcmodem(tp->t_dev, VMOD_ON);
vxcparam(tp->t_dev, 0);
/*
* If carrier has changed while we were resetting,
* take appropriate action.
*/
on = vp->v_dcd&1<<i;
if (on && (tp->t_state&TS_CARR_ON) == 0) {
/*
* Carrier has come on.
*/
tp->t_state |= TS_CARR_ON;
wakeup((caddr_t)&tp->t_canq);
} else if (!on && tp->t_state&TS_CARR_ON) {
/*
* Carrier has gone off.
*/
tp->t_state &= ~TS_CARR_ON;
if (tp->t_state&TS_ISOPEN) {
ttyflush(tp, FREAD|FWRITE);
if (tp->t_state&TS_FLUSH)
wakeup((caddr_t)&tp->t_state);
if ((tp->t_flags&NOHANG)==0) {
gsignal(tp->t_pgrp, SIGHUP);
gsignal(tp->t_pgrp, SIGCONT);
}
}
}
/*
* If anyone is awaiting a transmission interrupt,
* wake them up.
*/
if (tp->t_state&TS_ASLEEP) {
tp->t_state &= ~TS_ASLEEP;
wakeup((caddr_t)&tp->t_outq);
}
/*
* Restart pending output.
*/
tp->t_state &= ~(TS_BUSY|TS_TIMEOUT);
if (tp->t_state&(TS_ISOPEN|TS_WOPEN))
vxstart(tp);
}
printf("done\n");
splx(s);
}
vxreset(dev)
dev_t dev;
{
vxstreset(minor(dev)>>4); /* completes asynchronously */
}
vxfreset(n)
register int n;
{
register struct vblok *vp;
if (n < 0 || n > NVX || VBAS(n) == NULL)
return(ENODEV);
vcx[n].v_state &= ~V_RESETTING;
vxstreset(n);
return(0); /* completes asynchronously */
}
#endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.