RL02 Driver for 4.1Bsd
Perry Kivolowitz
perry at sbcs.UUCP
Wed Sep 21 12:45:18 AEST 1983
This is the rl02 driver (kernel) we received from the net. We noticed
one problem:
If the rl is not spun up at boot time then the driver's probe routine
will not fire an interrupt - Thus you can't access the drive. Fix:
quick and dirty - hard code the vector and ipl into the probe routine
This file goes in /sys/dev
----------------------------------------------------------------------------
/* rl.c 4.1 83/02/08 */
#include "rl.h"
#if NHL > 0
/*
* UNIBUS RL02 disk driver
* (not yet converted to 4.1c)
*/
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/cpu.h"
#include "../h/nexus.h"
#include "../h/dk.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/map.h"
#include "../h/pte.h"
#include "../h/mtpr.h"
#include "../h/vm.h"
#include "../h/ubavar.h"
#include "../h/ubareg.h"
#include "../h/cmap.h"
#include "../h/rlreg.h"
/* Pending Controller items and statistics */
struct rl_softc {
int rl_softas; /* Attention sumary, (seeks pending) */
int rl_ndrive; /* Number of drives on controller */
int rl_wticks; /* Monitor time for function */
} rl_softc[NHL];
/*
* this struct is used to keep the state of the controller for the last
* transfer done. Since only one transfer can be done at a time per
* controller, only allocate one for each controller.
*/
struct rl_stat {
short rl_cyl[4]; /* Current cylinder for each drive */
short rl_dn; /* drive number currently transferring */
short rl_cylnhd; /* current cylinder and head of transfer */
u_short rl_bleft; /* bytes left to transfer */
u_short rl_bpart; /* bytes transferred */
} rl_stat[NHL];
/* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */
struct size {
daddr_t nblocks;
int cyloff;
} rl02_sizes[8] = {
14440, 0, /* A=cyl 0 thru 360 */
6040, 361, /* B=cyl 361 thru 511 */
20480, 0, /* C=cyl 0 thru 511 */
0, 0, /* D= Not Defined */
0, 0, /* E= Not Defined */
0, 0, /* F= Not Defined */
0, 0, /* G= Not Defined */
0, 0, /* H= Not Defined */
};
/* END OF STUFF WHICH SHOULD BE READ IN PER DISK */
int rlprobe(), rlslave(), rlattach(), rldgo(), rlintr();
struct uba_ctlr *rlminfo[NHL];
struct uba_device *rldinfo[NRL];
struct uba_device *rlip[NHL][4];
/* RL02 driver structure */
u_short rlstd[] = { 0174400 };
struct uba_driver rldriver =
{ rlprobe, rlslave, rlattach, rldgo, rlstd, "rl", rldinfo, "hl", rlminfo };
/* User table per controller */
struct buf rlutab[NRL];
/* RL02 drive structure */
struct RL02 {
short nbpt; /* Number of 512 byte blocks/track */
short ntrak;
short nbpc; /* Number of 512 byte blocks/cylinder */
short ncyl;
short btrak; /* Number of bytes/track */
struct size *sizes;
} rl02 = {
20, 2, 40, 512, 20*512, rl02_sizes /* rl02/DEC*/
};
struct buf rrlbuf[NRL];
#define b_cylin b_resid /* Last seek as CYL<<1 | HD */
#ifdef INTRLVE
daddr_t dkblock();
#endif
int rlwstart, rlwatch(); /* Have started guardian */
/* Check that controller exists */
/*ARGSUSED*/
rlprobe(reg)
caddr_t reg;
{
register int br, cvec;
#ifdef lint
br = 0; cvec = br; br = cvec;
#endif
((struct rldevice *)reg)->rlcs = RL_IE | RL_NOOP; /* Enable intrpt */
DELAY(10); /* Ensure interrupt takes place (10 microsec ) */
((struct rldevice *)reg)->rlcs &= ~RL_IE; /* Disable intrpt */
return (1);
}
/* Check that drive exists and is functional*/
rlslave(ui, reg)
struct uba_device *ui;
caddr_t reg;
{
register struct rldevice *rladdr = (struct rldevice *)reg;
short ctr = 0;
/*
* DEC reports that:
* For some unknown reason the RL02 (seems to be only drive 1)
* does not return a valid drive status the first time that a
* GET STATUS request is issued for the drive, in fact it can
* take up to three or more GET STATUS requests to obtain the
* correct status.
* In order to overcome this, the driver has been modified to
* issue a GET STATUS request and validate the drive status
* returned. If a valid status is not returned after eight
* attempts, then an error message is printed.
*/
do {
rladdr->rlda.getstat = RL_RESET;
rladdr->rlcs = (ui->ui_slave <<8) | RL_GETSTAT; /* Get status*/
rlwait(rladdr);
} while( (rladdr->rlmp.getstat&RLMP_STATUS) != RLMP_STATOK && ++ctr<8 );
if ((rladdr->rlcs & RL_DE) || (ctr >= 8))
return (0); /* Error return */
if ((rladdr->rlmp.getstat & RLMP_DT) == 0 ) { /* NO RL01'S */
printf("rl01 drives not supported (drive %d)\n", ui->ui_slave );
return(0);
}
return (1);
}
/* Initialize controller */
rlattach(ui)
register struct uba_device *ui;
{
register struct rldevice *rladdr;
if (rlwstart == 0) {
timeout(rlwatch, (caddr_t)0, hz); /* Watch for lost intr */
rlwstart++;
}
/* Initialize iostat values */
if (ui->ui_dk >= 0)
dk_mspw[ui->ui_dk] = .000003906; /* 16bit transfer time? */
rlip[ui->ui_ctlr][ui->ui_slave] = ui;
rl_softc[ui->ui_ctlr].rl_ndrive++; /* increment device/ctrl */
rladdr = (struct rldevice *)ui->ui_addr;
/* reset controller */
rladdr->rlda.getstat = RL_RESET; /* SHOULD BE REPEATED? */
rladdr->rlcs = (ui->ui_slave << 8) | RL_GETSTAT; /* Reset DE bit */
rlwait(rladdr);
/* Determine disk posistion */
rladdr->rlcs = (ui->ui_slave << 8) | RL_RHDR;
rlwait(rladdr);
/* save disk drive posistion */
rl_stat[ui->ui_ctlr].rl_cyl[ui->ui_slave] =
(rladdr->rlmp.readhdr & 0177700) >> 6;
rl_stat[ui->ui_ctlr].rl_dn = -1;
}
rlstrategy(bp)
register struct buf *bp;
{
register struct uba_device *ui;
register int drive;
register struct buf *dp;
int partition = minor(bp->b_dev) & 07;
long bn, sz;
sz = (bp->b_bcount+511) >> 9; /* Blocks to transfer */
drive = dkunit(bp); /* Drive number */
if (drive >= NRL)
goto bad;
ui = rldinfo[drive]; /* Controller uba_device */
if (ui == 0 || ui->ui_alive == 0)
goto bad;
if (bp->b_blkno < 0 ||
(bn = dkblock(bp))+sz > rl02.sizes[partition].nblocks)
goto bad;
/* bn is in 512 byte block size */
bp->b_cylin = bn/rl02.nbpc + rl02.sizes[partition].cyloff;
(void) spl5();
dp = &rlutab[ui->ui_unit];
disksort(dp, bp);
if (dp->b_active == 0) {
(void) rlustart(ui);
bp = &ui->ui_mi->um_tab;
if (bp->b_actf && bp->b_active == 0)
(void) rlstart(ui->ui_mi);
}
(void) spl0();
return;
bad:
bp->b_flags |= B_ERROR;
iodone(bp);
return;
}
/*
* Unit start routine.
* Seek the drive to be where the data is
* and then generate another interrupt
* to actually start the transfer.
*/
rlustart(ui)
register struct uba_device *ui;
{
register struct buf *bp, *dp;
register struct uba_ctlr *um;
register struct rldevice *rladdr;
daddr_t bn;
short cyl, sn, hd, diff;
if (ui == 0)
return (0);
um = ui->ui_mi;
dk_busy &= ~(1<<ui->ui_dk); /* Kernel define, drives busy */
dp = &rlutab[ui->ui_unit];
if ((bp = dp->b_actf) == NULL)
goto out;
/*
* If the controller is active, just remember
* that this device has to be positioned...
*/
if (um->um_tab.b_active) {
rl_softc[um->um_ctlr].rl_softas |= 1<<ui->ui_slave;
return (0);
}
/*
* If we have already positioned this drive,
* then just put it on the ready queue.
*/
if (dp->b_active)
goto done;
dp->b_active = 1; /* Posistioning drive */
rladdr = (struct rldevice *)um->um_addr;
/*
* Figure out where this transfer is going to
* and see if we are seeked correctly.
*/
bn = dkblock(bp); /* Block # desired */
/*
* these next two look funky... but we need to map
* 512 byte logical disk blocks to 256 byte sectors.
* (rl02's are stupid).
*/
sn = (bn % rl02.nbpt) << 1; /* Sector # desired */
hd = (bn / rl02.nbpt) & 1; /* Get head required */
diff = (rl_stat[um->um_ctlr].rl_cyl[ui->ui_slave] >> 1) - bp->b_cylin;
if ( diff == 0 && (rl_stat[um->um_ctlr].rl_cyl[ui->ui_slave] & 1) == hd)
goto done; /* on cylinder and head */
search:
/*
* Not at correct position.
*/
rl_stat[um->um_ctlr].rl_cyl[ui->ui_slave] = (bp->b_cylin << 1) | hd;
if (diff < 0)
rladdr->rlda.seek = -diff << 7 | RLDA_HGH | hd << 4;
else
rladdr->rlda.seek = diff << 7 | RLDA_LOW | hd << 4;
rladdr->rlcs = (ui->ui_slave << 8) | RL_SEEK;
/*
* Mark unit busy for iostat.
*/
if (ui->ui_dk >= 0) {
dk_busy |= 1<<ui->ui_dk;
dk_seek[ui->ui_dk]++;
}
rlwait( rladdr );
/*
* fall through since we are now at the correct cylinder
*/
done:
/*
* Device is ready to go.
* Put it on the ready queue for the controller
* (unless its already there.)
*/
if (dp->b_active != 2) {
dp->b_forw = NULL;
if (um->um_tab.b_actf == NULL)
um->um_tab.b_actf = dp;
else
um->um_tab.b_actl->b_forw = dp;
um->um_tab.b_actl = dp;
dp->b_active = 2; /* Request on ready queue */
}
out:
return (0);
}
/*
* Start up a transfer on a drive.
*/
rlstart(um)
register struct uba_ctlr *um;
{
register struct buf *bp, *dp;
register struct uba_device *ui;
register struct rldevice *rladdr;
register struct rl_stat *st = &rl_stat[um->um_ctlr];
daddr_t bn;
short sn, cyl, cmd;
loop:
/*
* Pull a request off the controller queue
*/
if ((dp = um->um_tab.b_actf) == NULL) {
st->rl_dn = -1;
st->rl_cylnhd = 0;
st->rl_bleft = 0;
st->rl_bpart = 0;
return (0);
}
if ((bp = dp->b_actf) == NULL) {
um->um_tab.b_actf = dp->b_forw;
goto loop;
}
/*
* Mark controller busy, and
* determine destinationst.
*/
um->um_tab.b_active++;
ui = rldinfo[dkunit(bp)]; /* Controller */
bn = dkblock(bp); /* 512 byte Block number */
cyl = bp->b_cylin << 1; /* Cylinder */
cyl |= (bn / rl02.nbpt) & 1; /* Get head required */
sn = (bn % rl02.nbpt) << 1; /* Sector number */
rladdr = (struct rldevice *)ui->ui_addr;
/*
* Check that controller is ready
*/
rlwait( rladdr );
/*
* Setup for the transfer, and get in the
* UNIBUS adaptor queue.
*/
rladdr->rlda.rw = cyl<<6 | sn;
/* save away current transfers drive status */
st->rl_dn = ui->ui_slave;
st->rl_cylnhd = cyl;
st->rl_bleft = bp->b_bcount;
st->rl_bpart = rl02.btrak - (sn * NRLBPSC);
/* RL02 must seek between cylinders and between tracks */
/* Determine maximum data transfer at this time */
if( st->rl_bleft < st->rl_bpart)
st->rl_bpart = st->rl_bleft;
rladdr->rlmp.rw = -(st->rl_bpart >> 1);
if (bp->b_flags & B_READ)
cmd = RL_IE | RL_READ | (ui->ui_slave << 8);
else
cmd = RL_IE | RL_WRITE | (ui->ui_slave << 8);
um->um_cmd = cmd;
(void) ubago(ui);
return (1);
}
/*
* Now all ready to go, stuff the registers.
*/
rldgo(um)
register struct uba_ctlr *um;
{
register struct rldevice *rladdr = (struct rldevice *)um->um_addr;
/* Place in unibus address for transfer (lower 18 bits of um_ubinfo) */
/* Then execute instruction */
rladdr->rlba = um->um_ubinfo;
rladdr->rlcs = um->um_cmd|((um->um_ubinfo>>12)&RL_BAE);
}
/*
* Handle a disk interrupt.
*/
rlintr(rl21)
register rl21;
{
register struct buf *bp, *dp;
register struct uba_ctlr *um = rlminfo[rl21];
register struct uba_device *ui;
register struct rldevice *rladdr = (struct rldevice *)um->um_addr;
register unit;
struct rl_softc *rl = &rl_softc[um->um_ctlr];
struct rl_stat *st = &rl_stat[um->um_ctlr];
int as = rl->rl_softas;
int needie = 1, waitdry, status;
rl->rl_wticks = 0;
rl->rl_softas = 0;
/*
* Get device and block structures, and a pointer
* to the uba_device for the drive.
*/
dp = um->um_tab.b_actf;
bp = dp->b_actf;
ui = rldinfo[dkunit(bp)];
dk_busy &= ~(1 << ui->ui_dk); /* Clear busy bit */
/*
* Check for and process errors on
* either the drive or the controller.
*/
if (rladdr->rlcs & RL_ERR) {
u_short err;
rlwait( rladdr );
err = rladdr->rlcs;
/* get staus and reset controller */
rladdr->rlda.getstat = RL_GSTAT;
rladdr->rlcs = (ui->ui_slave << 8) | RL_GETSTAT;
rlwait(rladdr);
status = rladdr->rlmp.getstat;
/* reset drive */
rladdr->rlda.getstat = RL_RESET;
rladdr->rlcs = (ui->ui_slave <<8) | RL_GETSTAT; /* Get status*/
rlwait(rladdr);
if ( (status & RLMP_WL) == RLMP_WL ) {
/*
* Give up on write protected devices
* immediately.
*/
printf("rl%d: write protected\n", dkunit(bp));
bp->b_flags |= B_ERROR;
} else if (++um->um_tab.b_errcnt > 10) {
/*
* After 10 retries give up.
*/
harderr(bp, "rl");
printf("cs=%b mp=%b\n",
err, RLCS_BITS, status, RLER_BITS);
bp->b_flags |= B_ERROR;
} else
um->um_tab.b_active = 0; /* force retry */
/* Determine disk posistion */
rladdr->rlcs = (ui->ui_slave << 8) | RL_RHDR;
rlwait(rladdr);
/* save disk drive posistion */
st->rl_cyl[ui->ui_slave] = (rladdr->rlmp.readhdr & 0177700) >> 6;
}
/*
* If still ``active'', then don't need any more retries.
*/
if (um->um_tab.b_active) {
/* RL02 check if more data from previous request */
if ( (bp->b_flags & B_ERROR) == 0 &&
(st->rl_bleft -= st->rl_bpart) > 0 ) {
/*
* the following code was modeled from the rk07
* driver when an ECC error occured. It has to
* fix the bits then restart the transfer which is
* what we have to do (restart transfer).
*/
int reg, npf, o, cmd, ubaddr, diff, head;
/* seek to next head/track */
/* increment head and/or cylinder */
st->rl_cylnhd++;
diff = (st->rl_cyl[ui->ui_slave] >> 1) -
(st->rl_cylnhd >> 1);
st->rl_cyl[ui->ui_slave] = st->rl_cylnhd;
head = st->rl_cylnhd & 1;
rlwait( rladdr );
if ( diff < 0 )
rladdr->rlda.seek = -diff << 7 | RLDA_HGH | head << 4;
else
rladdr->rlda.seek = diff << 7 | RLDA_LOW | head << 4;
rladdr->rlcs = (ui->ui_slave << 8) | RL_SEEK;
npf = btop( bp->b_bcount - st->rl_bleft );
reg = btop(um->um_ubinfo&0x3ffff) + npf;
o = (int)bp->b_un.b_addr & PGOFSET;
ubapurge(um);
um->um_tab.b_active++;
rlwait( rladdr );
rladdr->rlda.rw = st->rl_cylnhd << 6;
if ( st->rl_bleft < (st->rl_bpart = rl02.btrak) )
st->rl_bpart = st->rl_bleft;
rladdr->rlmp.rw = -(st->rl_bpart >> 1);
cmd = (bp->b_flags&B_READ ? RL_READ : RL_WRITE) |
RL_IE | (ui->ui_slave << 8);
ubaddr = (int)ptob(reg) + o;
cmd |= ((ubaddr >> 12) & RL_BAE);
rladdr->rlba = ubaddr;
rladdr->rlcs = cmd;
return;
}
um->um_tab.b_active = 0;
um->um_tab.b_errcnt = 0;
dp->b_active = 0;
dp->b_errcnt = 0;
/* "b_resid" words remaining after error */
bp->b_resid = st->rl_bleft;
um->um_tab.b_actf = dp->b_forw;
dp->b_actf = bp->av_forw;
retry:
st->rl_dn = -1;
st->rl_bpart = st->rl_bleft = 0;
iodone(bp);
/*
* If this unit has more work to do,
* then start it up right away.
*/
if (dp->b_actf)
if (rlustart(ui))
needie = 0;
as &= ~(1<<ui->ui_slave);
} else
as |= (1<<ui->ui_slave);
/*
* Release unibus resources and flush data paths.
*/
ubadone(um);
/* reset state info */
st->rl_dn = -1;
st->rl_cylnhd = st->rl_bpart = st->rl_bleft = 0;
doattn:
/*
* Process other units which need attention.
* For each unit which needs attention, call
* the unit start routine to place the slave
* on the controller device queue.
*/
while (unit = ffs(as)) {
unit--; /* was 1 origin */
as &= ~(1<<unit);
(void) rlustart(rlip[rl21][unit]);
}
/*
* If the controller is not transferring, but
* there are devices ready to transfer, start
* the controller.
*/
if (um->um_tab.b_actf && um->um_tab.b_active == 0)
(void) rlstart(um);
}
rlwait( rladdr )
register struct rldevice *rladdr;
{
while ((rladdr->rlcs & RL_CRDY) == 0)
continue; /* Wait */
}
rlread(dev)
dev_t dev;
{
register int unit = minor(dev) >> 3;
if (unit >= NRL)
u.u_error = ENXIO;
else
physio(rlstrategy, &rrlbuf[unit], dev, B_READ, minphys);
}
rlwrite(dev)
dev_t dev;
{
register int unit = minor(dev) >> 3;
if (unit >= NRL)
u.u_error = ENXIO;
else
physio(rlstrategy, &rrlbuf[unit], dev, B_WRITE, minphys);
}
/*
* Reset driver after UBA init.
* Cancel software state of all pending transfers
* and restart all units and the controller.
*/
rlreset(uban)
int uban;
{
register struct uba_ctlr *um;
register struct uba_device *ui;
register struct rldevice *rladdr;
register struct rl_stat *st;
register int rl21, unit;
for (rl21 = 0; rl21 < NHL; rl21++) {
if ((um = rlminfo[rl21]) == 0 || um->um_ubanum != uban ||
um->um_alive == 0)
continue;
printf(" Reset hl%d", rl21);
rladdr = (struct rldevice *)um->um_addr;
st = &rl_stat[rl21];
um->um_tab.b_active = 0;
um->um_tab.b_actf = um->um_tab.b_actl = 0;
if (um->um_ubinfo) {
printf("<%d>", (um->um_ubinfo>>28)&0xf);
ubadone(um);
}
/* reset controller */
st->rl_dn = -1;
st->rl_cylnhd = 0;
st->rl_bleft = 0;
st->rl_bpart = 0;
rlwait( rladdr );
for (unit = 0; unit < NRL; unit++) {
rladdr->rlcs = (unit << 8) | RL_GETSTAT;
rlwait( rladdr );
/* Determine disk posistion */
rladdr->rlcs = (unit << 8) | RL_RHDR;
rlwait(rladdr);
/* save disk drive posistion */
st->rl_cyl[unit] =
(rladdr->rlmp.readhdr & 0177700) >> 6;
if ((ui = rldinfo[unit]) == 0)
continue;
if (ui->ui_alive == 0 || ui->ui_mi != um)
continue;
rlutab[unit].b_active = 0;
(void) rlustart(ui);
}
(void) rlstart(um);
}
}
/*
* Wake up every second and if an interrupt is pending
* but nothing has happened increment a counter.
* If nothing happens for 20 seconds, reset the UNIBUS
* and begin anew.
*/
rlwatch()
{
register struct uba_ctlr *um;
register rl21, unit;
register struct rl_softc *rl;
timeout(rlwatch, (caddr_t)0, hz);
for (rl21 = 0; rl21 < NHL; rl21++) {
um = rlminfo[rl21];
if (um == 0 || um->um_alive == 0)
continue;
rl = &rl_softc[rl21];
if (um->um_tab.b_active == 0) {
for (unit = 0; unit < NRL; unit++)
if (rlutab[unit].b_active &&
rldinfo[unit]->ui_mi == um)
goto active;
rl->rl_wticks = 0;
continue;
}
active:
rl->rl_wticks++;
if (rl->rl_wticks >= 20) {
rl->rl_wticks = 0;
printf("hl%d: lost interrupt\n", rl21);
ubareset(um->um_ubanum);
}
}
}
rldump(dev)
dev_t dev;
{
/* don't think there is room on swap for it anyway. */
}
More information about the Comp.sources.unix
mailing list