V8/usr/sys/dev/scn.c
/* scn.c 84/09/05 */
#include "scn.h"
#if NSCN > 0
/*
* UNIBUS DR11-B driver for teletype scanner
*
*/
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/cpu.h"
#include "../h/nexus.h"
#include "../h/dk.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/map.h"
#include "../h/pte.h"
#include "../h/mtpr.h"
#include "../h/vm.h"
#include "../h/ubavar.h"
#include "../h/ubareg.h"
#include "../h/cmap.h"
#include "../h/scncmd.h"
struct scndevice{
u_short scn_recvst; /* receive status register */
u_short scn_recv; /* receive data register */
u_short scn_sendst; /* send status register */
u_short scn_send; /* send data register */
short scnwc; /* word count reg - unused but load -1 */
u_short scnba; /* base address reg - must be 16k bdry */
u_short scncs; /* command/status reg */
u_short scndata; /* data reg */
};
struct scnsoftc {
u_short sbmode; /*select bit mode*/
u_short bpmode; /* bit pack mode*/
u_short sc_intno; /* dr11b interrupt number */
int scnreg; /* index into uba_map */
u_short scn_offset; /* offset from 16k bdry */
u_int sc_maxi; /* max interrupt */
caddr_t scnb_addr;
unsigned scn_bdp;
u_short scn_sync;
} scnsoftc[NSCN];
#define SCN_GO 01
#define SCN_IE 0100
#define SCN_NEX 040000
#define SCN_ERROR 0100000
#define SCNSIZE 16384
#define SCNPAD 16384
#define SCNMASK (SCNPAD-1)
#define SCNHSIZE 8192
#define SCNHREG 16
#define SCNREGMASK 0760
#define SCNREST 037777740000
#define SCN_LADDR 037777
#define SCN_HADDR 074
int scnprobe(), scnattach(), scndgo(), scnintr();
int scnstrategy(), scnstart();
u_int scnminp();
struct uba_ctlr *scnminfo[NSCN];
struct uba_device *scndinfo[NSCN];
struct uba_ctlr scnctlr[NSCN]; /* scminfo points to this */
u_short scnstd[] = { 0174020, 0};
struct uba_driver scndriver =
{ scnprobe,0,scnattach,scndgo,scnstd,"scn",scndinfo,"scn",scnminfo };
#define ui_open ui_type
struct buf scnbuf[NSCN];
struct buf scnutab[NSCN];
/*ARGSUSED*/
scnprobe(reg)
caddr_t reg;
{
register int br, cvec; /*int vecs at 310 & 314 */
register struct scndevice *scnaddr = (struct scndevice *) reg;
register short bucket;
#ifdef LINT
br = 0; cvec = br; br = cvec;
#endif
scnaddr->scn_recvst = SCN_REC_IE;
scnaddr->scn_send = SCNRS_S;
DELAY(10000);
bucket = scnaddr->scn_recv;
scnaddr->scn_recvst = 0;
return(1);
}
scnattach(ui)
register struct uba_device *ui;
{
register struct uba_ctlr *um;
register int unit;
unit = ui->ui_unit;
um = &scnctlr[unit];
scnminfo[unit] = um;
ui->ui_ctlr = unit;
ui->ui_mi = um;
um->um_driver = ui->ui_driver;
um->um_ctlr = unit;
um->um_ubanum = ui->ui_ubanum;
um->um_alive = 1;
um->um_intr = ui->ui_intr;
um->um_addr = ui->ui_addr;
um->um_hd = ui->ui_hd;
}
scnopen(dev)
dev_t dev;
{
register int unit = minor(dev);
register struct scnsoftc *scnp = &scnsoftc[minor(dev)];
register struct uba_device *ui = scndinfo[unit];
register struct scndevice *draddr = (struct scndevice *)ui->ui_addr;
if((unit >= NSCN) || (scndinfo[unit]->ui_open)) {
u.u_error = ENXIO;
return;
}
scndinfo[unit]->ui_open++;
draddr->scn_recvst = 0;
scnp->sbmode = SCN_MOD_BW;
scnp->bpmode = 0;
}
scnclose(dev)
dev_t dev;
{
register int unit;
scndinfo[minor(dev)]->ui_open = 0;
}
scnread(dev)
dev_t dev;
{
register int unit = minor(dev);
physio(scnstrategy,&scnbuf[unit],dev,B_READ,scnminp);
}
scnwrite(dev)
dev_t dev;
{
register int unit = minor(dev);
physio(scnstrategy,&scnbuf[unit],dev,B_WRITE,scnminp);
}
u_int
scnminp(bp)
struct buf *bp;
{
register struct scnsoftc *scnp;
scnp = &scnsoftc[minor(bp->b_dev)];
scnp->sc_maxi = ((bp->b_bcount - SCNPAD)/SCNHSIZE);
}
/*
* Due to the fact the scstrategy routine is called only by scnread
* and scnwrite via physio, there will only be one transaction in each
* DR11-B's queue at any time. Therefore, one can just tack the given
* buffer header pointer on at the end of the queue, and call scstart.
*/
scnstrategy(bp)
register struct buf *bp;
{
register struct uba_device *ui;
register struct uba_ctlr *um;
register struct buf *dp;
register int s;
struct scndevice *draddr;
dev_t unit;
unit = minor(bp->b_dev); /* chose a DR11-B */
ui = scndinfo[unit];
um = ui->ui_mi; /* get ctlr ptr */
dp = &scnutab[unit];
s = spl5();
dp->b_actf = bp;
dp->b_actl = bp;
bp->av_forw = NULL;
um->um_tab.b_actf = dp;
um->um_tab.b_actl = dp;
scnstart(um);
splx(s); /*dennis says this should go away*/
switch(tsleep((caddr_t)bp, PRIBIO+1, 20)) {
case TS_OK:
break;
case TS_SIG: /* not supposed to happen*/
case TS_TIME:
draddr = (struct scndevice *)um->um_addr;
bp->b_flags |= B_ERROR;
s = spl6(); /* if other goes this can to*/
printf("timeout calling scintr\n");
scnintr(unit);
splx(s); /*likewise this*/
}
/* splx(s); this is where dennis thinks it should be*/
}
scnstart(um)
register struct uba_ctlr *um;
{
register struct buf *bp,*dp;
register struct scndevice *draddr;
register struct scnsoftc *scnp;
int cmd;
dp = um->um_tab.b_actf;
bp = dp->b_actf;
scnp = &scnsoftc[minor(bp->b_dev)];
scnp->sc_intno = 0;
um->um_tab.b_active++;
draddr = (struct scndevice *)um->um_addr;
draddr->scnwc = -1;
if(bp->b_flags & B_READ)
cmd = SCN_IE;
else{
u.u_error = EFAULT;
}
um->um_cmd = cmd|SCN_GO;
draddr->scndata = scnp->sbmode|scnp->bpmode|SCN_ARM;
draddr->scncs = cmd;
bp->b_bcount = SCNSIZE + SCNPAD;
DELAY(10);
if( ubago(scndinfo[minor(bp->b_dev)])== 0)
printf("ubago returned 0\n");
}
scndgo(um)
struct uba_ctlr *um;
{
register struct scndevice *draddr = (struct scndevice *)um->um_addr;
register unsigned addr, temp;
register struct scnsoftc *scnp;
struct buf *bp, *dp;
dp = um->um_tab.b_actf;
bp = dp->b_actf;
scnp = &scnsoftc[minor(bp->b_dev)];
/* temp = bp->b_un.b_addr;*/
addr = um->um_ubinfo;
addr = (addr + (SCNSIZE-1)) & ~(SCNSIZE-1);
/* temp = (temp + (SCNSIZE-1)) & ~(SCNSIZE-1);*/
scnp->scnreg = UBAI_MR(addr);
scnp->scn_offset = (addr - um->um_ubinfo) & (SCNSIZE-1);
/* add half so intr can */
scnp->scnb_addr = bp->b_un.b_addr + SCNHSIZE + scnp->scn_offset;
/*was temp*/
temp = UBAI_BDP(addr);
scnp->scn_bdp = (temp << UBAMR_DPSHIFT) | UBAMR_MRV;
printf("scndgo uminfo %o addr %o reg %o b_addr %o\n",um->um_ubinfo,
addr, scnp->scnreg,bp->b_un.b_addr);
printf("ba %o cs %o scnbaddr %o offset %o\n",(addr&040000),
(um->um_cmd|((addr>>12)&SCN_HADDR)), scnp->scnb_addr,
scnp->scn_offset);
draddr->scnba = 0;
scnp->scn_sync = 0;
draddr->scncs = um->um_cmd|((addr>>12)&SCN_HADDR);
draddr->scn_recvst |= SCN_REC_IE;
draddr->scn_send = SCN_SCAN;
}
scnioctl(dev, cmd, arg)
dev_t dev;
int cmd;
register caddr_t arg;
{
register struct scnsoftc *scnp = &scnsoftc[minor(dev)];
register struct uba_device *ui = scndinfo[minor(dev)];
register struct scndevice *draddr = (struct scndevice *)ui->ui_addr;
register struct uba_ctlr *um;
u_short realcmd;
u_short stat;
if( (cmd != SCNRECST) && (cmd != SCNREC) && (cmd != SCNSENDST)
& (cmd != SCNHACK)){
if(copyin(arg, (caddr_t)&realcmd, sizeof(realcmd))){
u.u_error = EFAULT;
return;
}
}
switch(cmd){
case SCNSENDST:
realcmd = draddr->scn_sendst;
break;
case SCNSEND:
stat = draddr->scn_sendst;
if(stat & SCN_SREADY)
draddr->scn_send = realcmd;
else u.u_error = EFAULT;
return;
case SCNRECST:
realcmd = draddr->scn_recvst;
break;
case SCNREC:
realcmd = draddr->scn_recv;
break;
case SCNHACK:
realcmd = scnp->scn_offset;
break;
case SCNSBMOD:
scnp->sbmode = realcmd & SCN_SBMASK;
return;
case SCNBPMOD:
scnp->bpmode = realcmd & SCN_BPMASK;
return;
default:
u.u_error = ENXIO;
return;
}
if(copyout((caddr_t)&realcmd, arg, sizeof(realcmd)))
u.u_error = EFAULT;
}
scnreset() {}
scnxintr(dr11)
register dr11;
{
register struct scndevice *draddr;
register struct uba_ctlr *um;
struct buf *dp, *bp;
register int stat;
struct scnsoftc *scnp;
um = scnminfo[dr11];
draddr = (struct scndevice *)um->um_addr;
stat = draddr->scn_recv;
if(stat == SCN_STATE2){
draddr->scn_send = SCN_WAIT;
return;
}
if(stat == SCN_STATE3){
scnp = &scnsoftc[minor(dr11)];
printf("got ack state 3:intno %o max %o\n", scnp->sc_intno,scnp->sc_maxi);
dp = um->um_tab.b_actf;
bp= dp->b_actf;
draddr->scndata = 0;
draddr->scn_recvst = 0;
draddr->scncs = 0;
um->um_tab.b_active = 0;
um->um_tab.b_errcnt = 0;
um->um_tab.b_actf = dp->b_forw;
dp->b_errcnt = 0;
dp->b_active = 0;
bp->b_resid = 0;
ubadone(um);
iodone(bp);
return;
}
return;
}
scnintr(dr11)
register dr11;
{
register struct buf *bp,*dp;
register struct scndevice *draddr;
register struct uba_ctlr *um;
register unsigned stat, reg,temp;
u_short bit;
struct pte *io, *pte;
struct scnsoftc *scnp;
um = scnminfo[dr11];
scnp = &scnsoftc[dr11];
if(um->um_tab.b_active == 0)
return;
if(scndinfo[dr11]->ui_open == 0)
return;
dp = um->um_tab.b_actf;
bp = dp->b_actf;
draddr = (struct scndevice *)um->um_addr;
stat = draddr->scncs;
if(stat & SCN_ERROR){
reg = draddr->scnba;
printf("scanner error %o addr %o intno %d\n",stat,
reg,scnp->sc_intno);
goto scbad;
}
if(scnp->sc_intno >= scnp->sc_maxi){
printf("quiting on maxi %o\n",scnp->sc_intno);
goto scbad;
}
bit = draddr->scnba & 020000;
if(scnp->scn_sync == bit)
printf("scn out of sync %d addr=%o\n",
scnp->sc_intno,draddr->scnba);
scnp->scn_sync = bit;
reg = scnp->scnreg;
if(scnp->sc_intno++ & 01)
reg += SCNHREG;
scnp->scnb_addr += SCNHSIZE;
stat = btop(scnp->scnb_addr);
pte = vtopte(bp->b_proc, stat);
io = &um->um_hd->uh_uba->uba_map[reg];
stat = SCNHREG;
temp = scnp->scn_bdp;
while(stat-- > 0){
if(pte->pg_pfnum == 0){
printf("about to write to 0 pte int %o stat %d\n",
scnp->sc_intno,stat);
goto scbad;
}
*(int *)io++ = pte++->pg_pfnum | temp;
}
return;
scbad:
draddr->scndata = 0; /*turn off arm bit*/
draddr->scn_recvst = 0; /*turn off recv intr*/
draddr->scncs = 0; /*turn off scn intr*/
um->um_tab.b_active = 0;
um->um_tab.b_errcnt = 0;
um->um_tab.b_actf = dp->b_forw;
dp->b_errcnt = 0;
dp->b_active = 0;
bp->b_resid = 0;
ubadone(um);
iodone(bp);
}
#endif