/* * This driver runs the RD50/51 controller on the pro3xx. It * expects bad sector forwarding * tables to be in a BAD144 style. Bad sectors are marked by writing a nonzero * backup revision field in the sector header. The standalone program, "rdfmt" * should be run to format and bad scan the disk before this driver is used * if BADSECT is enabled. */ #include "rd.h" #if NRD > 0 #include #include #include #include #include #include #include #ifndef INTRLVE #include #endif INTRLVE #ifdef BADSECT #include #endif BADSECT #include #include #define OPEN 1 #define GOTBAD 2 extern struct size rd_sizes[]; extern struct rddevice *RDADDR; struct buf rdtab; struct buf rrdbuf; #ifdef BADSECT struct dkbad rdbad; #endif BADSECT union wordval rdval; struct rdst rdst[] = { 16, 4, 16*4, 305, 16, 4, 16*4, 152, 0, 0, 0, 0, }; struct rd_softc { char sc_stat; char sc_type; } rd_softc; void rdroot() { rdattach(RDADDR, 0); } rdattach(addr, unit) struct rddevice *addr; { if (unit == 0) { RDADDR = addr; return(1); } return(0); } rdopen(dev) { register int cnt, *ptr, tmp; if (rd_softc.sc_stat & OPEN) return; rd_softc.sc_stat |= OPEN; rdinit(); if (!(rd_softc.sc_stat & GOTBAD)) { while (1) { RDADDR->sec = 0; RDADDR->trk = rdst[rd_softc.sc_type].ntrak-1; RDADDR->cyl = rdst[rd_softc.sc_type].ncyl; RDADDR->csr = RD_READCOM; while (RDADDR->st & RD_BUSY) ; if (RDADDR->csr & RD_ERROR) { if (RDADDR->err & RD_IDNF) rd_softc.sc_type++; rdinit(); } else { #ifdef BADSECT ptr = &rdbad; #endif BADSECT for (cnt = 0; cnt < 256; cnt++) { while ((RDADDR->st & RD_DRQ) == 0) ; #ifdef BADSECT if (cnt < ((sizeof rdbad)/2)) *ptr++ = RDADDR->db; else #endif BADSECT tmp = RDADDR->db; } break; } } rd_softc.sc_stat |= GOTBAD; } } rdstrategy(bp) register struct buf *bp; { register struct buf *dp; register struct rdst *st; register int unit; int s; long bn; long sz; unit = minor(bp->b_dev)&07; sz = bp->b_bcount; sz = (sz+511)>>9; st = &rdst[rd_softc.sc_type]; if (unit >= (NRD<<3) || (RDADDR == (struct rddevice *) NULL)) { bp->b_error = ENXIO; goto errexit; } if (bp->b_blkno < 0 || ((bn = dkblock(bp))+sz > rd_sizes[unit].nblocks && rd_sizes[unit].nblocks >= 0)) { bp->b_error = EINVAL; errexit: bp->b_flags |= B_ERROR; iodone(bp); return; } bp->b_cylin = bn/st->nspc+rd_sizes[unit].cyloff; dp = &rdtab; (void) _spl4(); disksort(dp, bp); if (dp->b_active == NULL) rdstart(); (void) _spl0(); } rdstart() { register struct buf *bp; register int unit; if ((bp = rdtab.b_actf) == NULL) return; bp->b_resid = bp->b_bcount; rdstrt(dkblock(bp), bp); #ifdef RD_DKN dk_busy |= 1<st & RD_BUSY) ; RDADDR->cyl = bn/(st->nspc)+rd_sizes[minor(bp->b_dev)&07].cyloff; sn = bn % st->nspc; RDADDR->trk = sn / st->nsect; RDADDR->sec = sn % st->nsect; if (bp->b_flags & B_READ) { ienable(IVEC(RDADDR, APOS)); ienable(IVEC(RDADDR, BPOS)); RDADDR->csr = RD_READCOM; } else { /* Kernel seg. 5 is used to map the i/o buffer into kernel * virtual so that the copy loop can be performed. */ segm save; xfer = (bp->b_resid < 01000)?bp->b_resid:01000; taddr = (((((long)bp->b_xmem)<<16)&0x3f0000l)| (((long)bp->b_un.b_addr)&0xffff))+ (bp->b_bcount-bp->b_resid); ptr = (char *)((taddr & 077)+SEG5); ienable(IVEC(RDADDR, APOS)); RDADDR->csr = RD_WRITECOM; saveseg5(save); mapseg5(((caddr_t)(taddr>>6)), 04406); for (cnt = 0; cnt < 0400; cnt++) { while ((RDADDR->st & RD_DRQ) == 0) ; if (cnt < (xfer/02)) { rdval.byte[0] = *ptr++; rdval.byte[1] = *ptr++; } RDADDR->db = rdval.word; } restorseg5(save); } } /* This interrupt service routine is accessed via. both the A and B vectors */ rdintr() { register struct buf *bp; register u_short cnt; register char *ptr; long taddr; daddr_t bn; u_short xfer; if (rdtab.b_active == NULL) { idisable(IVEC(RDADDR, APOS)); idisable(IVEC(RDADDR, BPOS)); return; } #ifdef RD_DKN dk_busy &= ~(1<st & RD_BUSY) ; if ((RDADDR->csr & (RD_WFAULT|RD_ERROR)) #ifdef BADSECT || (RDADDR->sec & 0177400) #endif ) { #ifdef BADSECT if ((RDADDR->err & (RD_DMNF|RD_IDNF|RD_CRC)) || (RDADDR->sec & 0177400)) { idisable(IVEC(RDADDR, APOS)); idisable(IVEC(RDADDR, BPOS)); if (baderr(bp)) { return; } } #endif if (++rdtab.b_errcnt <= 10) { rdinit(); bn = dkblock(bp)+((bp->b_bcount-bp->b_resid)>>9); idisable(IVEC(RDADDR, APOS)); idisable(IVEC(RDADDR, BPOS)); rdstrt(bn, bp); return; } #ifdef UCB_DEVERR harderr(bp, "rd"); printf("cs=%b er=%b\n",RDADDR->csr,RDCS_BITS,RDADDR->err,RDER_BITS); #else deverror(bp, RDADDR->csr, RDADDR->err); #endif rdinit(); bp->b_flags |= B_ERROR; rdtab.b_errcnt = 0; rdtab.b_actf = bp->av_forw; bp->b_resid = 0; iodone(bp); idisable(IVEC(RDADDR, APOS)); idisable(IVEC(RDADDR, BPOS)); rdstart(); return; } xfer = (bp->b_resid < 01000)?bp->b_resid:01000; if (bp->b_flags & B_READ) { segm save; taddr = (((((long)bp->b_xmem)<<16)&0x3f0000l)| (((long)bp->b_un.b_addr)&0xffff))+ (bp->b_bcount-bp->b_resid); ptr = (char *)((taddr & 077)+SEG5); saveseg5(save); mapseg5(((caddr_t)(taddr>>6)), 04406); for (cnt = 0; cnt < 0400; cnt++) { while ((RDADDR->st & RD_DRQ) == 0) ; rdval.word = RDADDR->db; if (cnt < (xfer/02)) { *ptr++ = rdval.byte[0]; *ptr++ = rdval.byte[1]; } } restorseg5(save); } rdtab.b_errcnt = 0; bp->b_resid -= xfer; if (bp->b_resid == 0) { rdtab.b_actf = bp->av_forw; iodone(bp); idisable(IVEC(RDADDR, APOS)); idisable(IVEC(RDADDR, BPOS)); rdstart(); return; } bn = dkblock(bp)+((bp->b_bcount-bp->b_resid)>>9); idisable(IVEC(RDADDR, APOS)); idisable(IVEC(RDADDR, BPOS)); rdstrt(bn, bp); } rdinit() { RDADDR->st = RD_INIT; while (RDADDR->st & RD_BUSY) ; RDADDR->csr = RD_RESTORE; while ((RDADDR->st & RD_BUSY) || (!(RDADDR->st & RD_OPENDED))) ; } rdread(dev) { physio(rdstrategy, &rrdbuf, dev, B_READ); } rdwrite(dev) { physio(rdstrategy, &rrdbuf, dev, B_WRITE); } #ifdef BADSECT baderr(bp) struct buf *bp; { register int sn,cn; register struct rdst *st; int tn; daddr_t bn, isbad(); st = &rdst[rd_softc.sc_type]; bn = dkblock(bp) + ((bp->b_bcount-bp->b_resid)>>9)+ rd_sizes[(minor(bp->b_dev)&07)].cyloff; cn = bn/(st->nspc); sn = bn%(st->nspc); tn = sn/(st->nsect); sn %= (st->nsect); if ((bn = isbad(&rdbad, cn, tn, sn)) < 0) return(0); cn = st->ncyl-rd_sizes[(minor(bp->b_dev)&07)].cyloff; bn = cn*st->nspc+st->nsect*(st->ntrak-1)-1-bn; rdstrt(bn, bp); return(1); } #endif #ifdef RD_DUMP /* This is the RD50/51 dump routine. It uses mapin/mapout to get * at all of memory. */ rddump(dev) dev_t dev; { register int *ptr, cnt; register struct rdst *st = &rdst[rd_softc.sc_type]; daddr_t bn, dumpsize; long paddr; int sn, unit; segm save; unit = minor(dev) >> 3; if ((bdevsw[major(dev)].d_strategy != rdstrategy) || unit >= NRD) return(EINVAL); dumpsize = rd_sizes[minor(dev)&07].nblocks; if ((dumplo < 0) || (dumplo >= dumpsize)) return(EINVAL); dumpsize -= dumplo; /* Initialize the controller/drive */ rdinit(); /* Loop until dump complete */ for (paddr = 0L; dumpsize > 0; dumpsize--) { bn = dumplo + (paddr>>PGSHIFT); RDADDR->cyl = (bn/st->nspc) + rd_sizes[minor(dev)&07].cyloff; sn = bn%st->nspc; RDADDR->trk = sn/st->nsect; RDADDR->sec = sn%st->nsect; RDADDR->csr = RD_WRITECOM; ptr = (int *)((paddr & 077)+SEG5); saveseg5(save); mapseg5(((caddr_t)(paddr>>6)), 04406); for (cnt = 0; cnt < 256; cnt++) { while ((RDADDR->st & RD_DRQ) == 0) ; RDADDR->db = *ptr++; } restorseg5(save); while ((RDADDR->st & RD_BUSY) || (!(RDADDR->st & RD_OPENDED))) ; if ((RDADDR->csr & (RD_WFAULT|RD_ERROR)) return(EIO); paddr += (1<