6c3038d7ce10a3d816dc755c299fa075f0eb5e63
[unix-history] / usr / src / sys / tahoe / vba / vd.c
/* vd.c 1.3 86/01/12 */
#include "fsd.h"
#if NVD > 0
/*
* VDDC - Versabus SMD/ESMD driver.
*/
#include "../tahoe/mtpr.h"
#include "../tahoe/pte.h"
#include "param.h"
#include "buf.h"
#include "cmap.h"
#include "conf.h"
#include "dir.h"
#include "dk.h"
#include "map.h"
#include "systm.h"
#include "user.h"
#include "vmmac.h"
#include "proc.h"
#include "uio.h"
#include "../tahoevba/vbavar.h"
#define VDGENDATA
#include "../tahoevba/vddcreg.h"
#undef VDGENDATA
#define MAX_BLOCKSIZE (MAXBPTE*NBPG)
#define DUMPSIZE 64 /* controller limit */
#define VDUNIT(x) (minor(x) >> 3)
#define FILSYS(x) (minor(x) & 0x07)
#define PHYS(x) (vtoph((struct proc *)0, (unsigned)(x)))
#define TRUE 1
#define FALSE 0
#define CTLR_ERROR 1
#define DRIVE_ERROR 2
#define HARD_DATA_ERROR 3
#define SOFT_DATA_ERROR 4
#define b_cylin b_resid
#define b_daddr b_error
struct vba_ctlr *vdminfo[NVD];
struct vba_device *vddinfo[NFSD];
int vdprobe(), vdslave(), vdattach(), vddgo();
struct vba_driver vddriver =
{ vdprobe, vdslave, vdattach, vddgo, vddcaddr, "smd/fsd",
vddinfo, "vd", vdminfo };
/*
* Per-drive state.
*/
typedef struct {
struct buf raw_q_element;
short sec_per_blk;
short sec_per_cyl;
char status;
struct buf xfer_queue;
int drive_type;
fs_tab info;
} unit_tab;
/*
* Per-controller state.
*/
typedef struct {
char ctlr_type; /* controller type */
char *map; /* i/o page map */
char *utl; /* mapped i/o space */
u_int cur_slave:8; /* last active unit number */
u_int int_expected:1; /* expect an interupt */
u_int ctlr_started:1; /* start command was issued */
u_int overlap_seeks:1;/* should overlap seeks */
u_int off_cylinder:16;/* off cylinder bit map */
u_int unit_type[16]; /* slave types */
u_int cur_cyl[16]; /* cylinder last selected */
long cur_trk[16]; /* track last selected */
fmt_mdcb ctlr_mdcb; /* controller mdcb */
fmt_dcb ctlr_dcb; /* r/w dcb */
fmt_dcb seek_dcb[4]; /* dcbs for overlapped seeks */
/* buffer for raw/swap i/o */
char rawbuf[MAX_BLOCKSIZE];
} ctlr_tab;
extern char vd0utl[];
#if NVD > 1
extern char vd1utl[];
#endif
#if NVD > 2
extern char vd2utl[];
#endif
#if NVD > 3
extern char vd3utl[];
#endif
#define VDCINIT(map, utl) { \
UNKNOWN, (char *)map, utl, 0, FALSE, FALSE, TRUE, 0, \
{ UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN, \
UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN } \
}
ctlr_tab vdctlr_info[NVD] = {
VDCINIT(VD0map, vd0utl),
#if NVD > 1
VDCINIT(VD1map, vd1utl),
#endif
#if NVD > 2
VDCINIT(VD2map, vd2utl),
#endif
#if NVD > 3
VDCINIT(VD3map, vd3utl),
#endif
};
unit_tab vdunit_info[NFSD];
/*
* See if the controller is really there; if so, initialize it.
*/
vdprobe(reg, vm)
caddr_t reg;
struct vba_ctlr *vm;
{
register br, cvec; /* must be r12, r11 */
register cdr *cp = (cdr *)reg;
if (badaddr((caddr_t)reg, 2))
return (0);
cp->cdr_reset = 0xffffffff;
DELAY(1000000);
if (cp->cdr_reset != (unsigned)0xffffffff) {
DELAY(1000000);
} else {
cp->cdr_reserved = 0x0;
DELAY(3000000);
}
br = 0x17, cvec = 0xe0 + vm->um_ctlr; /* XXX */
return (sizeof (*cp));
}
/*
* See if a drive is really there
* Try to reset/configure the drive, then test its status.
*/
vdslave(vi, addr)
register struct vba_device *vi;
register cdr *addr;
{
register ctlr_tab *ci = &vdctlr_info[vi->ui_ctlr];
register unit_tab *ui = &vdunit_info[vi->ui_unit];
register fmt_mdcb *mdcb = &ci->ctlr_mdcb;
register fmt_dcb *dcb = &ci->ctlr_dcb;
register int type;
if (ci->ctlr_type == UNKNOWN) {
addr->cdr_reset = 0xffffffff;
DELAY(1000000);
if (addr->cdr_reset != (unsigned)0xffffffff) {
ci->ctlr_type = SMDCTLR;
ci->overlap_seeks = 0;
DELAY(1000000);
} else {
ci->overlap_seeks = 1;
ci->ctlr_type = SMD_ECTLR;
addr->cdr_reserved = 0x0;
DELAY(3000000);
addr->cdr_csr = 0;
addr->mdcb_tcf = AM_ENPDA;
addr->dcb_tcf = AM_ENPDA;
addr->trail_tcf = AM_ENPDA;
addr->data_tcf = AM_ENPDA;
addr->cdr_ccf = CCF_STS | XMD_32BIT | BSZ_16WRD |
CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR;
}
if (vdnotrailer(addr,
vi->ui_ctlr, vi->ui_slave, INIT, 10) & HRDERR) {
printf("vd%d: init error\n", vi->ui_unit);
return (0);
}
if (vdnotrailer(addr,
vi->ui_ctlr, vi->ui_slave, DIAG, 10) & HRDERR) {
printf("vd%d: diagnostic error\n", vi->ui_unit);
return (0);
}
}
/*
* Seek on all drive types starting from the largest one.
* a successful seek to the last sector/cylinder/track verifies
* the drive type connected to this port.
*/
for (type = 0; type < nvddrv; type++) {
/* XXX */
if (ci->ctlr_type == SMDCTLR && vdst[type].nsec != 32)
continue;
/* XXX */
if (!vdconfigure_drive(addr, vi->ui_ctlr, vi->ui_slave, type,0))
return (0);
dcb->opcode = (short)RD;
dcb->intflg = NOINT;
dcb->nxtdcb = (fmt_dcb *)0; /* end of chain */
dcb->operrsta = 0;
dcb->devselect = (char)(vi->ui_slave);
dcb->trailcnt = (char)(sizeof (trrw) / sizeof (long));
dcb->trail.rwtrail.memadr = (char *)PHYS(ci->rawbuf);
dcb->trail.rwtrail.wcount = vdst[type].secsize/sizeof(short);
dcb->trail.rwtrail.disk.cylinder = vdst[type].ncyl - 2;
dcb->trail.rwtrail.disk.track = vdst[type].ntrak - 1;
dcb->trail.rwtrail.disk.sector = vdst[type].nsec - 1;
mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb));
mdcb->vddcstat = 0;
VDDC_ATTENTION(addr, (fmt_mdcb *)(PHYS(mdcb)), ci->ctlr_type);
POLLTILLDONE(addr, dcb, 60, ci->ctlr_type);
if (vdtimeout <= 0)
printf(" during probe\n");
if ((dcb->operrsta&HRDERR) == 0)
break;
}
if (type >= nvddrv) {
/*
* If reached here, a drive which is not defined in the
* 'vdst' tables is connected. Cannot set it's type.
*/
printf("vd%d: unknown drive type\n", vi->ui_unit);
return (0);
}
ui->drive_type = type;
ui->info = vdst[type];
ui->sec_per_blk = DEV_BSIZE / ui->info.secsize;
vi->ui_type = type;
vi->ui_dk = 1;
vddriver.ud_dname = ui->info.type_name;
return (1);
}
vdconfigure_drive(addr, ctlr, slave, type, pass)
register cdr *addr;
int ctlr, slave, type, pass;
{
register ctlr_tab *ci = &vdctlr_info[ctlr];
ci->ctlr_dcb.opcode = RSTCFG; /* command */
ci->ctlr_dcb.intflg = NOINT;
ci->ctlr_dcb.nxtdcb = (fmt_dcb *)0; /* end of chain */
ci->ctlr_dcb.operrsta = 0;
ci->ctlr_dcb.devselect = (char)slave;
ci->ctlr_dcb.trail.rstrail.ncyl = vdst[type].ncyl;
ci->ctlr_dcb.trail.rstrail.nsurfaces = vdst[type].ntrak;
if (ci->ctlr_type == SMD_ECTLR) {
ci->ctlr_dcb.trailcnt = (char)4;
ci->ctlr_dcb.trail.rstrail.nsectors = vdst[type].nsec;
ci->ctlr_dcb.trail.rstrail.slip_sec = vdst[type].nslip;
} else
ci->ctlr_dcb.trailcnt = (char)2;
ci->ctlr_mdcb.firstdcb = (fmt_dcb *)(PHYS(&ci->ctlr_dcb));
ci->ctlr_mdcb.vddcstat = 0;
VDDC_ATTENTION(addr, (fmt_mdcb *)(PHYS(&ci->ctlr_mdcb)), ci->ctlr_type);
POLLTILLDONE(addr, &ci->ctlr_dcb, 5, ci->ctlr_type);
if (vdtimeout <= 0) {
printf(" during config\n");
return (0);
}
if (ci->ctlr_dcb.operrsta & HRDERR) {
if ((ci->ctlr_dcb.operrsta & (NOTCYLERR|DRVNRDY)) == 0)
printf("vd%d: drive %d: config error\n", ctlr, slave);
else if (pass == 0) {
vdstart_drive(addr, ctlr, slave);
return (vdconfigure_drive(addr, ctlr, slave, type, 1));
} else if (pass == 2)
return (vdconfigure_drive(addr, ctlr, slave, type, 3));
return (0);
}
return (1);
}
vdstart_drive(addr, ctlr, slave)
cdr *addr;
register int ctlr, slave;
{
int error = 0;
printf("vd%d: starting drive %d, wait...", ctlr, slave);
if (vdctlr_info[ctlr].ctlr_started) {
printf("DELAY(5500000)...");
DELAY(5500000);
goto done;
}
vdctlr_info[ctlr].ctlr_started = 1;
error = vdnotrailer(addr, ctlr, 0, VDSTART, (slave*6)+62) & HRDERR;
if (!error) {
printf("DELAY(%d)...", (slave * 5500000) + 62000000);
DELAY((slave * 5500000) + 62000000);
}
done:
printf("\n");
return (error == 0);
}
vdnotrailer(addr, ctlr, unit, function, time)
register cdr *addr;
int ctlr, unit, function, time;
{
fmt_mdcb *mdcb = &vdctlr_info[ctlr].ctlr_mdcb;
fmt_dcb *dcb = &vdctlr_info[ctlr].ctlr_dcb;
int type = vdctlr_info[ctlr].ctlr_type;
dcb->opcode = function; /* command */
dcb->intflg = NOINT;
dcb->nxtdcb = (fmt_dcb *)0; /* end of chain */
dcb->operrsta = 0;
dcb->devselect = (char)unit;
dcb->trailcnt = (char)0;
mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb));
mdcb->vddcstat = 0;
VDDC_ATTENTION(addr, (fmt_mdcb *)(PHYS(mdcb)), type);
POLLTILLDONE(addr, dcb, time, type);
if (vdtimeout <= 0) {
printf(" during init\n");
return (DCBCMP|ANYERR|HRDERR|OPABRT);
}
return (dcb->operrsta);
}
vdattach(vi)
register struct vba_device *vi;
{
register unit_tab *ui = &vdunit_info[vi->ui_unit];
register ctlr_tab *ci = &vdctlr_info[vi->ui_ctlr];
register struct buf *cq = &vi->ui_mi->um_tab;
register struct buf *uq = cq->b_forw;
register struct buf *start_queue = uq;
register fs_tab *fs = &ui->info;
ui->info = vdst[vi->ui_type];
ui->sec_per_blk = DEV_BSIZE / ui->info.secsize;
ui->sec_per_cyl = ui->info.nsec * ui->info.ntrak;
ui->xfer_queue.b_dev = vi->ui_slave;
ci->unit_type[vi->ui_slave] = vi->ui_type;
/* load unit into controller's active unit list */
if (uq == NULL) {
cq->b_forw = &ui->xfer_queue;
ui->xfer_queue.b_forw = &ui->xfer_queue;
ui->xfer_queue.b_back = &ui->xfer_queue;
} else {
while (uq->b_forw != start_queue)
uq = uq->b_forw;
ui->xfer_queue.b_forw = start_queue;
ui->xfer_queue.b_back = uq;
uq->b_forw = &ui->xfer_queue;
start_queue->b_back = &ui->xfer_queue;
}
/*
* (60 / rpm) / (number of sectors per track * (bytes per sector / 2))
*/
dk_mspw[vi->ui_unit] = 120.0 / (fs->rpm * fs->nsec * fs->secsize);
}
/*ARGSUSED*/
vddgo(um)
struct vba_ctlr *um;
{
}
vdstrategy(bp)
register struct buf *bp;
{
register int unit = VDUNIT(bp->b_dev);
register struct vba_device *vi = vddinfo[unit];
register par_tab *par;
register unit_tab *ui;
register fs_tab *fs;
register int blks, bn, s;
if (bp->b_bcount == 0 || vi == 0 || vi->ui_alive == 0)
goto bad;
ui = &vdunit_info[unit];
fs = &ui->info;
par = &fs->partition[FILSYS(bp->b_dev)];
blks = (bp->b_bcount + DEV_BSIZE-1) >> DEV_BSHIFT;
if (bp->b_blkno + blks >= par->par_len) {
blks = par->par_len - bp->b_blkno;
if (blks <= 0)
goto bad;
bp->b_bcount = blks * DEV_BSIZE;
}
bn = bp->b_blkno + par->par_start;
bn *= ui->sec_per_blk;
bp->b_daddr = (bn / fs->nsec) % fs->ntrak;
bp->b_cylin = bn / ui->sec_per_cyl;
vbasetup(bp, ui->info.secsize);
s = spl7();
if (ui->xfer_queue.av_forw == NULL) {
register ctlr_tab *ci = &vdctlr_info[vi->ui_ctlr];
int slave = vi->ui_slave;
if (bp->b_cylin != ci->cur_cyl[slave] ||
bp->b_daddr != ci->cur_trk[slave])
ci->off_cylinder |= 1 << slave;
}
bp->b_daddr |= (bn % fs->nsec) << 8;
disksort(&ui->xfer_queue, bp);
if (!vddinfo[unit]->ui_mi->um_tab.b_active++) {
splx(s);
vdstart(vddinfo[unit]->ui_mi);
} else
splx(s);
return;
bad:
bp->b_flags |= B_ERROR, bp->b_error = ENXIO;
bp->b_resid = bp->b_bcount;
iodone(bp);
}
/*
* Start up a transfer on a drive.
*/
vdstart(ci)
register struct vba_ctlr *ci;
{
register struct buf *cq = &ci->um_tab;
register struct buf *uq = cq->b_forw;
/* search for next ready unit */
cq->b_forw = cq->b_forw->b_forw;
uq = cq->b_forw;
do {
if (uq->av_forw != NULL) {
cq->b_forw = uq;
vdexecute(ci, uq);
return;
}
uq = uq->b_forw;
} while (uq != cq->b_forw);
}
/*
* Initiate seeks for all drives off-cylinder.
*/
vdload_seeks(ci, uq)
register ctlr_tab *ci;
register struct buf *uq;
{
register int unit, slave, nseeks;
register fmt_dcb *dcb;
register struct buf *bp;
register struct buf *start_queue = uq;
nseeks = 0;
do {
bp = uq->av_forw;
if (bp != NULL) {
unit = VDUNIT(bp->b_dev);
slave = vddinfo[unit]->ui_slave;
if (ci->off_cylinder & (1 << slave)) {
ci->off_cylinder &= ~(1 << slave);
if (ci->cur_cyl[slave] != bp->b_cylin) {
ci->cur_cyl[slave] = bp->b_cylin;
dk_seek[unit]++;
}
ci->cur_trk[slave] = bp->b_daddr&0xff;
dcb = &ci->seek_dcb[nseeks++];
dcb->opcode = SEEK;
dcb->intflg = NOINT | INT_PBA;
dcb->operrsta = 0;
dcb->devselect = (char)slave;
dcb->trailcnt = (char)1;
dcb->trail.sktrail.skaddr.cylinder =
bp->b_cylin;
dcb->trail.sktrail.skaddr.track =
bp->b_daddr & 0xff;
dcb->trail.sktrail.skaddr.sector = 0;
}
}
uq = uq->b_forw;
} while (uq != start_queue && nseeks < 4);
return (nseeks);
}
extern vd_int_timeout();
/*
* Execute the next command on the unit queue uq.
*/
vdexecute(controller_info, uq)
register struct vba_ctlr *controller_info;
register struct buf *uq;
{
register struct buf *bp = uq->av_forw;
register int ctlr = controller_info->um_ctlr;
register ctlr_tab *ci = &vdctlr_info[ctlr];
register int unit = VDUNIT(bp->b_dev);
register int slave = vddinfo[unit]->ui_slave;
register fmt_mdcb *mdcb = &ci->ctlr_mdcb;
register fmt_dcb *dcb = &ci->ctlr_dcb;
/*
* If there are overlapped seeks to perform, shuffle
* them to the front of the queue and get them started
* before any data transfers (to get some parallelism).
*/
if ((ci->off_cylinder & ~(1<<slave)) && ci->overlap_seeks) {
register int i, nseeks;
/* setup seek requests in seek-q */
nseeks = vdload_seeks(ci, uq);
/* place at the front of the master q */
mdcb->firstdcb = (fmt_dcb *)PHYS(&ci->seek_dcb[0]);
/* shuffle any remaining seeks up in the seek-q */
for (i = 1; i < nseeks; i++)
ci->seek_dcb[i-1].nxtdcb =
(fmt_dcb *)PHYS(&ci->seek_dcb[i]);
ci->seek_dcb[nseeks-1].nxtdcb = (fmt_dcb *)PHYS(dcb);
} else {
if (bp->b_cylin != ci->cur_cyl[slave]) {
ci->cur_cyl[slave] = bp->b_cylin;
dk_seek[unit]++;
}
ci->cur_trk[slave] = bp->b_daddr & 0xff;
ci->off_cylinder = 0;
mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb));
}
dcb->opcode = (bp->b_flags & B_READ) ? RD : WD;
dcb->intflg = INTDONE;
dcb->nxtdcb = (fmt_dcb *)0; /* end of chain */
dcb->operrsta = 0;
dcb->devselect = (char)slave;
dcb->trailcnt = (char)(sizeof (trrw) / sizeof (long));
dcb->trail.rwtrail.memadr = (char *)
vbastart(bp, ci->rawbuf, (long *)ci->map, ci->utl);
dcb->trail.rwtrail.wcount = (short)((bp->b_bcount+1) / sizeof (short));
dcb->trail.rwtrail.disk.cylinder = bp->b_cylin;
dcb->trail.rwtrail.disk.track = bp->b_daddr & 0xff;
dcb->trail.rwtrail.disk.sector = bp->b_daddr >> 8;
mdcb->vddcstat = 0;
dk_wds[unit] += bp->b_bcount / 32;
ci->int_expected = 1;
timeout(vd_int_timeout, (caddr_t)ctlr, 20*60);
dk_busy |= 1 << unit;
#ifdef VDDCPERF
scope_out(1);
#endif
VDDC_ATTENTION((cdr *)(vdminfo[ctlr]->um_addr),
(fmt_mdcb *)(PHYS(mdcb)), ci->ctlr_type);
}
/*
* Watch for lost interrupts.
*/
vd_int_timeout(ctlr)
register int ctlr;
{
register ctlr_tab *ci = &vdctlr_info[ctlr];
register fmt_dcb *dcb = &ci->ctlr_dcb;
uncache(&dcb->operrsta);
printf("vd%d: lost interupt, status %x", ctlr, dcb->operrsta);
if (ci->ctlr_type == SMD_ECTLR) {
uncache(&dcb->err_code);
printf(", error code %x", dcb->err_code);
}
printf("\n");
if ((dcb->operrsta&DCBCMP) == 0) {
VDDC_ABORT((cdr *)(vdminfo[ctlr]->um_addr), ci->ctlr_type);
dcb->operrsta |= DCBUSC | DCBABT | ANYERR | HRDERR | CTLRERR;
}
vdintr(ctlr);
}
/*
* Handle a disk interrupt.
*/
vdintr(ctlr)
register int ctlr;
{
register ctlr_tab *ci;
register struct buf *cq, *uq, *bp;
register int slave, unit;
register fmt_mdcb *mdcb;
register fmt_dcb *dcb;
int code, s;
untimeout(vd_int_timeout, (caddr_t)ctlr);
#ifdef VDDCPERF
scope_out(2);
#endif
ci = &vdctlr_info[ctlr];
if (!ci->int_expected) {
printf("vd%d: stray interrupt\n", ctlr);
return;
}
/*
* Take first request off controller's queue.
*/
cq = &vdminfo[ctlr]->um_tab;
uq = cq->b_forw;
bp = uq->av_forw;
unit = VDUNIT(bp->b_dev);
dk_busy &= ~(1 << unit);
dk_xfer[unit]++;
ci->int_expected = 0;
/* find associated control blocks */
mdcb = &ci->ctlr_mdcb, uncache(&mdcb->intdcb);
dcb = &ci->ctlr_dcb, uncache(&dcb->operrsta);
if (ci->ctlr_type == SMD_ECTLR)
uncache(&dcb->err_code);
slave = uq->b_dev;
switch (code = vddecode_error(dcb)) {
case CTLR_ERROR:
case DRIVE_ERROR:
if (cq->b_errcnt >= 2)
vdhard_error(ci, bp, dcb);
if (code == CTLR_ERROR)
vdreset_ctlr((cdr *)vdminfo[ctlr]->um_addr, ctlr);
else
reset_drive((cdr *)vdminfo[ctlr]->um_addr, ctlr,
slave, 2);
if (cq->b_errcnt++ < 2) { /* retry error */
cq->b_forw = uq->b_back;
vdstart(vdminfo[ctlr]);
return;
}
bp->b_resid = bp->b_bcount;
break;
case HARD_DATA_ERROR:
vdhard_error(ci, bp, dcb);
bp->b_resid = 0;
break;
case SOFT_DATA_ERROR:
vdsoft_error(ci, bp, dcb);
/* fall thru... */
default: /* operation completed */
bp->b_error = 0;
bp->b_resid = 0;
break;
}
vbadone(bp, ci->rawbuf, (long *)ci->map, ci->utl);
/*
* Take next request on this unit q, or, if none,
* the next request on the next active unit q.
*/
s = spl7();
uq->av_forw = bp->av_forw;
if (uq->av_back != bp) {
register struct buf *next;
unit = VDUNIT(uq->av_forw->b_dev);
slave = vddinfo[unit]->ui_slave;
next = uq->av_forw;
if (next->b_cylin != ci->cur_cyl[slave] ||
(next->b_daddr & 0xff) != ci->cur_trk[slave])
ci->off_cylinder |= 1 << slave;
} else
uq->av_back = NULL;
splx(s);
/* reset controller state */
cq->b_errcnt = 0;
cq->b_active--;
#ifdef VDDCPERF
scope_out(3);
#endif
if (bp->b_flags & B_ERROR)
bp->b_error = EIO;
iodone(bp);
vdstart(vdminfo[ctlr]);
}
/*
* Convert controller status to internal operation/error code.
*/
vddecode_error(dcb)
register fmt_dcb *dcb;
{
if (dcb->operrsta & HRDERR) {
if (dcb->operrsta & (HCRCERR | HCMPERR | UCDATERR | WPTERR |
DSEEKERR | NOTCYLERR |DRVNRDY | INVDADR))
return (DRIVE_ERROR);
if (dcb->operrsta & (CTLRERR | OPABRT | INVCMD | DNEMEM))
return (CTLR_ERROR);
return (HARD_DATA_ERROR);
}
if (dcb->operrsta & SFTERR)
return (SOFT_DATA_ERROR);
return (0);
}
/*
* Report a hard error.
*/
vdhard_error(ci, bp, dcb)
ctlr_tab *ci;
register struct buf *bp;
register fmt_dcb *dcb;
{
unit_tab *ui = &vdunit_info[VDUNIT(bp->b_dev)];
bp->b_flags |= B_ERROR;
harderr(bp, ui->info.type_name);
printf("status %x", dcb->operrsta);
if (ci->ctlr_type == SMD_ECTLR)
printf(" ecode %x", dcb->err_code);
printf("\n");
}
/*
* Report a soft error.
*/
vdsoft_error(ci, bp, dcb)
ctlr_tab *ci;
register struct buf *bp;
register fmt_dcb *dcb;
{
unit_tab *ui = &vdunit_info[VDUNIT(bp->b_dev)];
printf("%s%d%c: soft error sn%d status %x", ui->info.type_name,
minor(bp->b_dev) >> 3, 'a'+(minor(bp->b_dev)&07), bp->b_blkno,
dcb->operrsta);
if (ci->ctlr_type == SMD_ECTLR)
printf(" ecode %x", dcb->err_code);
printf("\n");
}
/*ARGSUSED*/
vdopen(dev, flag)
dev_t dev;
int flag;
{
register unit = VDUNIT(dev);
register struct vba_device *vi = vddinfo[unit];
if (vi == 0 || vi->ui_alive == 0 || vi->ui_type >= nvddrv)
return (ENXIO);
if (vdunit_info[unit].info.partition[FILSYS(dev)].par_len == 0)
return (ENXIO);
return (0);
}
vdread(dev, uio)
dev_t dev;
struct uio *uio;
{
register int unit = VDUNIT(dev);
register unit_tab *ui = &vdunit_info[unit];
if (unit >= NFSD)
return (ENXIO);
return (physio(vdstrategy, &ui->raw_q_element, dev, B_READ,
minphys, uio));
}
vdwrite(dev, uio)
dev_t dev;
struct uio *uio;
{
register int unit = VDUNIT(dev);
register unit_tab *ui = &vdunit_info[unit];
if (unit >= NFSD)
return (ENXIO);
return (physio(vdstrategy, &ui->raw_q_element, dev, B_WRITE,
minphys, uio));
}
/*
* Crash dump.
*/
vddump(dev)
dev_t dev;
{
register int unit = VDUNIT(dev);
register unit_tab *ui = &vdunit_info[unit];
register fs_tab *fs = &ui->info;
register int ctlr = vddinfo[unit]->ui_ctlr;
register struct vba_ctlr *vba_vdctlr_info = vdminfo[ctlr];
register int filsys = FILSYS(dev);
register cdr *addr = (cdr *)(vba_vdctlr_info->um_addr);
register int cur_blk, blkcount, blocks;
caddr_t memaddr;
vdreset_ctlr(addr, ctlr);
blkcount = maxfree - 2; /* In 1k byte pages */
if (dumplo + blkcount > fs->partition[filsys].par_len) {
blkcount = fs->partition[filsys].par_len - dumplo;
printf("vd%d: Dump truncated to %dMB\n", unit, blkcount/1024);
}
cur_blk = fs->partition[filsys].par_start + dumplo;
memaddr = 0;
while (blkcount > 0) {
blocks = MIN(blkcount, DUMPSIZE);
if (!vdwrite_block(addr, ctlr, unit, memaddr, cur_blk, blocks))
return (EIO);
blkcount -= blocks;
memaddr += blocks * NBPG;
cur_blk += blocks;
}
return (0);
}
/*
* Write a block to disk during a crash dump.
*/
vdwrite_block(caddr, ctlr, unit, addr, block, blocks)
register cdr *caddr;
register int ctlr, unit;
register caddr_t addr;
register int block, blocks;
{
register fmt_mdcb *mdcb = &vdctlr_info[ctlr].ctlr_mdcb;
register fmt_dcb *dcb = &vdctlr_info[ctlr].ctlr_dcb;
register unit_tab *ui = &vdunit_info[unit];
register fs_tab *fs = &ui->info;
block *= (int)ui->sec_per_blk;
blocks *= (int)ui->sec_per_blk;
mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb));
dcb->intflg = NOINT;
dcb->opcode = WD;
dcb->operrsta = 0;
dcb->devselect = (char)(vddinfo[unit])->ui_slave;
dcb->trailcnt = (char)(sizeof (trrw) / sizeof (long));
dcb->trail.rwtrail.memadr = addr;
dcb->trail.rwtrail.wcount = (short)
((blocks * fs->secsize)/ sizeof (short));
dcb->trail.rwtrail.disk.cylinder = (short)(block / ui->sec_per_cyl);
dcb->trail.rwtrail.disk.track = (char)((block / fs->nsec) % fs->ntrak);
dcb->trail.rwtrail.disk.sector = (char)(block % fs->nsec);
VDDC_ATTENTION(caddr, (fmt_mdcb *)(PHYS(mdcb)),
vdctlr_info[ctlr].ctlr_type);
POLLTILLDONE(caddr, dcb, 5, vdctlr_info[ctlr].ctlr_type);
if (vdtimeout <= 0) {
printf(" during dump\n");
return (0);
}
if (dcb->operrsta & HRDERR) {
printf("vd%d: hard error, status %x\n", unit, dcb->operrsta);
return (0);
}
return (1);
}
vdsize(dev)
dev_t dev;
{
struct vba_device *vi = vddinfo[VDUNIT(dev)];
if (vi == 0 || vi->ui_alive == 0 || vi->ui_type >= nvddrv)
return (-1);
return (vdunit_info[VDUNIT(dev)].info.partition[FILSYS(dev)].par_len);
}
/*
* Perform a controller reset.
*/
vdreset_ctlr(addr, ctlr)
register cdr *addr;
register int ctlr;
{
register struct buf *cq = &vdminfo[ctlr]->um_tab;
register struct buf *uq = cq->b_forw;
register ctlr_tab *ci = &vdctlr_info[ctlr];
VDDC_RESET(addr, ci->ctlr_type);
ci->ctlr_started = 0;
if (ci->ctlr_type == SMD_ECTLR) {
addr->cdr_csr = 0;
addr->mdcb_tcf = AM_ENPDA;
addr->dcb_tcf = AM_ENPDA;
addr->trail_tcf = AM_ENPDA;
addr->data_tcf = AM_ENPDA;
addr->cdr_ccf = CCF_STS | XMD_32BIT | BSZ_16WRD |
CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR;
}
if (vdnotrailer(addr, ctlr, 0, INIT, 10) & HRDERR) {
printf("failed to init\n");
return (0);
}
if (vdnotrailer(addr, ctlr, 0, DIAG, 10) & HRDERR) {
printf("diagnostic error\n");
return (0);
}
/* reset all units attached to controller */
uq = cq->b_forw;
do {
reset_drive(addr, ctlr, uq->b_dev, 0);
uq = uq->b_forw;
} while (uq != cq->b_forw);
return (1);
}
/*
* Perform a reset on a drive.
*/
reset_drive(addr, ctlr, slave, start)
register cdr *addr;
register int ctlr, slave, start;
{
register int type = vdctlr_info[ctlr].unit_type[slave];
if (type == UNKNOWN)
return;
if (!vdconfigure_drive(addr, ctlr, slave, type, start))
printf("vd%d: drive %d: couldn't reset\n", ctlr, slave);
}
#ifdef notdef
/*
* Dump the mdcb and DCB for diagnostic purposes.
*/
vdprintdcb(lp)
register long *lp;
{
register int i, dcb, tc;
for (dcb = 0; lp; lp = (long *)(*lp), dcb++) {
lp = (long *)((long)lp | 0xc0000000);
printf("\nDump of dcb%d@%x:", dcb, lp);
for (i = 0, tc = lp[3] & 0xff; i < tc+7; i++)
printf(" %lx", lp[i]);
printf("\n");
}
DELAY(1750000);
}
#endif
#endif