Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

A replacement for the 4.3BSD UDA50 driver (3 of 3)

1 view
Skip to first unread message

ch...@mimsy.uucp

unread,
Apr 5, 1987, 3:20:58 AM4/5/87
to
: Run this shell script with "sh" not "csh"
PATH=/bin:/usr/bin:/usr/ucb:/etc:$PATH
export PATH
all=FALSE
if [ x$1 = x-a ]; then
all=TRUE
fi
echo Making directory vaxuba
mkdir vaxuba
echo Extracting vaxuba/uba.c.diff
sed 's/^X//' <<'//go.sysin dd *' >vaxuba/uba.c.diff
*** vaxuba/uba.c.4.3 Thu Jun 5 04:19:32 1986
--- vaxuba/uba.c Tue Feb 10 21:14:26 1987
***************
*** 33,36 ****
--- 33,38 ----
#define spluba spl7 /* IPL 17 */

+ #define BDPMASK 0xf0000000 /* see ubavar.h */
+
/*
* Do transfer on device argument. The controller
***************
*** 38,66 ****
* We queue for resource wait in the uba code if necessary.
* We return 1 if the transfer was started, 0 if it was not.
! * If you call this routine with the head of the queue for a
! * UBA, it will automatically remove the device from the UBA
! * queue before it returns. If some other device is given
! * as argument, it will be added to the request queue if the
! * request cannot be started immediately. This means that
! * passing a device which is on the queue but not at the head
! * of the request queue is likely to be a disaster.
*/
! ubago(ui)
register struct uba_device *ui;
{
register struct uba_ctlr *um = ui->ui_mi;
register struct uba_hd *uh;
register int s, unit;

uh = &uba_hd[um->um_ubanum];
s = spluba();
! if (um->um_driver->ud_xclu && uh->uh_users > 0 || uh->uh_xclu)
goto rwait;
! um->um_ubinfo = ubasetup(um->um_ubanum, um->um_tab.b_actf->b_actf,
! UBA_NEEDBDP|UBA_CANTWAIT);
if (um->um_ubinfo == 0)
goto rwait;
uh->uh_users++;
! if (um->um_driver->ud_xclu)
uh->uh_xclu = 1;
splx(s);
--- 40,93 ----
* We queue for resource wait in the uba code if necessary.
* We return 1 if the transfer was started, 0 if it was not.
! *
! * The onq argument must be zero iff the device is not on the
! * queue for this UBA. If onq is set, the device must be at the
! * head of the queue. In any case, if the transfer is started,
! * the device will be off the queue, and if not, it will be on.
! *
! * Drivers that allocate one BDP and hold it for some time should
! * set ud_keepbdp. In this case um_bdp tells which BDP is allocated
! * to the controller, unless it is zero, indicating that the controller
! * does not now have a BDP.
*/
! ubaqueue(ui, onq)
register struct uba_device *ui;
+ int onq;
{
register struct uba_ctlr *um = ui->ui_mi;
register struct uba_hd *uh;
+ register struct uba_driver *ud;
register int s, unit;

uh = &uba_hd[um->um_ubanum];
+ ud = um->um_driver;
s = spluba();
! /*
! * Honor exclusive BDP use requests.
! */
! if (ud->ud_xclu && uh->uh_users > 0 || uh->uh_xclu)
goto rwait;
! if (ud->ud_keepbdp) {
! /*
! * First get just a BDP (though in fact it comes with
! * one map register too).
! */
! if (um->um_bdp == 0) {
! um->um_bdp = uballoc(um->um_ubanum,
! (caddr_t)0, 0, UBA_NEEDBDP|UBA_CANTWAIT);
! if (um->um_bdp == 0)
! goto rwait;
! }
! /* now share it with this transfer */
! um->um_ubinfo = ubasetup(um->um_ubanum,
! um->um_tab.b_actf->b_actf,
! um->um_bdp|UBA_HAVEBDP|UBA_CANTWAIT);
! } else
! um->um_ubinfo = ubasetup(um->um_ubanum,
! um->um_tab.b_actf->b_actf, UBA_NEEDBDP|UBA_CANTWAIT);
if (um->um_ubinfo == 0)
goto rwait;
uh->uh_users++;
! if (ud->ud_xclu)
uh->uh_xclu = 1;
splx(s);
***************
*** 71,80 ****
dk_wds[unit] += um->um_tab.b_actf->b_actf->b_bcount>>6;
}
! if (uh->uh_actf == ui)
uh->uh_actf = ui->ui_forw;
! (*um->um_driver->ud_dgo)(um);
return (1);
rwait:
! if (uh->uh_actf != ui) {
ui->ui_forw = NULL;
if (uh->uh_actf == NULL)
--- 98,107 ----
dk_wds[unit] += um->um_tab.b_actf->b_actf->b_bcount>>6;
}
! if (onq)
uh->uh_actf = ui->ui_forw;
! (*ud->ud_dgo)(um);
return (1);
rwait:
! if (!onq) {
ui->ui_forw = NULL;
if (uh->uh_actf == NULL)
***************
*** 96,99 ****
--- 123,128 ----
uh->uh_xclu = 0;
uh->uh_users--;
+ if (um->um_driver->ud_keepbdp)
+ um->um_ubinfo &= ~BDPMASK; /* keep BDP for misers */
ubarelse(um->um_ubanum, &um->um_ubinfo);
}
***************
*** 273,277 ****
wakeup((caddr_t)&uh->uh_mrwant);
}
! while (uh->uh_actf && ubago(uh->uh_actf))
;
}
--- 302,306 ----
wakeup((caddr_t)&uh->uh_mrwant);
}
! while (uh->uh_actf && ubaqueue(uh->uh_actf, 1))
;
}
//go.sysin dd *
if [ `wc -c < vaxuba/uba.c.diff` != 4146 ]; then
made=FALSE
echo error transmitting vaxuba/uba.c.diff --
echo length should be 4146, not `wc -c < vaxuba/uba.c.diff`
else
made=TRUE
fi
if [ $made = TRUE ]; then
chmod 644 vaxuba/uba.c.diff
echo -n ' '; ls -ld vaxuba/uba.c.diff
fi
echo Extracting vaxuba/ubavar.h.diff
sed 's/^X//' <<'//go.sysin dd *' >vaxuba/ubavar.h.diff
*** vaxuba/ubavar.h.4.3 Thu Jun 5 04:20:06 1986
--- vaxuba/ubavar.h Tue Feb 10 20:56:55 1987
***************
*** 60,64 ****
};

- #ifndef LOCORE
/*
* Per-controller structure.
--- 60,63 ----
***************
*** 83,86 ****
--- 82,86 ----
int um_cmd; /* communication to dgo() */
int um_ubinfo; /* save unibus registers, etc */
+ int um_bdp; /* for controllers that hang on to bdp's */
struct buf um_tab; /* queue of devices for this controller */
};
***************
*** 121,125 ****
struct uba_hd *ui_hd;
};
- #endif

/*
--- 121,124 ----
***************
*** 141,144 ****
--- 140,144 ----
struct uba_ctlr **ud_minfo; /* backpointers to ubminit structs */
short ud_xclu; /* want exclusive use of bdp's */
+ short ud_keepbdp; /* hang on to bdp's once allocated */
int (*ud_ubamem)(); /* see if dedicated memory is present */
};
***************
*** 164,167 ****
--- 164,169 ----
#ifndef LOCORE
#ifdef KERNEL
+ #define ubago(ui) ubaqueue(ui, 0)
+
/*
* UBA related kernel variables
//go.sysin dd *
if [ `wc -c < vaxuba/ubavar.h.diff` != 1033 ]; then
made=FALSE
echo error transmitting vaxuba/ubavar.h.diff --
echo length should be 1033, not `wc -c < vaxuba/ubavar.h.diff`
else
made=TRUE
fi
if [ $made = TRUE ]; then
chmod 644 vaxuba/ubavar.h.diff
echo -n ' '; ls -ld vaxuba/ubavar.h.diff
fi
echo Extracting vaxuba/uda.c
sed 's/^X//' <<'//go.sysin dd *' >vaxuba/uda.c
X/*
* UDA50/MSCP device driver
*/

X/*
* TODO
* write bad block forwarding code
*/

#include "ra.h"

#if NUDA > 0

X/*
* CONFIGURATION OPTIONS. The next three defines are tunable -- tune away!
*
* NRSPL2 and NCMDL2 control the number of response and command
* packets respectively. They may be any value from 0 to 7, though
* setting them higher than 5 is unlikely to be of any value.
* If you get warnings about your command ring being too small,
* try increasing the values by one.
*
* MAXUNIT controls the maximum unit number (number of drives per
* controller) we are prepared to handle.
*
* DEFAULT_BURST must be at least 1.
*/
#define NRSPL2 5 /* log2 number of response packets */
#define NCMDL2 5 /* log2 number of command packets */
#define MAXUNIT 8 /* maximum allowed unit number */
#define DEFAULT_BURST 4 /* default DMA burst size */

#include "../machine/pte.h"

