/* * RX-11 Driver * Peter Collinson May 1977 * Version 1 * Each UNIX block maps into 4 RX sectors, so a READ or * WRITE operation has four actions upon it. The flag d_active * is used to count these phases. * The sectors are usually spaced 6 apart, to minimised access time. But * they may be contiguous by specifying minor device number 8(010) or 9(011) * Modified January 1977 * defines ERRLOG * keeps a global count of retrys per physical device * stores contents of error register for first error */ #define ERRLOG 1 #include "../param.h" #include "../buf.h" #include "../conf.h" #include "../user.h" #define RXADDR 0177170 #define NRXBLK 499 #define NPRX 2 /* Number of physical drives */ struct { int rxcs; int rxbuf; }; #ifdef ERRLOG struct { int rx_retry[NPRX]; int rx_edrive; int rx_esect; int rx_etrk; int rx_estat; /* error status */ } rx_err; #endif /* Various RX interface values */ #define GO 01 #define FILL 0 #define EMPTY 02 #define WRITE 04 #define READ 06 #define DEV1 020 #define DONE 040 #define IENABLE 0100 #define TR 0200 #define INIT 040000 #define INITDONE 04 struct devtab rxtab; int rx_sector 0; /* Current sector being dealt with */ int rx_track 0; /* Current track */ rxopen(dev) int dev; { } rxclose(dev) { bflush(dev); } /* * General strategy routine - deals with buffers and calls rxstart * if nothing happening */ rxstrategy(abp) struct buf *abp; { register struct buf *bp; bp = abp; #ifdef PDP1170 /* 11/70 requires this call */ if(bp->b_flags&B_PHYS) mapalloc(bp); #endif /* Check for block no in range */ if(bp->b_blkno > NRXBLK) { if( !((bp->b_blkno == NRXBLK+1) && (bp->b_dev.d_minor&010))) { bp->b_flags =| B_ERROR; iodone(bp); return; } } /* Link the block into lists */ bp->av_forw = 0; spl5(); if(rxtab.d_actf == 0) rxtab.d_actf = bp; else rxtab.d_actl->av_forw = bp; rxtab.d_actl = bp; if(rxtab.d_active == 0) rxstart(); spl0(); } rxstart() { register struct buf *bp; register int gblk; if((bp = rxtab.d_actf) == 0) return; rxtab.d_active++; /* Now calculate sector and track from the block number */ gblk = (bp->b_blkno<<2); rx_track = gblk/26; rx_sector = (gblk%26) + 1; rxsect(bp); } /* * rxsect initiates I/O for a single sector */ rxsect(abp) struct buf *abp; { register struct buf *bp; bp = abp; if(bp->b_flags&B_READ) rxcom(bp, READ); else { rxmov(bp); /* Move the current 128 bytes into RX */ rxcom(bp, WRITE); /* and write it */ } } /* * rxcom issues a write or read command to RX, only these commands are * allowed to give an interrupt */ rxcom(abp, acom) struct buf *abp; int acom; { register struct buf *bp; register int com; bp = abp; com = acom; com =| GO|IENABLE; /* Check for Unit 1 */ if((bp->b_dev.d_minor&01) == 1) com =| DEV1; RXADDR->rxcs = com; while((RXADDR->rxcs&TR) == 0); RXADDR->rxbuf = rx_sector & 037; while((RXADDR->rxcs&TR) == 0); RXADDR->rxbuf = rx_track & 0177; } /* * rxmov moves 128 bytes from/to the UNIX buffer to/from the RX * this is done at low priority and not using interrupts */ rxmov(abp) struct buf *abp; { register struct buf *bp; register int ct; register char *ch; bp = abp; /* Set up FILL or EMPTY command */ RXADDR->rxcs = GO|((bp->b_flags&B_READ)? EMPTY: FILL); /* Calculate start of character area * = base address + (d_active-1) * 128 */ spl0(); ch = bp->b_addr +((rxtab.d_active-1)<<7); for(ct = 128; ct--; ch++) { while((RXADDR->rxcs&TR) == 0); if(bp->b_flags&B_READ) *ch = RXADDR->rxbuf; else RXADDR->rxbuf = *ch; } /* Wait for done flag to ignore interrupts */ while((RXADDR->rxcs&DONE) == 0); spl5(); } /* * Interrupt routine */ rxintr() { register struct buf *bp; bp = rxtab.d_actf; if(RXADDR->rxcs < 0) /* Test error bit */ { #ifdef ERRLOG rx_err.rx_edrive = (bp->b_dev.d_minor)&03; rx_err.rx_retry[rx_err.rx_edrive]++; if(rxtab.d_errcnt == 0) { rx_err.rx_etrk = rx_track; rx_err.rx_esect = rx_sector; rx_err.rx_estat = RXADDR->rxbuf; /* pick up error status */ } #endif RXADDR->rxcs = INIT; /* Init the device to try again */ while((RXADDR->rxbuf&INITDONE) == 0); if(rxtab.d_errcnt++ < 10) { rxsect(bp); /* try again */ return; } /* No - get out */ bp->b_flags =| B_ERROR; rxtab.d_active = 4; /* to force exit */ } rxtab.d_errcnt = 0; /* No find out what to do next */ if(((bp->b_flags&B_ERROR) == 0) && (bp->b_flags&B_READ)) rxmov(bp); if(rxtab.d_active++ != 4) { rxinc(bp->b_dev.d_minor); rxsect(bp); } else { rxtab.d_active = 0; rxtab.d_actf = bp->av_forw; iodone(bp); rxstart(); } } /* * rxinc increments the global sector and track numbers */ rxinc(minordev) char minordev; { register int ct; ct = (minordev&010) ? 1 : 5; spl0(); while(ct--) { if(rx_sector++ == 26) { rx_sector = 1; if(rx_track++ == 76) { rx_track = 0; rx_sector = 3; /* To avoid discontinuity */ } } } spl5(); }