* Massbus driver; arbitrates massbusses through device driver routines
* and provides common functions.
#define dprintf if (mbadebug) printf
* Start activity on a massbus device.
* We are given the device's mba_info structure and activate
* the device via the unit start routine. The unit start
* routine may indicate that it is finished (e.g. if the operation
* was a ``sense'' on a tape drive), that the (multi-ported) unit
* is busy (we will get an interrupt later), that it started the
* unit (e.g. for a non-data transfer operation), or that it has
* set up a data transfer operation and we should start the massbus adaptor.
register struct mba_info
*mi
;
register struct mba_drv
*mdp
; /* drive registers */
register struct buf
*bp
; /* i/o operation at head of queue */
register struct mba_hd
*mhp
; /* header for mba device is on */
dprintf("enter mbustart\n");
* Get the first thing to do off device queue.
* Since we clear attentions on the drive when we are
* finished processing it, the fact that an attention
* status shows indicated confusion in the hardware or our logic.
if (mdp
->mbd_as
& (1 << mi
->mi_drive
)) {
printf("mbustart: ata on for %d\n", mi
->mi_drive
);
mdp
->mbd_as
= 1 << mi
->mi_drive
;
* Let the drivers unit start routine have at it
* and then process the request further, per its instructions.
switch ((*mi
->mi_driver
->md_ustart
)(mi
)) {
case MBU_NEXT
: /* request is complete (e.g. ``sense'') */
mi
->mi_tab
.b_actf
= bp
->av_forw
;
case MBU_DODATA
: /* all ready to do data transfer */
* Queue the device mba_info structure on the massbus
* mba_hd structure for processing as soon as the
* data path is available.
if (mhp
->mh_actf
== NULL
)
mhp
->mh_actl
->mi_forw
= mi
;
* If data path is idle, start transfer now.
* In any case the device is ``active'' waiting for the
case MBU_STARTED
: /* driver started a non-data transfer */
dprintf("mbu_started\n");
* Mark device busy during non-data transfer
* and count this as a ``seek'' on the device.
case MBU_BUSY
: /* dual port drive busy */
* We mark the device structure so that when an
* interrupt occurs we will know to restart the unit.
mi
->mi_tab
.b_flags
|= B_BUSY
;
* Start an i/o operation on the massbus specified by the argument.
* We peel the first operation off its queue and insure that the drive
* is present and on-line. We then use the drivers start routine
* (if any) to prepare the drive, setup the massbus map for the transfer
* and start the transfer.
register struct mba_hd
*mhp
;
register struct mba_info
*mi
;
register struct mba_drv
*daddr
;
register struct mba_regs
*mbp
;
* Look for an operation at the front of the queue.
if ((mi
= mhp
->mh_actf
) == NULL
) {
dprintf("nothing to do\n");
if ((bp
= mi
->mi_tab
.b_actf
) == NULL
) {
dprintf("nothing on actf\n");
mhp
->mh_actf
= mi
->mi_forw
;
* If this device isn't present and on-line, then
* we screwed up, and can't really do the operation.
if ((mi
->mi_drv
->mbd_ds
& (MBD_DPR
|MBD_MOL
)) != (MBD_DPR
|MBD_MOL
)) {
dprintf("not on line ds %x\n", mi
->mi_drv
->mbd_ds
);
mi
->mi_tab
.b_actf
= bp
->av_forw
;
* We can do the operation; mark the massbus active
* and let the device start routine setup any necessary
* device state for the transfer (e.g. desired cylinder, etc
if (mi
->mi_driver
->md_start
) {
(*mi
->mi_driver
->md_start
)(mi
);
* Setup the massbus control and map registers and start
mbp
->mba_sr
= -1; /* conservative */
mbp
->mba_var
= mbasetup(mi
);
mbp
->mba_bcr
= -bp
->b_bcount
;
(bp
->b_flags
& B_READ
) ? MBD_RCOM
|MBD_GO
: MBD_WCOM
|MBD_GO
;
dk_busy
|= 1 << mi
->mi_dk
;
dk_wds
[mi
->mi_dk
] += bp
->b_bcount
>> 6;
* Take an interrupt off of massbus mbanum,
* and dispatch to drivers as appropriate.
register struct mba_hd
*mhp
= &mba_hd
[mbanum
];
register struct mba_regs
*mbp
= mhp
->mh_mba
;
register struct mba_info
*mi
;
* Read out the massbus status register
* and attention status register and clear
* the bits in same by writing them back.
/* note: the mbd_as register is shared between drives */
as
= mbp
->mba_drv
[0].mbd_as
;
mbp
->mba_drv
[0].mbd_as
= as
;
dprintf("mbintr mbastat %x as %x\n", mbastat
, as
);
* Disable interrupts from the massbus adapter
* for the duration of the operation of the massbus
* driver, so that spurious interrupts won't be generated.
* If the mba was active, process the data transfer
* complete interrupt; otherwise just process units which
if ((mbastat
& MBS_DTCMP
) == 0) {
printf("mbintr(%d),b_active,no DTCMP!\n", mbanum
);
* Clear attention status for drive whose data
* transfer completed, and give the dtint driver
* routine a chance to say what is next.
as
&= ~(1 << mi
->mi_drive
);
dk_busy
&= ~(1 << mi
->mi_dk
);
switch((*mi
->mi_driver
->md_dtint
)(mi
, mbastat
)) {
case MBD_DONE
: /* all done, for better or worse */
* Flush request from drive queue.
mi
->mi_tab
.b_actf
= bp
->av_forw
;
case MBD_RETRY
: /* attempt the operation again */
* Dequeue data transfer from massbus queue;
* if there is still a i/o request on the device
* queue then start the next operation on the device.
* (Common code for DONE and RETRY).
mhp
->mh_actf
= mi
->mi_forw
;
case MBD_RESTARTED
: /* driver restarted op (ecc, e.g.)
dprintf("mbd_restarted\n");
* Note that mp->b_active is still on.
printf("mbaintr,DTCMP,!b_active\n");
* Service drives which require attention
* after non-data-transfer operations.
for (drive
= 0; as
&& drive
< 8; drive
++)
dprintf("service as %d\n", drive
);
* Consistency check the implied attention,
* to make sure the drive should have interrupted.
mi
= mhp
->mh_mbip
[drive
];
goto random
; /* no such drive */
if (mi
->mi_tab
.b_active
== 0 &&
(mi
->mi_tab
.b_flags
&B_BUSY
) == 0)
goto random
; /* not active */
if ((bp
= mi
->mi_tab
.b_actf
) == NULL
) {
printf("random mbaintr %d %d\n",mbanum
,drive
);
* If this interrupt wasn't a notification that
* a dual ported drive is available, and if the
* driver has a handler for non-data transfer
* interrupts, give it a chance to tell us that
* the operation needs to be redone
if ((mi
->mi_tab
.b_flags
&B_BUSY
) == 0 &&
mi
->mi_driver
->md_ndint
) {
switch((*mi
->mi_driver
->md_ndint
)(mi
)) {
* Non-data transfer interrupt
* completed i/o request's processing.
mi
->mi_tab
.b_actf
= bp
->av_forw
;
* If there is an operation available and
* the massbus isn't active, get it going.
if (mhp
->mh_actf
&& !mhp
->mh_active
)
* Setup the mapping registers for a transfer.
register struct mba_info
*mi
;
register struct mba_regs
*mbap
= mi
->mi_mba
;
struct buf
*bp
= mi
->mi_tab
.b_actf
;
register struct pte
*pte
, *io
;
v
= btop(bp
->b_un
.b_addr
);
o
= (int)bp
->b_un
.b_addr
& PGOFSET
;
npf
= btoc(bp
->b_bcount
+ o
);
rp
= bp
->b_flags
&B_DIRTY
? &proc
[2] : bp
->b_proc
;
if (bp
->b_flags
& B_UAREA
) {
for (i
= 0; i
< UPAGES
; i
++) {
if (rp
->p_addr
[i
].pg_pfnum
== 0)
panic("mba: zero upage");
*(int *)io
++ = rp
->p_addr
[i
].pg_pfnum
| PG_V
;
} else if ((bp
->b_flags
& B_PHYS
) == 0) {
pte
= &Sysmap
[btop(((int)bp
->b_un
.b_addr
)&0x7fffffff)];
*(int *)io
++ = pte
++->pg_pfnum
| PG_V
;
if (bp
->b_flags
& B_PAGET
)
pte
= &Usrptmap
[btokmx((struct pte
*)bp
->b_un
.b_addr
)];
panic("mba, zero entry");
*(int *)io
++ = pte
++->pg_pfnum
| PG_V
;