V7M/sys/dev/rl.c
/*
* UNIX/v7m RL01/2 disk driver
*
* This driver is based on the standard v7 RL driver.
*
* 7/80 - Modified by Armando P. Stettner to support both
* RL01 and RL02 drives on the same controller.
*
* 5/81 - Modified by Fred Canter to improve drive type
* identification and disk block number overflow checking.
*
* 9/81 - Modified by Fred Canter to include the new
* iostat code.
*
* Fred Canter 9/4/81
*/
#include "../h/param.h"
#include "../h/buf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/systm.h"
#define BLKRL1 10240 /* Number of UNIX blocks for an RL01 drive */
#define BLKRL2 20480 /* Number of UNIX blocks for an RL02 drive */
#define RLCYLSZ 10240 /* bytes per cylinder */
#define RLSECSZ 256 /* bytes per sector */
#define RESET 013
#define RL02TYP 0200 /* drive type bit */
#define STAT 03
#define GETSTAT 04
#define WCOM 012
#define RCOM 014
#define SEEK 06
#define SEEKHI 5
#define SEEKLO 1
#define RDHDR 010
#define IENABLE 0100
#define CRDY 0200
#define OPI 02000
#define CRCERR 04000
#define TIMOUT 010000
#define NXM 020000
#define DE 040000
struct device
{
int rlcs; /* RL control & status register */
int rlba; /* RL bus addess register */
int rlda; /* RL disk address register */
int rlmp; /* RL multipurpose register */
};
#define RLADDR ((struct device *)0174400)
#define NRL 4 /* maximum number of RL drives, DO NOT CHANGE ! */
/*
* Instrumentation (iostat) structures
*/
#define DK_N 5
#define DK_T 9 /* RL xfer rate indicator */
struct ios rl_ios[NRL];
struct buf rrlbuf;
struct buf rltab;
struct
{
int cn[4]; /* location of heads for each drive */
int type[4]; /* RL disk drive type indicator */
/* -1 = non existent drive */
/* BLKRL1 = RL01 */
/* BLKRL2 = RL02 */
char openf; /* RL open flag, 1 = open not completed */
int dn; /* drive number */
int com; /* read or write command word */
int chn; /* cylinder and head number */
unsigned int bleft; /* bytes left to be transferred */
unsigned int bpart; /* number of bytes transferred */
int sn; /* sector number */
union {
int w[2];
long l;
} addr; /* address of memory for transfer */
/*
* Initialize the RL structure as follows:
*
* cn[] = all heads, location unknown
* type[] = all drives, non existent
* openf = RL open not completed yet
*/
} rl = {-1,-1,-1,-1, -1,-1,-1,-1, 1} ;
rlstrategy(bp)
register struct buf *bp;
{
register struct device *rp ;
register int ctr;
int dn, nblks;
long sz;
rp = RLADDR ;
if(bp->b_flags&B_PHYS)
mapalloc(bp);
/*
* If the RL open has not been completed,
* then get the status of all possible drives
* as follows:
*
* 1. Execute a get status with reset up to 8 times
* to get a valid status from the drive.
*
* 2. If an OPI error is detected then the drive
* is non-existent (NED).
*
* 3. If a vaild status cannot be obtained after 8 attempts,
* then print the "can't get status" message and
* mark the drive non-existent.
*
* 4. If a valid status is obtained, then use the drive
* type to set rl.type to the number of blocks for that
* type drive.
* 10240 for RL01 or 20480 for RL02
*
* The above sequence only occurs on the first access
* of the RL disk driver. The drive status obtained is
* retained until the system in rebooted.
* Packs may be mounted and dismounted,
* HOWEVER the disk unit number select plugs may
* NOT be changed without rebooting the system.
*
****************************************************************
* *
* For some unknown reason the RL02 does not return a valid *
* status the first time a GET STATUS request is issued for *
* the drive, in fact it can take up to three or more *
* GET STATUS requests to obtain a valid drive status. *
* This is why the GET STATUS is repeated eight times *
* in step one above. *
* *
****************************************************************
*/
if(rl.openf)
{
rl.openf = 0;
nblks = 0;
for (dn=0; dn<NRL; dn++)
{
for(ctr=0; ctr<8; ctr++)
{
rp->rlda = RESET;
rp->rlcs = (dn << 8) | GETSTAT;
while ((rp->rlcs & CRDY) == 0) ;
if(rp->rlcs & OPI)
break; /* NED */
if((rp->rlmp & 0157400) == 0)
break; /* valid status */
}
if(rp->rlcs & OPI)
continue; /* NED */
if(ctr >= 8)
{
printf("\nCan't get status of RL unit %d\n", dn);
continue;
}
if(rp->rlmp & RL02TYP)
rl.type[dn] = BLKRL2; /* RL02 */
else
rl.type[dn] = BLKRL1; /* RL01 */
rl_ios[dn].dk_tr = DK_T;
nblks++;
}
ctr = spl6();
dk_iop[DK_N] = &rl_ios[0];
dk_nd[DK_N] = nblks;
splx(ctr);
}
dn = minor(bp->b_dev);
if((dn >= NRL) || (rl.type[dn] < 0))
goto bad;
nblks = rl.type[dn]; /* get # of blocks on this drive type */
sz = bp->b_bcount;
sz = (sz+511) >> 9;
if(bp->b_blkno < 0)
goto bad;
if((bp->b_blkno + sz) > nblks)
goto bad;
if(bp->b_blkno >= nblks) {
if(bp->b_blkno == nblks && bp->b_flags&B_READ)
bp->b_resid = bp->b_bcount;
else {
bad:
bp->b_flags |= B_ERROR;
bp->b_error = ENXIO;
}
iodone(bp);
return;
}
bp->av_forw = NULL;
spl5();
if(rltab.b_actf == NULL)
rltab.b_actf = bp;
else
rltab.b_actl->av_forw = bp;
rltab.b_actl = bp;
if(rltab.b_active == NULL)
rlstart();
spl0();
}
rlstart()
{
register struct buf *bp;
if ((bp = rltab.b_actf) == NULL)
return;
rltab.b_active++;
rl.dn = minor(bp->b_dev);
rl.chn = bp->b_blkno/20;
rl.sn = (bp->b_blkno%20) << 1;
rl.bleft = bp->b_bcount;
rl.addr.w[0] = bp->b_xmem & 3;
rl.addr.w[1] = (int)bp->b_un.b_addr;
rl.com = (rl.dn << 8) | IENABLE;
if (bp->b_flags & B_READ)
rl.com |= RCOM;
else
rl.com |= WCOM;
rl_ios[rl.dn].dk_busy++;
rl_ios[rl.dn].dk_numb++;
rl_ios[rl.dn].dk_wds += (bp->b_bcount >> 6);
rlio();
}
rlintr()
{
register struct buf *bp;
register struct device *rp;
register int status;
rp = RLADDR;
if (rltab.b_active == NULL)
return;
bp = rltab.b_actf;
rl_ios[rl.dn].dk_busy = 0;
if (rp->rlcs < 0) { /* error bit */
if (rp->rlcs & 036000) {
if(rltab.b_errcnt > 2)
{
printf("rlcs, rlda ") ;
deverror(bp, rp->rlcs, rp->rlda);
}
}
if (rp->rlcs & 040000) {
rp->rlda = STAT;
rp->rlcs = (rl.dn << 8) | GETSTAT;
while ((rp->rlcs & CRDY) == 0)
;
status = rp->rlmp;
if(rltab.b_errcnt > 2)
{
printf("rlmp, rlda ") ;
deverror(bp, status, rp->rlda);
}
rp->rlda = RESET;
rp->rlcs = (rl.dn << 8) | GETSTAT;
while ((rp->rlcs & CRDY) == 0)
;
if(status & 01000) {
rlstart();
return;
}
}
if (++rltab.b_errcnt <= 10) {
rl.cn[rl.dn] = -1;
rlstart();
return;
}
else {
bp->b_flags |= B_ERROR;
rl.bpart = rl.bleft;
}
}
if ((rl.bleft -= rl.bpart) > 0) {
rl.addr.l += rl.bpart;
rl.sn=0;
rl.chn++;
rlio();
return;
}
rltab.b_active = NULL;
rltab.b_errcnt = 0;
rltab.b_actf = bp->av_forw;
bp->b_resid = 0;
iodone(bp);
rlstart();
}
rlio()
{
register struct device *rp;
register dif;
register int head;
rp = RLADDR;
/*
* When the device is first touched, find where the heads are and
* what type of drive it is.
* rl.cn[] is initialized as -1 and this indicates that we have not touched
* this drive before.
*/
if (rl.cn[rl.dn] < 0) {
/* find where the heads are */
rp->rlcs = (rl.dn << 8) | RDHDR;
while ((rp->rlcs&CRDY) == 0)
;
rl.cn[rl.dn] = (rp->rlmp >> 6) & 01777;
}
dif =(rl.cn[rl.dn] >> 1) - (rl.chn >>1);
head = (rl.chn & 1) << 4;
if (dif < 0)
rp->rlda = (-dif <<7) | SEEKHI | head;
else
rp->rlda = (dif << 7) | SEEKLO | head;
rp->rlcs = (rl.dn << 8) | SEEK;
rl.cn[rl.dn] = rl.chn;
if (rl.bleft < (rl.bpart = RLCYLSZ - (rl.sn * RLSECSZ)))
rl.bpart = rl.bleft;
while ((rp->rlcs&CRDY) == 0)
;
rp->rlda = (rl.chn << 6) | rl.sn;
rp->rlba = rl.addr.w[1];
rp->rlmp = -(rl.bpart >> 1);
rp->rlcs = rl.com | rl.addr.w[0] << 4;
}
rlread(dev)
{
physio(rlstrategy, &rrlbuf, dev, B_READ);
}
rlwrite(dev)
{
physio(rlstrategy, &rrlbuf, dev, B_WRITE);
}