* UDA50/RAxx disk device driver
* Unit numbers must be less than 8.
#define printd if(udadebug&1)printf
* Parameters for the communications area
short sc_state
; /* state of controller */
short sc_mapped
; /* Unibus map allocated for uda struct? */
int sc_ubainfo
; /* Unibus mapping info */
struct uda
*sc_uda
; /* Unibus address of uda struct */
int sc_ivec
; /* interrupt vector address */
short sc_credits
; /* transfer credits */
short sc_lastcmd
; /* pointer into command ring */
short sc_lastrsp
; /* pointer into response ring */
#define S_IDLE 0 /* hasn't been initialized */
#define S_STEP1 1 /* doing step 1 init */
#define S_STEP2 2 /* doing step 2 init */
#define S_STEP3 3 /* doing step 3 init */
#define S_SCHAR 4 /* doing "set controller characteristics" */
#define S_RUN 5 /* running */
struct udaca uda_ca
; /* communications area */
struct mscp uda_rsp
[NRSP
]; /* response packets */
struct mscp uda_cmd
[NCMD
]; /* command packets */
/* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */
15884, 0, /* A=blk 0 thru 15883 */
33440, 15884, /* B=blk 15884 thru 49323 */
-1, 0, /* C=blk 0 thru end */
0, 0, /* D reserved for RA81 */
0, 0, /* E reserved for RA81 */
0, 0, /* F reserved for RA81 */
82080, 49324, /* G=blk 49324 thru 131403 */
-1, 131404, /* H=blk 131404 thru end */
/* END OF STUFF WHICH SHOULD BE READ IN PER DISK */
daddr_t radsize
[NRA
]; /* disk size, from ONLINE end packet */
int udprobe(), udslave(), udattach(), udintr();
struct uba_ctlr
*udminfo
[NUDA
];
struct uba_device
*uddinfo
[NRA
];
struct uba_device
*udip
[NUDA
][8]; /* 8 == max number of drives */
u_short udstd
[] = { 0777550, 0 };
struct uba_driver udadriver
=
{ udprobe
, udslave
, udattach
, 0, udstd
, "ra", uddinfo
, "uda", udminfo
, 0 };
struct buf udwtab
[NUDA
]; /* I/O wait queue, per controller */
#define b_qsize b_resid /* queue size per drive, in udutab */
#define b_ubinfo b_resid /* Unibus mapping info, per buffer */
register struct uda_softc
*sc
= &uda_softc
[ctlr
];
br
= 0; cvec
= br
; br
= cvec
; reg
= reg
;
udread(0); udwrite(0); udreset(0); udintr(0);
/* SHOULD CHECK THAT IT REALLY IS A UDA */
cvec
= sc
->sc_ivec
= (uba_hd
[numuba
].uh_lastiv
-= 4);
* TOO HARD TO FIND OUT IF DISK IS THERE UNTIL
* INITIALIZED. WE'LL FIND OUT WHEN WE FIRST
register struct uba_device
*ui
;
dk_mspw
[ui
->ui_dk
] = 1.0 / (60 * 31 * 256); /* approx */
udip
[ui
->ui_ctlr
][ui
->ui_slave
] = ui
;
radsize
[ui
->ui_unit
] = (daddr_t
)0xffffff; /* max possible size */
* Open a UDA. Initialize the device and
register struct uba_device
*ui
;
register struct uda_softc
*sc
;
if (unit
>= NRA
|| (ui
= uddinfo
[unit
]) == 0 || ui
->ui_alive
== 0) {
sc
= &uda_softc
[ui
->ui_ctlr
];
if (sc
->sc_state
!= S_RUN
) {
if (sc
->sc_state
== S_IDLE
)
/* wait for initialization to complete */
sleep((caddr_t
)ui
->ui_mi
, 0);
if (sc
->sc_state
!= S_RUN
) {
/* SHOULD PROBABLY FORCE AN ONLINE ATTEMPT
TO SEE IF DISK IS REALLY THERE */
* Initialize a UDA. Set up UBA mapping registers,
* initialize data structures, and start hardware
* initialization sequence.
register struct uda_softc
*sc
;
struct udadevice
*udaddr
;
udaddr
= (struct udadevice
*)um
->um_addr
;
if (sc
->sc_mapped
== 0) {
* Map the communications area and command
* and response packets into Unibus address
sc
->sc_ubainfo
= uballoc(um
->um_ubanum
, (caddr_t
)ud
,
sc
->sc_uda
= (struct uda
*)(sc
->sc_ubainfo
& 0x3ffff);
* Start the hardware initialization sequence.
udaddr
->udaip
= 0; /* start initialization */
while ((udaddr
->udasa
& UDA_STEP1
) == 0)
udaddr
->udasa
= UDA_ERR
|(NCMDL2
<<11)|(NRSPL2
<<8)|UDA_IE
|(sc
->sc_ivec
/4);
* Initialization continues in interrupt routine.
register struct uba_device
*ui
;
register struct uba_ctlr
*um
;
int xunit
= minor(bp
->b_dev
) & 07;
sz
= (bp
->b_bcount
+511) >> 9;
if (ui
== 0 || ui
->ui_alive
== 0)
if ((maxsz
= ra_sizes
[xunit
].nblocks
) < 0)
maxsz
= radsize
[unit
] - ra_sizes
[xunit
].blkoff
;
if (bp
->b_blkno
< 0 || bp
->b_blkno
+sz
> maxsz
||
ra_sizes
[xunit
].blkoff
>= radsize
[unit
])
* Link the buffer onto the drive queue
dp
= &udutab
[ui
->ui_unit
];
dp
->b_actl
->av_forw
= bp
;
* Link the drive onto the controller queue
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;
if (um
->um_tab
.b_active
== 0) {
printf("uda: ubinfo %x\n",um
->um_ubinfo
);
uballoc(um
->um_ubanum
, (caddr_t
)0, 0,
register struct uba_ctlr
*um
;
register struct buf
*bp
, *dp
;
register struct mscp
*mp
;
register struct uda_softc
*sc
;
register struct uba_device
*ui
;
struct udadevice
*udaddr
;
sc
= &uda_softc
[um
->um_ctlr
];
if ((dp
= um
->um_tab
.b_actf
) == NULL
) {
* Release uneeded UBA resources and return
printf("uda: um_ubinfo == 0\n");
ubarelse(um
->um_ubanum
, &um
->um_ubinfo
);
if ((bp
= dp
->b_actf
) == NULL
) {
* No more requests for this drive, remove
* from controller queue and look at next drive.
* We know we're at the head of the controller queue.
um
->um_tab
.b_actf
= dp
->b_forw
;
udaddr
= (struct udadevice
*)um
->um_addr
;
if ((udaddr
->udasa
&UDA_ERR
) || sc
->sc_state
!= S_RUN
) {
printf("udasa %o, state %d\n", udaddr
->udasa
&0xffff, sc
->sc_state
);
/* SHOULD REQUEUE OUTSTANDING REQUESTS, LIKE UDRESET */
ui
= uddinfo
[dkunit(bp
)];
* If no credits, can't issue any commands
* until some outstanding commands complete.
if ((mp
= udgetcp(um
)) == NULL
)
sc
->sc_credits
--; /* committed to issuing a command */
if (ui
->ui_flags
== 0) { /* not online */
mp
->mscp_opcode
= M_OP_ONLIN
;
mp
->mscp_unit
= ui
->ui_slave
;
um
->um_tab
.b_actf
= dp
->b_forw
; /* remove from controller q */
printd("uda: bring unit %d online\n", ui
->ui_slave
);
*((long *)mp
->mscp_dscptr
) |= UDA_OWN
|UDA_INT
;
i
= UBA_NEEDBDP
|UBA_CANTWAIT
;
i
= um
->um_ubinfo
|UBA_HAVEBDP
|UBA_CANTWAIT
;
if ((i
= ubasetup(um
->um_ubanum
, bp
, i
)) == 0) {
mp
->mscp_opcode
= M_OP_GTUNT
;
mp
->mscp_unit
= ui
->ui_slave
;
*((long *)mp
->mscp_dscptr
) |= UDA_OWN
|UDA_INT
;
i
= udaddr
->udaip
; /* initiate polling */
return(1); /* wait for interrupt */
mp
->mscp_cmdref
= (long)bp
; /* pointer to get back */
mp
->mscp_opcode
= bp
->b_flags
&B_READ
? M_OP_READ
: M_OP_WRITE
;
mp
->mscp_unit
= ui
->ui_slave
;
mp
->mscp_lbn
= bp
->b_blkno
+ ra_sizes
[minor(bp
->b_dev
)&7].blkoff
;
mp
->mscp_bytecnt
= bp
->b_bcount
;
mp
->mscp_buffer
= (i
& 0x3ffff) | (((i
>>28)&0xf)<<24);
i
&= 0xfffffff; /* mask off bdp */
bp
->b_ubinfo
= i
; /* save mapping info */
*((long *)mp
->mscp_dscptr
) |= UDA_OWN
|UDA_INT
;
i
= udaddr
->udaip
; /* initiate polling */
dk_wds
[ui
->ui_dk
] += bp
->b_bcount
>>6;
* Move drive to the end of the controller queue
if (dp
->b_forw
!= NULL
) {
um
->um_tab
.b_actf
= dp
->b_forw
;
um
->um_tab
.b_actl
->b_forw
= dp
;
* Move buffer to I/O wait queue
dp
->b_actf
= bp
->av_forw
;
dp
= &udwtab
[um
->um_ctlr
];
bp
->av_back
= dp
->av_back
;
dp
->av_back
->av_forw
= bp
;
register struct uba_ctlr
*um
= udminfo
[d
];
register struct udadevice
*udaddr
= (struct udadevice
*)um
->um_addr
;
register struct uda_softc
*sc
= &uda_softc
[d
];
register struct uda
*ud
= &uda
[d
];
printd("udintr: state %d, udasa %o\n", sc
->sc_state
, udaddr
->udasa
);
printf("uda%d: random interrupt ignored\n", d
);
#define STEP1GOOD (UDA_STEP2|UDA_IE|(NCMDL2<<3)|NRSPL2)
if ((udaddr
->udasa
&(UDA_ERR
|STEP1GOOD
)) != STEP1GOOD
) {
udaddr
->udasa
= ((int)&sc
->sc_uda
->uda_ca
.ca_ringbase
)|
(cpu
== VAX_780
? UDA_PI
: 0);
#define STEP2GOOD (UDA_STEP3|UDA_IE|(sc->sc_ivec/4))
if ((udaddr
->udasa
&(UDA_ERR
|STEP2GOOD
)) != STEP2GOOD
) {
udaddr
->udasa
= ((int)&sc
->sc_uda
->uda_ca
.ca_ringbase
)>>16;
#define STEP3GOOD UDA_STEP4
if ((udaddr
->udasa
&(UDA_ERR
|STEP3GOOD
)) != STEP3GOOD
) {
* Initialize the data structures.
for (i
= 0; i
< NRSP
; i
++) {
ud
->uda_ca
.ca_rspdsc
[i
] = UDA_OWN
|UDA_INT
|
(long)&uud
->uda_rsp
[i
].mscp_cmdref
;
ud
->uda_rsp
[i
].mscp_dscptr
= &ud
->uda_ca
.ca_rspdsc
[i
];
ud
->uda_rsp
[i
].mscp_header
.uda_msglen
= sizeof (struct mscp
);
for (i
= 0; i
< NCMD
; i
++) {
ud
->uda_ca
.ca_cmddsc
[i
] = UDA_INT
|
(long)&uud
->uda_cmd
[i
].mscp_cmdref
;
ud
->uda_cmd
[i
].mscp_dscptr
= &ud
->uda_ca
.ca_cmddsc
[i
];
ud
->uda_cmd
[i
].mscp_header
.uda_msglen
= sizeof (struct mscp
);
bp
->av_forw
= bp
->av_back
= bp
;
if ((mp
= udgetcp(um
)) == NULL
) {
mp
->mscp_opcode
= M_OP_STCON
;
mp
->mscp_cntflgs
= M_CF_ATTN
|M_CF_MISC
|M_CF_THIS
;
*((long *)mp
->mscp_dscptr
) |= UDA_OWN
|UDA_INT
;
i
= udaddr
->udaip
; /* initiate polling */
printf("uda%d: interrupt in unknown state %d ignored\n",
if (udaddr
->udasa
&UDA_ERR
) {
printf("uda%d: fatal error (%o)\n", d
, udaddr
->udasa
&0xffff);
* Check for a buffer purge request.
* Maybe we should change the entire
* UBA interface structure.
printd("uda: purge bdp %d\n", ud
->uda_ca
.ca_bdp
);
um
->um_ubinfo
= ud
->uda_ca
.ca_bdp
<<28;
udaddr
->udasa
= 0; /* signal purge complete */
* Check for response ring transition.
if (ud
->uda_ca
.ca_rspint
) {
ud
->uda_ca
.ca_rspint
= 0;
for (i
= sc
->sc_lastrsp
;; i
++) {
if (ud
->uda_ca
.ca_rspdsc
[i
]&UDA_OWN
)
ud
->uda_ca
.ca_rspdsc
[i
] |= UDA_OWN
;
* Check for command ring transition.
if (ud
->uda_ca
.ca_cmdint
) {
printd("uda: command ring transition\n");
ud
->uda_ca
.ca_cmdint
= 0;
* Process a response packet
register struct uba_ctlr
*um
;
register struct uda_softc
*sc
;
register struct mscp
*mp
;
mp
->mscp_header
.uda_msglen
= sizeof (struct mscp
);
sc
->sc_credits
+= mp
->mscp_header
.uda_credits
& 0xf;
if ((mp
->mscp_header
.uda_credits
& 0xf0) > 0x10)
* If it's an error log message (datagram),
* pass it on for more extensive processing.
if ((mp
->mscp_header
.uda_credits
& 0xf0) == 0x10) {
uderror(um
, (struct mslg
*)mp
);
if ((ui
= udip
[um
->um_ctlr
][mp
->mscp_unit
]) == 0)
st
= mp
->mscp_status
&M_ST_MASK
;
switch (mp
->mscp_opcode
) {
case M_OP_STCON
|M_OP_END
:
case M_OP_ONLIN
|M_OP_END
:
* Link the drive onto the controller queue
dp
= &udutab
[ui
->ui_unit
];
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;
ui
->ui_flags
= 1; /* mark it online */
radsize
[ui
->ui_unit
] = (daddr_t
)mp
->mscp_untsize
;
printd("uda: unit %d online\n", mp
->mscp_unit
);
harderr(dp
->b_actf
, "ra");
while (bp
= dp
->b_actf
) {
dp
->b_actf
= bp
->av_forw
;
printd("uda: unit %d attention\n", mp
->mscp_unit
);
ui
->ui_flags
= 0; /* it went offline and we didn't notice */
case M_OP_WRITE
|M_OP_END
:
bp
= (struct buf
*)mp
->mscp_cmdref
;
ubarelse(um
->um_ubanum
, (int *)&bp
->b_resid
);
* Unlink buffer from I/O wait queue.
bp
->av_back
->av_forw
= bp
->av_forw
;
bp
->av_forw
->av_back
= bp
->av_back
;
dp
= &udutab
[ui
->ui_unit
];
dk_busy
&= ~(1<<ui
->ui_dk
);
if (st
== M_ST_OFFLN
|| st
== M_ST_AVLBL
) {
ui
->ui_flags
= 0; /* mark unit offline */
* Link the buffer onto the front of the drive queue
if ((bp
->av_forw
= dp
->b_actf
) == 0)
* Link the drive onto the controller queue
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;
printf("status %o\n", mp
->mscp_status
);
bp
->b_resid
= bp
->b_bcount
- mp
->mscp_bytecnt
;
case M_OP_GTUNT
|M_OP_END
:
printf("uda: unknown packet\n");
* Process an error log message
* For now, just log the error on the console.
* Only minimal decoding is done, only "useful"
* information is printed. Eventually should
* send message to an error logger.
register struct uba_ctlr
*um
;
register struct mslg
*mp
;
printf("uda%d:%d: %s error, ", um
->um_ctlr
, mp
->mslg_seqnum
,
mp
->mslg_flags
&M_LF_SUCC
? "soft" : "hard");
switch (mp
->mslg_format
) {
printf("controller error, event 0%o\n", mp
->mslg_event
);
printf("host memory access error, event 0%o, addr 0%o\n",
mp
->mslg_event
, *((long *)&mp
->mslg_busaddr
[0]));
printf("disk transfer error, unit %d, grp %d, cyl %d, sec %d, ",
mp
->mslg_unit
, mp
->mslg_group
, mp
->mslg_cylinder
,
printf("trk %d, lbn %d, retry %d, level %d\n", mp
->mslg_track
,
mp
->mslg_lbn
, mp
->mslg_retry
, mp
->mslg_level
);
printf("SDI error, unit %d, event 0%o, cyl %d\n", mp
->mslg_unit
,
mp
->mslg_event
, mp
->mslg_cylinder
);
printf("small disk error, unit %d, event 0%o, cyl %d\n",
mp
->mslg_unit
, mp
->mslg_event
, mp
->mslg_sdecyl
);
printf("unknown error, unit %d, format 0%o, event 0%o\n",
mp
->mslg_unit
, mp
->mslg_format
, mp
->mslg_event
);
* Find an unused command packet
register struct mscp
*mp
;
register struct udaca
*cp
;
register struct uda_softc
*sc
;
cp
= &uda
[um
->um_ctlr
].uda_ca
;
sc
= &uda_softc
[um
->um_ctlr
];
if ((cp
->ca_cmddsc
[i
] & (UDA_OWN
|UDA_INT
)) == UDA_INT
) {
cp
->ca_cmddsc
[i
] &= ~UDA_INT
;
mp
= &uda
[um
->um_ctlr
].uda_cmd
[i
];
mp
->mscp_unit
= mp
->mscp_modifier
= 0;
mp
->mscp_opcode
= mp
->mscp_flags
= 0;
mp
->mscp_bytecnt
= mp
->mscp_buffer
= 0;
mp
->mscp_errlgfl
= mp
->mscp_copyspd
= 0;
sc
->sc_lastcmd
= (i
+ 1) % NCMD
;
register int unit
= minor(dev
) >> 3;
physio(udstrategy
, &rudbuf
[unit
], dev
, B_READ
, minphys
);
register int unit
= minor(dev
) >> 3;
physio(udstrategy
, &rudbuf
[unit
], dev
, B_WRITE
, minphys
);
register struct uba_ctlr
*um
;
register struct uba_device
*ui
;
register struct buf
*bp
, *dp
;
for (d
= 0; d
< NUDA
; d
++) {
if ((um
= udminfo
[d
]) == 0 || um
->um_ubanum
!= uban
||
um
->um_tab
.b_actf
= um
->um_tab
.b_actl
= 0;
uda_softc
[d
].sc_state
= S_IDLE
;
for (unit
= 0; unit
< NRA
; unit
++) {
if ((ui
= uddinfo
[unit
]) == 0)
if (ui
->ui_alive
== 0 || ui
->ui_mi
!= um
)
udutab
[unit
].b_active
= 0;
udutab
[unit
].b_qsize
= 0;
for (bp
= udwtab
[d
].av_forw
; bp
!= &udwtab
[d
]; bp
= nbp
) {
ubarelse(uban
, (int *)&bp
->b_ubinfo
);
* Link the buffer onto the drive queue
dp
= &udutab
[dkunit(bp
)];
dp
->b_actl
->av_forw
= bp
;
* Link the drive onto the controller queue
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;