#include "param.h"
#include "systm.h"
#include "buf.h"
#include "conf.h"
#include "dir.h"
#include "user.h"
#include "map.h"
#include "vm.h"
#include "dk.h"
#include "cmap.h"
#include "syslog.h"

#include "../vax/cpu.h"
#include "ubareg.h"
#include "ubavar.h"

#define NRSP (1 << NRSPL2)
#define NCMD (1 << NCMDL2)

#include "udareg.h"
#include "../vax/mscp.h"
#include "../vax/mscpvar.h"
#include "../vax/mtpr.h"

X/*
* Backwards compatibility: Reuse the old names. Should fix someday.
*/
#define udaprobe udprobe
#define udaslave udslave
#define udaattach udattach
#define udaopen udopen
#define udastrategy udstrategy
#define udaread udread
#define udawrite udwrite
#define udareset udreset
#define udaintr udintr
#define udadump uddump
#define udasize udsize

X/*
* UDA communications area and MSCP packet pools, per controller.
*/
struct uda {
struct udaca uda_ca; /* communications area */
struct mscp uda_rsp[NRSP]; /* response packets */
struct mscp uda_cmd[NCMD]; /* command packets */
} uda[NUDA];

X/*
* Software status, per controller.
*/
struct uda_softc {
struct uda *sc_uda; /* Unibus address of uda struct */
short sc_state; /* UDA50 state; see below */
short sc_flags; /* flags; see below */
int sc_micro; /* microcode revision */
int sc_ivec; /* interrupt vector address */
struct mscp_info sc_mi;/* MSCP info (per mscpvar.h) */
int sc_wticks; /* watchdog timer ticks */
} uda_softc[NUDA];

X/*
* Controller states
*/
#define ST_IDLE 0 /* uninitialised */
#define ST_STEP1 1 /* in `STEP 1' */
#define ST_STEP2 2 /* in `STEP 2' */
#define ST_STEP3 3 /* in `STEP 3' */
#define ST_SETCHAR 4 /* in `Set Controller Characteristics' */
#define ST_RUN 5 /* up and running */

X/*
* Flags
*/
#define SC_MAPPED 0x01 /* mapped in Unibus I/O space */
#define SC_INSTART 0x02 /* inside udastart() */
#define SC_GRIPED 0x04 /* griped about cmd ring too small */
#define SC_INSLAVE 0x08 /* inside udaslave() */
#define SC_DOWAKE 0x10 /* wakeup when ctlr init done */

X/*
* Device to unit number and partition:
*/
#define UNITSHIFT 3
#define UNITMASK 7
#define udaunit(dev) (minor(dev) >> UNITSHIFT)
#define udapart(dev) (minor(dev) & UNITMASK)

X/* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */
struct size {
daddr_t nblocks;
daddr_t blkoff;
} ra81_sizes[8] = {
#ifdef MARYLAND
#ifdef ENEEVAX
30706, 0, /* A=cyl 0 thru 42 + 2 sectors */
40696, 30706, /* B=cyl 43 thru 99 - 2 sectors */
-1, 0, /* C=cyl 0 thru 1247 */
-1, 71400, /* D=cyl 100 thru 1247 */

15884, 0, /* E=blk 0 thru 15883 */
33440, 15884, /* F=blk 15884 thru 49323 */
82080, 49324, /* G=blk 49324 thru 131403 */
-1, 131404, /* H=blk 131404 thru end */
#else
67832, 0, /* A=cyl 0 thru 94 + 2 sectors */
67828, 67832, /* B=cyl 95 thru 189 - 2 sectors */
-1, 0, /* C=cyl 0 thru 1247 */
-1, 135660, /* D=cyl 190 thru 1247 */
0, 0,
0, 0,
0, 0,
0, 0,
#endif ENEEVAX
#else
/* THE FOLLOWING ARE STRAIGHT FROM THE 4.3BSD uda.c */
/* THIS KIND OF GARBAGE IS WHY THIS SHOULD BE READ FROM THE PACK */

X/*
* These are the new standard partition sizes for ra81's.
* An RA_COMPAT system is compiled with D, E, and F corresponding
* to the 4.2 partitions for G, H, and F respectively.
*/
#ifndef UCBRA
15884, 0, /* A=sectors 0 thru 15883 */
66880, 16422, /* B=sectors 16422 thru 83301 */
891072, 0, /* C=sectors 0 thru 891071 */
#ifdef RA_COMPAT
82080, 49324, /* 4.2 G => D=sectors 49324 thru 131403 */
759668, 131404, /* 4.2 H => E=sectors 131404 thru 891071 */
478582, 412490, /* 4.2 F => F=sectors 412490 thru 891071 */
#else
15884, 375564, /* D=sectors 375564 thru 391447 */
307200, 391986, /* E=sectors 391986 thru 699185 */
191352, 699720, /* F=sectors 699720 thru 891071 */
#endif RA_COMPAT
515508, 375564, /* G=sectors 375564 thru 891071 */
291346, 83538, /* H=sectors 83538 thru 374883 */

X/*
* These partitions correspond to the sizes used by sites at Berkeley,
* and by those sites that have received copies of the Berkeley driver
* with deltas 6.2 or greater (11/15/83).
*/
#else UCBRA

15884, 0, /* A=sectors 0 thru 15883 */
33440, 15884, /* B=sectors 15884 thru 49323 */
891072, 0, /* C=sectors 0 thru 891071 */
15884, 242606, /* D=sectors 242606 thru 258489 */
307200, 258490, /* E=sectors 258490 thru 565689 */
325382, 565690, /* F=sectors 565690 thru 891071 */
648466, 242606, /* G=sectors 242606 thru 891071 */
193282, 49324, /* H=sectors 49324 thru 242605 */

#endif UCBRA
#endif MARYLAND
},
#if GYRE
cdc9771_sizes[8] = { /* HACK: treat some RA81s as 9771s on gyre */
79680, 0, /* A = cyl 0 thru 59 */
79680, 79680, /* B = cyl 60 thru 119 */
-1, 0, /* C = cyl 0 thru 1021 */
-1, 159360, /* D = cyl 120 thru 1021 */
0, 0,
0, 0,
0, 0,
0, 0,
},
#endif
ra80_sizes[8] = {
15884, 0, /* A=blk 0 thru 15883 */
33440, 15884, /* B=blk 15884 thru 49323 */
-1, 0, /* C=blk 0 thru end */
0, 0,
0, 0,
0, 0,
82080, 49324, /* G=blk 49324 thru 131403 */
-1, 131404, /* H=blk 131404 thru end */
}, ra60_sizes[8] = {
15884, 0, /* A=blk 0 thru 15883 */
33440, 15884, /* B=blk 15884 thru 49323 */
-1, 0, /* C=blk 0 thru end */
-1, 49324, /* D=blk 49324 thru end */
0, 0,
0, 0,
82080, 49324, /* G=blk 49324 thru 131403 */
-1, 131404, /* H=blk 131404 thru end */
};
X/* END OF STUFF WHICH SHOULD BE READ IN PER DISK */

X/*
* Drive type index decoding table. `ut_name' is null iff the
* type is not known.
*/
struct udatypes {
char *ut_name; /* drive type name */
struct size *ut_sizes; /* partition tables */
} udatypes[] = {
NULL, NULL,
"ra80", ra80_sizes, /* 1 = ra80 */
"old ra81", ra81_sizes, /* 2 = old ra81 microcode */
NULL, NULL, /* 3 = old ra60?? */
"ra60", ra60_sizes, /* 4 = ra60 */
"ra81", ra81_sizes, /* 5 = ra81 */
#if GYRE
/*
* This CDC partition hack depends on the fact that the
* Emulex SC41/MS controller is `version 6 model 6' and
* the current DEC devices are `version 5 model 6'.
*/
"cdc9771", cdc9771_sizes,
#define CDCTYPE 6 /* note that this is past the last real type */
#define ISCDC(sc) ((sc)->sc_micro == 0x66)
#endif
};

#define NTYPES 6

X/*
* Definition of the driver for autoconf.
*/
int udaprobe(), udaslave(), udaattach(), udadgo(), udaintr();
struct uba_ctlr *udaminfo[NUDA];
struct uba_device *udadinfo[NRA];

u_short udastd[] = { 0772150, 0772550, 0777550, 0 };
struct uba_driver udadriver =
{ udaprobe, udaslave, udaattach, udadgo, udastd, "ra", udadinfo, "uda",
udaminfo };

X/*
* More driver definitions, for generic MSCP code.
*/
int udadgram(), udactlrdone(), udaunconf(), udaonline(), udagotstatus();
int udaioerror(), udareplace(), udabb();

struct buf udautab[NRA]; /* per drive transfer queue */

struct mscp_driver udamscpdriver =
{ MAXUNIT, NRA, UNITSHIFT, udautab,
udadgram, udactlrdone, udaunconf,
udaonline, udagotstatus, udareplace, udaioerror, udabb };

X/*
* Miscellaneous private variables.
*/
struct buf rudabuf[NRA]; /* raw I/O buffer headers */

char udasr_bits[] = UDASR_BITS;

struct uba_device *udaip[NUDA][MAXUNIT];
/* inverting pointers: ctlr & unit => Unibus
device pointer */

