#include "../h/local.h" #ifdef SCCS_ID static char SCCS_ID [] = "@(#)d40.c 3.2 14:56:11 - 82/09/10 "; #endif SCCS_ID #include "../h/param.h" #include "../h/buf.h" #include "../h/conf.h" #include "../h/selch.h" #include "../h/dskmap.h" /* * 40-mb disk driver */ extern int nd40; extern char d40addr[]; extern int d40selch; extern int d40cntl; extern struct dskmap d40map[]; #define NDSKERR 10 /* number of error retries */ int d40start(), d40scintr(); struct buf d40tab; struct buf rd40buf; struct selchq d40scq { &d40start, &d40scintr, 0, 0 }; /*** CAUTION: offsets of fields in following struct are not to be tampered with ***/ struct dsk { char dk_active; /* disc active flag */ char dk_errcnt; /* error count */ char dk_prep; /* i/o preparatory flag */ short dk_cyl; /* current cylinder pos'n */ short dk_dir; /* current arm direction */ struct buf *dk_actf; /* head of current i/o queue */ struct buf *av_forw; /* head of pending i/o queue */ } d40[4]; #define drivemap(dev) &d40[minor(dev) >> 3] #define b_dskad b_resid struct dsk_head_locn { /* fields of b_dskad */ short b_cyl; /* cylinder number */ char b_head; /* head number */ char b_sect; /* sector number */ }; #define d40file(bp) d40addr[minor(bp->b_dev)>>3] #define d40cyl(bp) bp->b_dskad.b_cyl #define d40head(bp) bp->b_dskad.b_head #define d40sect(bp) bp->b_dskad.b_sect #define DSEEK 1 #define DIO 2 /* disk file status & commands */ #define WRT_PROT 0x80 #define UNRCV 0x19 #define DSK_JUNK 0x24 /* irrelevant bits */ #define NOT_READY 0x08 #define SEEK 0x42 /* disk controller status & commands */ #define CNTL_UNRCV 0x61 #define CYL_OV 0x10 #define IDLE 0x02 #define WRITE 0x42 #define READ 0x41 #define SETHEAD 0x20 #define SETCYL 0x10 #define RSTGATN 0x08 #define RSTHEAD 0x04 /* * disc size info */ #define SECTORS 20 /* sectors per track */ #define HEADS 20 /* tracks per cylinder */ #define PERTRACK 200 /* # blocks per cylinder */ /* * disk strategy routine */ d40strategy(bp) register struct buf *bp; { register struct dsk *dp; register struct buf *p1, *p2; register int i; bp->b_resid = bp->b_bcount; if(minor(bp->b_dev) >= nd40<<3) { bp->b_flags |= B_ERROR; iodone(bp); return; } /* * Block no. too high -- looks like EOF for raw read, error otherwise */ if(bp->b_blkno >= d40map[minor(bp->b_dev)&7].dm_nblk) { if((bp->b_flags & (B_PHYS|B_READ)) != (B_PHYS|B_READ)) bp->b_flags |= B_ERROR; iodone(bp); return; } /* Calculate cylinder : head : sector */ i = (bp->b_blkno + d40map[minor(bp->b_dev)&07].dm_baddr) * 2; bp->b_dskad.b_sect = i % SECTORS; i /= SECTORS; bp->b_dskad.b_head = i % HEADS; bp->b_dskad.b_cyl = i / HEADS; dp = drivemap(bp->b_dev); spl5(); for(p1 = dp; (p2 = p1->av_forw) && ( (dp->dk_dir) ? (d40cyl(p2) <= d40cyl(bp) ) : (d40cyl(p2) >= d40cyl(bp) ) ); ) p1 = p2; bp->av_forw = p2; p1->av_forw = bp; if(!(dp->dk_active || d40tab.b_active)) selchreq(d40selch, &d40scq, 0); spl0(); } /* * start next disk i/o operation * - check disk status * - initiate seek */ d40start(unit) { register struct dsk *dp, *idp; register struct buf *bp; register int scmd, ccmd, stat, file, ead; idp = 0; if((dp = d40tab.b_actf) == 0) /* get active drive */ d40tab.b_actf = dp = d40; /* if none, start at first */ do { if(++dp >= &d40[4]) dp = d40; /* round-robin */ while(!dp->dk_active) { /* while disc not active */ if(!(bp = dp->dk_actf)) { /* if active q empty */ if(!(bp = dp->av_forw)) /* if pending q empty */ break; else { dp->dk_actf = bp; /* swap queues */ dp->av_forw = 0; dp->dk_dir = ~dp->dk_dir; /* change arm dir'n */ } } file = d40file(bp); stat = ss(file); if(dp->dk_cyl == d40cyl(bp)) { /* seek done */ if(idp) break; if (stat & ~(WRT_PROT|DSK_JUNK)) { if (stat & UNRCV) dp->dk_errcnt = NDSKERR; d40error(dp, stat, file); continue; } if (stat & WRT_PROT && !(bp->b_flags & B_READ)) { dp->dk_errcnt = 0; dp->dk_actf = bp->av_forw; d40tab.b_active = 0; bp->b_flags |= B_ERROR; bp->b_resid = bp->b_bcount; /* ?? */ iodone(bp); continue; } idp = dp; break; } /* seek required */ if (stat & UNRCV) { dp->dk_errcnt = NDSKERR; /* no retries */ d40error(dp, stat, file); continue; } dp->dk_active = DSEEK; wh(file, d40cyl(bp)); oc(file, SETCYL); while(!(ss( d40cntl )&IDLE)) ; d40prep(dp); oc(file, SEEK); break; } } while(dp != d40tab.b_actf); /* start next i/o : */ if(!idp) { selchfree(d40selch); return; } d40tab.b_actf = dp = idp; bp = idp->dk_actf; if(!dp->dk_prep) d40prep(dp); else ss(d40file(bp)); ead = bp->b_un.b_addr+bp->b_bcount-1; if(bp->b_flags & B_READ) { scmd = READ_GO; ccmd = READ; } else { scmd = GO; ccmd = WRITE; } trace(010<<16, "dcmd", ccmd); trace(010<<16, "sector", d40sect(bp)); trace(010<<16, "head", d40head(bp)); oc(d40selch, STOP); trace(010<<16, "bufstart", bp->b_un.b_addr); trace(010<<16, "bufend", ead); wdh(d40selch, bp->b_un.b_addr); wdh(d40selch, ead); wd(d40cntl, d40sect(bp)); wh(d40cntl, (d40head(bp)<<10) | d40cyl(bp)); oc(d40cntl, ccmd); oc(d40selch, scmd); idp->dk_active = DIO; d40tab.b_active++; dp->dk_prep = 0; } /* * disk interrupt routine * -disk interrupts only after a seek (I hope) */ d40intr(dev, stat) { register struct buf *bp; register struct dsk *dp; trace(020<<16, "status", stat); dp = &d40[dev]; if(dp->dk_active != DSEEK) return; trace(020<<16, "interrupt", d40addr[dev]); dp->dk_active = 0; dp->dk_cyl = d40cyl(dp->dk_actf); if(!d40tab.b_active) selchreq(d40selch, &d40scq, 0); } /* * selch interrupt routine * -selch interrupt will be followed by a controller interrupt */ d40scintr(dev, stat, unit) { register struct dsk *dp; if(!(dp = d40tab.b_actf)) return; oc(d40selch, STOP); } /* * disk controller interrupt routine * -on cylinder overflow start i/o on next cylinder * -check ending status, signal i/o done */ d40cintr(dev, stat) { register struct buf *bp; register struct dsk *dp; register i, resid; trace(020<<16, "interrupt", d40cntl); trace(020<<16, "status", stat); d40tab.b_active = 0; if(!(dp = d40tab.b_actf) || dp->dk_active != DIO) return; dp->dk_active = 0; bp = dp->dk_actf; if(stat&CNTL_UNRCV) { d40error(dp, stat, d40cntl); d40start(0); return; } /* * Cylinder overflow */ if(!(stat&CYL_OV)) { bp->b_resid = 0; } else { oc(d40selch, STOP); resid = (rdh(d40selch) - bp->b_un.b_addr + 2) & ~0377; trace(020<<16, "cyl ov", bp->b_un.b_addr); bp->b_un.b_addr += resid; bp->b_bcount -= resid; bp->b_dskad.b_sect = 0; bp->b_dskad.b_head = 0; i = minor(bp->b_dev) & 07; if(++bp->b_dskad.b_cyl < (d40map[i].dm_baddr+d40map[i].dm_nblk)/PERTRACK) { d40start(0); return; } else { if((bp->b_flags & (B_PHYS|B_READ)) != (B_PHYS|B_READ)) bp->b_flags |= B_ERROR; bp->b_resid = resid; } } dp->dk_errcnt = 0; dp->dk_actf = bp->av_forw; d40tab.b_active = 0; iodone(bp); d40start(0); } d40error(dp, stat, addr) register struct dsk *dp; { register struct buf *bp; bp = dp->dk_actf; deverror(bp, stat, addr); dp->dk_cyl = -1; /* force a seek next I/O */ if(++dp->dk_errcnt > NDSKERR) { dp->dk_errcnt = 0; bp->b_flags |= B_ERROR; bp->b_resid = bp->b_bcount; /** ?? needed or not ?? **/ dp->dk_actf = bp->av_forw; iodone(bp); } } /* * 'Raw' disc interface */ d40read(dev) { physio(d40strategy, &rd40buf, dev, B_READ); } d40write(dev) { physio(d40strategy, &rd40buf, dev, B_WRITE); } /* * prepare for I/O - done before seek if any is needed */ d40prep(dp) struct dsk *dp; { register struct buf *bp; register file, cntrl; bp = dp->dk_actf; file = d40file(bp); cntrl = d40cntl; oc(file, RSTGATN); while(!(ss(cntrl)&IDLE)) ; oc(file, RSTHEAD); while(!(ss(cntrl)&IDLE)) ; wh(file, d40head(bp)); oc(file, SETHEAD); while(!(ss(cntrl)&IDLE)) ; dp->dk_prep = 1; }