From 937f1f86c7892d32ae912c3934c41ff659d1c3a0 Mon Sep 17 00:00:00 2001 From: CSRG Date: Tue, 10 Nov 1981 22:43:55 -0800 Subject: [PATCH 1/1] BSD 4_1_snap development Work on file sys/dev/uba.c Work on file sys/dev/uda.c Synthesized-from: CSRG/cd1/4.1.snap --- sys/dev/uba.c | 517 ++++++++++++++++++++++++++++++ sys/dev/uda.c | 850 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1367 insertions(+) create mode 100644 sys/dev/uba.c create mode 100644 sys/dev/uda.c diff --git a/sys/dev/uba.c b/sys/dev/uba.c new file mode 100644 index 0000000000..bb5493a3b6 --- /dev/null +++ b/sys/dev/uba.c @@ -0,0 +1,517 @@ +/* uba.c 4.36 81/10/27 */ + +#include "../h/param.h" +#include "../h/systm.h" +#include "../h/cpu.h" +#include "../h/map.h" +#include "../h/pte.h" +#include "../h/buf.h" +#include "../h/vm.h" +#include "../h/ubareg.h" +#include "../h/ubavar.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/proc.h" +#include "../h/conf.h" +#include "../h/mtpr.h" +#include "../h/nexus.h" +#include "../h/dk.h" + +#if VAX780 +char ubasr_bits[] = UBASR_BITS; +#endif + +/* + * Do transfer on device argument. The controller + * and uba involved are implied by the device. + * We queue for resource wait in the uba code if necessary. + * We return 1 if the transfer was started, 0 if it was not. + * If you call this routine with the head of the queue for a + * UBA, it will automatically remove the device from the UBA + * queue before it returns. If some other device is given + * as argument, it will be added to the request queue if the + * request cannot be started immediately. This means that + * passing a device which is on the queue but not at the head + * of the request queue is likely to be a disaster. + */ +ubago(ui) + register struct uba_device *ui; +{ + register struct uba_ctlr *um = ui->ui_mi; + register struct uba_hd *uh; + register int s, unit; + + uh = &uba_hd[um->um_ubanum]; + s = spl6(); + if (um->um_driver->ud_xclu && uh->uh_users > 0 || uh->uh_xclu) + goto rwait; + um->um_ubinfo = ubasetup(um->um_ubanum, um->um_tab.b_actf->b_actf, + UBA_NEEDBDP|UBA_CANTWAIT); + if (um->um_ubinfo == 0) + goto rwait; + uh->uh_users++; + if (um->um_driver->ud_xclu) + uh->uh_xclu = 1; + splx(s); + if (ui->ui_dk >= 0) { + unit = ui->ui_dk; + dk_busy |= 1<uh_actf == ui) + uh->uh_actf = ui->ui_forw; + (*um->um_driver->ud_dgo)(um); + if (ui->ui_dk >= 0) { + dk_xfer[unit]++; + dk_wds[unit] += um->um_tab.b_actf->b_actf->b_bcount>>6; + } + return (1); +rwait: + if (uh->uh_actf != ui) { + ui->ui_forw = NULL; + if (uh->uh_actf == NULL) + uh->uh_actf = ui; + else + uh->uh_actl->ui_forw = ui; + uh->uh_actl = ui; + } + splx(s); + return (0); +} + +ubadone(um) + register struct uba_ctlr *um; +{ + register struct uba_hd *uh = &uba_hd[um->um_ubanum]; + + if (um->um_driver->ud_xclu) + uh->uh_xclu = 0; + uh->uh_users--; + ubarelse(um->um_ubanum, &um->um_ubinfo); +} + +/* + * Allocate and setup UBA map registers, and bdp's + * Flags says whether bdp is needed, whether the caller can't + * wait (e.g. if the caller is at interrupt level). + * + * Return value: + * Bits 0-8 Byte offset + * Bits 9-17 Start map reg. no. + * Bits 18-27 No. mapping reg's + * Bits 28-31 BDP no. + */ +ubasetup(uban, bp, flags) + struct buf *bp; +{ + register struct uba_hd *uh = &uba_hd[uban]; + register int temp, i; + int npf, reg, bdp; + unsigned v; + register struct pte *pte, *io; + struct proc *rp; + int a, o, ubinfo; + +#if VAX7ZZ + if (cpu == VAX_7ZZ) + flags &= ~UBA_NEEDBDP; +#endif + v = btop(bp->b_un.b_addr); + o = (int)bp->b_un.b_addr & PGOFSET; + npf = btoc(bp->b_bcount + o) + 1; + a = spl6(); + while ((reg = rmalloc(uh->uh_map, npf)) == 0) { + if (flags & UBA_CANTWAIT) { + splx(a); + return (0); + } + uh->uh_mrwant++; + sleep((caddr_t)uh->uh_map, PSWP); + } + bdp = 0; + if (flags & UBA_NEEDBDP) { + while ((bdp = ffs(uh->uh_bdpfree)) == 0) { + if (flags & UBA_CANTWAIT) { + rmfree(uh->uh_map, npf, reg); + splx(a); + return (0); + } + uh->uh_bdpwant++; + sleep((caddr_t)uh->uh_map, PSWP); + } + uh->uh_bdpfree &= ~(1 << (bdp-1)); + } else if (flags & UBA_HAVEBDP) + bdp = (flags >> 28) & 0xf; + splx(a); + reg--; + ubinfo = (bdp << 28) | (npf << 18) | (reg << 9) | o; + io = &uh->uh_uba->uba_map[reg]; + temp = (bdp << 21) | UBAMR_MRV; + rp = bp->b_flags&B_DIRTY ? &proc[2] : bp->b_proc; + if (bdp && (o & 01)) + temp |= UBAMR_BO; + if (bp->b_flags & B_UAREA) { + for (i = UPAGES - bp->b_bcount / NBPG; i < UPAGES; i++) { + if (rp->p_addr[i].pg_pfnum == 0) + panic("uba: zero upage"); + *(int *)io++ = rp->p_addr[i].pg_pfnum | temp; + } + } else if ((bp->b_flags & B_PHYS) == 0) { + pte = &Sysmap[btop(((int)bp->b_un.b_addr)&0x7fffffff)]; + while (--npf != 0) + *(int *)io++ = pte++->pg_pfnum | temp; + } else { + if (bp->b_flags & B_PAGET) + pte = &Usrptmap[btokmx((struct pte *)bp->b_un.b_addr)]; + else + pte = vtopte(rp, v); + while (--npf != 0) { + if (pte->pg_pfnum == 0) + panic("uba zero uentry"); + *(int *)io++ = pte++->pg_pfnum | temp; + } + } + *(int *)io++ = 0; + return (ubinfo); +} + +/* + * Non buffer setup interface... set up a buffer and call ubasetup. + */ +uballoc(uban, addr, bcnt, flags) + int uban; + caddr_t addr; + int bcnt, flags; +{ + struct buf ubabuf; + + ubabuf.b_un.b_addr = addr; + ubabuf.b_flags = B_BUSY; + ubabuf.b_bcount = bcnt; + /* that's all the fields ubasetup() needs */ + return (ubasetup(uban, &ubabuf, flags)); +} + +/* + * Release resources on uba uban, and then unblock resource waiters. + * The map register parameter is by value since we need to block + * against uba resets on 11/780's. + */ +ubarelse(uban, amr) + int *amr; +{ + register struct uba_hd *uh = &uba_hd[uban]; + register int bdp, reg, npf, s; + int mr; + + /* + * Carefully see if we should release the space, since + * it may be released asynchronously at uba reset time. + */ + s = spl6(); + mr = *amr; + if (mr == 0) { + /* + * A ubareset() occurred before we got around + * to releasing the space... no need to bother. + */ + splx(s); + return; + } + *amr = 0; + splx(s); /* let interrupts in, we're safe for a while */ + bdp = (mr >> 28) & 0x0f; + if (bdp) { + switch (cpu) { +#if VAX780 + case VAX_780: + uh->uh_uba->uba_dpr[bdp] |= UBADPR_BNE; + break; +#endif +#if VAX750 + case VAX_750: + uh->uh_uba->uba_dpr[bdp] |= + UBADPR_PURGE|UBADPR_NXM|UBADPR_UCE; + break; +#endif + } + uh->uh_bdpfree |= 1 << (bdp-1); /* atomic */ + if (uh->uh_bdpwant) { + uh->uh_bdpwant = 0; + wakeup((caddr_t)uh->uh_map); + } + } + /* + * Put back the registers in the resource map. + * The map code must not be reentered, so we do this + * at high ipl. + */ + npf = (mr >> 18) & 0x3ff; + reg = ((mr >> 9) & 0x1ff) + 1; + s = spl6(); + rmfree(uh->uh_map, npf, reg); + splx(s); + + /* + * Wakeup sleepers for map registers, + * and also, if there are processes blocked in dgo(), + * give them a chance at the UNIBUS. + */ + if (uh->uh_mrwant) { + uh->uh_mrwant = 0; + wakeup((caddr_t)uh->uh_map); + } + while (uh->uh_actf && ubago(uh->uh_actf)) + ; +} + +ubapurge(um) + register struct uba_ctlr *um; +{ + register struct uba_hd *uh = um->um_hd; + register int bdp = (um->um_ubinfo >> 28) & 0x0f; + + switch (cpu) { +#if VAX780 + case VAX_780: + uh->uh_uba->uba_dpr[bdp] |= UBADPR_BNE; + break; +#endif +#if VAX750 + case VAX_750: + uh->uh_uba->uba_dpr[bdp] |= UBADPR_PURGE|UBADPR_NXM|UBADPR_UCE; + break; +#endif + } +} + +/* + * Generate a reset on uba number uban. Then + * call each device in the character device table, + * giving it a chance to clean up so as to be able to continue. + */ +ubareset(uban) + int uban; +{ + register struct cdevsw *cdp; + register struct uba_hd *uh = &uba_hd[uban]; + int s; + + s = spl6(); + uh->uh_users = 0; + uh->uh_zvcnt = 0; + uh->uh_xclu = 0; + uh->uh_hangcnt = 0; + uh->uh_actf = uh->uh_actl = 0; + uh->uh_bdpwant = 0; + uh->uh_mrwant = 0; + wakeup((caddr_t)&uh->uh_bdpwant); + wakeup((caddr_t)&uh->uh_mrwant); + printf("uba%d: reset", uban); + ubainit(uh->uh_uba); + for (cdp = cdevsw; cdp->d_open; cdp++) + (*cdp->d_reset)(uban); + printf("\n"); + splx(s); +} + +/* + * Init a uba. This is called with a pointer + * rather than a virtual address since it is called + * by code which runs with memory mapping disabled. + * In these cases we really don't need the interrupts + * enabled, but since we run with ipl high, we don't care + * if they are, they will never happen anyways. + */ +ubainit(uba) + register struct uba_regs *uba; +{ + + switch (cpu) { +#if VAX780 + case VAX_780: + uba->uba_cr = UBACR_ADINIT; + uba->uba_cr = UBACR_IFS|UBACR_BRIE|UBACR_USEFIE|UBACR_SUEFIE; + while ((uba->uba_cnfgr & UBACNFGR_UBIC) == 0) + ; + break; +#endif +#if VAX750 + case VAX_750: +#endif +#if VAX7ZZ + case VAX_7ZZ: +#endif +#if defined(VAX750) || defined(VAX7ZZ) + mtpr(IUR, 0); + /* give devices time to recover from power fail */ +/* THIS IS PROBABLY UNNECESSARY */ + DELAY(500000); +/* END PROBABLY UNNECESSARY */ + break; +#endif + } +} + +#if VAX780 +/* + * Check to make sure the UNIBUS adaptor is not hung, + * with an interrupt in the register to be presented, + * but not presenting it for an extended period (5 seconds). + */ +unhang() +{ + register int uban; + + for (uban = 0; uban < numuba; uban++) { + register struct uba_hd *uh = &uba_hd[uban]; + register struct uba_regs *up = uh->uh_uba; + + if (up->uba_sr == 0) + return; + up->uba_sr = UBASR_CRD|UBASR_LEB; + uh->uh_hangcnt++; + if (uh->uh_hangcnt > 5*hz) { + uh->uh_hangcnt = 0; + printf("uba%d: hung\n", uban); + ubareset(uban); + } + } +} + +/* + * This is a timeout routine which decrements the ``i forgot to + * interrupt'' counts, on an 11/780. This prevents slowly growing + * counts from causing a UBA reset since we are interested only + * in hang situations. + */ +ubawatch() +{ + register struct uba_hd *uh; + register int uban; + + if (panicstr) + return; + for (uban = 0; uban < numuba; uban++) { + uh = &uba_hd[uban]; + if (uh->uh_hangcnt) + uh->uh_hangcnt--; + } +} + +int ubawedgecnt = 10; +int ubacrazy = 500; +/* + * This routine is called by the locore code to + * process a UBA error on an 11/780. The arguments are passed + * on the stack, and value-result (through some trickery). + * In particular, the uvec argument is used for further + * uba processing so the result aspect of it is very important. + * It must not be declared register. + */ +/*ARGSUSED*/ +ubaerror(uban, uh, xx, uvec, uba) + register int uban; + register struct uba_hd *uh; + int uvec; + register struct uba_regs *uba; +{ + register sr, s; + + if (uvec == 0) { + uh->uh_zvcnt++; + if (uh->uh_zvcnt > 250000) { + printf("uba%d: too many zero vectors\n"); + ubareset(uban); + } + uvec = 0; + return; + } + if (uba->uba_cnfgr & NEX_CFGFLT) { + printf("uba%d: sbi fault sr=%b cnfgr=%b\n", + uban, uba->uba_sr, ubasr_bits, + uba->uba_cnfgr, NEXFLT_BITS); + ubareset(uban); + uvec = 0; + return; + } + sr = uba->uba_sr; + s = spl7(); + printf("uba%d: uba error sr=%b fmer=%x fubar=%o\n", + uban, uba->uba_sr, ubasr_bits, uba->uba_fmer, 4*uba->uba_fubar); + splx(s); + uba->uba_sr = sr; + uvec &= UBABRRVR_DIV; + if (++uh->uh_errcnt % ubawedgecnt == 0) { + if (uh->uh_errcnt > ubacrazy) + panic("uba crazy"); + printf("ERROR LIMIT "); + ubareset(uban); + uvec = 0; + return; + } + return; +} +#endif + +/* + * This routine allows remapping of previously + * allocated UNIBUS bdp and map resources + * onto different memory addresses. + * It should only be used by routines which need + * small fixed length mappings for long periods of time + * (like the ARPANET ACC IMP interface). + * It only maps kernel addresses. + */ +ubaremap(uban, ubinfo, addr) + int uban; + register unsigned ubinfo; + caddr_t addr; +{ + register struct uba_hd *uh = &uba_hd[uban]; + register struct pte *pte, *io; + register int temp, bdp; + int npf, o; + + o = (int)addr & PGOFSET; + bdp = (ubinfo >> 28) & 0xf; + npf = (ubinfo >> 18) & 0x3ff; + io = &uh->uh_uba->uba_map[(ubinfo >> 9) & 0x1ff]; + temp = (bdp << 21) | UBAMR_MRV; + + /* + * If using buffered data path initiate purge + * of old data and set byte offset bit if next + * transfer will be from odd address. + */ + if (bdp) { + switch (cpu) { +#if VAX780 + case VAX_780: + uh->uh_uba->uba_dpr[bdp] |= UBADPR_BNE; + break; +#endif +#if VAX750 + case VAX_750: + uh->uh_uba->uba_dpr[bdp] |= + UBADPR_PURGE|UBADPR_NXM|UBADPR_UCE; + break; +#endif + } + if (o & 1) + temp |= UBAMR_BO; + } + + /* + * Set up the map registers, leaving an invalid reg + * at the end to guard against wild unibus transfers. + */ + pte = &Sysmap[btop(((int)addr)&0x7fffffff)]; + while (--npf != 0) + *(int *)io++ = pte++->pg_pfnum | temp; + *(int *)io = 0; + + /* + * Return effective UNIBUS address. + */ + return (ubinfo | o); +} diff --git a/sys/dev/uda.c b/sys/dev/uda.c new file mode 100644 index 0000000000..db1a6c54e0 --- /dev/null +++ b/sys/dev/uda.c @@ -0,0 +1,850 @@ +/* uda.c 1.2 81/10/27 */ + +#include "ra.h" +#if NUDA > 0 +/* + * UDA50/RAxx disk device driver + * + * Restrictions: + * Unit numbers must be less than 8. + * + * TO DO: + * write dump code + * test on 750 + */ + +#include "../h/param.h" +#include "../h/systm.h" +#include "../h/buf.h" +#include "../h/conf.h" +#include "../h/dir.h" +#include "../h/user.h" +#include "../h/pte.h" +#include "../h/map.h" +#include "../h/vm.h" +#include "../h/ubareg.h" +#include "../h/ubavar.h" +#include "../h/dk.h" +#include "../h/cpu.h" +#include "../h/cmap.h" + +int udadebug; +#define printd if(udadebug&1)printf + +/* + * Parameters for the communications area + */ + +#define NRSPL2 3 +#define NCMDL2 3 +#define NRSP (1<sc_ivec = (uba_hd[numuba].uh_lastiv -= 4); + return(1); +} + +udslave(ui, reg) + struct uba_device *ui; + caddr_t reg; +{ + /* + * TOO HARD TO FIND OUT IF DISK IS THERE UNTIL + * INITIALIZED. WE'LL FIND OUT WHEN WE FIRST + * TRY TO ACCESS IT. + */ + return(1); +} + +udattach(ui) + register struct uba_device *ui; +{ + + if (ui->ui_dk > 0) + dk_mspw[ui->ui_dk] = 1.0 / (60 * 31 * 256); /* approx */ + ui->ui_flags = 0; + udip[ui->ui_ctlr][ui->ui_slave] = ui; + radsize[ui->ui_unit] = (daddr_t)0xffffff; /* max possible size */ +} + +/* + * Open a UDA. Initialize the device and + * set the unit online. + */ +udopen(dev, flag) + dev_t dev; + int flag; +{ + register int unit; + register struct uba_device *ui; + register struct uda_softc *sc; + + unit = minor(dev) >> 3; + if (unit >= NRA || (ui = uddinfo[unit]) == 0 || ui->ui_alive == 0) { + u.u_error = ENXIO; + return; + } + sc = &uda_softc[ui->ui_ctlr]; + (void) spl5(); + if (sc->sc_state != S_RUN) { + if (sc->sc_state == S_IDLE) + udinit(ui->ui_ctlr); + sleep(ui->ui_mi, 0); /* wait for initialization to complete */ + if (sc->sc_state != S_RUN) { + u.u_error = EIO; + return; + } + } + (void) spl0(); + /* SHOULD PROBABLY FORCE AN ONLINE ATTEMPT + TO SEE IF DISK IS REALLY THERE */ +} + +/* + * Initialize a UDA. Set up UBA mapping registers, + * initialize data structures, and start hardware + * initialization sequence. + */ +udinit(d) + int d; +{ + register struct uda_softc *sc; + register struct uda *ud; + struct udadevice *udaddr; + struct uba_ctlr *um; + + sc = &uda_softc[d]; + um = udminfo[d]; + um->um_tab.b_active++; + ud = &uda[d]; + udaddr = (struct udadevice *)um->um_addr; + if (sc->sc_mapped == 0) { + /* + * Map the communications area and command + * and response packets into Unibus address + * space. + */ + sc->sc_ubainfo = uballoc(um->um_ubanum, (caddr_t)ud, + sizeof (struct uda), 0); + sc->sc_uda = (struct uda *)(sc->sc_ubainfo & 0x3ffff); + sc->sc_mapped = 1; + } + + /* + * Start the hardware initialization sequence. + */ + udaddr->udaip = 0; /* start initialization */ + while ((udaddr->udasa & UDA_STEP1) == 0) + ; + udaddr->udasa = UDA_ERR|(NCMDL2<<11)|(NRSPL2<<8)|UDA_IE|(sc->sc_ivec/4); + /* + * Initialization continues in interrupt routine. + */ + sc->sc_state = S_STEP1; + sc->sc_credits = 0; +} + +udstrategy(bp) + register struct buf *bp; +{ + register struct uba_device *ui; + register struct uba_ctlr *um; + register struct buf *dp; + register int unit; + int xunit = minor(bp->b_dev) & 07; + daddr_t sz, maxsz; + + sz = (bp->b_bcount+511) >> 9; + unit = dkunit(bp); + if (unit >= NRA) + goto bad; + ui = uddinfo[unit]; + um = ui->ui_mi; + if (ui == 0 || ui->ui_alive == 0) + goto bad; + if ((maxsz = ra_sizes[xunit].nblocks) < 0) + maxsz = radsize[unit] - ra_sizes[xunit].blkoff; + if (bp->b_blkno < 0 || bp->b_blkno+sz > maxsz || + ra_sizes[xunit].blkoff >= radsize[unit]) + goto bad; + (void) spl5(); + /* + * Link the buffer onto the drive queue + */ + dp = &udutab[ui->ui_unit]; + if (dp->b_actf == 0) + dp->b_actf = bp; + else + dp->b_actl->av_forw = bp; + dp->b_actl = bp; + bp->av_forw = 0; + /* + * Link the drive onto the controller queue + */ + if (dp->b_active == 0) { + dp->b_forw = NULL; + if (um->um_tab.b_actf == NULL) + um->um_tab.b_actf = dp; + else + um->um_tab.b_actl->b_forw = dp; + um->um_tab.b_actl = dp; + dp->b_active = 1; + } + if (um->um_tab.b_active == 0) { +#if defined(VAX750) + if (cpu == VAX_750) { + if (um->um_ubinfo != 0) + printf("uda: ubinfo %x\n",um->um_ubinfo); + else + um->um_ubinfo = + uballoc(um->um_ubanum, 0, 0, UBA_NEEDBDP); + } +#endif + (void) udstart(um); + } + (void) spl0(); + return; + +bad: + bp->b_flags |= B_ERROR; + iodone(bp); + return; +} + +udstart(um) + register struct uba_ctlr *um; +{ + register struct buf *bp, *dp; + register struct mscp *mp; + register struct uda_softc *sc; + register struct uba_device *ui; + struct udadevice *udaddr; + int i; + + sc = &uda_softc[um->um_ctlr]; + +loop: + if ((dp = um->um_tab.b_actf) == NULL) { + /* + * Release uneeded UBA resources and return + */ + um->um_tab.b_active = 0; +#if defined(VAX750) + if (cpu == VAX_750) { + if (um->um_ubinfo == 0) + printf("uda: um_ubinfo == 0\n"); + else + ubarelse(um->um_ubanum, &um->um_ubinfo); + } +#endif + return(0); + } + if ((bp = dp->b_actf) == NULL) { + /* + * No more requests for this drive, remove + * from controller queue and look at next drive. + * We know we're at the head of the controller queue. + */ + dp->b_active = 0; + um->um_tab.b_actf = dp->b_forw; + goto loop; + } + um->um_tab.b_active++; + udaddr = (struct udadevice *)um->um_addr; + if ((udaddr->udasa&UDA_ERR) || sc->sc_state != S_RUN) { + harderr(bp, "ra"); + printf("udasa %o, state %d\n", udaddr->udasa&0xffff, sc->sc_state); + udinit(um->um_ctlr); + /* SHOULD REQUEUE OUTSTANDING REQUESTS, LIKE UDRESET */ + return; + } + ui = uddinfo[dkunit(bp)]; + /* + * If no credits, can't issue any commands + * until some outstanding commands complete. + */ + if (sc->sc_credits < 2) + return(0); + if ((mp = udgetcp(um)) == NULL) + return(0); + sc->sc_credits--; /* committed to issuing a command */ + if (ui->ui_flags == 0) { /* not online */ + mp->mscp_opcode = M_OP_ONLIN; + mp->mscp_unit = ui->ui_slave; + dp->b_active = 2; + um->um_tab.b_actf = dp->b_forw; /* remove from controller q */ + printd("uda: bring unit %d online\n", ui->ui_slave); + *((long *)mp->mscp_dscptr) |= UDA_OWN|UDA_INT; + i = udaddr->udaip; + goto loop; + } + switch (cpu) { + case VAX_780: + i = UBA_NEEDBDP|UBA_CANTWAIT; + break; + + case VAX_750: + i = um->um_ubinfo|UBA_HAVEBDP|UBA_CANTWAIT; + break; + + case VAX_7ZZ: + i = UBA_CANTWAIT; + break; + } + if ((i = ubasetup(um->um_ubanum, bp, i)) == 0) { + mp->mscp_opcode = M_OP_GTUNT; + mp->mscp_unit = ui->ui_slave; + *((long *)mp->mscp_dscptr) |= UDA_OWN|UDA_INT; + i = udaddr->udaip; /* initiate polling */ + return(1); /* wait for interrupt */ + } + mp->mscp_cmdref = (long)bp; /* pointer to get back */ + mp->mscp_opcode = bp->b_flags&B_READ ? M_OP_READ : M_OP_WRITE; + mp->mscp_unit = ui->ui_slave; + mp->mscp_lbn = bp->b_blkno + ra_sizes[minor(bp->b_dev)&7].blkoff; + mp->mscp_bytecnt = bp->b_bcount; + mp->mscp_buffer = (i & 0x3ffff) | (((i>>28)&0xf)<<24); +#if defined(VAX750) + if (cpu == VAX_750) + i &= 0xfffffff; /* mask off bdp */ +#endif + bp->b_ubinfo = i; /* save mapping info */ + *((long *)mp->mscp_dscptr) |= UDA_OWN|UDA_INT; + i = udaddr->udaip; /* initiate polling */ + if (ui->ui_dk >= 0) { + dk_busy |= 1<ui_dk; + dp->b_qsize++; + dk_xfer[ui->ui_dk]++; + dk_wds[ui->ui_dk] += bp->b_bcount>>6; + } + + /* + * Move drive to the end of the controller queue + */ + if (dp->b_forw != NULL) { + um->um_tab.b_actf = dp->b_forw; + um->um_tab.b_actl->b_forw = dp; + um->um_tab.b_actl = dp; + dp->b_forw = NULL; + } + /* + * Move buffer to I/O wait queue + */ + dp->b_actf = bp->av_forw; + dp = &udwtab[um->um_ctlr]; + bp->av_forw = dp; + bp->av_back = dp->av_back; + dp->av_back->av_forw = bp; + dp->av_back = bp; + goto loop; +} + +/* + * UDA interrupt routine. + */ +udintr(d) + int d; +{ + register struct uba_ctlr *um = udminfo[d]; + register struct udadevice *udaddr = (struct udadevice *)um->um_addr; + struct buf *bp; + register int i; + register struct uda_softc *sc = &uda_softc[d]; + register struct uda *ud = &uda[d]; + struct uda *uud; + struct mscp *mp; + + printd("udintr: state %d, udasa %o\n", sc->sc_state, udaddr->udasa); + switch (sc->sc_state) { + case S_IDLE: + printf("uda%d: random interrupt ignored\n", d); + return; + + case S_STEP1: +#define STEP1GOOD (UDA_STEP2|UDA_IE|(NCMDL2<<3)|NRSPL2) + if ((udaddr->udasa&(UDA_ERR|STEP1GOOD)) != STEP1GOOD) { + sc->sc_state = S_IDLE; + wakeup(um); + return; + } + udaddr->udasa = ((int)&sc->sc_uda->uda_ca.ca_ringbase)| + (cpu == VAX_780 ? UDA_PI : 0); + sc->sc_state = S_STEP2; + return; + + case S_STEP2: +#define STEP2GOOD (UDA_STEP3|UDA_IE|(sc->sc_ivec/4)) + if ((udaddr->udasa&(UDA_ERR|STEP2GOOD)) != STEP2GOOD) { + sc->sc_state = S_IDLE; + wakeup(um); + return; + } + udaddr->udasa = ((int)&sc->sc_uda->uda_ca.ca_ringbase)>>16; + sc->sc_state = S_STEP3; + return; + + case S_STEP3: +#define STEP3GOOD UDA_STEP4 + if ((udaddr->udasa&(UDA_ERR|STEP3GOOD)) != STEP3GOOD) { + sc->sc_state = S_IDLE; + wakeup(um); + return; + } + udaddr->udasa = UDA_GO; + sc->sc_state = S_SCHAR; + + /* + * Initialize the data structures. + */ + uud = sc->sc_uda; + for (i = 0; i < NRSP; i++) { + ud->uda_ca.ca_rspdsc[i] = UDA_OWN|UDA_INT| + (long)&uud->uda_rsp[i].mscp_cmdref; + ud->uda_rsp[i].mscp_dscptr = &ud->uda_ca.ca_rspdsc[i]; + ud->uda_rsp[i].mscp_header.uda_msglen = sizeof (struct mscp); + } + for (i = 0; i < NCMD; i++) { + ud->uda_ca.ca_cmddsc[i] = UDA_INT| + (long)&uud->uda_cmd[i].mscp_cmdref; + ud->uda_cmd[i].mscp_dscptr = &ud->uda_ca.ca_cmddsc[i]; + ud->uda_cmd[i].mscp_header.uda_msglen = sizeof (struct mscp); + } + bp = &udwtab[d]; + bp->av_forw = bp->av_back = bp; + sc->sc_lastcmd = 0; + sc->sc_lastrsp = 0; + if ((mp = udgetcp(um)) == NULL) { + sc->sc_state = S_IDLE; + wakeup(um); + return; + } + mp->mscp_opcode = M_OP_STCON; + mp->mscp_cntflgs = M_CF_ATTN|M_CF_MISC|M_CF_THIS; + *((long *)mp->mscp_dscptr) |= UDA_OWN|UDA_INT; + i = udaddr->udaip; /* initiate polling */ + return; + + case S_SCHAR: + case S_RUN: + break; + + default: + printf("uda%d: interrupt in unknown state %d ignored\n", + d, sc->sc_state); + return; + } + + if (udaddr->udasa&UDA_ERR) { + printf("uda%d: fatal error (%o)\n", d, udaddr->udasa&0xffff); + udaddr->udaip = 0; + wakeup(um); + } + + /* + * Check for a buffer purge request. + */ + if (ud->uda_ca.ca_bdp) { + /* + * THIS IS A KLUDGE. + * Maybe we should change the entire + * UBA interface structure. + */ + int s = spl7(); + + i = um->um_ubinfo; + printd("uda: purge bdp %d\n", ud->uda_ca.ca_bdp); + um->um_ubinfo = ud->uda_ca.ca_bdp<<28; + ubapurge(um); + um->um_ubinfo = i; + (void) splx(s); + ud->uda_ca.ca_bdp = 0; + udaddr->udasa = 0; /* signal purge complete */ + } + + /* + * Check for response ring transition. + */ + if (ud->uda_ca.ca_rspint) { + ud->uda_ca.ca_rspint = 0; + for (i = sc->sc_lastrsp;; i++) { + i %= NRSP; + if (ud->uda_ca.ca_rspdsc[i]&UDA_OWN) + break; + udrsp(um, ud, sc, i); + ud->uda_ca.ca_rspdsc[i] |= UDA_OWN; + } + sc->sc_lastrsp = i; + } + + /* + * Check for command ring transition. + */ + if (ud->uda_ca.ca_cmdint) { + printd("uda: command ring transition\n"); + ud->uda_ca.ca_cmdint = 0; + } + udstart(um); +} + +/* + * Process a response packet + */ +udrsp(um, ud, sc, i) + register struct uba_ctlr *um; + register struct uda *ud; + register struct uda_softc *sc; + int i; +{ + register struct mscp *mp; + struct uba_device *ui; + struct buf *dp, *bp; + int st; + + mp = &ud->uda_rsp[i]; + mp->mscp_header.uda_msglen = sizeof (struct mscp); + sc->sc_credits += mp->mscp_header.uda_credits & 0xf; + if ((mp->mscp_header.uda_credits & 0xf0) > 0x10) + return; + /* + * If it's an error log message (datagram), + * pass it on for more extensive processing. + */ + if ((mp->mscp_header.uda_credits & 0xf0) == 0x10) { + uderror(um, (struct mslg *)mp); + return; + } + if (mp->mscp_unit >= 8) + return; + if ((ui = udip[um->um_ctlr][mp->mscp_unit]) == 0) + return; + st = mp->mscp_status&M_ST_MASK; + switch (mp->mscp_opcode) { + case M_OP_STCON|M_OP_END: + if (st == M_ST_SUCC) + sc->sc_state = S_RUN; + else + sc->sc_state = S_IDLE; + um->um_tab.b_active = 0; + wakeup(um); + break; + + case M_OP_ONLIN|M_OP_END: + /* + * Link the drive onto the controller queue + */ + dp = &udutab[ui->ui_unit]; + dp->b_forw = NULL; + if (um->um_tab.b_actf == NULL) + um->um_tab.b_actf = dp; + else + um->um_tab.b_actl->b_forw = dp; + um->um_tab.b_actl = dp; + if (st == M_ST_SUCC) { + ui->ui_flags = 1; /* mark it online */ + radsize[ui->ui_unit] = (daddr_t)mp->mscp_untsize; + printd("uda: unit %d online\n", mp->mscp_unit); + } else { + harderr(dp->b_actf, "ra"); + printf("OFFLINE\n"); + while (bp = dp->b_actf) { + dp->b_actf = bp->av_forw; + bp->b_flags |= B_ERROR; + iodone(bp); + } + } + dp->b_active = 1; + break; + + case M_OP_AVATN: + printd("uda: unit %d attention\n", mp->mscp_unit); + ui->ui_flags = 0; /* it went offline and we didn't notice */ + break; + + case M_OP_READ|M_OP_END: + case M_OP_WRITE|M_OP_END: + bp = (struct buf *)mp->mscp_cmdref; + ubarelse(um->um_ubanum, &bp->b_resid); + /* + * Unlink buffer from I/O wait queue. + */ + bp->av_back->av_forw = bp->av_forw; + bp->av_forw->av_back = bp->av_back; + dp = &udutab[ui->ui_unit]; + if (ui->ui_dk >= 0) + if (--dp->b_qsize == 0) + dk_busy &= ~(1<ui_dk); + if (st == M_ST_OFFLN || st == M_ST_AVLBL) { + ui->ui_flags = 0; /* mark unit offline */ + /* + * Link the buffer onto the front of the drive queue + */ + if ((bp->av_forw = dp->b_actf) == 0) + dp->b_actl = bp; + dp->b_actf = bp; + /* + * Link the drive onto the controller queue + */ + if (dp->b_active == 0) { + dp->b_forw = NULL; + if (um->um_tab.b_actf == NULL) + um->um_tab.b_actf = dp; + else + um->um_tab.b_actl->b_forw = dp; + um->um_tab.b_actl = dp; + dp->b_active = 1; + } + return; + } + if (st != M_ST_SUCC) { + harderr(bp, "ra"); + printf("status %o\n", mp->mscp_status); + bp->b_flags |= B_ERROR; + } + bp->b_resid = bp->b_bcount - mp->mscp_bytecnt; + iodone(bp); + break; + + case M_OP_GTUNT|M_OP_END: + break; + + default: + printf("uda: unknown packet\n"); + } +} + + +/* + * Process an error log message + * + * For now, just log the error on the console. + * Only minimal decoding is done, only "useful" + * information is printed. Eventually should + * send message to an error logger. + */ +uderror(um, mp) + register struct uba_ctlr *um; + register struct mslg *mp; +{ + printf("uda%d:%d: %s error, ", um->um_ctlr, mp->mslg_seqnum, + mp->mslg_flags&M_LF_SUCC ? "soft" : "hard"); + switch (mp->mslg_format) { + case M_FM_CNTERR: + printf("controller error, event 0%o\n", mp->mslg_event); + break; + + case M_FM_BUSADDR: + printf("host memory access error, event 0%o, addr 0%o\n", + mp->mslg_event, *((long *)&mp->mslg_busaddr[0])); + break; + + case M_FM_DISKTRN: + printf("disk transfer error, unit %d, grp %d, cyl %d, sec %d, ", + mp->mslg_unit, mp->mslg_group, mp->mslg_cylinder, + mp->mslg_sector); + printf("trk %d, lbn %d, retry %d, level %d\n", mp->mslg_track, + mp->mslg_lbn, mp->mslg_retry, mp->mslg_level); + break; + + case M_FM_SDI: + printf("SDI error, unit %d, event 0%o, cyl %d\n", mp->mslg_unit, + mp->mslg_event, mp->mslg_cylinder); + break; + + case M_FM_SMLDSK: + printf("small disk error, unit %d, event 0%o, cyl %d\n", + mp->mslg_unit, mp->mslg_event, mp->mslg_sdecyl); + break; + + default: + printf("unknown error, unit %d, format 0%o, event 0%o\n", + mp->mslg_unit, mp->mslg_format, mp->mslg_event); + } +} + + +/* + * Find an unused command packet + */ +struct mscp * +udgetcp(um) + struct uba_ctlr *um; +{ + register struct mscp *mp; + register struct udaca *cp; + register struct uda_softc *sc; + register int i; + + cp = &uda[um->um_ctlr].uda_ca; + sc = &uda_softc[um->um_ctlr]; + i = sc->sc_lastcmd; + if ((cp->ca_cmddsc[i] & (UDA_OWN|UDA_INT)) == UDA_INT) { + cp->ca_cmddsc[i] &= ~UDA_INT; + mp = &uda[um->um_ctlr].uda_cmd[i]; + mp->mscp_unit = mp->mscp_modifier = 0; + mp->mscp_opcode = mp->mscp_flags = 0; + mp->mscp_bytecnt = mp->mscp_buffer = 0; + mp->mscp_errlgfl = mp->mscp_copyspd = 0; + sc->sc_lastcmd = (i + 1) % NCMD; + return(mp); + } + return(NULL); +} + +udread(dev) + dev_t dev; +{ + register int unit = minor(dev) >> 3; + + if (unit >= NRA) + u.u_error = ENXIO; + else + physio(udstrategy, &rudbuf[unit], dev, B_READ, minphys); +} + +udwrite(dev) + dev_t dev; +{ + register int unit = minor(dev) >> 3; + + if (unit >= NRA) + u.u_error = ENXIO; + else + physio(udstrategy, &rudbuf[unit], dev, B_WRITE, minphys); +} + +udreset(uban) + int uban; +{ + register struct uba_ctlr *um; + register struct uba_device *ui; + register struct buf *bp, *dp; + register int unit; + struct buf *nbp; + int d; + + for (d = 0; d < NUDA; d++) { + if ((um = udminfo[d]) == 0 || um->um_ubanum != uban || + um->um_alive == 0) + continue; + printf(" uda%d", d); + um->um_tab.b_active = 0; + um->um_tab.b_actf = um->um_tab.b_actl = 0; + uda_softc[d].sc_state = S_IDLE; + for (unit = 0; unit < NRA; unit++) { + if ((ui = uddinfo[unit]) == 0) + continue; + if (ui->ui_alive == 0 || ui->ui_mi != um) + continue; + udutab[unit].b_active = 0; + udutab[unit].b_qsize = 0; + } + for (bp = udwtab[d].av_forw; bp != &udwtab[d]; bp = nbp) { + nbp = bp->av_forw; + ubarelse(uban, &bp->b_ubinfo); + /* + * Link the buffer onto the drive queue + */ + dp = &udutab[dkunit(bp)]; + if (dp->b_actf == 0) + dp->b_actf = bp; + else + dp->b_actl->av_forw = bp; + dp->b_actl = bp; + bp->av_forw = 0; + /* + * Link the drive onto the controller queue + */ + if (dp->b_active == 0) { + dp->b_forw = NULL; + if (um->um_tab.b_actf == NULL) + um->um_tab.b_actf = dp; + else + um->um_tab.b_actl->b_forw = dp; + um->um_tab.b_actl = dp; + dp->b_active = 1; + } + } + udinit(d); + } +} + +uddump() +{ + return(ENXIO); +} -- 2.20.1