int udaburst[NUDA] = {0}; /* burst size, per UDA50, zero => default;
in data space so patchable via adb */

daddr_t ra_dsize[NRA]; /* drive sizes, from on line end packets */

struct mscp udaslavereply; /* get unit status response packet, set
for udaslave by udaunconf, via udaintr */

static struct uba_ctlr *probeum;/* this is a hack---autoconf should pass ctlr
info to slave routine; instead, we remember
the last ctlr argument to probe */

int udawstart, udawatch(); /* watchdog timer */

X/*
* Externals
*/
int wakeup();
int hz;

X/*
* Poke at a supposed UDA50 to see if it is there.
* This routine duplicates some of the code in udainit() only
* because autoconf has not set up the right information yet.
* We have to do everything `by hand'.
*/
udaprobe(reg, ctlr, um)
caddr_t reg;
int ctlr;
struct uba_ctlr *um;
{
register int br, cvec;
register struct uda_softc *sc;
register struct udadevice *udaddr;
register struct mscp_info *mi;
int timeout, tries;

#ifdef VAX750
/*
* The UDA50 wants to share BDPs on 750s, but not on 780s or
* 8600s. (730s have no BDPs anyway.) Toward this end, we
* here set the `keep bdp' flag in the per-driver information
* if this is a 750. (We just need to do it once, but it is
* easiest to do it now, for each UDA50.)
*/
if (cpu == VAX_750)
udadriver.ud_keepbdp = 1;
#endif

probeum = um; /* remember for udaslave() */
#ifdef lint
br = 0; cvec = br; br = cvec; udaintr(0);
#endif
/*
* Set up the controller-specific generic MSCP driver info.
* Note that this should really be done in the (nonexistent)
* controller attach routine.
*/
sc = &uda_softc[ctlr];
mi = &sc->sc_mi;
mi->mi_md = &udamscpdriver;
mi->mi_um = um;
mi->mi_ip = udaip[ctlr];
mi->mi_cmd.mri_size = NCMD;
mi->mi_cmd.mri_desc = uda[ctlr].uda_ca.ca_cmddsc;
mi->mi_cmd.mri_ring = uda[ctlr].uda_cmd;
mi->mi_rsp.mri_size = NRSP;
mi->mi_rsp.mri_desc = uda[ctlr].uda_ca.ca_rspdsc;
mi->mi_rsp.mri_ring = uda[ctlr].uda_rsp;
mi->mi_wtab.av_forw = mi->mi_wtab.av_back = &mi->mi_wtab;

/*
* More controller specific variables. Again, this should
* be in the controller attach routine.
*/
if (udaburst[ctlr] == 0)
udaburst[ctlr] = DEFAULT_BURST;

/*
* Get an interrupt vector. Note that even if the controller
* does not respond, we keep the vector. This is not a serious
* problem; but it would be easily fixed if we had a controller
* attach routine. Sigh.
*/
sc->sc_ivec = (uba_hd[numuba].uh_lastiv -= 4);
udaddr = (struct udadevice *) reg;

/*
* Initialise the controller (partially). The UDA50 programmer's
* manual states that if initialisation fails, it should be retried
* at least once, but after a second failure the port should be
* considered `down'; it also mentions that the controller should
* initialise within ten seconds. Or so I hear; I have not seen
* this manual myself.
*
* N.B.: mfpr(TODR) will not work on uVaxen.
*/
tries = 0;
again:
udaddr->udaip = 0; /* start initialisation */
timeout = mfpr(TODR) + 1000; /* timeout in 10 seconds */
while ((udaddr->udasa & UDA_STEP1) == 0)
if (mfpr(TODR) > timeout)
goto bad;
udaddr->udasa = UDA_ERR | (NCMDL2 << 11) | (NRSPL2 << 8) | UDA_IE |
(sc->sc_ivec >> 2);
while ((udaddr->udasa & UDA_STEP2) == 0)
if (mfpr(TODR) > timeout)
goto bad;

/* should have interrupted by now */
return (sizeof (struct udadevice));
bad:
if (++tries < 2)
goto again;
return (0);
}

X/*
* Find a slave. We allow wildcard slave numbers (something autoconf
* is not really prepared to deal with); and we need to know the
* controller number to talk to the UDA. For the latter, we keep
* track of the last controller probed, since a controller probe
* immediately precedes all slave probes for that controller. For the
* former, we simply put the unit number into ui->ui_slave after we
* have found one.
*
* Note that by the time udaslave is called, the interrupt vector
* for the UDA50 has been set up (so that udaunconf() will be called).
*/
udaslave(ui, reg)
register struct uba_device *ui;
caddr_t reg;
{
register struct uba_ctlr *um = probeum;
register struct mscp *mp;
register struct uda_softc *sc;
int next = 0, type, timeout, tries, i;

#ifdef lint
i = 0; i = i;
#endif
/*
* Make sure the controller is fully initialised, by waiting
* for it if necessary.
*/
sc = &uda_softc[um->um_ctlr];
if (sc->sc_state == ST_RUN)
goto findunit;
tries = 0;
again:
if (udainit(ui->ui_ctlr))
return (0);
timeout = mfpr(TODR) + 1000; /* 10 seconds */
while (mfpr(TODR) < timeout)
if (sc->sc_state == ST_RUN) /* made it */
goto findunit;
if (++tries < 2)
goto again;
printf("uda%d: controller hung\n", um->um_ctlr);
return (0);

/*
* The controller is all set; go find the unit. Grab an
* MSCP packet and send out a Get Unit Status command, with
* the `next unit' modifier if we are looking for a generic
* unit. We set the `in slave' flag so that udaunconf()
* knows to copy the response to `udaslavereply'.
*/
findunit:
udaslavereply.mscp_opcode = 0;
sc->sc_flags |= SC_INSLAVE;
if ((mp = mscp_getcp(&sc->sc_mi, MSCP_DONTWAIT)) == NULL)
panic("udaslave"); /* `cannot happen' */
mp->mscp_opcode = M_OP_GETUNITST;
if (ui->ui_slave == '?') {
mp->mscp_unit = next;
mp->mscp_modifier = M_GUM_NEXTUNIT;
} else {
mp->mscp_unit = ui->ui_slave;
mp->mscp_modifier = 0;
}
*mp->mscp_seq.seq_addr |= MSCP_OWN | MSCP_INT;
i = ((struct udadevice *) reg)->udaip; /* initiate polling */
mp = &udaslavereply;
timeout = mfpr(TODR) + 1000;
while (mfpr(TODR) < timeout)
if (mp->mscp_opcode)
goto gotit;
printf("uda%d: no response to Get Unit Status request\n",
um->um_ctlr);
sc->sc_flags &= ~SC_INSLAVE;
return (0);

gotit:
sc->sc_flags &= ~SC_INSLAVE;

/*
* Got a slave response. If the unit is there, use it.
*/
switch (mp->mscp_status & M_ST_MASK) {

case M_ST_SUCCESS: /* worked */
case M_ST_AVAILABLE: /* found another drive */
break; /* use it */

case M_ST_OFFLINE:
/*
* Figure out why it is off line. It may be because
* it is nonexistent, or because it is spun down, or
* for some other reason.
*/
switch (mp->mscp_status & ~M_ST_MASK) {

case M_OFFLINE_UNKNOWN:
/*
* No such drive, and there are none with
* higher unit numbers either, if we are
* using M_GUM_NEXTUNIT.
*/
return (0);

case M_OFFLINE_UNMOUNTED:
/*
* The drive is not spun up. Use it anyway.
*
* N.B.: this seems to be a common occurrance
* after a power failure. The first attempt
* to bring it on line seems to spin it up
* (and thus takes several minutes). Perhaps
* we should note here that the on-line may
* take longer than usual.
*/
break;

default:
/*
* In service, or something else equally unusable.
*/
printf("uda%d: unit %d off line: ", um->um_ctlr,
mp->mscp_unit);
mscp_printevent(mp);
goto try_another;
}
break;

default:
printf("uda%d: unable to get unit status: ", um->um_ctlr);
mscp_printevent(mp);
return (0);
}

/*
* Does this ever happen? What (if anything) does it mean?
*/
if (mp->mscp_unit < next) {
printf("uda%d: unit %d, next %d\n",
um->um_ctlr, mp->mscp_unit, next);
return (0);
}

if (mp->mscp_unit >= MAXUNIT) {
printf("uda%d: cannot handle unit number %d (max is %d)\n",
um->um_ctlr, mp->mscp_unit, MAXUNIT - 1);
return (0);
}

/*
* See if we already handle this drive.
* (Only likely if ui->ui_slave=='?'.)
*/
if (udaip[um->um_ctlr][mp->mscp_unit] != NULL)
goto try_another;

/*
* Make sure we know about this kind of drive.
* Others say we should treat unknowns as RA81s; I am
* not sure this is safe.
*/
type = mp->mscp_guse.guse_drivetype;
if (type >= NTYPES || udatypes[type].ut_name == 0) {
printf("uda%d: unit %d (media ID `", um->um_ctlr,
mp->mscp_unit);
uda_decode_media(mp->mscp_guse.guse_mediaid);
printf("') is of unknown type %d; ignored\n", type);
try_another:
if (ui->ui_slave != '?')
return (0);
next = mp->mscp_unit + 1;
goto findunit;
}
#if GYRE
if (ISCDC(sc))
type = CDCTYPE;
#endif

/*
* Voila!
*/
ui->ui_type = type;
ui->ui_flags = 0; /* not on line, nor anything else */
ui->ui_slave = mp->mscp_unit;
return (1);
}

