* UNIBUS RL02 disk driver
#include "../machine/pte.h"
#include "../vax/nexus.h"
#include "../vaxuba/ubavar.h"
#include "../vaxuba/ubareg.h"
#include "../vaxuba/rlreg.h"
/* Pending Controller items and statistics */
int rl_softas
; /* Attention sumary, (seeks pending) */
int rl_ndrive
; /* Number of drives on controller */
int rl_wticks
; /* Monitor time for function */
* State of controller from last transfer.
* Since only one transfer can be done at a time per
* controller, only allocate one for each controller.
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 */
/* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */
/* Last cylinder not used. Saved for Bad Sector File */
15884, 0, /* A=cyl 0 thru 397 */
4520, 398, /* B=cyl 398 thru 510 */
-1, 0, /* C=cyl 0 thru 511 */
4520, 398, /* D=cyl 398 thru 510 */
0, 0, /* F= Not Defined */
20440, 0, /* G=cyl 0 thru 510 */
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 hldriver
=
{ rlprobe
, rlslave
, rlattach
, rldgo
, rlstd
, "rl", rldinfo
, "hl", rlminfo
};
/* User table per controller */
/* RL02 drive structure */
short nbpt
; /* Number of 512 byte blocks/track */
short nbpc
; /* Number of 512 byte blocks/cylinder */
short btrak
; /* Number of bytes/track */
20, 2, 40, 512, 20*512, rl02_sizes
/* rl02/DEC*/
#define b_cylin b_resid /* Last seek as CYL<<1 | HD */
int rlwstart
, rlwatch(); /* Have started guardian */
/* Check that controller exists */
br
= 0; cvec
= br
; br
= cvec
;
((struct rldevice
*)reg
)->rlcs
= RL_IE
| RL_NOOP
;
((struct rldevice
*)reg
)->rlcs
&= ~RL_IE
;
return (sizeof (struct rldevice
));
register struct rldevice
*rladdr
= (struct rldevice
*)reg
;
* 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
* 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.
rladdr
->rlda
.getstat
= RL_RESET
;
rladdr
->rlcs
= (ui
->ui_slave
<<8) | RL_GETSTAT
; /* Get status*/
} while ((rladdr
->rlmp
.getstat
&RLMP_STATUS
) != RLMP_STATOK
&& ++ctr
<8);
if ((rladdr
->rlcs
& RL_DE
) || (ctr
>= 8))
if ((rladdr
->rlmp
.getstat
& RLMP_DT
) == 0 ) {
printf("rl%d: rl01's not supported\n", ui
->ui_slave
);
register struct uba_device
*ui
;
register struct rldevice
*rladdr
;
timeout(rlwatch
, (caddr_t
)0, hz
);
/* Initialize iostat values */
dk_mspw
[ui
->ui_dk
] = .000003906; /* 16bit transfer time? */
rlip
[ui
->ui_ctlr
][ui
->ui_slave
] = ui
;
rl_softc
[ui
->ui_ctlr
].rl_ndrive
++;
rladdr
= (struct rldevice
*)ui
->ui_addr
;
rladdr
->rlda
.getstat
= RL_RESET
; /* SHOULD BE REPEATED? */
rladdr
->rlcs
= (ui
->ui_slave
<< 8) | RL_GETSTAT
; /* Reset DE bit */
/* determine disk posistion */
rladdr
->rlcs
= (ui
->ui_slave
<< 8) | RL_RHDR
;
/* 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;
register int unit
= minor(dev
) >> 3;
register struct uba_device
*ui
;
if (unit
>= NRL
|| (ui
= rldinfo
[unit
]) == 0 || ui
->ui_alive
== 0)
register struct uba_device
*ui
;
int partition
= minor(bp
->b_dev
) & 07, s
;
sz
= (bp
->b_bcount
+511) >> 9;
if (ui
== 0 || ui
->ui_alive
== 0)
(bn
= dkblock(bp
))+sz
> rl02
.sizes
[partition
].nblocks
)
/* bn is in 512 byte block size */
bp
->b_cylin
= bn
/rl02
.nbpc
+ rl02
.sizes
[partition
].cyloff
;
dp
= &rlutab
[ui
->ui_unit
];
if (bp
->b_actf
&& bp
->b_active
== 0)
* Seek the drive to be where the data is
* and then generate another interrupt
* to actually start the transfer.
register struct uba_device
*ui
;
register struct buf
*bp
, *dp
;
register struct uba_ctlr
*um
;
register struct rldevice
*rladdr
;
dk_busy
&= ~(1 << ui
->ui_dk
);
dp
= &rlutab
[ui
->ui_unit
];
if ((bp
= dp
->b_actf
) == NULL
)
* 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
;
* If we have already positioned this drive,
* then just put it on the ready queue.
dp
->b_active
= 1; /* positioning 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 */
* Map 512 byte logical disk blocks
* to 256 byte sectors (rl02's are stupid).
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 */
* Not at correct position.
rl_stat
[um
->um_ctlr
].rl_cyl
[ui
->ui_slave
] = (bp
->b_cylin
<< 1) | hd
;
rladdr
->rlda
.seek
= -diff
<< 7 | RLDA_HGH
| hd
<< 4;
rladdr
->rlda
.seek
= diff
<< 7 | RLDA_LOW
| hd
<< 4;
rladdr
->rlcs
= (ui
->ui_slave
<< 8) | RL_SEEK
;
* Mark unit busy for iostat.
* Put it on the ready queue for the controller
* (unless its already there.)
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;
dp
->b_active
= 2; /* Request on ready queue */
* Start up a transfer on a drive.
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
];
if ((dp
= um
->um_tab
.b_actf
) == NULL
) {
if ((bp
= dp
->b_actf
) == NULL
) {
um
->um_tab
.b_actf
= dp
->b_forw
;
* Mark controller busy, and
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
;
rladdr
->rlda
.rw
= cyl
<<6 | sn
;
/* save away current transfers drive status */
st
->rl_dn
= ui
->ui_slave
;
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);
cmd
= RL_IE
| RL_WRITE
| (ui
->ui_slave
<< 8);
register struct uba_ctlr
*um
;
register struct rldevice
*rladdr
= (struct rldevice
*)um
->um_addr
;
rladdr
->rlba
= um
->um_ubinfo
;
rladdr
->rlcs
= um
->um_cmd
|((um
->um_ubinfo
>>12)&RL_BAE
);
* Handle a disk interrupt.
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
;
struct rl_softc
*rl
= &rl_softc
[um
->um_ctlr
];
struct rl_stat
*st
= &rl_stat
[um
->um_ctlr
];
int as
= rl
->rl_softas
, status
;
ui
= rldinfo
[dkunit(bp
)];
dk_busy
&= ~(1 << ui
->ui_dk
);
* Check for and process errors on
* either the drive or the controller.
if (rladdr
->rlcs
& RL_ERR
) {
/* get staus and reset controller */
rladdr
->rlda
.getstat
= RL_GSTAT
;
rladdr
->rlcs
= (ui
->ui_slave
<< 8) | RL_GETSTAT
;
status
= rladdr
->rlmp
.getstat
;
rladdr
->rlda
.getstat
= RL_RESET
;
rladdr
->rlcs
= (ui
->ui_slave
<<8) | RL_GETSTAT
; /* Get status*/
if ((status
& RLMP_WL
) == RLMP_WL
) {
* Give up on write protected devices
printf("rl%d: write protected\n", dkunit(bp
));
} else if (++um
->um_tab
.b_errcnt
> 10) {
* After 10 retries give up.
printf("cs=%b mp=%b\n", err
, RLCS_BITS
,
um
->um_tab
.b_active
= 0; /* force retry */
/* determine disk position */
rladdr
->rlcs
= (ui
->ui_slave
<< 8) | RL_RHDR
;
/* save disk drive position */
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 &&
(int)(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 */
diff
= (st
->rl_cyl
[ui
->ui_slave
] >> 1) -
st
->rl_cyl
[ui
->ui_slave
] = st
->rl_cylnhd
;
head
= st
->rl_cylnhd
& 1;
-diff
<< 7 | RLDA_HGH
| head
<< 4;
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
;
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
);
/* "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
;
st
->rl_bpart
= st
->rl_bleft
= 0;
* If this unit has more work to do,
* then start it up right away.
as
&= ~(1<<ui
->ui_slave
);
st
->rl_cylnhd
= st
->rl_bpart
= st
->rl_bleft
= 0;
* 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.
unit
--; /* was 1 origin */
rlustart(rlip
[rl21
][unit
]);
* If the controller is not transferring, but
* there are devices ready to transfer, start
if (um
->um_tab
.b_actf
&& um
->um_tab
.b_active
== 0)
register struct rldevice
*rladdr
;
while ((rladdr
->rlcs
& RL_CRDY
) == 0)
register int unit
= minor(dev
) >> 3;
return (physio(rlstrategy
, &rrlbuf
[unit
], dev
, B_READ
, minphys
, uio
));
register int unit
= minor(dev
) >> 3;
return (physio(rlstrategy
, &rrlbuf
[unit
], dev
, B_WRITE
, minphys
, uio
));
* Reset driver after UBA init.
* Cancel software state of all pending transfers
* and restart all units and the controller.
register struct uba_ctlr
*um
;
register struct uba_device
*ui
;
register struct rldevice
*rladdr
;
register struct rl_stat
*st
;
for (rl21
= 0; rl21
< NHL
; rl21
++) {
if ((um
= rlminfo
[rl21
]) == 0 || um
->um_ubanum
!= uban
||
rladdr
= (struct rldevice
*)um
->um_addr
;
um
->um_tab
.b_actf
= um
->um_tab
.b_actl
= 0;
printf("<%d>", (um
->um_ubinfo
>>28)&0xf);
for (unit
= 0; unit
< NRL
; unit
++) {
rladdr
->rlcs
= (unit
<< 8) | RL_GETSTAT
;
/* Determine disk posistion */
rladdr
->rlcs
= (unit
<< 8) | RL_RHDR
;
/* save disk drive posistion */
(rladdr
->rlmp
.readhdr
& 0177700) >> 6;
if ((ui
= rldinfo
[unit
]) == 0)
if (ui
->ui_alive
== 0 || ui
->ui_mi
!= um
)
rlutab
[unit
].b_active
= 0;
* 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
register struct uba_ctlr
*um
;
register struct rl_softc
*rl
;
timeout(rlwatch
, (caddr_t
)0, hz
);
for (rl21
= 0; rl21
< NHL
; rl21
++) {
if (um
== 0 || um
->um_alive
== 0)
if (um
->um_tab
.b_active
== 0) {
for (unit
= 0; unit
< NRL
; unit
++)
if (rlutab
[unit
].b_active
&&
rldinfo
[unit
]->ui_mi
== um
)
if (rl
->rl_wticks
>= 20) {
printf("hl%d: lost interrupt\n", rl21
);
/* don't think there is room on swap for it anyway. */
register int unit
= minor(dev
) >> 3;
register struct uba_device
*ui
;
if (unit
>= NRL
|| (ui
= rldinfo
[unit
]) == 0 || ui
->ui_alive
== 0)
return (rl02
.sizes
[minor(dev
) & 07].nblocks
);