1*29954Skarels /* vd.c 1.12 86/11/03 */ 224004Ssam 329564Ssam #include "dk.h" 424004Ssam #if NVD > 0 524004Ssam /* 629564Ssam * VDDC - Versabus SMD/SMDE driver. 725675Ssam */ 825877Ssam #ifdef VDDCPERF 925877Ssam #define DOSCOPE 1025877Ssam #endif 1125877Ssam 1225675Ssam #include "param.h" 1325675Ssam #include "buf.h" 1425675Ssam #include "cmap.h" 1525675Ssam #include "conf.h" 1625675Ssam #include "dir.h" 1729564Ssam #include "dkstat.h" 1825675Ssam #include "map.h" 1925675Ssam #include "systm.h" 2025675Ssam #include "user.h" 2125675Ssam #include "vmmac.h" 2225675Ssam #include "proc.h" 2325675Ssam #include "uio.h" 2424004Ssam 2529951Skarels #include "../tahoe/cpu.h" 2629951Skarels #include "../tahoe/mtpr.h" 2729951Skarels #include "../tahoe/pte.h" 2829951Skarels 2925675Ssam #include "../tahoevba/vbavar.h" 3025675Ssam #define VDGENDATA 3125928Ssam #include "../tahoevba/vdreg.h" 3225675Ssam #undef VDGENDATA 3325877Ssam #include "../tahoevba/scope.h" 3424004Ssam 3525925Ssam #define VDMAXIO (MAXBPTE*NBPG) 3625675Ssam #define DUMPSIZE 64 /* controller limit */ 3724004Ssam 3824004Ssam #define VDUNIT(x) (minor(x) >> 3) 3925675Ssam #define FILSYS(x) (minor(x) & 0x07) 4025675Ssam #define PHYS(x) (vtoph((struct proc *)0, (unsigned)(x))) 4124004Ssam 4225675Ssam #define CTLR_ERROR 1 4325675Ssam #define DRIVE_ERROR 2 4425675Ssam #define HARD_DATA_ERROR 3 4525675Ssam #define SOFT_DATA_ERROR 4 4629921Skarels #define WRITE_PROTECT 5 4724004Ssam 4825675Ssam #define b_cylin b_resid 4925675Ssam #define b_daddr b_error 5024004Ssam 5124004Ssam struct vba_ctlr *vdminfo[NVD]; 5229564Ssam struct vba_device *vddinfo[NDK]; 5325675Ssam int vdprobe(), vdslave(), vdattach(), vddgo(); 5425675Ssam struct vba_driver vddriver = 5529564Ssam { vdprobe, vdslave, vdattach, vddgo, vddcaddr, "dk", 5625675Ssam vddinfo, "vd", vdminfo }; 5724004Ssam 5824004Ssam /* 5925675Ssam * Per-drive state. 6025675Ssam */ 6125675Ssam typedef struct { 6225675Ssam struct buf raw_q_element; 6325675Ssam short sec_per_blk; 6425675Ssam short sec_per_cyl; 6525675Ssam char status; 6625675Ssam struct buf xfer_queue; 6725675Ssam int drive_type; 6825675Ssam fs_tab info; 6925675Ssam } unit_tab; 7024004Ssam 7124004Ssam /* 7225675Ssam * Per-controller state. 7325675Ssam */ 7425675Ssam typedef struct { 7525675Ssam char ctlr_type; /* controller type */ 7625925Ssam struct pte *map; /* i/o page map */ 7725925Ssam caddr_t utl; /* mapped i/o space */ 7825675Ssam u_int cur_slave:8; /* last active unit number */ 7929921Skarels u_int int_expected:1; /* expect an interrupt */ 8025675Ssam u_int ctlr_started:1; /* start command was issued */ 8125675Ssam u_int overlap_seeks:1;/* should overlap seeks */ 8225925Ssam u_int initdone:1; /* controller initialization completed */ 8325675Ssam u_int off_cylinder:16;/* off cylinder bit map */ 8425675Ssam u_int unit_type[16]; /* slave types */ 8525675Ssam u_int cur_cyl[16]; /* cylinder last selected */ 8625675Ssam long cur_trk[16]; /* track last selected */ 8725675Ssam fmt_mdcb ctlr_mdcb; /* controller mdcb */ 8825675Ssam fmt_dcb ctlr_dcb; /* r/w dcb */ 8925675Ssam fmt_dcb seek_dcb[4]; /* dcbs for overlapped seeks */ 9025950Ssam caddr_t rawbuf; /* buffer for raw+swap i/o */ 9125675Ssam } ctlr_tab; 9224004Ssam 9325925Ssam ctlr_tab vdctlr_info[NVD]; 9429564Ssam unit_tab vdunit_info[NDK]; 9524004Ssam 9624004Ssam /* 9725675Ssam * See if the controller is really there; if so, initialize it. 9825675Ssam */ 9925857Ssam vdprobe(reg, vm) 10025857Ssam caddr_t reg; 10125857Ssam struct vba_ctlr *vm; 10225675Ssam { 10325857Ssam register br, cvec; /* must be r12, r11 */ 10425925Ssam register cdr *addr = (cdr *)reg; 10525925Ssam register ctlr_tab *ci; 10625925Ssam int i; 10725857Ssam 10825857Ssam if (badaddr((caddr_t)reg, 2)) 10925675Ssam return (0); 11025925Ssam ci = &vdctlr_info[vm->um_ctlr]; 11125925Ssam addr->cdr_reset = 0xffffffff; 11225675Ssam DELAY(1000000); 11325925Ssam if (addr->cdr_reset != (unsigned)0xffffffff) { 11425925Ssam ci->ctlr_type = SMDCTLR; 11525925Ssam ci->overlap_seeks = 0; 11625675Ssam DELAY(1000000); 11725675Ssam } else { 11825925Ssam ci->overlap_seeks = 1; 11925925Ssam ci->ctlr_type = SMD_ECTLR; 12025925Ssam addr->cdr_reserved = 0x0; 12125675Ssam DELAY(3000000); 12225925Ssam addr->cdr_csr = 0; 12325925Ssam addr->mdcb_tcf = AM_ENPDA; 12425925Ssam addr->dcb_tcf = AM_ENPDA; 12525925Ssam addr->trail_tcf = AM_ENPDA; 12625925Ssam addr->data_tcf = AM_ENPDA; 12729921Skarels addr->cdr_ccf = CCF_SEN | CCF_DER | CCF_STS | 12829921Skarels XMD_32BIT | BSZ_16WRD | 12925925Ssam CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR; 13025675Ssam } 13125925Ssam /* 13225950Ssam * Allocate page tables and i/o buffer. 13325925Ssam */ 13425925Ssam vbmapalloc(btoc(VDMAXIO)+1, &ci->map, &ci->utl); 13525950Ssam ci->rawbuf = calloc(VDMAXIO); 13625925Ssam /* 13725925Ssam * Initialize all the drives to be of an unknown type. 13825925Ssam */ 13925925Ssam for (i = 0; i < 15; i++) 14025925Ssam ci->unit_type[i] = UNKNOWN; 14125857Ssam br = 0x17, cvec = 0xe0 + vm->um_ctlr; /* XXX */ 14225925Ssam return (sizeof (*addr)); 14325675Ssam } 14424004Ssam 14524004Ssam /* 14625675Ssam * See if a drive is really there 14725675Ssam * Try to reset/configure the drive, then test its status. 14825675Ssam */ 14925675Ssam vdslave(vi, addr) 15025675Ssam register struct vba_device *vi; 15125675Ssam register cdr *addr; 15225675Ssam { 15325675Ssam register ctlr_tab *ci = &vdctlr_info[vi->ui_ctlr]; 15425675Ssam register unit_tab *ui = &vdunit_info[vi->ui_unit]; 15525675Ssam register fmt_mdcb *mdcb = &ci->ctlr_mdcb; 15625675Ssam register fmt_dcb *dcb = &ci->ctlr_dcb; 15725675Ssam register int type; 15824004Ssam 15925925Ssam if (!ci->initdone) { 16025925Ssam printf("vd%d: %s controller\n", vi->ui_ctlr, 16129564Ssam ci->ctlr_type == SMDCTLR ? "smd" : "smde"); 16225925Ssam if (vdnotrailer(addr, vi->ui_ctlr, vi->ui_slave, INIT, 10) & 16325925Ssam HRDERR) { 16425925Ssam printf("vd%d: init error\n", vi->ui_ctlr); 16525675Ssam return (0); 16625675Ssam } 16725925Ssam if (vdnotrailer(addr, vi->ui_ctlr, vi->ui_slave, DIAG, 10) & 16825925Ssam HRDERR) { 16925925Ssam printf("vd%d: diagnostic error\n", vi->ui_ctlr); 17025675Ssam return (0); 17125675Ssam } 17225925Ssam ci->initdone = 1; 17325675Ssam } 17425675Ssam /* 17525675Ssam * Seek on all drive types starting from the largest one. 17625675Ssam * a successful seek to the last sector/cylinder/track verifies 17725675Ssam * the drive type connected to this port. 17825675Ssam */ 17925675Ssam for (type = 0; type < nvddrv; type++) { 18025675Ssam /* XXX */ 18125675Ssam if (ci->ctlr_type == SMDCTLR && vdst[type].nsec != 32) 18225675Ssam continue; 18325675Ssam /* XXX */ 18425675Ssam if (!vdconfigure_drive(addr, vi->ui_ctlr, vi->ui_slave, type,0)) 18525675Ssam return (0); 18625675Ssam dcb->opcode = (short)RD; 18725675Ssam dcb->intflg = NOINT; 18825675Ssam dcb->nxtdcb = (fmt_dcb *)0; /* end of chain */ 18925675Ssam dcb->operrsta = 0; 19025675Ssam dcb->devselect = (char)(vi->ui_slave); 19125675Ssam dcb->trailcnt = (char)(sizeof (trrw) / sizeof (long)); 19225675Ssam dcb->trail.rwtrail.memadr = (char *)PHYS(ci->rawbuf); 19325675Ssam dcb->trail.rwtrail.wcount = vdst[type].secsize/sizeof(short); 19425675Ssam dcb->trail.rwtrail.disk.cylinder = vdst[type].ncyl - 2; 19525675Ssam dcb->trail.rwtrail.disk.track = vdst[type].ntrak - 1; 19625675Ssam dcb->trail.rwtrail.disk.sector = vdst[type].nsec - 1; 19725675Ssam mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb)); 19825675Ssam mdcb->vddcstat = 0; 19925675Ssam VDDC_ATTENTION(addr, (fmt_mdcb *)(PHYS(mdcb)), ci->ctlr_type); 20025925Ssam if (!vdpoll(ci, addr, 60)) 20125675Ssam printf(" during probe\n"); 20225675Ssam if ((dcb->operrsta&HRDERR) == 0) 20325675Ssam break; 20425675Ssam } 20525675Ssam if (type >= nvddrv) { 20625675Ssam /* 20725675Ssam * If reached here, a drive which is not defined in the 20829921Skarels * 'vdst' tables is connected. Cannot set its type. 20925675Ssam */ 21029564Ssam printf("dk%d: unknown drive type\n", vi->ui_unit); 21125675Ssam return (0); 21225675Ssam } 21325675Ssam ui->drive_type = type; 21425675Ssam ui->info = vdst[type]; 21525675Ssam ui->sec_per_blk = DEV_BSIZE / ui->info.secsize; 21625675Ssam vi->ui_type = type; 21725675Ssam vi->ui_dk = 1; 21825675Ssam return (1); 21924004Ssam } 22024004Ssam 22125675Ssam vdconfigure_drive(addr, ctlr, slave, type, pass) 22225675Ssam register cdr *addr; 22325675Ssam int ctlr, slave, type, pass; 22424004Ssam { 22525675Ssam register ctlr_tab *ci = &vdctlr_info[ctlr]; 22625675Ssam 22725675Ssam ci->ctlr_dcb.opcode = RSTCFG; /* command */ 22825675Ssam ci->ctlr_dcb.intflg = NOINT; 22925675Ssam ci->ctlr_dcb.nxtdcb = (fmt_dcb *)0; /* end of chain */ 23025675Ssam ci->ctlr_dcb.operrsta = 0; 23125675Ssam ci->ctlr_dcb.devselect = (char)slave; 23225675Ssam ci->ctlr_dcb.trail.rstrail.ncyl = vdst[type].ncyl; 23325675Ssam ci->ctlr_dcb.trail.rstrail.nsurfaces = vdst[type].ntrak; 23425675Ssam if (ci->ctlr_type == SMD_ECTLR) { 23529921Skarels ci->ctlr_dcb.trailcnt = (char)5; 23625675Ssam ci->ctlr_dcb.trail.rstrail.nsectors = vdst[type].nsec; 23725675Ssam ci->ctlr_dcb.trail.rstrail.slip_sec = vdst[type].nslip; 23829921Skarels ci->ctlr_dcb.trail.rstrail.recovery = 0x18f; 23925675Ssam } else 24025675Ssam ci->ctlr_dcb.trailcnt = (char)2; 24125675Ssam ci->ctlr_mdcb.firstdcb = (fmt_dcb *)(PHYS(&ci->ctlr_dcb)); 24225675Ssam ci->ctlr_mdcb.vddcstat = 0; 24325675Ssam VDDC_ATTENTION(addr, (fmt_mdcb *)(PHYS(&ci->ctlr_mdcb)), ci->ctlr_type); 24425925Ssam if (!vdpoll(ci, addr, 5)) { 24525675Ssam printf(" during config\n"); 24625675Ssam return (0); 24725675Ssam } 24825675Ssam if (ci->ctlr_dcb.operrsta & HRDERR) { 24925675Ssam if ((ci->ctlr_dcb.operrsta & (NOTCYLERR|DRVNRDY)) == 0) 25025675Ssam printf("vd%d: drive %d: config error\n", ctlr, slave); 25125675Ssam else if (pass == 0) { 25225675Ssam vdstart_drive(addr, ctlr, slave); 25325675Ssam return (vdconfigure_drive(addr, ctlr, slave, type, 1)); 25425675Ssam } else if (pass == 2) 25525675Ssam return (vdconfigure_drive(addr, ctlr, slave, type, 3)); 25625675Ssam return (0); 25725675Ssam } 25825675Ssam return (1); 25924004Ssam } 26024004Ssam 26125675Ssam vdstart_drive(addr, ctlr, slave) 26225675Ssam cdr *addr; 26325675Ssam register int ctlr, slave; 26424004Ssam { 26525675Ssam int error = 0; 26624004Ssam 26725675Ssam printf("vd%d: starting drive %d, wait...", ctlr, slave); 26825675Ssam if (vdctlr_info[ctlr].ctlr_started) { 26929921Skarels #ifdef notdef 27025675Ssam printf("DELAY(5500000)..."); 27125675Ssam DELAY(5500000); 27229921Skarels #endif 27325675Ssam goto done; 27424004Ssam } 27525675Ssam vdctlr_info[ctlr].ctlr_started = 1; 27625675Ssam error = vdnotrailer(addr, ctlr, 0, VDSTART, (slave*6)+62) & HRDERR; 27725675Ssam if (!error) { 27829921Skarels #ifdef notdef 27925675Ssam DELAY((slave * 5500000) + 62000000); 28029921Skarels #endif 28124004Ssam } 28225675Ssam done: 28325675Ssam printf("\n"); 28425675Ssam return (error == 0); 28525675Ssam } 28624004Ssam 28725675Ssam vdnotrailer(addr, ctlr, unit, function, time) 28825675Ssam register cdr *addr; 28925675Ssam int ctlr, unit, function, time; 29024004Ssam { 29125925Ssam register ctlr_tab *ci = &vdctlr_info[ctlr]; 29225925Ssam fmt_mdcb *mdcb = &ci->ctlr_mdcb; 29325925Ssam fmt_dcb *dcb = &ci->ctlr_dcb; 29424004Ssam 29525675Ssam dcb->opcode = function; /* command */ 29624004Ssam dcb->intflg = NOINT; 29725675Ssam dcb->nxtdcb = (fmt_dcb *)0; /* end of chain */ 29825675Ssam dcb->operrsta = 0; 29925675Ssam dcb->devselect = (char)unit; 30024004Ssam dcb->trailcnt = (char)0; 30125675Ssam mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb)); 30224004Ssam mdcb->vddcstat = 0; 30325925Ssam VDDC_ATTENTION(addr, (fmt_mdcb *)(PHYS(mdcb)), ci->ctlr_type); 30425925Ssam if (!vdpoll(ci, addr, time)) { 30525675Ssam printf(" during init\n"); 30625675Ssam return (DCBCMP|ANYERR|HRDERR|OPABRT); 30724004Ssam } 30825675Ssam return (dcb->operrsta); 30925675Ssam } 31024004Ssam 31125675Ssam vdattach(vi) 31225675Ssam register struct vba_device *vi; 31325675Ssam { 31425675Ssam register unit_tab *ui = &vdunit_info[vi->ui_unit]; 31525675Ssam register ctlr_tab *ci = &vdctlr_info[vi->ui_ctlr]; 31625675Ssam register struct buf *cq = &vi->ui_mi->um_tab; 31725675Ssam register struct buf *uq = cq->b_forw; 31825675Ssam register struct buf *start_queue = uq; 31925675Ssam register fs_tab *fs = &ui->info; 32025675Ssam 32125675Ssam ui->info = vdst[vi->ui_type]; 32225675Ssam ui->sec_per_blk = DEV_BSIZE / ui->info.secsize; 32325675Ssam ui->sec_per_cyl = ui->info.nsec * ui->info.ntrak; 32425675Ssam ui->xfer_queue.b_dev = vi->ui_slave; 32525675Ssam ci->unit_type[vi->ui_slave] = vi->ui_type; 32625675Ssam /* load unit into controller's active unit list */ 32725675Ssam if (uq == NULL) { 32825675Ssam cq->b_forw = &ui->xfer_queue; 32925675Ssam ui->xfer_queue.b_forw = &ui->xfer_queue; 33025675Ssam ui->xfer_queue.b_back = &ui->xfer_queue; 33125675Ssam } else { 33225675Ssam while (uq->b_forw != start_queue) 33325675Ssam uq = uq->b_forw; 33425675Ssam ui->xfer_queue.b_forw = start_queue; 33525675Ssam ui->xfer_queue.b_back = uq; 33625675Ssam uq->b_forw = &ui->xfer_queue; 33725675Ssam start_queue->b_back = &ui->xfer_queue; 33825675Ssam } 33929921Skarels printf("dk%d: %s <ntrak %d, ncyl %d, nsec %d>\n", 34029921Skarels vi->ui_unit, fs->type_name, 34129921Skarels ui->info.ntrak, ui->info.ncyl, ui->info.nsec); 34224004Ssam /* 34325675Ssam * (60 / rpm) / (number of sectors per track * (bytes per sector / 2)) 34424004Ssam */ 34525675Ssam dk_mspw[vi->ui_unit] = 120.0 / (fs->rpm * fs->nsec * fs->secsize); 34624004Ssam } 34724004Ssam 34825675Ssam /*ARGSUSED*/ 34925675Ssam vddgo(um) 35025675Ssam struct vba_ctlr *um; 35124004Ssam { 35224004Ssam 35324004Ssam } 35424004Ssam 35524004Ssam vdstrategy(bp) 35625675Ssam register struct buf *bp; 35724004Ssam { 35825675Ssam register int unit = VDUNIT(bp->b_dev); 35925675Ssam register struct vba_device *vi = vddinfo[unit]; 36025675Ssam register par_tab *par; 36125675Ssam register unit_tab *ui; 36225675Ssam register fs_tab *fs; 36325675Ssam register int blks, bn, s; 36424004Ssam 365*29954Skarels if (bp->b_bcount == 0 || vi == 0 || vi->ui_alive == 0) { 366*29954Skarels bp->b_error = ENXIO; 36725675Ssam goto bad; 368*29954Skarels } 36925675Ssam ui = &vdunit_info[unit]; 37025675Ssam fs = &ui->info; 37125675Ssam par = &fs->partition[FILSYS(bp->b_dev)]; 37225675Ssam blks = (bp->b_bcount + DEV_BSIZE-1) >> DEV_BSHIFT; 373*29954Skarels if ((unsigned) bp->b_blkno + blks > par->par_len) { 374*29954Skarels if (bp->b_blkno == par->par_len) { 375*29954Skarels bp->b_resid = bp->b_bcount; 376*29954Skarels goto done; 377*29954Skarels } 378*29954Skarels #ifdef notdef 379*29954Skarels /* 380*29954Skarels * I'm not sure I want to smash bp->b_bcount. 381*29954Skarels */ 38225675Ssam blks = par->par_len - bp->b_blkno; 383*29954Skarels if (blks <= 0) { 384*29954Skarels bp->b_error = EINVAL; 38525675Ssam goto bad; 386*29954Skarels } 38725675Ssam bp->b_bcount = blks * DEV_BSIZE; 388*29954Skarels #else 389*29954Skarels bp->b_error = EINVAL; 390*29954Skarels goto bad; 391*29954Skarels #endif 39225675Ssam } 39325675Ssam bn = bp->b_blkno + par->par_start; 39425675Ssam bn *= ui->sec_per_blk; 39525675Ssam bp->b_daddr = (bn / fs->nsec) % fs->ntrak; 39625675Ssam bp->b_cylin = bn / ui->sec_per_cyl; 39725675Ssam vbasetup(bp, ui->info.secsize); 39825675Ssam s = spl7(); 39925675Ssam if (ui->xfer_queue.av_forw == NULL) { 40025675Ssam register ctlr_tab *ci = &vdctlr_info[vi->ui_ctlr]; 40125675Ssam int slave = vi->ui_slave; 40224004Ssam 40325675Ssam if (bp->b_cylin != ci->cur_cyl[slave] || 40425675Ssam bp->b_daddr != ci->cur_trk[slave]) 40525675Ssam ci->off_cylinder |= 1 << slave; 40624004Ssam } 40725675Ssam bp->b_daddr |= (bn % fs->nsec) << 8; 40825675Ssam disksort(&ui->xfer_queue, bp); 40925675Ssam if (!vddinfo[unit]->ui_mi->um_tab.b_active++) { 41025675Ssam splx(s); 41125675Ssam vdstart(vddinfo[unit]->ui_mi); 41225675Ssam } else 41325675Ssam splx(s); 41424004Ssam return; 41525675Ssam bad: 416*29954Skarels bp->b_flags |= B_ERROR; 417*29954Skarels done: 41824004Ssam iodone(bp); 41924004Ssam } 42024004Ssam 42124004Ssam /* 42224004Ssam * Start up a transfer on a drive. 42324004Ssam */ 42425675Ssam vdstart(ci) 42525675Ssam register struct vba_ctlr *ci; 42624004Ssam { 42725675Ssam register struct buf *cq = &ci->um_tab; 42825675Ssam register struct buf *uq = cq->b_forw; 42924004Ssam 43025675Ssam /* search for next ready unit */ 43125675Ssam cq->b_forw = cq->b_forw->b_forw; 43225675Ssam uq = cq->b_forw; 43325675Ssam do { 43425675Ssam if (uq->av_forw != NULL) { 43525675Ssam cq->b_forw = uq; 43625675Ssam vdexecute(ci, uq); 43725675Ssam return; 43825675Ssam } 43925675Ssam uq = uq->b_forw; 44025675Ssam } while (uq != cq->b_forw); 44125675Ssam } 44225675Ssam 44325675Ssam /* 44425675Ssam * Initiate seeks for all drives off-cylinder. 44525675Ssam */ 44625675Ssam vdload_seeks(ci, uq) 44725675Ssam register ctlr_tab *ci; 44825675Ssam register struct buf *uq; 44925675Ssam { 45025675Ssam register int unit, slave, nseeks; 45125675Ssam register fmt_dcb *dcb; 45225675Ssam register struct buf *bp; 45325675Ssam register struct buf *start_queue = uq; 45425675Ssam 45525675Ssam nseeks = 0; 45625675Ssam do { 45725675Ssam bp = uq->av_forw; 45825675Ssam if (bp != NULL) { 45925675Ssam unit = VDUNIT(bp->b_dev); 46025675Ssam slave = vddinfo[unit]->ui_slave; 46125675Ssam if (ci->off_cylinder & (1 << slave)) { 46225675Ssam ci->off_cylinder &= ~(1 << slave); 46325675Ssam if (ci->cur_cyl[slave] != bp->b_cylin) { 46425675Ssam ci->cur_cyl[slave] = bp->b_cylin; 46525675Ssam dk_seek[unit]++; 46625675Ssam } 46725675Ssam ci->cur_trk[slave] = bp->b_daddr&0xff; 46825675Ssam dcb = &ci->seek_dcb[nseeks++]; 46925675Ssam dcb->opcode = SEEK; 47025675Ssam dcb->intflg = NOINT | INT_PBA; 47125675Ssam dcb->operrsta = 0; 47225675Ssam dcb->devselect = (char)slave; 47325675Ssam dcb->trailcnt = (char)1; 47425675Ssam dcb->trail.sktrail.skaddr.cylinder = 47525675Ssam bp->b_cylin; 47625675Ssam dcb->trail.sktrail.skaddr.track = 47725675Ssam bp->b_daddr & 0xff; 47825675Ssam dcb->trail.sktrail.skaddr.sector = 0; 47925675Ssam } 48025675Ssam } 48125675Ssam uq = uq->b_forw; 48225675Ssam } while (uq != start_queue && nseeks < 4); 48325675Ssam return (nseeks); 48425675Ssam } 48525675Ssam 48625675Ssam extern vd_int_timeout(); 48725675Ssam /* 48825675Ssam * Execute the next command on the unit queue uq. 48925675Ssam */ 49025675Ssam vdexecute(controller_info, uq) 49125675Ssam register struct vba_ctlr *controller_info; 49225675Ssam register struct buf *uq; 49325675Ssam { 49425675Ssam register struct buf *bp = uq->av_forw; 49525675Ssam register int ctlr = controller_info->um_ctlr; 49625675Ssam register ctlr_tab *ci = &vdctlr_info[ctlr]; 49725675Ssam register int unit = VDUNIT(bp->b_dev); 49825675Ssam register int slave = vddinfo[unit]->ui_slave; 49925675Ssam register fmt_mdcb *mdcb = &ci->ctlr_mdcb; 50025675Ssam register fmt_dcb *dcb = &ci->ctlr_dcb; 50125675Ssam 50224004Ssam /* 50325675Ssam * If there are overlapped seeks to perform, shuffle 50425675Ssam * them to the front of the queue and get them started 50525675Ssam * before any data transfers (to get some parallelism). 50624004Ssam */ 50725675Ssam if ((ci->off_cylinder & ~(1<<slave)) && ci->overlap_seeks) { 50825675Ssam register int i, nseeks; 50925675Ssam 51025675Ssam /* setup seek requests in seek-q */ 51125675Ssam nseeks = vdload_seeks(ci, uq); 51225675Ssam /* place at the front of the master q */ 51325675Ssam mdcb->firstdcb = (fmt_dcb *)PHYS(&ci->seek_dcb[0]); 51425675Ssam /* shuffle any remaining seeks up in the seek-q */ 51525675Ssam for (i = 1; i < nseeks; i++) 51625675Ssam ci->seek_dcb[i-1].nxtdcb = 51725675Ssam (fmt_dcb *)PHYS(&ci->seek_dcb[i]); 51825675Ssam ci->seek_dcb[nseeks-1].nxtdcb = (fmt_dcb *)PHYS(dcb); 51925675Ssam } else { 52025675Ssam if (bp->b_cylin != ci->cur_cyl[slave]) { 52125675Ssam ci->cur_cyl[slave] = bp->b_cylin; 52225675Ssam dk_seek[unit]++; 52325675Ssam } 52425675Ssam ci->cur_trk[slave] = bp->b_daddr & 0xff; 52525675Ssam ci->off_cylinder = 0; 52625675Ssam mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb)); 52724004Ssam } 52824004Ssam dcb->opcode = (bp->b_flags & B_READ) ? RD : WD; 52925675Ssam dcb->intflg = INTDONE; 53025675Ssam dcb->nxtdcb = (fmt_dcb *)0; /* end of chain */ 53124004Ssam dcb->operrsta = 0; 53225675Ssam dcb->devselect = (char)slave; 53325675Ssam dcb->trailcnt = (char)(sizeof (trrw) / sizeof (long)); 53425675Ssam dcb->trail.rwtrail.memadr = (char *) 53525675Ssam vbastart(bp, ci->rawbuf, (long *)ci->map, ci->utl); 53625675Ssam dcb->trail.rwtrail.wcount = (short)((bp->b_bcount+1) / sizeof (short)); 53725675Ssam dcb->trail.rwtrail.disk.cylinder = bp->b_cylin; 53825675Ssam dcb->trail.rwtrail.disk.track = bp->b_daddr & 0xff; 53925675Ssam dcb->trail.rwtrail.disk.sector = bp->b_daddr >> 8; 54025675Ssam mdcb->vddcstat = 0; 54125675Ssam dk_wds[unit] += bp->b_bcount / 32; 54225675Ssam ci->int_expected = 1; 54325675Ssam timeout(vd_int_timeout, (caddr_t)ctlr, 20*60); 54425675Ssam dk_busy |= 1 << unit; 54525675Ssam scope_out(1); 54625675Ssam VDDC_ATTENTION((cdr *)(vdminfo[ctlr]->um_addr), 54725675Ssam (fmt_mdcb *)(PHYS(mdcb)), ci->ctlr_type); 54825675Ssam } 54924004Ssam 55024004Ssam /* 55125675Ssam * Watch for lost interrupts. 55225675Ssam */ 55325675Ssam vd_int_timeout(ctlr) 55425675Ssam register int ctlr; 55525675Ssam { 55625675Ssam register ctlr_tab *ci = &vdctlr_info[ctlr]; 55725675Ssam register fmt_dcb *dcb = &ci->ctlr_dcb; 55824004Ssam 55925675Ssam uncache(&dcb->operrsta); 56029921Skarels printf("vd%d: lost interrupt, status %b", ctlr, dcb->operrsta, ERRBITS); 56125675Ssam if (ci->ctlr_type == SMD_ECTLR) { 56225675Ssam uncache(&dcb->err_code); 56325675Ssam printf(", error code %x", dcb->err_code); 56424004Ssam } 56525675Ssam printf("\n"); 56625675Ssam if ((dcb->operrsta&DCBCMP) == 0) { 56725675Ssam VDDC_ABORT((cdr *)(vdminfo[ctlr]->um_addr), ci->ctlr_type); 56825675Ssam dcb->operrsta |= DCBUSC | DCBABT | ANYERR | HRDERR | CTLRERR; 56925675Ssam } 57025675Ssam vdintr(ctlr); 57124004Ssam } 57224004Ssam 57324004Ssam /* 57424004Ssam * Handle a disk interrupt. 57524004Ssam */ 57625675Ssam vdintr(ctlr) 57725675Ssam register int ctlr; 57824004Ssam { 57925675Ssam register ctlr_tab *ci; 58025675Ssam register struct buf *cq, *uq, *bp; 58125675Ssam register int slave, unit; 58225675Ssam register fmt_mdcb *mdcb; 58325675Ssam register fmt_dcb *dcb; 58425675Ssam int code, s; 58524004Ssam 58625675Ssam untimeout(vd_int_timeout, (caddr_t)ctlr); 58724004Ssam scope_out(2); 58825675Ssam ci = &vdctlr_info[ctlr]; 58925675Ssam if (!ci->int_expected) { 59025675Ssam printf("vd%d: stray interrupt\n", ctlr); 59124004Ssam return; 59224004Ssam } 59325675Ssam /* 59425675Ssam * Take first request off controller's queue. 59525675Ssam */ 59625675Ssam cq = &vdminfo[ctlr]->um_tab; 59725675Ssam uq = cq->b_forw; 59825675Ssam bp = uq->av_forw; 59924004Ssam unit = VDUNIT(bp->b_dev); 60025675Ssam dk_busy &= ~(1 << unit); 60125675Ssam dk_xfer[unit]++; 60225675Ssam ci->int_expected = 0; 60325675Ssam /* find associated control blocks */ 60425675Ssam mdcb = &ci->ctlr_mdcb, uncache(&mdcb->intdcb); 60525675Ssam dcb = &ci->ctlr_dcb, uncache(&dcb->operrsta); 60625675Ssam if (ci->ctlr_type == SMD_ECTLR) 60725675Ssam uncache(&dcb->err_code); 60825675Ssam slave = uq->b_dev; 60925675Ssam switch (code = vddecode_error(dcb)) { 61024004Ssam 61125675Ssam case CTLR_ERROR: 61225675Ssam case DRIVE_ERROR: 61325675Ssam if (cq->b_errcnt >= 2) 61425675Ssam vdhard_error(ci, bp, dcb); 61525675Ssam if (code == CTLR_ERROR) 61625675Ssam vdreset_ctlr((cdr *)vdminfo[ctlr]->um_addr, ctlr); 61725675Ssam else 61825675Ssam reset_drive((cdr *)vdminfo[ctlr]->um_addr, ctlr, 61925675Ssam slave, 2); 62025675Ssam if (cq->b_errcnt++ < 2) { /* retry error */ 62125675Ssam cq->b_forw = uq->b_back; 62225675Ssam vdstart(vdminfo[ctlr]); 62325675Ssam return; 62425675Ssam } 62525675Ssam bp->b_resid = bp->b_bcount; 62625675Ssam break; 62725675Ssam 62825675Ssam case HARD_DATA_ERROR: 62929921Skarels case WRITE_PROTECT: 63025675Ssam vdhard_error(ci, bp, dcb); 63125675Ssam bp->b_resid = 0; 63225675Ssam break; 63325675Ssam 63425675Ssam case SOFT_DATA_ERROR: 63525675Ssam vdsoft_error(ci, bp, dcb); 63625675Ssam /* fall thru... */ 63725675Ssam 63825675Ssam default: /* operation completed */ 63925675Ssam bp->b_error = 0; 64025675Ssam bp->b_resid = 0; 64125675Ssam break; 64224004Ssam } 64325675Ssam vbadone(bp, ci->rawbuf, (long *)ci->map, ci->utl); 64425675Ssam /* 64525675Ssam * Take next request on this unit q, or, if none, 64625675Ssam * the next request on the next active unit q. 64725675Ssam */ 64825675Ssam s = spl7(); 64925675Ssam uq->av_forw = bp->av_forw; 65025675Ssam if (uq->av_back != bp) { 65125675Ssam register struct buf *next; 65224004Ssam 65325675Ssam unit = VDUNIT(uq->av_forw->b_dev); 65425675Ssam slave = vddinfo[unit]->ui_slave; 65525675Ssam next = uq->av_forw; 65625675Ssam if (next->b_cylin != ci->cur_cyl[slave] || 65725675Ssam (next->b_daddr & 0xff) != ci->cur_trk[slave]) 65825675Ssam ci->off_cylinder |= 1 << slave; 65925675Ssam } else 66025675Ssam uq->av_back = NULL; 66125675Ssam splx(s); 66225675Ssam /* reset controller state */ 66325675Ssam cq->b_errcnt = 0; 66425675Ssam cq->b_active--; 66524004Ssam scope_out(3); 66625675Ssam if (bp->b_flags & B_ERROR) 66725675Ssam bp->b_error = EIO; 66824004Ssam iodone(bp); 66925675Ssam vdstart(vdminfo[ctlr]); 67024004Ssam } 67124004Ssam 67225675Ssam /* 67325675Ssam * Convert controller status to internal operation/error code. 67425675Ssam */ 67525675Ssam vddecode_error(dcb) 67625675Ssam register fmt_dcb *dcb; 67725675Ssam { 67824004Ssam 67925675Ssam if (dcb->operrsta & HRDERR) { 68029921Skarels if (dcb->operrsta & WPTERR) 68129921Skarels return (WRITE_PROTECT); 68229921Skarels if (dcb->operrsta & (HCRCERR | HCMPERR | UCDATERR | 68325675Ssam DSEEKERR | NOTCYLERR |DRVNRDY | INVDADR)) 68425675Ssam return (DRIVE_ERROR); 68525675Ssam if (dcb->operrsta & (CTLRERR | OPABRT | INVCMD | DNEMEM)) 68625675Ssam return (CTLR_ERROR); 68725675Ssam return (HARD_DATA_ERROR); 68825675Ssam } 68925675Ssam if (dcb->operrsta & SFTERR) 69025675Ssam return (SOFT_DATA_ERROR); 69125675Ssam return (0); 69225675Ssam } 69325675Ssam 69425675Ssam /* 69525675Ssam * Report a hard error. 69625675Ssam */ 69725675Ssam vdhard_error(ci, bp, dcb) 69825675Ssam ctlr_tab *ci; 69925675Ssam register struct buf *bp; 70025675Ssam register fmt_dcb *dcb; 70125675Ssam { 70225675Ssam unit_tab *ui = &vdunit_info[VDUNIT(bp->b_dev)]; 70325675Ssam 70425675Ssam bp->b_flags |= B_ERROR; 70525675Ssam harderr(bp, ui->info.type_name); 70629921Skarels if (dcb->operrsta & WPTERR) 70729921Skarels printf("write protected"); 70829921Skarels else { 70929921Skarels printf("status %b", dcb->operrsta, ERRBITS); 71029921Skarels if (ci->ctlr_type == SMD_ECTLR) 71129921Skarels printf(" ecode %x", dcb->err_code); 71229921Skarels } 71325675Ssam printf("\n"); 71425675Ssam } 71525675Ssam 71625675Ssam /* 71725675Ssam * Report a soft error. 71825675Ssam */ 71925675Ssam vdsoft_error(ci, bp, dcb) 72025675Ssam ctlr_tab *ci; 72125675Ssam register struct buf *bp; 72225675Ssam register fmt_dcb *dcb; 72325675Ssam { 72425675Ssam unit_tab *ui = &vdunit_info[VDUNIT(bp->b_dev)]; 72525675Ssam 72629921Skarels printf("dk%d%c: soft error sn%d status %b", minor(bp->b_dev) >> 3, 72729921Skarels 'a'+(minor(bp->b_dev)&07), bp->b_blkno, dcb->operrsta, ERRBITS); 72825675Ssam if (ci->ctlr_type == SMD_ECTLR) 72925675Ssam printf(" ecode %x", dcb->err_code); 73025675Ssam printf("\n"); 73125675Ssam } 73225675Ssam 73325675Ssam /*ARGSUSED*/ 73425675Ssam vdopen(dev, flag) 73525675Ssam dev_t dev; 73625675Ssam int flag; 73725675Ssam { 73825675Ssam register unit = VDUNIT(dev); 73925675Ssam register struct vba_device *vi = vddinfo[unit]; 74025675Ssam 74125675Ssam if (vi == 0 || vi->ui_alive == 0 || vi->ui_type >= nvddrv) 74225675Ssam return (ENXIO); 74325675Ssam if (vdunit_info[unit].info.partition[FILSYS(dev)].par_len == 0) 74425675Ssam return (ENXIO); 74525675Ssam return (0); 74625675Ssam } 74725675Ssam 74824004Ssam vdread(dev, uio) 74925675Ssam dev_t dev; 75025675Ssam struct uio *uio; 75124004Ssam { 75224004Ssam register int unit = VDUNIT(dev); 75325675Ssam register unit_tab *ui = &vdunit_info[unit]; 75424004Ssam 75529564Ssam if (unit >= NDK) 75625675Ssam return (ENXIO); 75725675Ssam return (physio(vdstrategy, &ui->raw_q_element, dev, B_READ, 75825675Ssam minphys, uio)); 75924004Ssam } 76024004Ssam 76124004Ssam vdwrite(dev, uio) 76225675Ssam dev_t dev; 76325675Ssam struct uio *uio; 76424004Ssam { 76524004Ssam register int unit = VDUNIT(dev); 76625675Ssam register unit_tab *ui = &vdunit_info[unit]; 76724004Ssam 76829564Ssam if (unit >= NDK) 76925675Ssam return (ENXIO); 77025675Ssam return (physio(vdstrategy, &ui->raw_q_element, dev, B_WRITE, 77125675Ssam minphys, uio)); 77224004Ssam } 77324004Ssam 77424004Ssam /* 77525675Ssam * Crash dump. 77624004Ssam */ 77725675Ssam vddump(dev) 77825675Ssam dev_t dev; 77924004Ssam { 78025675Ssam register int unit = VDUNIT(dev); 78125675Ssam register unit_tab *ui = &vdunit_info[unit]; 78225675Ssam register fs_tab *fs = &ui->info; 78325675Ssam register int ctlr = vddinfo[unit]->ui_ctlr; 78425675Ssam register struct vba_ctlr *vba_vdctlr_info = vdminfo[ctlr]; 78525675Ssam register int filsys = FILSYS(dev); 78625675Ssam register cdr *addr = (cdr *)(vba_vdctlr_info->um_addr); 78725675Ssam register int cur_blk, blkcount, blocks; 78825675Ssam caddr_t memaddr; 78924004Ssam 79025675Ssam vdreset_ctlr(addr, ctlr); 79124004Ssam blkcount = maxfree - 2; /* In 1k byte pages */ 79225675Ssam if (dumplo + blkcount > fs->partition[filsys].par_len) { 79325675Ssam blkcount = fs->partition[filsys].par_len - dumplo; 79425675Ssam printf("vd%d: Dump truncated to %dMB\n", unit, blkcount/1024); 79525675Ssam } 79625675Ssam cur_blk = fs->partition[filsys].par_start + dumplo; 79725675Ssam memaddr = 0; 79824004Ssam while (blkcount > 0) { 79925675Ssam blocks = MIN(blkcount, DUMPSIZE); 80025675Ssam if (!vdwrite_block(addr, ctlr, unit, memaddr, cur_blk, blocks)) 80125675Ssam return (EIO); 80225675Ssam blkcount -= blocks; 80325675Ssam memaddr += blocks * NBPG; 80425675Ssam cur_blk += blocks; 80524004Ssam } 80625675Ssam return (0); 80724004Ssam } 80824004Ssam 80925675Ssam /* 81025675Ssam * Write a block to disk during a crash dump. 81125675Ssam */ 81225675Ssam vdwrite_block(caddr, ctlr, unit, addr, block, blocks) 81325675Ssam register cdr *caddr; 81425675Ssam register int ctlr, unit; 81525675Ssam register caddr_t addr; 81625675Ssam register int block, blocks; 81724004Ssam { 81825925Ssam register ctlr_tab *ci = &vdctlr_info[ctlr]; 81925925Ssam register fmt_mdcb *mdcb = &ci->ctlr_mdcb; 82025925Ssam register fmt_dcb *dcb = &ci->ctlr_dcb; 82125675Ssam register unit_tab *ui = &vdunit_info[unit]; 82225675Ssam register fs_tab *fs = &ui->info; 82324004Ssam 82425675Ssam block *= (int)ui->sec_per_blk; 82525675Ssam blocks *= (int)ui->sec_per_blk; 82625675Ssam mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb)); 82725675Ssam dcb->intflg = NOINT; 82825675Ssam dcb->opcode = WD; 82925675Ssam dcb->operrsta = 0; 83025675Ssam dcb->devselect = (char)(vddinfo[unit])->ui_slave; 83125675Ssam dcb->trailcnt = (char)(sizeof (trrw) / sizeof (long)); 83225675Ssam dcb->trail.rwtrail.memadr = addr; 83325675Ssam dcb->trail.rwtrail.wcount = (short) 83425675Ssam ((blocks * fs->secsize)/ sizeof (short)); 83525675Ssam dcb->trail.rwtrail.disk.cylinder = (short)(block / ui->sec_per_cyl); 83625675Ssam dcb->trail.rwtrail.disk.track = (char)((block / fs->nsec) % fs->ntrak); 83725675Ssam dcb->trail.rwtrail.disk.sector = (char)(block % fs->nsec); 83825925Ssam VDDC_ATTENTION(caddr, (fmt_mdcb *)(PHYS(mdcb)), ci->ctlr_type); 83925925Ssam if (!vdpoll(ci, caddr, 5)) { 84025675Ssam printf(" during dump\n"); 84125675Ssam return (0); 84225675Ssam } 84325675Ssam if (dcb->operrsta & HRDERR) { 84429921Skarels printf("dk%d: hard error, status=%b\n", unit, 84529921Skarels dcb->operrsta, ERRBITS); 84625675Ssam return (0); 84725675Ssam } 84825675Ssam return (1); 84924004Ssam } 85024004Ssam 85124004Ssam vdsize(dev) 85225675Ssam dev_t dev; 85324004Ssam { 85425675Ssam struct vba_device *vi = vddinfo[VDUNIT(dev)]; 85524004Ssam 85625675Ssam if (vi == 0 || vi->ui_alive == 0 || vi->ui_type >= nvddrv) 85725675Ssam return (-1); 85825675Ssam return (vdunit_info[VDUNIT(dev)].info.partition[FILSYS(dev)].par_len); 85924004Ssam } 86024004Ssam 86125675Ssam /* 86225675Ssam * Perform a controller reset. 86325675Ssam */ 86425675Ssam vdreset_ctlr(addr, ctlr) 86525675Ssam register cdr *addr; 86625675Ssam register int ctlr; 86724004Ssam { 86825675Ssam register struct buf *cq = &vdminfo[ctlr]->um_tab; 86925675Ssam register struct buf *uq = cq->b_forw; 87025675Ssam register ctlr_tab *ci = &vdctlr_info[ctlr]; 87125675Ssam 87225675Ssam VDDC_RESET(addr, ci->ctlr_type); 87325675Ssam ci->ctlr_started = 0; 87425675Ssam if (ci->ctlr_type == SMD_ECTLR) { 87525675Ssam addr->cdr_csr = 0; 87625675Ssam addr->mdcb_tcf = AM_ENPDA; 87725675Ssam addr->dcb_tcf = AM_ENPDA; 87825675Ssam addr->trail_tcf = AM_ENPDA; 87925675Ssam addr->data_tcf = AM_ENPDA; 88025675Ssam addr->cdr_ccf = CCF_STS | XMD_32BIT | BSZ_16WRD | 88125675Ssam CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR; 88225675Ssam } 88325675Ssam if (vdnotrailer(addr, ctlr, 0, INIT, 10) & HRDERR) { 88425675Ssam printf("failed to init\n"); 88525675Ssam return (0); 88625675Ssam } 88725675Ssam if (vdnotrailer(addr, ctlr, 0, DIAG, 10) & HRDERR) { 88825675Ssam printf("diagnostic error\n"); 88925675Ssam return (0); 89025675Ssam } 89125675Ssam /* reset all units attached to controller */ 89225675Ssam uq = cq->b_forw; 89325675Ssam do { 89425675Ssam reset_drive(addr, ctlr, uq->b_dev, 0); 89525675Ssam uq = uq->b_forw; 89625675Ssam } while (uq != cq->b_forw); 89725675Ssam return (1); 89825675Ssam } 89924004Ssam 90025675Ssam /* 90125675Ssam * Perform a reset on a drive. 90225675Ssam */ 90325675Ssam reset_drive(addr, ctlr, slave, start) 90425675Ssam register cdr *addr; 90525675Ssam register int ctlr, slave, start; 90625675Ssam { 90725675Ssam register int type = vdctlr_info[ctlr].unit_type[slave]; 90825675Ssam 90925675Ssam if (type == UNKNOWN) 91025675Ssam return; 91125675Ssam if (!vdconfigure_drive(addr, ctlr, slave, type, start)) 91225675Ssam printf("vd%d: drive %d: couldn't reset\n", ctlr, slave); 91325675Ssam } 91425675Ssam 91525925Ssam /* 91625925Ssam * Poll controller until operation completes 91725925Ssam * or timeout expires. 91825925Ssam */ 91925925Ssam vdpoll(ci, addr, t) 92025925Ssam register ctlr_tab *ci; 92125925Ssam register cdr *addr; 92225925Ssam register int t; 92325925Ssam { 92425925Ssam register fmt_dcb *dcb = &ci->ctlr_dcb; 92525925Ssam 92625925Ssam t *= 1000; 92725925Ssam uncache(&dcb->operrsta); 92825925Ssam while ((dcb->operrsta&(DCBCMP|DCBABT)) == 0) { 92925925Ssam DELAY(1000); 93025925Ssam uncache(&dcb->operrsta); 93125925Ssam if (--t <= 0) { 93225925Ssam printf("vd%d: controller timeout", ci-vdctlr_info); 93325925Ssam VDDC_ABORT(addr, ci->ctlr_type); 93425925Ssam DELAY(30000); 93525925Ssam uncache(&dcb->operrsta); 93625925Ssam return (0); 93725925Ssam } 93825925Ssam } 93925925Ssam if (ci->ctlr_type == SMD_ECTLR) { 94025925Ssam uncache(&addr->cdr_csr); 94125925Ssam while (addr->cdr_csr&CS_GO) { 94225925Ssam DELAY(50); 94325925Ssam uncache(&addr->cdr_csr); 94425925Ssam } 94525925Ssam DELAY(300); 94625925Ssam } 94725925Ssam DELAY(200); 94825925Ssam uncache(&dcb->operrsta); 94925925Ssam return (1); 95025925Ssam } 95125925Ssam 95225675Ssam #ifdef notdef 95325675Ssam /* 95425675Ssam * Dump the mdcb and DCB for diagnostic purposes. 95525675Ssam */ 95625675Ssam vdprintdcb(lp) 95725675Ssam register long *lp; 95825675Ssam { 95925675Ssam register int i, dcb, tc; 96025675Ssam 96125675Ssam for (dcb = 0; lp; lp = (long *)(*lp), dcb++) { 96225675Ssam lp = (long *)((long)lp | 0xc0000000); 96325675Ssam printf("\nDump of dcb%d@%x:", dcb, lp); 96425675Ssam for (i = 0, tc = lp[3] & 0xff; i < tc+7; i++) 96525675Ssam printf(" %lx", lp[i]); 96625675Ssam printf("\n"); 96724004Ssam } 96825675Ssam DELAY(1750000); 96924004Ssam } 97024004Ssam #endif 97125675Ssam #endif 972