X/*
* Decode and print the media ID. It is made up of five 5-bit
* `characters' and 7 bits of numeric information. BITS(i)
* selects character i's bits; CHAR returns the corresponding
* character.
*/
uda_decode_media(id)
register long id;
{
int c4, c3, c2, c1, c0;
#define BITS(i) ((id >> ((i) * 5 + 7)) & 0x1f)
#define CHAR(c) ((c) ? (c) + '@' : ' ')

c4 = BITS(4);
c3 = BITS(3);
c2 = BITS(2);
c1 = BITS(1);
c0 = BITS(0);
printf("%c%c %c%c%c%d", CHAR(c4), CHAR(c3), CHAR(c2),
CHAR(c1), CHAR(c0), id & 0x7f);
#undef BITS
#undef CHAR
}

X/*
* Attach a found slave. Make sure the watchdog timer is running.
* If this disk is being profiled, fill in the `mspw' value (used by
* what?). Set up the inverting pointer, and attempt to bring the
* drive on line.
*/
udaattach(ui)
register struct uba_device *ui;
{

if (udawstart == 0) {
timeout(udawatch, (caddr_t) 0, hz);
udawstart++;
}
if (ui->ui_dk >= 0)
dk_mspw[ui->ui_dk] = 1.0 / (60 * 31 * 256); /* approx */
udaip[ui->ui_ctlr][ui->ui_slave] = ui;
(void) uda_bringonline(&uda_softc[ui->ui_ctlr], ui, 1);
/* should we get its status too? */
}

X/*
* Initialise a UDA50. Return true iff something goes wrong.
*/
udainit(ctlr)
int ctlr;
{
register struct uda_softc *sc;
register struct udadevice *udaddr;
struct uba_ctlr *um;
int timo, ubinfo;

sc = &uda_softc[ctlr];
um = udaminfo[ctlr];
if ((sc->sc_flags & SC_MAPPED) == 0) {
/*
* Map the communication area and command and
* response packets into Unibus space.
*/
ubinfo = uballoc(um->um_ubanum, (caddr_t) &uda[ctlr],
sizeof (struct uda), UBA_CANTWAIT);
if (ubinfo == 0) {
printf("uda%d: uballoc map failed\n", ctlr);
return (-1);
}
sc->sc_uda = (struct uda *) (ubinfo & 0x3ffff);
sc->sc_flags |= SC_MAPPED;
}

/*
* While we are thinking about it, reset the next command
* and response indicies.
*/
sc->sc_mi.mi_cmd.mri_next = 0;
sc->sc_mi.mi_rsp.mri_next = 0;

/*
* Start up the hardware initialisation sequence.
*/
#define STEP0MASK (UDA_ERR | UDA_STEP4 | UDA_STEP3 | UDA_STEP2 | \
UDA_STEP1 | UDA_NV)

sc->sc_state = ST_IDLE; /* in case init fails */
udaddr = (struct udadevice *) um->um_addr;
udaddr->udaip = 0;
timo = mfpr(TODR) + 1000;
while ((udaddr->udasa & STEP0MASK) == 0) {
if (mfpr(TODR) > timo) {
printf("uda%d: timeout during init\n", ctlr);
return (-1);
}
}
if ((udaddr->udasa & STEP0MASK) != UDA_STEP1) {
printf("uda%d: init failed, sa=%b\n", ctlr,
udaddr->udasa, udasr_bits);
return (-1);
}

/*
* Success! Record new state, and start step 1 initialisation.
* The rest is done in the interrupt handler.
*/
sc->sc_state = ST_STEP1;
udaddr->udasa = UDA_ERR | (NCMDL2 << 11) | (NRSPL2 << 8) | UDA_IE |
(sc->sc_ivec >> 2);
return (0);
}

X/*
* Open a drive.
*/
X/*ARGSUSED*/
udaopen(dev, flag)
dev_t dev;
int flag;
{
register int unit;
register struct uba_device *ui;
register struct uda_softc *sc;
int s;

/*
* Make sure this is a reasonable open request.
*/
unit = udaunit(dev);
if (unit >= NRA || (ui = udadinfo[unit]) == 0 || ui->ui_alive == 0)
return (ENXIO);

/*
* Make sure the controller is running, by (re)initialising it if
* necessary.
*/
sc = &uda_softc[ui->ui_ctlr];
s = spl5();
if (sc->sc_state != ST_RUN) {
if (sc->sc_state == ST_IDLE && udainit(ui->ui_ctlr)) {
splx(s);
return (EIO);
}
/*
* In case it does not come up, make sure we will be
* restarted in 10 seconds. This corresponds to the
* 10 second timeouts in udaprobe() and udaslave().
*/
sc->sc_flags |= SC_DOWAKE;
timeout(wakeup, (caddr_t) sc, 10 * hz);
sleep((caddr_t) sc, PRIBIO);
if (sc->sc_state != ST_RUN) {
splx(s);
printf("uda%d: controller hung\n", ui->ui_ctlr);
return (EIO);
}
untimeout(wakeup, (caddr_t) sc);
}
if ((ui->ui_flags & UNIT_ONLINE) == 0) {
/*
* Bring the drive on line so we can find out how
* big it is. If it is not spun up, it will not
* come on line; this cannot really be considered
* an `error condition'.
*/
if (uda_bringonline(sc, ui, 0)) {
splx(s);
printf("ra%d: drive will not come on line\n", unit);
return (EIO);
}
}
splx(s);
return (0);
}

X/*
* Bring a drive on line. In case it fails to respond, we set
* a timeout on it. The `nosleep' parameter should be set if
* we are to spin-wait; otherwise this must be called at spl5().
*/
uda_bringonline(sc, ui, nosleep)
register struct uda_softc *sc;
register struct uba_device *ui;
int nosleep;
{
register struct mscp *mp;
int i;

if (nosleep) {
mp = mscp_getcp(&sc->sc_mi, MSCP_DONTWAIT);
if (mp == NULL)
return (-1);
} else
mp = mscp_getcp(&sc->sc_mi, MSCP_WAIT);
mp->mscp_opcode = M_OP_ONLINE;
mp->mscp_unit = ui->ui_slave;
mp->mscp_cmdref = (long) &ui->ui_flags;
*mp->mscp_seq.seq_addr |= MSCP_OWN | MSCP_INT;
i = ((struct udadevice *) ui->ui_addr)->udaip;

if (nosleep) {
i = mfpr(TODR) + 1000;
while ((ui->ui_flags & UNIT_ONLINE) == 0)
if (mfpr(TODR) > i)
return (-1);
} else {
timeout(wakeup, (caddr_t) &ui->ui_flags, 10 * hz);
sleep((caddr_t) &ui->ui_flags, PRIBIO);
if ((ui->ui_flags & UNIT_ONLINE) == 0)
return (-1);
untimeout(wakeup, (caddr_t) &ui->ui_flags);
}
return (0); /* made it */
}

X/*
* Queue a transfer request, and if possible, hand it to the controller.
*
* This routine is broken into two so that the internal version
* udastrat1() can be called by the (nonexistent, as yet) bad block
* revectoring routine.
*/
udastrategy(bp)
register struct buf *bp;
{
register int unit;
register struct uba_device *ui;
register struct size *st;
daddr_t sz, maxsz;

/*
* Make sure this is a reasonable drive to use.
*/
if ((unit = udaunit(bp->b_dev)) >= NRA ||
(ui = udadinfo[unit]) == NULL || ui->ui_alive == 0) {
bp->b_error = ENXIO;
bp->b_flags |= B_ERROR;
biodone(bp);
return;
}

/*
* Determine the size of the transfer, and make sure it is
* within the boundaries of the drive.
*/
sz = (bp->b_bcount + 511) >> 9;
st = &udatypes[ui->ui_type].ut_sizes[udapart(bp->b_dev)];
if ((maxsz = st->nblocks) < 0)
maxsz = ra_dsize[unit] - st->blkoff;
if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz ||
st->blkoff >= ra_dsize[unit]) {
/* if exactly at end of disk, return an EOF */
if (bp->b_blkno == maxsz)
bp->b_resid = bp->b_bcount;
else {
bp->b_error = EINVAL;
bp->b_flags |= B_ERROR;
}
biodone(bp);
return;
}
udastrat1(bp);
}

