/* * 'SMD' disk driver - 67MB, 256MB, all CDD's, and the ITDC */ /* * This driver now supports all of Perkin-Elmer's current disc drives. * Overlapping seeks are supported. * UCB_BHASH must be defined to use this driver, else bio.c requires * modification. */ #include "../h/local.h" #ifndef NO_SCCS_ID static char SCCS_ID [] = "@(#)mmsm.c 3.5 16:20:19 - 83/04/13 "; #endif NO_SCCS_ID #include "../h/param.h" #include "../h/buf.h" #include "../h/conf.h" #include "../h/selch.h" #include "../h/systm.h" #include "../h/dskmap.h" #ifdef MONITORING #include "../h/sysmon.h" /* TEK-1 */ #endif MONITORING #ifdef OVERLAP int overlap = 1; #endif OVERLAP #define NBPT (msmst[msmtype[unit]].nbpt) /* physical 256-byte blocks per track */ #define NBPC (msmst[msmtype[unit]].nbpc) /* 256-byte blocks per cylinder */ #define OFFSET (msmst[msmtype[unit]].offset) /* head offset for cdd disc */ #define CDDTYP (msmst[msmtype[unit]].overlap) /* controler tpye 0=msm 1=idc*/ #define MAXMSM 16 /* max number of drives */ extern int nmsm; /* number of units (spindles) */ /* one of these for each unit: */ extern char msmtype[]; /* D67, D256, etc. */ extern int msmselch[]; /* selch address for MSM */ extern char msmaddr[]; /* drive addresses */ extern char msmcntl[]; /* controller address */ extern char msmmap[]; /* layout number */ extern char msmcntlnum[]; /* controller number: 0, 1, 2, etc. */ extern struct dskmap msmlayout[][8]; /* logical device mapping */ extern char devmap[]; extern char msmspindle[]; /* real spindle for logical dish */ int msmseek(), msmscintr(), msmio(); /* one of each of these for each controller number */ struct selchq msmscq[MAXMSM] { { &msmseek, &msmscintr, 0, 0 }, { &msmseek, &msmscintr, 0, 0 }, { &msmseek, &msmscintr, 0, 0 }, { &msmseek, &msmscintr, 0, 0 }, { &msmseek, &msmscintr, 0, 0 }, { &msmseek, &msmscintr, 0, 0 }, { &msmseek, &msmscintr, 0, 0 }, { &msmseek, &msmscintr, 0, 0 }, { &msmseek, &msmscintr, 0, 0 }, { &msmseek, &msmscintr, 0, 0 }, { &msmseek, &msmscintr, 0, 0 }, { &msmseek, &msmscintr, 0, 0 }, { &msmseek, &msmscintr, 0, 0 }, { &msmseek, &msmscintr, 0, 0 }, { &msmseek, &msmscintr, 0, 0 }, { &msmseek, &msmscintr, 0, 0 }, }; struct buf msmtab[MAXMSM]; struct buf rmsmbuf[MAXMSM]; int msmcyl[MAXMSM]; int msmhead[MAXMSM]; int msmsector[MAXMSM]; int msmsad[MAXMSM]; int msmead[MAXMSM]; int bn[MAXMSM]; int msmcurcyl[MAXMSM]; int msmnumsec[MAXMSM]; int msmctltab[MAXMSM]; int msm4eerr[MAXMSM]; int msmanyerr[MAXMSM]; int msmstat[MAXMSM]; int msmlstun[MAXMSM]; struct msmst { char nbpt; /* 256-byte blocks per track */ char ntrak; /* tracks per cylinder */ short nbpc; /* 256-byte blocks per cylinder */ short ncyl; /* number of cylinders */ char offset; /* head offset */ char type; /* msm type */ char overlap; /* controler type */ } msmst[] = { 64, 5, 64*5, 823, 0, 0, 1, /* D67 */ 64, 19, 64*19, 823, 0, 1, 1, /* D256 */ 64, 1, 64*1, 823, 0, 2, 0, /* C13R CDD removable */ 64, 1, 64*1, 823, 0x10, 3, 0, /* C13F CDD fixed */ 64, 3, 64*3, 823, 0x10, 4, 0, /* C40F CDD fixed */ 64, 5, 64*5, 823, 0x10, 5, 0, /* C67F CDD fixed */ 59, 5, 59*5, 815, 0, 6, 1, /* ITDC */ }; /* disk drive status & commands */ #define ALTBSY 0x20 #define UNS 0x10 #define UNREADY 0x08 #define SINC 0x02 #define OFFL 0x01 #define DISABLE 0x80 #define ENABLE 0x40 #define DISARM 0xc0 #define SETHEAD 0x20 #define SETCYL 0x10 #define SEEK 0x02 #define RELEASE 0xb0 /* disk controller status & commands */ #define CNTL_UNRCV 0xc1 #define CYL_OV 0x10 #define IDLE 0x02 #define READ 0x01 #define WRITE 0x02 #define RESET 0x08 #define NMSMERR 10 int errthres = -1; int cddlast = 0,cdddelay = 3000; /* * Status in msmtab.b_active */ #define WSELCH 1 /* waiting for selch */ #define WALT 2 /* waiting for alternate channel release */ #define WSEEK 3 /* waiting for seek to complete */ #define WIO 4 /* waiting for I/O to complete */ /* * disk strategy routine */ msmstrategy(bp) register struct buf *bp; { register int unit; #ifdef MONITORING extern struct buf swbuf1 , swbuf2; register *p1,*p2; #endif MONITORING bp->b_resid = bp->b_bcount; unit = (minor((bp)->b_dev) >> 3); if (unit >= nmsm ){ bp->b_flags |= B_ERROR; iodone(bp); return; } /* * Block no. too high -- looks like EOF for raw read, error otherwise */ if (bp->b_blkno >= msmlayout[msmmap[unit]][minor(bp->b_dev)&07].dm_nblk) { if ((bp->b_flags&(B_PHYS|B_READ)) != (B_PHYS|B_READ)) bp->b_flags |= B_ERROR; iodone(bp); return; } if (bp->b_blkno+bp->b_bcount/PGSIZE > msmlayout[msmmap[unit]][minor(bp->b_dev)&07].dm_nblk) { bp->b_resid = bp->b_bcount = (msmlayout[msmmap[unit]][minor(bp->b_dev)&07].dm_nblk - bp->b_blkno)*PGSIZE; } if( unit < 4 ){ dk_numb[unit]++; dk_wds[unit] += (bp->b_bcount>>6); } bp->av_forw = 0; #ifdef MONITORING if((bp == &swbuf1) || (bp == &swbuf2)) p1 = &sysmon.swapmon; else p1 = &sysmon.hpmon; p1->access[unit]++; p1->sizes += ( bp->b_resid + PGSIZE -1 ) >> PGSHIFT; #endif MONITORING spl5(); if (msmtab[msmspindle[unit]].b_actf == 0) msmtab[msmspindle[unit]].b_actf = bp; else msmtab[msmspindle[unit]].b_actl->av_forw = bp; msmtab[msmspindle[unit]].b_actl = bp; if (msmtab[msmspindle[unit]].b_active == 0) msmstart(unit); spl0(); } /* * start next disk i/o operation * - set up drive address, cylinder/head/sector address * - set up memory start & end addresses * - initiate seek */ msmstart(unit) register int unit; { register struct buf *bp; register stat; #ifdef MONITORING extern struct buf swbuf1,swbuf2; register struct dkmon *p1; register int *p2; #endif MONITORING if (!(bp = msmtab[msmspindle[unit]].b_actf)) return; #ifdef MONITORING if((bp == &swbuf1) || (bp == &swbuf2)) p1 = &sysmon.swapmon; else p1 = &sysmon.hpmon; #endif MONITORING msmtab[msmspindle[unit]].b_active = WSELCH; if( unit < 4 ) dk_busy |= 1<b_blkno + msmlayout[msmmap[unit]][minor(bp->b_dev)&07].dm_baddr; #ifdef MONITORING msmcurcyl[unit] = msmcyl[unit]; #endif MONITORING /* * We are doing Address calculation here in terms of * 256 byte blocks on the disc. However UNIX wants * I/O in terms of 512 byte blocks, so the bn[unit] is * terms of 512 byte blocks. */ /* We will locate the address on the disc by first * finding the cylinder and the track and the sector * and the head inside the cylinder associated with * this data transfer operation. */ #ifdef DEBUG /* printf("Block Number requested: %d\n",bn[unit]); printf("OLD ADDRESS: cyl=%d head %d sector %d\n",bn[unit]/(NBPC/2), (bn[unit]%(NBPC/2))/(NBPT/2), ((bn[unit]%(NBPC/2))%(NBPT/2))<< 1); */ #endif bn[unit] *= 2 ; /* Convert to 256 byte block request */ msmcyl[unit] = bn[unit] / NBPC; bn[unit] %= NBPC; msmhead[unit] = (bn[unit] / NBPT) | OFFSET; msmsector[unit] = ((bn[unit]) % NBPT); #ifdef DEBUG /* * Error Check for the itdc to * See if we are getting bad head * address */ if ( msmtype[unit] == 6 && msmhead[unit] > 4 ){ printf("Bad Head: bloc=%d, sector=%d cyl=%d head=%d\n", bn[unit],msmsector[unit],msmcyl[unit],msmhead[unit]); bp->b_flags |= B_ERROR ; iodone(bp); return; } #endif msmnumsec[unit] = msmsector[unit]; msmsad[unit] = bp->b_un.b_addr; msmead[unit] = bp->b_un.b_addr + bp->b_bcount - 1; #ifdef MONITORING if( msmnumsec[unit] = msmcyl[unit] - msmcurcyl[unit]){ p1->nseeks[unit]++; p1->sseeks[unit] += msmnumsec[unit] < 0 ? -msmnumsec[unit] : msmnumsec[unit]; } #endif MONITORING msmscq[unit].sq_sstart = &msmseek; selchreq(msmselch[unit], &msmscq[unit], unit); } msmseek(unit) register int unit; { register stat; while ((ss(msmcntl[unit])&IDLE) == 0) ; ss(msmaddr[unit]); ss(msmaddr[unit]); if ((stat = ss(msmaddr[unit]))&ALTBSY) { msmtab[msmspindle[unit]].b_active = WALT; oc(msmaddr[unit], ENABLE); selchfree(msmselch[unit]); return; } devmap[msmaddr[unit]] = unit; msmtab[msmspindle[unit]].b_active = WSEEK; wh(msmaddr[unit], msmcyl[unit]); while (((stat=ss(msmcntl[unit]))&IDLE) == 0) ; oc(msmaddr[unit], SETCYL|DISARM); while (((stat=ss(msmcntl[unit]))&IDLE) == 0) ; oc(msmaddr[unit], SEEK|ENABLE); while (((stat=ss(msmcntl[unit]))&IDLE) == 0) ; ss(msmaddr[unit]); ss(msmaddr[unit]); #ifdef OVERLAP selchfree(msmselch[unit]); #endif OVERLAP } /* * disk interrupt routine * -disk interrupts after a seek, or when Alternate Channel Busy clears * -check status, initiate read/write or seek */ msmintr(unit,stat) register char unit; { register struct buf *bp; register scmd, ccmd; register i; msmstat[unit] = stat; if (!(bp = msmtab[msmspindle[unit]].b_actf)) return; switch (msmtab[msmspindle[unit]].b_active) { case WALT: msmtab[msmspindle[unit]].b_active = WSELCH; msmscq[unit].sq_sstart = &msmseek; selchreq(msmselch[unit], &msmscq[unit], unit); return; case WSEEK: #ifdef OVERLAP msmscq[unit].sq_sstart = &msmio; selchreq(msmselch[unit], &msmscq[unit], unit); return; #endif OVERLAP break; default: return; } msmio(unit); } msmio(unit) register unit; { register struct buf *bp; register scmd, ccmd; register stat,i; msmtab[msmspindle[unit]].b_active = WIO; if (!(bp = msmtab[msmspindle[unit]].b_actf)) return; stat = msmstat[unit]; if (stat & (UNS|UNREADY|SINC|OFFL|ALTBSY)) { msmerror(bp, stat, msmaddr[unit]); return; } if (bp->b_flags & B_READ) { scmd = READ_GO; ccmd = READ|ENABLE; } else { scmd = GO; ccmd = WRITE|ENABLE; } oc(msmselch[unit], STOP); wdh(msmselch[unit], msmsad[unit]); wdh(msmselch[unit], msmead[unit]); wh(msmaddr[unit], msmhead[unit]); wh(msmaddr[unit], msmhead[unit]); oc(msmaddr[unit], SETHEAD|DISARM); while ((ss(msmcntl[unit])&IDLE) == 0) ; wd(msmcntl[unit], msmsector[unit]); wh(msmcntl[unit], ((msmhead[unit])<<10)+msmcyl[unit]); wh(msmaddr[unit], msmhead[unit]); wh(msmaddr[unit], msmhead[unit]); oc(msmaddr[unit], SETHEAD|DISARM); if( CDDTYP == 0 && msmlstun[msmspindle[unit]] != unit ){ msmlstun[msmspindle[unit]] = unit; for( i = 0; i < cdddelay; i++ ) ; } while ((ss(msmcntl[unit])&IDLE) == 0) ; oc(msmcntl[unit], ccmd); oc(msmselch[unit], scmd); msmctltab[devmap[msmcntl[unit]]] = unit; } /* * selch interrupt routine * -selch interrupt will be followed by a controller interrupt * ( nothing to do here? ) */ msmscintr(dev, stat, unit) register int unit; { oc(msmselch[unit], STOP); } /* * disk controller interrupt routine * -check ending status, signal i/o done */ msmcintr(unit, stat) register char unit; register int stat; { register struct buf *bp; register struct dskmap *dm; #ifdef MONITORING extern struct buf swbuf1,swbuf2; register char *p1; register int savetime; #endif MONITORING unit = msmctltab[unit]; if (!(bp = msmtab[msmspindle[unit]].b_actf)) return; if (msmtab[msmspindle[unit]].b_active != WIO) return; #ifdef MONITORING if((bp == &swbuf1) || (bp == &swbuf2)) p1 = &sysmon.swapmon; else p1 = &sysmon.hpmon; #endif MONITORING if (stat & CNTL_UNRCV) { oc(msmcntl[unit], RESET); msmerror(bp, stat, msmcntl[unit]); return; } bp->b_resid = 0; #ifdef MONITORING savetime = time; savetime =- bp->b_error; p1->atimes += savetime < 0 ? -savetime : savetime; #endif MONITORING /* * Cylinder overflow */ if (stat & CYL_OV) { oc(msmcntl[unit], RESET); oc(msmselch[unit], STOP); msmsad[unit] += (rdh(msmselch[unit])-msmsad[unit]+2)&~0377; msmsector[unit] = msmhead[unit] = 0; msmhead[unit] |= OFFSET; bp->b_resid = msmead[unit] - msmsad[unit]; dm = &msmlayout[msmmap[unit]][minor(bp->b_dev)&07]; if (++msmcyl[unit]*(NBPC/2) < dm->dm_baddr + dm->dm_nblk) { #ifdef DEBUG /* * * For cylinder overlow in an ITDC */ if(msmtype[unit] == 6) printf("Cylinder Overflow on the ITDC\n"); #endif msmseek(unit); return; } else if ((bp->b_flags&(B_PHYS|B_READ)) != (B_PHYS|B_READ)) bp->b_flags |= B_ERROR; } oc(msmaddr[unit], RELEASE); /*** This sense status is required to fully release the drive when operating with more than one drive on a single controller. ***/ ss(msmaddr[unit]); ss(msmaddr[unit]); while ((ss(msmcntl[unit])&IDLE) == 0) ; msmtab[msmspindle[unit]].b_errcnt = 0; msmtab[msmspindle[unit]].b_active = 0; if( unit < 4 ) dk_busy &= ~(1<av_forw; iodone(bp); selchfree(msmselch[unit]); msmstart(unit); return; bad: bp->b_flags |= B_ERROR; iodone(bp); return; } msmerror(bp, stat, addr) register struct buf *bp; { register int unit; unit = (minor((bp)->b_dev) >> 3); if( msmtab[msmspindle[unit]].b_errcnt > errthres ){ deverror(bp, stat, addr); if( stat == 0x4E ) ++msm4eerr[unit]; } if( stat != 0x4e ) ++msmanyerr[unit]; if (++msmtab[msmspindle[unit]].b_errcnt <= NMSMERR) { oc(msmaddr[unit], RELEASE); /*** See comment above on ss after release ***/ ss(msmaddr[unit]); ss(msmaddr[unit]); while ((ss(msmcntl[unit])&IDLE) == 0) ; msmseek(unit); return; } oc(msmaddr[unit], RELEASE); /*** See comment above on ss after release ***/ ss(msmaddr[unit]); ss(msmaddr[unit]); while ((ss(msmcntl[unit])&IDLE) == 0) ; msmtab[msmspindle[unit]].b_errcnt = 0; bp->b_flags |= B_ERROR; msmtab[msmspindle[unit]].b_active = 0; dk_busy &= ~(1<av_forw; iodone(bp); selchfree(msmselch[unit]); msmstart(unit); } /* * 'Raw' disc interface */ msmread(dev) char dev; { register int unit; physio(msmstrategy, &rmsmbuf[msmspindle[minor(dev) >> 3]], dev, B_READ); } msmwrite(dev) char dev; { register int unit; physio(msmstrategy, &rmsmbuf[msmspindle[minor(dev) >> 3]], dev, B_WRITE); }