/* * 'MSM1' disk driver - 67MB and 256MB */ #include "../h/local.h" #ifdef SCCS_ID static char SCCS_ID [] = "@(#)msm1.c 3.1 17:30:22 - 81/11/18 "; #endif SCCS_ID #include "../h/param.h" #undef trace #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 #define DN 1 /* disk number for monitoring */ #define NBPT 32 /* blocks per track */ #define NBPC (msm1size? 608 : 160) /* blocks per cylinder */ #define NMSM1ERR 10 /* number of error retries */ extern int nmsm1; /* number of disks */ extern int msm1selch; /* selch address for MSM1 */ extern int msm1cntl; /* controller address */ extern char msm1addr[]; /* drive addresses */ extern int msm1size; /* 0 => 67MB, 1 => 256MB */ extern struct dskmap msm1map[]; /* logical device mapping */ int msm1seek(), msm1scintr(); struct buf msm1tab; struct buf rmsm1buf; struct selchq msm1scq { &msm1seek, &msm1scintr, 0 }; int msm1drive; int msm1cyl; int msm1head; int msm1sector; int msm1sad, msm1ead; /* 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 /* * Status in msm1tab.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 */ msm1strategy(bp) register struct buf *bp; { #ifdef MONITORING extern struct buf swbuf1 , swbuf2; register *p1,*p2; #endif MONITORING bp->b_resid = bp->b_bcount; if ((minor(bp->b_dev)&07)==1) /* printf("strat blkno=%x count=%x flag=%x\n",bp->b_blkno, btoc(bp->b_bcount), bp->b_flags&(B_READ)); */ if ((minor(bp->b_dev)>>3) >= nmsm1) { bp->b_flags |= B_ERROR; iodone(bp); return; } /* * Block no. too high -- looks like EOF for raw read, error otherwise */ if (bp->b_blkno >= msm1map[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; } dk_numb[DN]++; dk_wds[DN] += (bp->b_bcount>>6); bp->av_forw = 0; #ifdef MONITORING if((bp == &swbuf1) || (bp == &swbuf2)) p1 = &sysmon.swapmon; else p1 = &sysmon.hpmon; p1->access[DN]++; p1->sizes += ( bp->b_resid + PGSIZE -1 ) >> PGSHIFT; #endif MONITORING spl5(); if (msm1tab.b_actf == 0) msm1tab.b_actf = bp; else msm1tab.b_actl->av_forw = bp; msm1tab.b_actl = bp; if (msm1tab.b_active == 0) msm1start(); spl0(); } /* * start next disk i/o operation * - set up drive address, cylinder/head/sector address * - set up memory start & end addresses * - initiate seek */ msm1start() { register struct buf *bp; register stat; register bn; #ifdef MONITORING extern struct buf swbuf1,swbuf2; register struct dkmon *p1; int *p2; int msm1curcyl; int msm1numsec; #endif MONITORING if (!(bp = msm1tab.b_actf)) return; #ifdef MONITORING if((bp == &swbuf1) || (bp == &swbuf2)) p1 = &sysmon.swapmon; else p1 = &sysmon.hpmon; #endif MONITORING msm1tab.b_active = WSELCH; dk_busy |= 1<b_dev)>>3]; bn = bp->b_blkno + msm1map[minor(bp->b_dev)&07].dm_baddr; #ifdef MONITORING msm1curcyl = msm1cyl; #endif MONITORING msm1cyl = bn / NBPC; bn %= NBPC; msm1head = bn / NBPT; if ((minor(bp->b_dev)>>3) == 1) msm1head |= 0x10; msm1sector = (bn % NBPT)<<1; msm1numsec = msm1sector; msm1sad = bp->b_un.b_addr; msm1ead = bp->b_un.b_addr + bp->b_bcount - 1; #ifdef MONITORING if( msm1numsec = msm1cyl - msm1curcyl){ p1->nseeks[DN]++; p1->sseeks[DN] += msm1numsec < 0 ? -msm1numsec : msm1numsec; } #endif MONITORING selchreq(msm1selch, &msm1scq); } msm1seek() { register stat; trace(0x80<<16, "seek", msm1drive); trace(0x80<<16, "cyl", msm1cyl); while ((ss(msm1cntl)&IDLE) == 0) ; ss(msm1drive); ss(msm1drive); if ((stat = ss(msm1drive))&ALTBSY) { trace(0x80<<16, "alt", stat); msm1tab.b_active = WALT; oc(msm1drive, ENABLE); selchfree(msm1selch); return; } trace(0x80<<16, "stat", stat); msm1tab.b_active = WSEEK; wh(msm1drive, msm1cyl); while (((stat=ss(msm1cntl))&IDLE) == 0) ; oc(msm1drive, SETCYL|DISARM); while (((stat=ss(msm1cntl))&IDLE) == 0) ; oc(msm1drive, SEEK|ENABLE); while (((stat=ss(msm1cntl))&IDLE) == 0) ; ss(msm1drive); } /* * disk interrupt routine * -disk interrupts after a seek, or when Alternate Channel Busy clears * -check status, initiate read/write or seek */ msm1intr(dev,stat) { register struct buf *bp; register scmd, ccmd; trace(0x40<<16, "interrupt", msm1addr[dev]); trace(0x40<<16, "status", stat); if (!(bp = msm1tab.b_actf)) return; switch (msm1tab.b_active) { case WALT: msm1tab.b_active = WSELCH; selchreq(msm1selch, &msm1scq); return; case WSEEK: msm1tab.b_active = WIO; break; default: return; } if (stat & (UNS|UNREADY|SINC|OFFL|ALTBSY)) { msm1error(bp, stat, msm1drive); return; } if (bp->b_flags & B_READ) { scmd = READ_GO; ccmd = READ|ENABLE; } else { scmd = GO; ccmd = WRITE|ENABLE; } trace(0x80<<16, "mcmd", ccmd); trace(0x80<<16, "head", msm1head); trace(0x80<<16, "sector", msm1sector); oc(msm1selch, STOP); trace(0x80<<16, "bufstart", msm1sad); trace(0x80<<16, "bufend", msm1ead); oc(msm1cntl, RESET); wdh(msm1selch, msm1sad); wdh(msm1selch, msm1ead); wh(msm1drive, msm1head); oc(msm1drive, SETHEAD|DISARM); while ((ss(msm1cntl)&IDLE) == 0) ; wd(msm1cntl, msm1sector); wh(msm1cntl, ((msm1head ) <<10)+msm1cyl); wh(msm1drive, msm1head); oc(msm1drive, SETHEAD|DISARM); while ((ss(msm1cntl)&IDLE) == 0) ; oc(msm1cntl, ccmd); oc(msm1selch, scmd); } /* * selch interrupt routine * -selch interrupt will be followed by a controller interrupt * ( nothing to do here? ) */ msm1scintr(dev, stat) { register struct buf *bp; trace(0x40<<16, "selch interrupt", msm1selch); oc(msm1selch, STOP); } /* * disk controller interrupt routine * -check ending status, signal i/o done */ msm1cintr(dev, stat) { register struct buf *bp; register struct dskmap *dm; #ifdef MONITORING extern struct buf swbuf1,swbuf2; char *p1; int savetime; #endif MONITORING trace(0x40<<16, "interrupt", msm1cntl); trace(0x40<<16, "status", stat); if (!(bp = msm1tab.b_actf)) return; if (msm1tab.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(msm1cntl, RESET); msm1error(bp, stat, msm1cntl); 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(msm1cntl, RESET); oc(msm1selch, STOP); msm1sad += (rdh(msm1selch)-msm1sad+2)&~0377; trace(0x40<<16, "cyl ov", msm1sad); msm1sector = 0; if ((minor(bp->b_dev)>>3) == 1) msm1head = 0x10; bp->b_resid = msm1ead - msm1sad; dm = &msm1map[minor(bp->b_dev)&07]; if ((++msm1cyl*NBPC) < dm->dm_baddr + dm->dm_nblk) { msm1seek(); return; } else if ((bp->b_flags&(B_PHYS|B_READ)) != (B_PHYS|B_READ)) bp->b_flags |= B_ERROR; } trace(0x80<<16, "release", msm1drive); oc(msm1drive, RELEASE); /*** This sense status is required to fully release the drive when operating with more than one drive on a single controller. ***/ ss(msm1drive); while ((ss(msm1cntl)&IDLE) == 0) ; msm1tab.b_errcnt = 0; msm1tab.b_active = 0; dk_busy &= ~(1<av_forw; iodone(bp); selchfree(msm1selch); msm1start(); } msm1error(bp, stat, addr) register struct buf *bp; { deverror(bp, stat, addr); if (++msm1tab.b_errcnt <= NMSM1ERR) { msm1seek(); return; } trace(0x80<<16, "release", msm1drive); oc(msm1drive, RELEASE); /*** See comment above on ss after release ***/ ss(msm1drive); while ((ss(msm1cntl)&IDLE) == 0) ; msm1tab.b_errcnt = 0; bp->b_flags |= B_ERROR; msm1tab.b_active = 0; dk_busy &= ~(1<av_forw; iodone(bp); selchfree(msm1selch); msm1start(); } /* * 'Raw' disc interface */ msm1read(dev) { physio(msm1strategy, &rmsm1buf, dev, B_READ); } msm1write(dev) { physio(msm1strategy, &rmsm1buf, dev, B_WRITE); }