X/*
* Work routine for udastrategy.
*/
udastrat1(bp)
register struct buf *bp;
{
register int unit = udaunit(bp->b_dev);
register struct uba_ctlr *um;
register struct buf *dp;
struct uba_device *ui;
int s = spl5();

/*
* Append the buffer to the drive queue, and if it is not
* already there, the drive to the controller queue. (However,
* if the drive queue is marked to be requeued, we must be
* awaiting an on line or get unit status command; in this
* case, leave it off the controller queue.)
*/
um = (ui = udadinfo[unit])->ui_mi;
dp = &udautab[unit];
APPEND(bp, dp, av_forw);
if (dp->b_active == 0 && (ui->ui_flags & UNIT_REQUEUE) == 0) {
APPEND(dp, &um->um_tab, b_forw);
dp->b_active++;
}

/*
* Start activity on the controller. Note that unlike other
* Unibus drivers, we must always do this, not just when the
* controller is not active.
*/
udastart(um);
splx(s);
}

X/*
* Start up whatever transfers we can find.
* Note that udastart() must be called at spl5().
*/
udastart(um)
register struct uba_ctlr *um;
{
register struct uda_softc *sc = &uda_softc[um->um_ctlr];
register struct buf *bp, *dp;
register struct mscp *mp;
struct uba_device *ui;
struct udadevice *udaddr;
int i;

#ifdef lint
i = 0; i = i;
#endif
/*
* If it is not running, try (again and again...) to initialise
* it. If it is currently initialising just ignore it for now.
*/
if (sc->sc_state != ST_RUN) {
if (sc->sc_state == ST_IDLE && udainit(um->um_ctlr))
printf("uda%d: still hung\n", um->um_ctlr);
return;
}

/*
* If um_cmd is nonzero, this controller is on the Unibus
* resource wait queue. It will not help to try more requests;
* instead, when the Unibus unblocks and calls udadgo(), we
* will call udastart() again.
*/
if (um->um_cmd)
return;

sc->sc_flags |= SC_INSTART;
udaddr = (struct udadevice *) um->um_addr;

loop:
/*
* Service the drive at the head of the queue. We take exactly
* one transfer from this drive, then move it to the end of the
* controller queue, so as to get more drive overlap.
*/
if ((dp = um->um_tab.b_actf) == NULL) {
um->um_tab.b_active = 0;
goto out;
}

/*
* Get the first request from the drive queue. There must be
* one, or something is rotten.
*/
if ((bp = dp->b_actf) == NULL)
panic("udastart: bp==NULL\n");

if (udaddr->udasa & UDA_ERR) { /* ctlr fatal error */
udasaerror(um);
goto out;
}

/*
* Get an MSCP packet, then figure out what to do. If
* we cannot get a command packet, the command ring may
* be too small: We should have at least as many command
* packets as credits, for best performance.
*/
if ((mp = mscp_getcp(&sc->sc_mi, MSCP_DONTWAIT)) == NULL) {
if (sc->sc_mi.mi_credits > MSCP_MINCREDITS &&
(sc->sc_flags & SC_GRIPED) == 0) {
log(LOG_NOTICE, "uda%d: command ring too small\n",
um->um_ctlr);
sc->sc_flags |= SC_GRIPED;/* complain only once */
}
goto out;
}

/*
* Mark the controller active, but do not move the drive off
* the controller queue: ubago() wants it there.
*/
um->um_tab.b_active = 1;

/*
* Bring the drive on line if it is not already. Get its status
* if we do not already have it. Otherwise just start the transfer.
*/
ui = udadinfo[udaunit(bp->b_dev)];
if ((ui->ui_flags & UNIT_ONLINE) == 0) {
mp->mscp_opcode = M_OP_ONLINE;
goto common;
}
if ((ui->ui_flags & UNIT_HAVESTATUS) == 0) {
mp->mscp_opcode = M_OP_GETUNITST;
common:
if (ui->ui_flags & UNIT_REQUEUE) panic("udastart");
/*
* Take the drive off the controller queue. When the
* command finishes, make sure the drive is requeued.
*/
um->um_tab.b_actf = dp->b_forw;
dp->b_active = 0;
ui->ui_flags |= UNIT_REQUEUE;
mp->mscp_unit = ui->ui_slave;
*mp->mscp_seq.seq_addr |= MSCP_OWN | MSCP_INT;
i = udaddr->udaip;
goto loop;
}

mp->mscp_opcode = (bp->b_flags & B_READ) ? M_OP_READ : M_OP_WRITE;
mp->mscp_unit = ui->ui_slave;
mp->mscp_seq.seq_lbn = bp->b_blkno +
udatypes[ui->ui_type].ut_sizes[udapart(bp->b_dev)].blkoff;
mp->mscp_seq.seq_bytecount = bp->b_bcount;
/* mscp_cmdref and mscp_buffer are filled in by mscp_go() */

/*
* Drop the packet pointer into the `command' field so udadgo()
* can tell what to start. If ubago returns 1, we can do another
* transfer. If not, um_cmd will still point at mp, so we will
* know that we are waiting for resources.
*/
um->um_cmd = (int) mp;
if (ubago(ui))
goto loop;

/*
* All done, or blocked in ubago().
*/
out:
sc->sc_flags &= ~SC_INSTART;
}

X/*
* Start a transfer.
*
* If we are not called from within udastart(), we must have been
* blocked, so call udastart to do more requests (if any). If
* this calls us again immediately we will not recurse, because
* that time we will be in udastart(). Clever....
*/
udadgo(um)
register struct uba_ctlr *um;
{
struct uda_softc *sc = &uda_softc[um->um_ctlr];
int i;

#ifdef lint
i = 0; i = i;
#endif
/*
* Fill in the MSCP packet and move the buffer to the
* I/O wait queue. Mark the controller as no longer on
* the resource queue, and initiate polling.
*/
mscp_go(um, &sc->sc_mi, (struct mscp *) um->um_cmd);
um->um_cmd = 0;
i = ((struct udadevice *) um->um_addr)->udaip;

if ((sc->sc_flags & SC_INSTART) == 0)
udastart(um);
}

X/*
* The error bit was set in the controller status register. Gripe,
* reset the controller, requeue pending transfers.
*/
udasaerror(um)
register struct uba_ctlr *um;
{

printf("uda%d: controller error, sa=%b\n", um->um_ctlr,
((struct udadevice *) um->um_addr)->udasa, udasr_bits);
mscp_requeue(&uda_softc[um->um_ctlr].sc_mi);
(void) udainit(um->um_ctlr);
}

X/*
* Interrupt routine. Depending on the state of the controller,
* continue initialisation, or acknowledge command and response
* interrupts, and process responses.
*/
udaintr(ctlr)
int ctlr;
{
register struct uba_ctlr *um = udaminfo[ctlr];
register struct uda_softc *sc = &uda_softc[ctlr];
register struct udadevice *udaddr = (struct udadevice *) um->um_addr;
register struct uda *ud;
register struct mscp *mp;
register int i;

sc->sc_wticks = 0; /* reset interrupt watchdog */

/*
* Combinations during steps 1, 2, and 3: STEPnMASK
* corresponds to which bits should be tested;
* STEPnGOOD corresponds to the pattern that should
* appear after the interrupt from STEPn initialisation.
* All steps test the bits in ALLSTEPS.
*/
#define ALLSTEPS (UDA_ERR | UDA_STEP4 | UDA_STEP3 | UDA_STEP2 | \
UDA_STEP1 | UDA_IE)

#define STEP1MASK (ALLSTEPS | UDA_NCNRMASK)
#define STEP1GOOD (UDA_STEP2 | UDA_IE | (NCMDL2 << 3) | NRSPL2)

#define STEP2MASK (ALLSTEPS | UDA_IVECMASK)
#define STEP2GOOD (UDA_STEP3 | UDA_IE | (sc->sc_ivec >> 2))

#define STEP3MASK ALLSTEPS
#define STEP3GOOD UDA_STEP4

