Interdata_v6/usr/sys/msm.c
#
#include "param.h"
#include "buf.h"
#include "conf.h"
#include "selch.h"
/*
* 67-mb 'MSM' disk driver
*/
/* Configuration */
#define NDSK 2 /* number of disks */
/* device addresses */
#define NSELCH 0 /* connected to selch 1 */
int msmcntl 0xfb; /* disk controller */
char msmaddr[NDSK] { /* disk drives */
0xfc,
0xfd
};
int msmseek(), msmscintr();
struct devtab msmtab;
struct buf rmsmbuf;
struct selchq msmscq {
&msmseek,
&msmscintr,
0
};
int msmdrive;
int msmcyl;
int msmhead;
int msmsector;
int msmsad, msmead;
int msmseekf;
int msmqlen; /* current length of i/o queue
(for display only */
#define NBPC (32*5) /* blocks per cylinder */
#define NBPT 32 /* blocks per track */
#define NDSKERR 10 /* number of error retries */
/* logical device mapping */
struct dskmap {
int dm_baddr; /* block offset */
int dm_nblk; /* number of blocks */
} msmap[] {
0, 60*NBPC,
60*NBPC, 60*NBPC,
120*NBPC, 702*NBPC,
0, 0,
0, 0,
0, 0,
0, 0,
0, 822*NBPC
};
/* disk drive status & commands */
#define BSY 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
/* disk controller status & commands */
#define CNTL_UNRCV 0xc1
#define CYL_OV 0x10
#define IDLE 0x02
#define READ 0x01
#define WRITE 0x02
#define RESET 0x08
/*
* disk strategy routine
*/
msmstrategy(abp)
{
register struct buf *bp;
bp = abp;
bp->b_resid = bp->b_bcount;
if ((bp->b_dev.d_minor>>3) >= NDSK) {
bp->b_flags =| B_ERROR;
iodone(bp);
return;
}
/*
* Block no. too high -- looks like EOF for raw read, error otherwise
*/
if (bp->b_blkno >= msmap[bp->b_dev.d_minor&07].dm_nblk) {
if ((bp->b_flags&(B_PHYS|B_READ)) != (B_PHYS|B_READ))
bp->b_flags =| B_ERROR;
iodone(bp);
return;
}
bp->av_forw = 0;
spl(5);
msmqlen = (msmqlen<<1) | 01;
if (msmtab.d_actf == 0)
msmtab.d_actf = bp;
else
msmtab.d_actl->av_forw = bp;
msmtab.d_actl = bp;
if (msmtab.d_active == 0)
msmstart();
spl(0);
}
/*
* start next disk i/o operation
* - set up drive address, cylinder/head/sector address
* - set up memory start & end addresses
* - initiate seek
*/
msmstart()
{
register struct buf *bp;
register stat;
register bn;
if (!(bp = msmtab.d_actf))
return;
msmtab.d_active++;
trace(010<<16, "mstart", bp);
msmdrive = msmaddr[bp->b_dev.d_minor>>3];
bn = bp->b_blkno + msmap[bp->b_dev.d_minor&07].dm_baddr;
msmcyl = bn / NBPC;
bn =% NBPC;
msmhead = bn / NBPT;
msmsector = (bn % NBPT)<<1;
msmsad = bp->b_addr;
msmead = bp->b_addr + bp->b_bcount - 1;
selchreq(NSELCH, &msmscq);
}
msmseek()
{
register stat;
trace(010<<16, "seek", msmdrive);
trace(010<<16, "cyl", msmcyl);
msmseekf++;
wh(msmdrive, msmcyl);
oc(msmdrive, SETCYL|DISARM);
oc(msmdrive, SEEK|ENABLE);
}
/*
* disk interrupt routine
* -disk interrupts only after a seek (I hope)
* -check status, initiate read/write
*/
msmintr(dev,stat)
{
register struct buf *bp;
register scmd, ccmd;
trace(020<<16, "interrupt", dev);
trace(020<<16, "status", stat);
if (!(bp = msmtab.d_actf) || !msmseekf)
return;
msmseekf = 0;
if (stat & (BSY|UNS|UNREADY|SINC|OFFL)) {
msmerror(bp, stat, msmdrive);
return;
}
if (bp->b_flags & B_READ) {
scmd = READ_GO;
ccmd = READ|ENABLE;
} else {
scmd = GO;
ccmd = WRITE|ENABLE;
}
trace(010<<16, "mcmd", ccmd);
trace(010<<16, "head", msmhead);
trace(010<<16, "sector", msmsector);
oc(selchaddr[NSELCH], STOP);
trace(010<<16, "bufstart", msmsad);
trace(010<<16, "bufend", msmead);
wdh(selchaddr[NSELCH], msmsad);
wdh(selchaddr[NSELCH], msmead);
wh(msmdrive, msmhead);
oc(msmdrive, SETHEAD|DISARM);
while ((ss(msmcntl)&IDLE) == 0)
;
wd(msmcntl, msmsector);
wh(msmcntl, (msmhead<<10)+msmcyl);
wh(msmdrive, msmhead);
oc(msmdrive, SETHEAD|DISARM);
while ((ss(msmcntl)&IDLE) == 0)
;
oc(msmcntl, ccmd);
oc(selchaddr[NSELCH], scmd);
}
/*
* selch interrupt routine
* -selch interrupt will be followed by a controller interrupt
* ( nothing to do here? )
*/
msmscintr(dev, stat)
{
register struct buf *bp;
oc(selchaddr[NSELCH], STOP);
}
/*
* disk controller interrupt routine
* -check ending status, signal i/o done
*/
msmcintr(dev, stat)
{
register struct buf *bp;
register struct dskmap *dm;
trace(020<<16, "interrupt", dev);
trace(020<<16, "status", stat);
if (!(bp = msmtab.d_actf))
return;
if (msmseekf)
return;
if (stat & CNTL_UNRCV) {
oc(msmcntl, RESET);
msmerror(bp, stat, msmcntl);
return;
}
bp->b_resid = 0;
/*
* Cylinder overflow
*/
if (stat & CYL_OV) {
oc(msmcntl, RESET);
oc(selchaddr[NSELCH], STOP);
msmsad =+ (rdh(selchaddr[NSELCH])-msmsad+2)&~0377;
trace(020<<16, "cyl ov", msmsad);
msmsector = msmhead = 0;
bp->b_resid = msmead - msmsad;
dm = &msmap[bp->b_dev.d_minor&07];
if ((++msmcyl*NBPC) < dm->dm_baddr + dm->dm_nblk) {
msmseek();
return;
} else
if ((bp->b_flags&(B_PHYS|B_READ)) != (B_PHYS|B_READ))
bp->b_flags =| B_ERROR;
}
msmtab.d_errcnt = 0;
msmtab.d_active = 0;
msmtab.d_actf = bp->av_forw;
msmqlen =>> 1;
iodone(bp);
selchfree(NSELCH);
msmstart();
}
msmerror(abp, stat, addr)
struct buf *abp;
{
register struct buf *bp;
bp = abp;
deverror(bp, stat, addr);
if (++msmtab.d_errcnt <= NDSKERR) {
msmseek();
return;
}
msmtab.d_errcnt = 0;
bp->b_flags =| B_ERROR;
msmtab.d_active = 0;
msmtab.d_actf = bp->av_forw;
msmqlen =>> 1;
iodone(bp);
selchfree(NSELCH);
msmstart();
}
/*
* 'Raw' disc interface
*/
msmread(dev)
{
physio(msmstrategy, &rmsmbuf, dev, B_READ);
}
msmwrite(dev)
{
physio(msmstrategy, &rmsmbuf, dev, B_WRITE);
}