switch (sc->sc_state) {

case ST_IDLE:
/*
* Ignore unsolicited interrupts.
*/
log(LOG_WARNING, "uda%d: stray intr\n", ctlr);
return;

case ST_STEP1:
/*
* Begin step two initialisation.
*/
if ((udaddr->udasa & STEP1MASK) != STEP1GOOD) {
i = 1;
initfailed:
printf("uda%d: init step %d failed, sa=%b\n",
ctlr, i, udaddr->udasa, udasr_bits);
sc->sc_state = ST_IDLE;
if (sc->sc_flags & SC_DOWAKE) {
sc->sc_flags &= ~SC_DOWAKE;
wakeup((caddr_t) sc);
}
return;
}
udaddr->udasa = (int) &sc->sc_uda->uda_ca.ca_rspdsc[0] |
(cpu == VAX_780 || cpu == VAX_8600 ? UDA_PI : 0);
sc->sc_state = ST_STEP2;
return;

case ST_STEP2:
/*
* Begin step 3 initialisation.
*/
if ((udaddr->udasa & STEP2MASK) != STEP2GOOD) {
i = 2;
goto initfailed;
}
udaddr->udasa = ((int) &sc->sc_uda->uda_ca.ca_rspdsc[0]) >> 16;
sc->sc_state = ST_STEP3;
return;

case ST_STEP3:
/*
* Set controller characteristics (finish initialisation).
*/
if ((udaddr->udasa & STEP3MASK) != STEP3GOOD) {
i = 3;
goto initfailed;
}
i = udaddr->udasa & 0xff;
if (i != sc->sc_micro) {
sc->sc_micro = i;
printf("uda%d: version %d model %d\n",
ctlr, i & 0xf, i >> 4);
}

/*
* Present the burst size, then remove it. Why this
* should be done this way, I have no idea.
*
* Note that this assumes udaburst[ctlr] > 0.
*/
udaddr->udasa = UDA_GO | (udaburst[ctlr] - 1) << 2;
udaddr->udasa = UDA_GO;
printf("uda%d: DMA burst size set to %d\n",
ctlr, udaburst[ctlr]);

udainitds(ctlr); /* initialise data structures */

/*
* Before we can get a command packet, we need some
* credits. Fake some up to keep mscp_getcp() happy,
* get a packet, and cancel all credits (the right
* number should come back in the response to the
* SCC packet).
*/
sc->sc_mi.mi_credits = MSCP_MINCREDITS + 1;
mp = mscp_getcp(&sc->sc_mi, MSCP_DONTWAIT);
if (mp == NULL) /* `cannot happen' */
panic("udaintr");
sc->sc_mi.mi_credits = 0;
mp->mscp_opcode = M_OP_SETCTLRC;
/*
* WHICH OF THE FOLLOWING ARE REQUIRED??
* IT SURE WOULD BE NICE IF DEC SOLD DOCUMENTATION
* FOR THEIR OWN CONTROLLERS.
*/
mp->mscp_unit = 0;
mp->mscp_flags = 0;
mp->mscp_modifier = 0;
mp->mscp_seq.seq_bytecount = 0;
mp->mscp_seq.seq_buffer = 0;
mp->mscp_sccc.sccc_errlgfl = 0;
mp->mscp_sccc.sccc_copyspd = 0;
mp->mscp_sccc.sccc_ctlrflags = M_CF_ATTN | M_CF_MISC |
M_CF_THIS;
*mp->mscp_sccc.sccc_addr |= MSCP_OWN | MSCP_INT;
i = udaddr->udaip;
sc->sc_state = ST_SETCHAR;
return;

case ST_SETCHAR:
case ST_RUN:
/*
* Handle Set Ctlr Characteristics responses and operational
* responses (via mscp_dorsp).
*/
break;

default:
printf("uda%d: driver bug, state %d\n", ctlr, sc->sc_state);
panic("udastate");
}

if (udaddr->udasa & UDA_ERR) { /* ctlr fatal error */
udasaerror(um);
return;
}

ud = &uda[ctlr];

/*
* Handle buffer purge requests.
* I have never seen these to work usefully, thus the log().
*/
if (ud->uda_ca.ca_bdp) {
log(LOG_DEBUG, "uda%d: purge bdp %d\n",
ctlr, ud->uda_ca.ca_bdp);
UBAPURGE(um->um_hd->uh_uba, ud->uda_ca.ca_bdp);
ud->uda_ca.ca_bdp = 0;
udaddr->udasa = 0; /* signal purge complete */
}

/*
* Check for response and command ring transitions.
*/
if (ud->uda_ca.ca_rspint) {
ud->uda_ca.ca_rspint = 0;
mscp_dorsp(&sc->sc_mi);
}
if (ud->uda_ca.ca_cmdint) {
ud->uda_ca.ca_cmdint = 0;
MSCP_DOCMD(&sc->sc_mi);
}
udastart(um);
}

X/*
* Initialise the various data structures that control the UDA50.
*/
udainitds(ctlr)
int ctlr;
{
register struct uda *ud = &uda[ctlr];
register struct uda *uud = uda_softc[ctlr].sc_uda;
register struct mscp *mp;
register int i;

for (i = 0, mp = ud->uda_rsp; i < NRSP; i++, mp++) {
ud->uda_ca.ca_rspdsc[i] = MSCP_OWN | MSCP_INT |
(long) &uud->uda_rsp[i].mscp_cmdref;
mp->mscp_seq.seq_addr = &ud->uda_ca.ca_rspdsc[i];
mp->mscp_msglen = MSCP_MSGLEN;
}
for (i = 0, mp = ud->uda_cmd; i < NCMD; i++, mp++) {
ud->uda_ca.ca_cmddsc[i] = MSCP_INT |
(long) &uud->uda_cmd[i].mscp_cmdref;
mp->mscp_seq.seq_addr = &ud->uda_ca.ca_cmddsc[i];
mp->mscp_msglen = MSCP_MSGLEN;
}
}

X/*
* Handle an error datagram. All we do now is decode it.
*/
udadgram(um, mp)
struct uba_ctlr *um;
struct mscp *mp;
{

mscp_decodeerror(um, mp);
}

X/*
* The Set Controller Characteristics command finished.
* Record the new state of the controller.
*/
udactlrdone(um, mp)
register struct uba_ctlr *um;
struct mscp *mp;
{
register struct uda_softc *sc = &uda_softc[um->um_ctlr];

if ((mp->mscp_status & M_ST_MASK) == M_ST_SUCCESS)
sc->sc_state = ST_RUN;
else {
printf("uda%d: SETCTLRC failed: ",
um->um_ctlr, mp->mscp_status);
mscp_printevent(mp);
sc->sc_state = ST_IDLE;
}
um->um_tab.b_active = 0;
if (sc->sc_flags & SC_DOWAKE) {
sc->sc_flags &= ~SC_DOWAKE;
wakeup((caddr_t) sc);
}
}

X/*
* Received a response from an as-yet unconfigured drive. Configure it
* in, if possible.
*/
udaunconf(um, mp)
struct uba_ctlr *um;
register struct mscp *mp;
{

/*
* If it is a slave response, copy it to udaslavereply for
* udaslave() to look at.
*/
if (mp->mscp_opcode == (M_OP_GETUNITST | M_OP_END) &&
(uda_softc[um->um_ctlr].sc_flags & SC_INSLAVE) != 0) {
udaslavereply = *mp;
return (MSCP_DONE);
}

/*
* Otherwise, it had better be an available attention response.
*/
if (mp->mscp_opcode != M_OP_AVAILATTN)
return (MSCP_FAILED);

/* do what autoconf does */
return (MSCP_FAILED); /* not yet, arwhite, not yet */
}

X/*
* A drive came on line. Check its type and size. Return DONE if
* we think the drive is truly on line. In any case, awaken anyone
* sleeping on the drive on-line-ness.
*/
udaonline(ui, mp)
register struct uba_device *ui;
struct mscp *mp;
{
register int type;

wakeup((caddr_t) &ui->ui_flags);
if ((mp->mscp_status & M_ST_MASK) != M_ST_SUCCESS) {
printf("uda%d: attempt to bring ra%d on line failed: ",
ui->ui_ctlr, ui->ui_unit);
mscp_printevent(mp);
return (MSCP_FAILED);
}

type = mp->mscp_onle.onle_drivetype;
if (type >= NTYPES || udatypes[type].ut_name == 0) {
printf("uda%d: ra%d: unknown type %d\n",
ui->ui_ctlr, ui->ui_unit, type);
return (MSCP_FAILED);
}
#if GYRE /* special partition hack */
if (ISCDC(&uda_softc[ui->ui_ctlr]))
type = CDCTYPE;
#endif
/*
* Note any change of types. Not sure if we should do
* something special about them, or if so, what....
*/
if (type != ui->ui_type) {
printf("ra%d: changed types! was %s\n",
ui->ui_unit, udatypes[ui->ui_type].ut_name);
ui->ui_type = type;
}
ra_dsize[ui->ui_unit] = (daddr_t) mp->mscp_onle.onle_unitsize;
printf("ra%d: %s, size = %d sectors\n",
ui->ui_unit, udatypes[type].ut_name, ra_dsize[ui->ui_unit]);
return (MSCP_DONE);
}

X/*
* We got some (configured) unit's status. Return DONE if it succeeded.
*/
udagotstatus(ui, mp)
register struct uba_device *ui;
register struct mscp *mp;
{

if ((mp->mscp_status & M_ST_MASK) != M_ST_SUCCESS) {
printf("uda%d: attempt to get status for ra%d failed: ",
ui->ui_ctlr, ui->ui_unit);
mscp_printevent(mp);
return (MSCP_FAILED);
}
/* need to record later for bad block forwarding - for now, print */
printf("\
ra%d: unit %d, nspt %d, group %d, ntpc %d, rctsize %d, nrpt %d, nrct %d\n",
ui->ui_unit, mp->mscp_unit,
mp->mscp_guse.guse_nspt, mp->mscp_guse.guse_group,
mp->mscp_guse.guse_ntpc, mp->mscp_guse.guse_rctsize,
mp->mscp_guse.guse_nrpt, mp->mscp_guse.guse_nrct);
return (MSCP_DONE);
}

X/*
* A transfer failed. We get a chance to fix or restart it.
* Need to write the bad block forwaring code first....
*/
X/*ARGSUSED*/
udaioerror(ui, mp, bp)
register struct uba_device *ui;
register struct mscp *mp;
struct buf *bp;
{

if (mp->mscp_flags & M_EF_BBLKR) {
/*
* A bad block report. Eventually we will
* restart this transfer, but for now, just
* log it and give up.
*/
log(LOG_ERR, "ra%d: bad block report: %d%s\n",
ui->ui_unit, mp->mscp_seq.seq_lbn,
mp->mscp_flags & M_EF_BBLKU ? " + others" : "");
} else {
/*
* What the heck IS a `serious exception' anyway?
* IT SURE WOULD BE NICE IF DEC SOLD DOCUMENTATION
* FOR THEIR OWN CONTROLLERS.
*/
if (mp->mscp_flags & M_EF_SEREX)
log(LOG_ERR, "ra%d: serious exception reported\n",
ui->ui_unit);
}
return (MSCP_FAILED);
}

X/*
* A replace operation finished.
*/
X/*ARGSUSED*/
udareplace(ui, mp)
struct uba_device *ui;
struct mscp *mp;
{

panic("udareplace");
}

X/*
* A bad block related operation finished.
*/
X/*ARGSUSED*/
udabb(ui, mp, bp)
struct uba_device *ui;
struct mscp *mp;
struct buf *bp;
{

panic("udabb");
}


X/*
* Raw read request.
*/
udaread(dev, uio)
dev_t dev;
struct uio *uio;
{
register int unit = udaunit(dev);

if (unit >= NRA)
return (ENXIO);
return (physio(udastrategy, &rudabuf[unit], dev, B_READ,
minphys, uio));
}

X/*
* Raw write request.
*/
udawrite(dev, uio)
dev_t dev;
struct uio *uio;
{
register int unit = udaunit(dev);

if (unit >= NRA)
return (ENXIO);
return (physio(udastrategy, &rudabuf[unit], dev, B_WRITE,
minphys, uio));
}

#ifdef notyet
X/*
* I/O controls. Not yet!
*/
udaioctl(dev, cmd, flag, data)
dev_t dev;
int cmd, flag;
caddr_t data;
{
int error = 0;
register int unit = udaunit(dev);

if (unit >= NRA || uddinfo[unit] == NULL)
return (ENXIO);

switch (cmd) {

case UDAIOCREPLACE:
/*
* Initiate bad block replacement for the given LBN.
* (Should we allow modifiers?)
*/
error = EOPNOTSUPP;
break;

case UDAIOCGMICRO:
/*
* Return the microcode revision for the UDA50 running
* this drive.
*/
*(int *) data = uda_softc[uddinfo[unit]->ui_ctlr].sc_micro;
break;

case UDAIOCGSIZE:
/*
* Return the size (in 512 byte blocks) of this
* disk drive.
*/
*(daddr_t *) data = ra_dsize[unit];
break;

default:
error = EINVAL;
break;
}
return (error);
}
#endif

X/*
* A Unibus reset has occurred on UBA uban. Reinitialise the controller(s)
* on that Unibus, and requeue outstanding I/O.
*/
udareset(uban)
int uban;
{
register struct uba_ctlr *um;
register struct uba_device *ui;
register struct uda_softc *sc;
register int unit, ctlr;
struct buf *dp;

for (ctlr = 0, sc = uda_softc; ctlr < NUDA; ctlr++, sc++) {
if ((um = udaminfo[ctlr]) == NULL || um->um_ubanum != uban ||
um->um_alive == 0)
continue;
printf(" uda%d", ctlr);

/*
* Our BDP (if any) is gone; our command (if any) is
* flushed; the device is no longer mapped; and the
* UDA50 is not yet initialised.
*/
if (um->um_bdp) {
printf("<%d>", (um->um_bdp >> 28) & 15);
um->um_bdp = 0;
}
um->um_ubinfo = 0;
um->um_cmd = 0;
sc->sc_flags &= ~SC_MAPPED;
sc->sc_state = ST_IDLE;

/* reset queues and requeue pending transfers */
mscp_requeue(&sc->sc_mi);

/*
* If it fails to initialise we will notice later and
* try again (and again...). Do not call udastart()
* here; it will be done after the controller finishes
* initialisation.
*/
if (udainit(ctlr))
printf(" (hung)");
}
}

X/*
* Watchdog timer: If the controller is active, and no interrupts
* have occurred for 30 seconds, assume it has gone away.
*/
udawatch()
{
register int i, unit;
register struct uba_ctlr *um;
register struct uda_softc *sc;

timeout(udawatch, (caddr_t) 0, hz); /* every second */
for (i = 0, sc = uda_softc; i < NUDA; i++, sc++) {
if ((um = udaminfo[i]) == 0 || um->um_alive == 0)
continue;
if (um->um_tab.b_active ||
sc->sc_mi.mi_wtab.av_forw != &sc->sc_mi.mi_wtab ||
sc->sc_state != ST_IDLE && sc->sc_state != ST_RUN)
goto active;
for (unit = 0; unit < NRA; unit++)
if (udautab[unit].b_active &&
udadinfo[unit]->ui_mi == um)
goto active;
sc->sc_wticks = 0;
continue;
active:
if (++sc->sc_wticks >= 30) {
sc->sc_wticks = 0;
printf("uda%d: lost interrupt\n", i);
ubareset(um->um_ubanum);
}
}
}

X/*
* Do a panic dump. We set up the controller for one command packet
* and one response packet, for which we use `struct uda1'.
*/
struct uda1 {
struct uda1ca uda1_ca; /* communications area */
struct mscp uda1_rsp; /* response packet */
struct mscp uda1_cmd; /* command packet */
} uda1;

#define DBSIZE 32 /* dump 16K at a time */

udadump(dev)
dev_t dev;
{
struct udadevice *udaddr;
struct uda1 *ud_ubaddr;
char *start;
int num, blk, unit, maxsz, blkoff, reg;
register struct uba_regs *uba;
register struct uba_device *ui;
register struct uda1 *ud;
register struct pte *io;
register int i;

/*
* Make sure the device is a reasonable place on which to dump.
*/
unit = udaunit(dev);
if (unit >= NRA)
return (ENXIO);
#define phys(cast, addr) ((cast) ((int) addr & 0x7fffffff))
ui = phys(struct uba_device *, udadinfo[unit]);
if (ui == NULL || ui->ui_alive == 0)
return (ENXIO);

/*
* Find and initialise the UBA; get the physical address of the
* device registers, and of communications area and command and
* response packet.
*/
uba = phys(struct uba_hd *, ui->ui_hd)->uh_physuba;
ubainit(uba);
udaddr = (struct udadevice *) ui->ui_physaddr;
ud = phys(struct uda1 *, &uda1);

/*
* Map the ca+packets into Unibus I/O space so the UDA50 can get
* at them. Use the registers at the end of the Unibus map (since
* we will use the registers at the beginning to map the memory
* we are dumping).
*/
num = btoc(sizeof (struct uda1)) + 1;
reg = NUBMREG - num;
io = &uba->uba_map[reg];
for (i = 0; i < num; i++)
*(int *)io++ = UBAMR_MRV | (btop(ud) + i);
ud_ubaddr = (struct uda1 *) (((int) ud & PGOFSET) | (reg << 9));

/*
* Initialise the controller, with one command and one response
* packet.
*/
udaddr->udaip = 0;
if (udadumpwait(udaddr, UDA_STEP1))
return (EFAULT);
udaddr->udasa = UDA_ERR;
if (udadumpwait(udaddr, UDA_STEP2))
return (EFAULT);
udaddr->udasa = (int) &ud_ubaddr->uda1_ca.ca_rspdsc;
if (udadumpwait(udaddr, UDA_STEP3))
return (EFAULT);
udaddr->udasa = ((int) &ud_ubaddr->uda1_ca.ca_rspdsc) >> 16;
if (udadumpwait(udaddr, UDA_STEP4))
return (EFAULT);
uda_softc[ui->ui_ctlr].sc_micro = udaddr->udasa & 0xff;
udaddr->udasa = UDA_GO;

/*
* Set up the command and response descriptor, then set the
* controller characteristics and bring the drive on line.
* Note that all uninitialised locations in uda1_cmd are zero.
*/
ud->uda1_ca.ca_rspdsc = (long) &ud_ubaddr->uda1_rsp.mscp_cmdref;
ud->uda1_ca.ca_cmddsc = (long) &ud_ubaddr->uda1_cmd.mscp_cmdref;
/* ud->uda1_cmd.mscp_sccc.sccc_ctlrflags = 0; */
/* ud->uda1_cmd.mscp_sccc.sccc_version = 0; */
if (udadumpcmd(M_OP_SETCTLRC, ud, ui))
return (EFAULT);
ud->uda1_cmd.mscp_unit = ui->ui_slave;
if (udadumpcmd(M_OP_ONLINE, ud, ui))
return (EFAULT);

/*
* Pick up the drive type from the on line end packet;
* convert that to a dump area size and a disk offset.
*/
i = ud->uda1_rsp.mscp_onle.onle_drivetype;
if (i >= NTYPES || udatypes[i].ut_name == 0) {
printf("disk type %d unknown\ndump ");
return (EINVAL);
}
#if GYRE
if (ISCDC(&uda_softc[ui->ui_ctlr]))
i = CDCTYPE;
#endif
printf("on %s ", udatypes[i].ut_name);
maxsz = udatypes[i].ut_sizes[udapart(dev)].nblocks;
blkoff = udatypes[i].ut_sizes[udapart(dev)].blkoff;

/*
* Dump all of physical memory, or as much as will fit in the
* space provided.
*/
start = 0;
num = maxfree;
if (dumplo < 0)
return (EINVAL);
if (dumplo + num >= maxsz)
num = maxsz - dumplo;
blkoff += dumplo;

/*
* Write out memory, DBSIZE pages at a time.
* N.B.: this code depends on the fact that the sector
* size == the page size.
*/
while (num > 0) {
blk = num > DBSIZE ? DBSIZE : num;
io = uba->uba_map;
/*
* Map in the pages to write, leaving an invalid entry
* at the end to guard against wild Unibus transfers.
* Then do the write.
*/
for (i = 0; i < blk; i++)
*(int *) io++ = UBAMR_MRV | (btop(start) + i);
*(int *) io = 0;
ud->uda1_cmd.mscp_unit = ui->ui_slave;
ud->uda1_cmd.mscp_seq.seq_lbn = btop(start) + blkoff;
ud->uda1_cmd.mscp_seq.seq_bytecount = blk << PGSHIFT;
if (udadumpcmd(M_OP_WRITE, ud, ui))
return (EIO);
start += blk << PGSHIFT;
num -= blk;
}
return (0); /* made it! */
}

X/*
* Wait for some of the bits in `bits' to come on. If the error bit
* comes on, or ten seconds pass without response, return true (error).
*/
udadumpwait(udaddr, bits)
register struct udadevice *udaddr;
register int bits;
{
register int timo = mfpr(TODR) + 1000;

while ((udaddr->udasa & bits) == 0) {
if (udaddr->udasa & UDA_ERR) {
printf("udasa=%b\ndump ", udaddr->udasa, udasr_bits);
return (1);
}
if (mfpr(TODR) >= timo) {
printf("timeout\ndump ");
return (1);
}
}
return (0);
}

X/*
* Feed a command to the UDA50, wait for its response, and return
* true iff something went wrong.
*/
udadumpcmd(op, ud, ui)
int op;
register struct uda1 *ud;
struct uba_device *ui;
{
register struct udadevice *udaddr;
register int n;
#define mp (&ud->uda1_rsp)

udaddr = (struct udadevice *) ui->ui_physaddr;
ud->uda1_cmd.mscp_opcode = op;
ud->uda1_cmd.mscp_msglen = MSCP_MSGLEN;
ud->uda1_rsp.mscp_msglen = MSCP_MSGLEN;
ud->uda1_ca.ca_rspdsc |= MSCP_OWN | MSCP_INT;
ud->uda1_ca.ca_cmddsc |= MSCP_OWN | MSCP_INT;
if (udaddr->udasa & UDA_ERR) {
printf("udasa=%b\ndump ", udaddr->udasa, udasr_bits);
return (1);
}
n = udaddr->udaip;
n = mfpr(TODR) + 1000;
for (;;) {
if (mfpr(TODR) > n) {
printf("timeout\ndump ");
return (1);
}
if (ud->uda1_ca.ca_cmdint)
ud->uda1_ca.ca_cmdint = 0;
if (ud->uda1_ca.ca_rspint == 0)
continue;
ud->uda1_ca.ca_rspint = 0;
if (mp->mscp_opcode == (op | M_OP_END))
break;
printf("\n");
switch (MSCP_MSGTYPE(mp->mscp_msgtc)) {

case MSCPT_SEQ:
printf("sequential");
break;

case MSCPT_DATAGRAM:
mscp_decodeerror(ui->ui_mi, mp);
printf("datagram");
break;

case MSCPT_CREDITS:
printf("credits");
break;

case MSCPT_MAINTENANCE:
printf("maintenance");
break;

default:
printf("unknown (type 0x%x)",
MSCP_MSGTYPE(mp->mscp_msgtc));
break;
}
printf(" ignored\ndump ");
ud->uda1_ca.ca_rspdsc |= MSCP_OWN | MSCP_INT;
}
if ((mp->mscp_status & M_ST_MASK) != M_ST_SUCCESS) {
printf("error: op 0x%x => 0x%x status 0x%x\ndump ", op,
mp->mscp_opcode, mp->mscp_status);
return (1);
}
return (0);
#undef mp
}

X/*
* Return the size of a partition, if known, or -1 if not.
*/
udasize(dev)
dev_t dev;
{
register int unit = udaunit(dev);
register struct uba_device *ui;
register struct size *st;

if (unit >= NRA || (ui = udadinfo[unit]) == NULL || ui->ui_alive == 0)
return (-1);
st = &udatypes[ui->ui_type].ut_sizes[udapart(dev)];
if (st->nblocks == -1) {
int s = spl5();

/*
* We need to have the drive on line to find the size
* of this particular partition.
* IS IT OKAY TO GO TO SLEEP IN THIS ROUTINE?
* (If not, better not page on one of these...)
*/
if ((ui->ui_flags & UNIT_ONLINE) == 0) {
if (uda_bringonline(&uda_softc[unit], ui, 0)) {
splx(s);
return (-1);
}
}
splx(s);
if (st->blkoff > ra_dsize[unit])
return (-1);
return (ra_dsize[unit] - st->blkoff);
}
return (st->nblocks);
}
#endif
//go.sysin dd *
if [ `wc -c < vaxuba/uda.c` != 48872 ]; then
made=FALSE
echo error transmitting vaxuba/uda.c --
echo length should be 48872, not `wc -c < vaxuba/uda.c`
else
made=TRUE
fi
if [ $made = TRUE ]; then
chmod 644 vaxuba/uda.c
echo -n ' '; ls -ld vaxuba/uda.c
fi
echo Extracting vaxuba/udareg.h
sed 's/^X//' <<'//go.sysin dd *' >vaxuba/udareg.h
X/*
* UDA50 registers and structures
*/

X/*
* Writing any value to udaip starts initialisation. Reading from it
* when the UDA is running makes the UDA look through the command ring
* to find any new commands. Reading udasa gives status; writing it
* during initialisation sets things up.
*/
struct udadevice {
u_short udaip; /* initialisation and polling */
u_short udasa; /* status and address */
};

X/*
* Bits in UDA status register during initialisation
*/
#define UDA_ERR 0x8000 /* error */
#define UDA_STEP4 0x4000 /* step 4 has started */
#define UDA_STEP3 0x2000 /* step 3 has started */
#define UDA_STEP2 0x1000 /* step 2 has started */
#define UDA_STEP1 0x0800 /* step 1 has started */
#define UDA_NV 0x0400 /* no host settable interrupt vector */
#define UDA_QB 0x0200 /* controller supports Q22 bus */
#define UDA_DI 0x0100 /* controller implements diagnostics */
#define UDA_IE 0x0080 /* interrupt enable */
#define UDA_NCNRMASK 0x003f /* in STEP1, bits 0-2=NCMDL2, 3-5=NRSPL2 */
#define UDA_IVECMASK 0x007f /* in STEP2, bits 0-6 are interruptvec / 4 */
#define UDA_PI 0x0001 /* host requests adapter purge interrupts */

X/*
* Bits in UDA status register after initialisation
*/
#define UDA_GO 0x0001 /* run */

#define UDASR_BITS \
"\20\20ERR\17STEP4\16STEP3\15STEP2\14STEP1\13NV\12QB\11DI\10IE\1GO"

X/*
* UDA Communications Area. Note that this structure definition
* requires NRSP and NCMD to be defined already.
*/
struct udaca {
short ca_xxx1; /* unused */
char ca_xxx2; /* unused */
char ca_bdp; /* BDP to purge */
short ca_cmdint; /* command ring transition flag */
short ca_rspint; /* response ring transition flag */
long ca_rspdsc[NRSP];/* response descriptors */
long ca_cmddsc[NCMD];/* command descriptors */
};

X/*
* Simplified routines (e.g., uddump) reprogram the UDA50 for one command
* and one response at a time; uda1ca is like udaca except that it provides
* exactly one command and response descriptor.
*/
struct uda1ca {
short ca_xxx1;
char ca_xxx2;
char ca_bdp;
short ca_cmdint;
short ca_rspint;
long ca_rspdsc;
long ca_cmddsc;
};
//go.sysin dd *
if [ `wc -c < vaxuba/udareg.h` != 2112 ]; then
made=FALSE
echo error transmitting vaxuba/udareg.h --
echo length should be 2112, not `wc -c < vaxuba/udareg.h`
else
made=TRUE
fi
if [ $made = TRUE ]; then
chmod 644 vaxuba/udareg.h
echo -n ' '; ls -ld vaxuba/udareg.h
fi
made=TRUE
if [ $made = TRUE ]; then
chmod 755 vaxuba
echo -n ' '; ls -ld vaxuba
fi
--
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
UUCP: seismo!mimsy!chris ARPA/CSNet: ch...@mimsy.umd.edu

0 new messages