1*25675Ssam /* vd.c 1.2 86/01/05 */ 224004Ssam 324004Ssam #include "fsd.h" 424004Ssam #if NVD > 0 524004Ssam /* 6*25675Ssam * VDDC - Versabus SMD/ESMD driver. 7*25675Ssam */ 8*25675Ssam #include "../tahoe/mtpr.h" 9*25675Ssam #include "../tahoe/pte.h" 1024004Ssam 11*25675Ssam #include "param.h" 12*25675Ssam #include "buf.h" 13*25675Ssam #include "cmap.h" 14*25675Ssam #include "conf.h" 15*25675Ssam #include "dir.h" 16*25675Ssam #include "dk.h" 17*25675Ssam #include "map.h" 18*25675Ssam #include "systm.h" 19*25675Ssam #include "user.h" 20*25675Ssam #include "vmmac.h" 21*25675Ssam #include "proc.h" 22*25675Ssam #include "uio.h" 2324004Ssam 24*25675Ssam #include "../tahoevba/vbavar.h" 25*25675Ssam #define VDGENDATA 26*25675Ssam #include "../tahoevba/vddcreg.h" 27*25675Ssam #undef VDGENDATA 2824004Ssam 29*25675Ssam #define MAX_BLOCKSIZE (MAXBPTE*NBPG) 30*25675Ssam #define DUMPSIZE 64 /* controller limit */ 3124004Ssam 3224004Ssam #define VDUNIT(x) (minor(x) >> 3) 33*25675Ssam #define FILSYS(x) (minor(x) & 0x07) 34*25675Ssam #define PHYS(x) (vtoph((struct proc *)0, (unsigned)(x))) 35*25675Ssam #define TRUE 1 36*25675Ssam #define FALSE 0 3724004Ssam 38*25675Ssam #define CTLR_ERROR 1 39*25675Ssam #define DRIVE_ERROR 2 40*25675Ssam #define HARD_DATA_ERROR 3 41*25675Ssam #define SOFT_DATA_ERROR 4 4224004Ssam 43*25675Ssam #define b_cylin b_resid 44*25675Ssam #define b_daddr b_error 4524004Ssam 4624004Ssam struct vba_ctlr *vdminfo[NVD]; 4724004Ssam struct vba_device *vddinfo[NFSD]; 48*25675Ssam int vdprobe(), vdslave(), vdattach(), vddgo(); 49*25675Ssam struct vba_driver vddriver = 50*25675Ssam { vdprobe, vdslave, vdattach, vddgo, vddcaddr, "smd/fsd", 51*25675Ssam vddinfo, "vd", vdminfo }; 5224004Ssam 5324004Ssam /* 54*25675Ssam * Per-drive state. 55*25675Ssam */ 56*25675Ssam typedef struct { 57*25675Ssam struct buf raw_q_element; 58*25675Ssam short sec_per_blk; 59*25675Ssam short sec_per_cyl; 60*25675Ssam char status; 61*25675Ssam struct buf xfer_queue; 62*25675Ssam int drive_type; 63*25675Ssam fs_tab info; 64*25675Ssam } unit_tab; 6524004Ssam 6624004Ssam /* 67*25675Ssam * Per-controller state. 68*25675Ssam */ 69*25675Ssam typedef struct { 70*25675Ssam char ctlr_type; /* controller type */ 71*25675Ssam char *map; /* i/o page map */ 72*25675Ssam char *utl; /* mapped i/o space */ 73*25675Ssam u_int cur_slave:8; /* last active unit number */ 74*25675Ssam u_int int_expected:1; /* expect an interupt */ 75*25675Ssam u_int ctlr_started:1; /* start command was issued */ 76*25675Ssam u_int overlap_seeks:1;/* should overlap seeks */ 77*25675Ssam u_int off_cylinder:16;/* off cylinder bit map */ 78*25675Ssam u_int unit_type[16]; /* slave types */ 79*25675Ssam u_int cur_cyl[16]; /* cylinder last selected */ 80*25675Ssam long cur_trk[16]; /* track last selected */ 81*25675Ssam fmt_mdcb ctlr_mdcb; /* controller mdcb */ 82*25675Ssam fmt_dcb ctlr_dcb; /* r/w dcb */ 83*25675Ssam fmt_dcb seek_dcb[4]; /* dcbs for overlapped seeks */ 84*25675Ssam /* buffer for raw/swap i/o */ 85*25675Ssam char rawbuf[MAX_BLOCKSIZE]; 86*25675Ssam } ctlr_tab; 8724004Ssam 88*25675Ssam extern char vd0utl[]; 89*25675Ssam #if NVD > 1 90*25675Ssam extern char vd1utl[]; 91*25675Ssam #endif 92*25675Ssam #if NVD > 2 93*25675Ssam extern char vd2utl[]; 94*25675Ssam #endif 95*25675Ssam #if NVD > 3 96*25675Ssam extern char vd3utl[]; 97*25675Ssam #endif 9824004Ssam 99*25675Ssam #define VDCINIT(map, utl) { \ 100*25675Ssam UNKNOWN, (char *)map, utl, 0, FALSE, FALSE, TRUE, 0, \ 101*25675Ssam { UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN, \ 102*25675Ssam UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN,UNKNOWN } \ 103*25675Ssam } 104*25675Ssam ctlr_tab vdctlr_info[NVD] = { 105*25675Ssam VDCINIT(VD0map, vd0utl), 106*25675Ssam #if NVD > 1 107*25675Ssam VDCINIT(VD1map, vd1utl), 108*25675Ssam #endif 109*25675Ssam #if NVD > 2 110*25675Ssam VDCINIT(VD2map, vd2utl), 111*25675Ssam #endif 112*25675Ssam #if NVD > 3 113*25675Ssam VDCINIT(VD3map, vd3utl), 114*25675Ssam #endif 11524004Ssam }; 11624004Ssam 117*25675Ssam unit_tab vdunit_info[NFSD]; 11824004Ssam 11924004Ssam /* 120*25675Ssam * See if the controller is really there; if so, initialize it. 121*25675Ssam */ 122*25675Ssam vdprobe(addr) 123*25675Ssam cdr *addr; 124*25675Ssam { 125*25675Ssam if (badaddr((caddr_t)addr, 2)) 126*25675Ssam return (0); 127*25675Ssam addr->cdr_reset = 0xffffffff; 128*25675Ssam DELAY(1000000); 129*25675Ssam if (addr->cdr_reset != (unsigned)0xffffffff) { 130*25675Ssam DELAY(1000000); 131*25675Ssam } else { 132*25675Ssam addr->cdr_reserved = 0x0; 133*25675Ssam DELAY(3000000); 134*25675Ssam } 135*25675Ssam return (sizeof (cdr)); 136*25675Ssam } 13724004Ssam 13824004Ssam /* 139*25675Ssam * See if a drive is really there 140*25675Ssam * Try to reset/configure the drive, then test its status. 141*25675Ssam */ 142*25675Ssam vdslave(vi, addr) 143*25675Ssam register struct vba_device *vi; 144*25675Ssam register cdr *addr; 145*25675Ssam { 146*25675Ssam register ctlr_tab *ci = &vdctlr_info[vi->ui_ctlr]; 147*25675Ssam register unit_tab *ui = &vdunit_info[vi->ui_unit]; 148*25675Ssam register fmt_mdcb *mdcb = &ci->ctlr_mdcb; 149*25675Ssam register fmt_dcb *dcb = &ci->ctlr_dcb; 150*25675Ssam register int type; 15124004Ssam 152*25675Ssam if (ci->ctlr_type == UNKNOWN) { 153*25675Ssam addr->cdr_reset = 0xffffffff; 154*25675Ssam DELAY(1000000); 155*25675Ssam if (addr->cdr_reset != (unsigned)0xffffffff) { 156*25675Ssam ci->ctlr_type = SMDCTLR; 157*25675Ssam ci->overlap_seeks = 0; 158*25675Ssam DELAY(1000000); 159*25675Ssam } else { 160*25675Ssam ci->overlap_seeks = 1; 161*25675Ssam ci->ctlr_type = SMD_ECTLR; 162*25675Ssam addr->cdr_reserved = 0x0; 163*25675Ssam DELAY(3000000); 164*25675Ssam addr->cdr_csr = 0; 165*25675Ssam addr->mdcb_tcf = AM_ENPDA; 166*25675Ssam addr->dcb_tcf = AM_ENPDA; 167*25675Ssam addr->trail_tcf = AM_ENPDA; 168*25675Ssam addr->data_tcf = AM_ENPDA; 169*25675Ssam addr->cdr_ccf = CCF_STS | XMD_32BIT | BSZ_16WRD | 170*25675Ssam CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR; 171*25675Ssam } 172*25675Ssam if (vdnotrailer(addr, 173*25675Ssam vi->ui_ctlr, vi->ui_slave, INIT, 10) & HRDERR) { 174*25675Ssam printf("vd%d: init error\n", vi->ui_unit); 175*25675Ssam return (0); 176*25675Ssam } 177*25675Ssam if (vdnotrailer(addr, 178*25675Ssam vi->ui_ctlr, vi->ui_slave, DIAG, 10) & HRDERR) { 179*25675Ssam printf("vd%d: diagnostic error\n", vi->ui_unit); 180*25675Ssam return (0); 181*25675Ssam } 182*25675Ssam } 183*25675Ssam /* 184*25675Ssam * Seek on all drive types starting from the largest one. 185*25675Ssam * a successful seek to the last sector/cylinder/track verifies 186*25675Ssam * the drive type connected to this port. 187*25675Ssam */ 188*25675Ssam for (type = 0; type < nvddrv; type++) { 189*25675Ssam /* XXX */ 190*25675Ssam if (ci->ctlr_type == SMDCTLR && vdst[type].nsec != 32) 191*25675Ssam continue; 192*25675Ssam /* XXX */ 193*25675Ssam if (!vdconfigure_drive(addr, vi->ui_ctlr, vi->ui_slave, type,0)) 194*25675Ssam return (0); 195*25675Ssam dcb->opcode = (short)RD; 196*25675Ssam dcb->intflg = NOINT; 197*25675Ssam dcb->nxtdcb = (fmt_dcb *)0; /* end of chain */ 198*25675Ssam dcb->operrsta = 0; 199*25675Ssam dcb->devselect = (char)(vi->ui_slave); 200*25675Ssam dcb->trailcnt = (char)(sizeof (trrw) / sizeof (long)); 201*25675Ssam dcb->trail.rwtrail.memadr = (char *)PHYS(ci->rawbuf); 202*25675Ssam dcb->trail.rwtrail.wcount = vdst[type].secsize/sizeof(short); 203*25675Ssam dcb->trail.rwtrail.disk.cylinder = vdst[type].ncyl - 2; 204*25675Ssam dcb->trail.rwtrail.disk.track = vdst[type].ntrak - 1; 205*25675Ssam dcb->trail.rwtrail.disk.sector = vdst[type].nsec - 1; 206*25675Ssam mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb)); 207*25675Ssam mdcb->vddcstat = 0; 208*25675Ssam VDDC_ATTENTION(addr, (fmt_mdcb *)(PHYS(mdcb)), ci->ctlr_type); 209*25675Ssam POLLTILLDONE(addr, dcb, 60, ci->ctlr_type); 210*25675Ssam if (vdtimeout <= 0) 211*25675Ssam printf(" during probe\n"); 212*25675Ssam if ((dcb->operrsta&HRDERR) == 0) 213*25675Ssam break; 214*25675Ssam } 215*25675Ssam if (type >= nvddrv) { 216*25675Ssam /* 217*25675Ssam * If reached here, a drive which is not defined in the 218*25675Ssam * 'vdst' tables is connected. Cannot set it's type. 219*25675Ssam */ 220*25675Ssam printf("vd%d: unknown drive type\n", vi->ui_unit); 221*25675Ssam return (0); 222*25675Ssam } 223*25675Ssam ui->drive_type = type; 224*25675Ssam ui->info = vdst[type]; 225*25675Ssam ui->sec_per_blk = DEV_BSIZE / ui->info.secsize; 226*25675Ssam vi->ui_type = type; 227*25675Ssam vi->ui_dk = 1; 228*25675Ssam vddriver.ud_dname = ui->info.type_name; 229*25675Ssam return (1); 23024004Ssam } 23124004Ssam 232*25675Ssam vdconfigure_drive(addr, ctlr, slave, type, pass) 233*25675Ssam register cdr *addr; 234*25675Ssam int ctlr, slave, type, pass; 23524004Ssam { 236*25675Ssam register ctlr_tab *ci = &vdctlr_info[ctlr]; 237*25675Ssam 238*25675Ssam ci->ctlr_dcb.opcode = RSTCFG; /* command */ 239*25675Ssam ci->ctlr_dcb.intflg = NOINT; 240*25675Ssam ci->ctlr_dcb.nxtdcb = (fmt_dcb *)0; /* end of chain */ 241*25675Ssam ci->ctlr_dcb.operrsta = 0; 242*25675Ssam ci->ctlr_dcb.devselect = (char)slave; 243*25675Ssam ci->ctlr_dcb.trail.rstrail.ncyl = vdst[type].ncyl; 244*25675Ssam ci->ctlr_dcb.trail.rstrail.nsurfaces = vdst[type].ntrak; 245*25675Ssam if (ci->ctlr_type == SMD_ECTLR) { 246*25675Ssam ci->ctlr_dcb.trailcnt = (char)4; 247*25675Ssam ci->ctlr_dcb.trail.rstrail.nsectors = vdst[type].nsec; 248*25675Ssam ci->ctlr_dcb.trail.rstrail.slip_sec = vdst[type].nslip; 249*25675Ssam } else 250*25675Ssam ci->ctlr_dcb.trailcnt = (char)2; 251*25675Ssam ci->ctlr_mdcb.firstdcb = (fmt_dcb *)(PHYS(&ci->ctlr_dcb)); 252*25675Ssam ci->ctlr_mdcb.vddcstat = 0; 253*25675Ssam VDDC_ATTENTION(addr, (fmt_mdcb *)(PHYS(&ci->ctlr_mdcb)), ci->ctlr_type); 254*25675Ssam POLLTILLDONE(addr, &ci->ctlr_dcb, 5, ci->ctlr_type); 255*25675Ssam if (vdtimeout <= 0) { 256*25675Ssam printf(" during config\n"); 257*25675Ssam return (0); 258*25675Ssam } 259*25675Ssam if (ci->ctlr_dcb.operrsta & HRDERR) { 260*25675Ssam if ((ci->ctlr_dcb.operrsta & (NOTCYLERR|DRVNRDY)) == 0) 261*25675Ssam printf("vd%d: drive %d: config error\n", ctlr, slave); 262*25675Ssam else if (pass == 0) { 263*25675Ssam vdstart_drive(addr, ctlr, slave); 264*25675Ssam return (vdconfigure_drive(addr, ctlr, slave, type, 1)); 265*25675Ssam } else if (pass == 2) 266*25675Ssam return (vdconfigure_drive(addr, ctlr, slave, type, 3)); 267*25675Ssam return (0); 268*25675Ssam } 269*25675Ssam return (1); 27024004Ssam } 27124004Ssam 272*25675Ssam vdstart_drive(addr, ctlr, slave) 273*25675Ssam cdr *addr; 274*25675Ssam register int ctlr, slave; 27524004Ssam { 276*25675Ssam int error = 0; 27724004Ssam 278*25675Ssam printf("vd%d: starting drive %d, wait...", ctlr, slave); 279*25675Ssam if (vdctlr_info[ctlr].ctlr_started) { 280*25675Ssam printf("DELAY(5500000)..."); 281*25675Ssam DELAY(5500000); 282*25675Ssam goto done; 28324004Ssam } 284*25675Ssam vdctlr_info[ctlr].ctlr_started = 1; 285*25675Ssam error = vdnotrailer(addr, ctlr, 0, VDSTART, (slave*6)+62) & HRDERR; 286*25675Ssam if (!error) { 287*25675Ssam printf("DELAY(%d)...", (slave * 5500000) + 62000000); 288*25675Ssam DELAY((slave * 5500000) + 62000000); 28924004Ssam } 290*25675Ssam done: 291*25675Ssam printf("\n"); 292*25675Ssam return (error == 0); 293*25675Ssam } 29424004Ssam 295*25675Ssam vdnotrailer(addr, ctlr, unit, function, time) 296*25675Ssam register cdr *addr; 297*25675Ssam int ctlr, unit, function, time; 29824004Ssam { 299*25675Ssam fmt_mdcb *mdcb = &vdctlr_info[ctlr].ctlr_mdcb; 300*25675Ssam fmt_dcb *dcb = &vdctlr_info[ctlr].ctlr_dcb; 301*25675Ssam int type = vdctlr_info[ctlr].ctlr_type; 30224004Ssam 303*25675Ssam dcb->opcode = function; /* command */ 30424004Ssam dcb->intflg = NOINT; 305*25675Ssam dcb->nxtdcb = (fmt_dcb *)0; /* end of chain */ 306*25675Ssam dcb->operrsta = 0; 307*25675Ssam dcb->devselect = (char)unit; 30824004Ssam dcb->trailcnt = (char)0; 309*25675Ssam mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb)); 31024004Ssam mdcb->vddcstat = 0; 311*25675Ssam VDDC_ATTENTION(addr, (fmt_mdcb *)(PHYS(mdcb)), type); 312*25675Ssam POLLTILLDONE(addr, dcb, time, type); 313*25675Ssam if (vdtimeout <= 0) { 314*25675Ssam printf(" during init\n"); 315*25675Ssam return (DCBCMP|ANYERR|HRDERR|OPABRT); 31624004Ssam } 317*25675Ssam return (dcb->operrsta); 318*25675Ssam } 31924004Ssam 320*25675Ssam vdattach(vi) 321*25675Ssam register struct vba_device *vi; 322*25675Ssam { 323*25675Ssam register unit_tab *ui = &vdunit_info[vi->ui_unit]; 324*25675Ssam register ctlr_tab *ci = &vdctlr_info[vi->ui_ctlr]; 325*25675Ssam register struct buf *cq = &vi->ui_mi->um_tab; 326*25675Ssam register struct buf *uq = cq->b_forw; 327*25675Ssam register struct buf *start_queue = uq; 328*25675Ssam register fs_tab *fs = &ui->info; 329*25675Ssam 330*25675Ssam ui->info = vdst[vi->ui_type]; 331*25675Ssam ui->sec_per_blk = DEV_BSIZE / ui->info.secsize; 332*25675Ssam ui->sec_per_cyl = ui->info.nsec * ui->info.ntrak; 333*25675Ssam ui->xfer_queue.b_dev = vi->ui_slave; 334*25675Ssam ci->unit_type[vi->ui_slave] = vi->ui_type; 335*25675Ssam /* load unit into controller's active unit list */ 336*25675Ssam if (uq == NULL) { 337*25675Ssam cq->b_forw = &ui->xfer_queue; 338*25675Ssam ui->xfer_queue.b_forw = &ui->xfer_queue; 339*25675Ssam ui->xfer_queue.b_back = &ui->xfer_queue; 340*25675Ssam } else { 341*25675Ssam while (uq->b_forw != start_queue) 342*25675Ssam uq = uq->b_forw; 343*25675Ssam ui->xfer_queue.b_forw = start_queue; 344*25675Ssam ui->xfer_queue.b_back = uq; 345*25675Ssam uq->b_forw = &ui->xfer_queue; 346*25675Ssam start_queue->b_back = &ui->xfer_queue; 347*25675Ssam } 34824004Ssam /* 349*25675Ssam * (60 / rpm) / (number of sectors per track * (bytes per sector / 2)) 35024004Ssam */ 351*25675Ssam dk_mspw[vi->ui_unit] = 120.0 / (fs->rpm * fs->nsec * fs->secsize); 35224004Ssam } 35324004Ssam 354*25675Ssam /*ARGSUSED*/ 355*25675Ssam vddgo(um) 356*25675Ssam struct vba_ctlr *um; 35724004Ssam { 35824004Ssam 35924004Ssam } 36024004Ssam 36124004Ssam vdstrategy(bp) 362*25675Ssam register struct buf *bp; 36324004Ssam { 364*25675Ssam register int unit = VDUNIT(bp->b_dev); 365*25675Ssam register struct vba_device *vi = vddinfo[unit]; 366*25675Ssam register par_tab *par; 367*25675Ssam register unit_tab *ui; 368*25675Ssam register fs_tab *fs; 369*25675Ssam register int blks, bn, s; 37024004Ssam 371*25675Ssam if (bp->b_bcount == 0 || vi == 0 || vi->ui_alive == 0) 372*25675Ssam goto bad; 373*25675Ssam ui = &vdunit_info[unit]; 374*25675Ssam fs = &ui->info; 375*25675Ssam par = &fs->partition[FILSYS(bp->b_dev)]; 376*25675Ssam blks = (bp->b_bcount + DEV_BSIZE-1) >> DEV_BSHIFT; 377*25675Ssam if (bp->b_blkno + blks >= par->par_len) { 378*25675Ssam blks = par->par_len - bp->b_blkno; 379*25675Ssam if (blks <= 0) 380*25675Ssam goto bad; 381*25675Ssam bp->b_bcount = blks * DEV_BSIZE; 382*25675Ssam } 383*25675Ssam bn = bp->b_blkno + par->par_start; 384*25675Ssam bn *= ui->sec_per_blk; 385*25675Ssam bp->b_daddr = (bn / fs->nsec) % fs->ntrak; 386*25675Ssam bp->b_cylin = bn / ui->sec_per_cyl; 387*25675Ssam vbasetup(bp, ui->info.secsize); 388*25675Ssam s = spl7(); 389*25675Ssam if (ui->xfer_queue.av_forw == NULL) { 390*25675Ssam register ctlr_tab *ci = &vdctlr_info[vi->ui_ctlr]; 391*25675Ssam int slave = vi->ui_slave; 39224004Ssam 393*25675Ssam if (bp->b_cylin != ci->cur_cyl[slave] || 394*25675Ssam bp->b_daddr != ci->cur_trk[slave]) 395*25675Ssam ci->off_cylinder |= 1 << slave; 39624004Ssam } 397*25675Ssam bp->b_daddr |= (bn % fs->nsec) << 8; 398*25675Ssam disksort(&ui->xfer_queue, bp); 399*25675Ssam if (!vddinfo[unit]->ui_mi->um_tab.b_active++) { 400*25675Ssam splx(s); 401*25675Ssam vdstart(vddinfo[unit]->ui_mi); 402*25675Ssam } else 403*25675Ssam splx(s); 40424004Ssam return; 405*25675Ssam bad: 406*25675Ssam bp->b_flags |= B_ERROR, bp->b_error = ENXIO; 407*25675Ssam bp->b_resid = bp->b_bcount; 40824004Ssam iodone(bp); 40924004Ssam } 41024004Ssam 41124004Ssam /* 41224004Ssam * Start up a transfer on a drive. 41324004Ssam */ 414*25675Ssam vdstart(ci) 415*25675Ssam register struct vba_ctlr *ci; 41624004Ssam { 417*25675Ssam register struct buf *cq = &ci->um_tab; 418*25675Ssam register struct buf *uq = cq->b_forw; 41924004Ssam 420*25675Ssam /* search for next ready unit */ 421*25675Ssam cq->b_forw = cq->b_forw->b_forw; 422*25675Ssam uq = cq->b_forw; 423*25675Ssam do { 424*25675Ssam if (uq->av_forw != NULL) { 425*25675Ssam cq->b_forw = uq; 426*25675Ssam vdexecute(ci, uq); 427*25675Ssam return; 428*25675Ssam } 429*25675Ssam uq = uq->b_forw; 430*25675Ssam } while (uq != cq->b_forw); 431*25675Ssam } 432*25675Ssam 433*25675Ssam /* 434*25675Ssam * Initiate seeks for all drives off-cylinder. 435*25675Ssam */ 436*25675Ssam vdload_seeks(ci, uq) 437*25675Ssam register ctlr_tab *ci; 438*25675Ssam register struct buf *uq; 439*25675Ssam { 440*25675Ssam register int unit, slave, nseeks; 441*25675Ssam register fmt_dcb *dcb; 442*25675Ssam register struct buf *bp; 443*25675Ssam register struct buf *start_queue = uq; 444*25675Ssam 445*25675Ssam nseeks = 0; 446*25675Ssam do { 447*25675Ssam bp = uq->av_forw; 448*25675Ssam if (bp != NULL) { 449*25675Ssam unit = VDUNIT(bp->b_dev); 450*25675Ssam slave = vddinfo[unit]->ui_slave; 451*25675Ssam if (ci->off_cylinder & (1 << slave)) { 452*25675Ssam ci->off_cylinder &= ~(1 << slave); 453*25675Ssam if (ci->cur_cyl[slave] != bp->b_cylin) { 454*25675Ssam ci->cur_cyl[slave] = bp->b_cylin; 455*25675Ssam dk_seek[unit]++; 456*25675Ssam } 457*25675Ssam ci->cur_trk[slave] = bp->b_daddr&0xff; 458*25675Ssam dcb = &ci->seek_dcb[nseeks++]; 459*25675Ssam dcb->opcode = SEEK; 460*25675Ssam dcb->intflg = NOINT | INT_PBA; 461*25675Ssam dcb->operrsta = 0; 462*25675Ssam dcb->devselect = (char)slave; 463*25675Ssam dcb->trailcnt = (char)1; 464*25675Ssam dcb->trail.sktrail.skaddr.cylinder = 465*25675Ssam bp->b_cylin; 466*25675Ssam dcb->trail.sktrail.skaddr.track = 467*25675Ssam bp->b_daddr & 0xff; 468*25675Ssam dcb->trail.sktrail.skaddr.sector = 0; 469*25675Ssam } 470*25675Ssam } 471*25675Ssam uq = uq->b_forw; 472*25675Ssam } while (uq != start_queue && nseeks < 4); 473*25675Ssam return (nseeks); 474*25675Ssam } 475*25675Ssam 476*25675Ssam extern vd_int_timeout(); 477*25675Ssam /* 478*25675Ssam * Execute the next command on the unit queue uq. 479*25675Ssam */ 480*25675Ssam vdexecute(controller_info, uq) 481*25675Ssam register struct vba_ctlr *controller_info; 482*25675Ssam register struct buf *uq; 483*25675Ssam { 484*25675Ssam register struct buf *bp = uq->av_forw; 485*25675Ssam register int ctlr = controller_info->um_ctlr; 486*25675Ssam register ctlr_tab *ci = &vdctlr_info[ctlr]; 487*25675Ssam register int unit = VDUNIT(bp->b_dev); 488*25675Ssam register int slave = vddinfo[unit]->ui_slave; 489*25675Ssam register fmt_mdcb *mdcb = &ci->ctlr_mdcb; 490*25675Ssam register fmt_dcb *dcb = &ci->ctlr_dcb; 491*25675Ssam 49224004Ssam /* 493*25675Ssam * If there are overlapped seeks to perform, shuffle 494*25675Ssam * them to the front of the queue and get them started 495*25675Ssam * before any data transfers (to get some parallelism). 49624004Ssam */ 497*25675Ssam if ((ci->off_cylinder & ~(1<<slave)) && ci->overlap_seeks) { 498*25675Ssam register int i, nseeks; 499*25675Ssam 500*25675Ssam /* setup seek requests in seek-q */ 501*25675Ssam nseeks = vdload_seeks(ci, uq); 502*25675Ssam /* place at the front of the master q */ 503*25675Ssam mdcb->firstdcb = (fmt_dcb *)PHYS(&ci->seek_dcb[0]); 504*25675Ssam /* shuffle any remaining seeks up in the seek-q */ 505*25675Ssam for (i = 1; i < nseeks; i++) 506*25675Ssam ci->seek_dcb[i-1].nxtdcb = 507*25675Ssam (fmt_dcb *)PHYS(&ci->seek_dcb[i]); 508*25675Ssam ci->seek_dcb[nseeks-1].nxtdcb = (fmt_dcb *)PHYS(dcb); 509*25675Ssam } else { 510*25675Ssam if (bp->b_cylin != ci->cur_cyl[slave]) { 511*25675Ssam ci->cur_cyl[slave] = bp->b_cylin; 512*25675Ssam dk_seek[unit]++; 513*25675Ssam } 514*25675Ssam ci->cur_trk[slave] = bp->b_daddr & 0xff; 515*25675Ssam ci->off_cylinder = 0; 516*25675Ssam mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb)); 51724004Ssam } 51824004Ssam dcb->opcode = (bp->b_flags & B_READ) ? RD : WD; 519*25675Ssam dcb->intflg = INTDONE; 520*25675Ssam dcb->nxtdcb = (fmt_dcb *)0; /* end of chain */ 52124004Ssam dcb->operrsta = 0; 522*25675Ssam dcb->devselect = (char)slave; 523*25675Ssam dcb->trailcnt = (char)(sizeof (trrw) / sizeof (long)); 524*25675Ssam dcb->trail.rwtrail.memadr = (char *) 525*25675Ssam vbastart(bp, ci->rawbuf, (long *)ci->map, ci->utl); 526*25675Ssam dcb->trail.rwtrail.wcount = (short)((bp->b_bcount+1) / sizeof (short)); 527*25675Ssam dcb->trail.rwtrail.disk.cylinder = bp->b_cylin; 528*25675Ssam dcb->trail.rwtrail.disk.track = bp->b_daddr & 0xff; 529*25675Ssam dcb->trail.rwtrail.disk.sector = bp->b_daddr >> 8; 530*25675Ssam mdcb->vddcstat = 0; 531*25675Ssam dk_wds[unit] += bp->b_bcount / 32; 532*25675Ssam ci->int_expected = 1; 533*25675Ssam timeout(vd_int_timeout, (caddr_t)ctlr, 20*60); 534*25675Ssam dk_busy |= 1 << unit; 535*25675Ssam #ifdef VDDCPERF 536*25675Ssam scope_out(1); 537*25675Ssam #endif 538*25675Ssam VDDC_ATTENTION((cdr *)(vdminfo[ctlr]->um_addr), 539*25675Ssam (fmt_mdcb *)(PHYS(mdcb)), ci->ctlr_type); 540*25675Ssam } 54124004Ssam 54224004Ssam /* 543*25675Ssam * Watch for lost interrupts. 544*25675Ssam */ 545*25675Ssam vd_int_timeout(ctlr) 546*25675Ssam register int ctlr; 547*25675Ssam { 548*25675Ssam register ctlr_tab *ci = &vdctlr_info[ctlr]; 549*25675Ssam register fmt_dcb *dcb = &ci->ctlr_dcb; 55024004Ssam 551*25675Ssam uncache(&dcb->operrsta); 552*25675Ssam printf("vd%d: lost interupt, status %x", ctlr, dcb->operrsta); 553*25675Ssam if (ci->ctlr_type == SMD_ECTLR) { 554*25675Ssam uncache(&dcb->err_code); 555*25675Ssam printf(", error code %x", dcb->err_code); 55624004Ssam } 557*25675Ssam printf("\n"); 558*25675Ssam if ((dcb->operrsta&DCBCMP) == 0) { 559*25675Ssam VDDC_ABORT((cdr *)(vdminfo[ctlr]->um_addr), ci->ctlr_type); 560*25675Ssam dcb->operrsta |= DCBUSC | DCBABT | ANYERR | HRDERR | CTLRERR; 561*25675Ssam } 562*25675Ssam vdintr(ctlr); 56324004Ssam } 56424004Ssam 56524004Ssam /* 56624004Ssam * Handle a disk interrupt. 56724004Ssam */ 568*25675Ssam vdintr(ctlr) 569*25675Ssam register int ctlr; 57024004Ssam { 571*25675Ssam register ctlr_tab *ci; 572*25675Ssam register struct buf *cq, *uq, *bp; 573*25675Ssam register int slave, unit; 574*25675Ssam register fmt_mdcb *mdcb; 575*25675Ssam register fmt_dcb *dcb; 576*25675Ssam int code, s; 57724004Ssam 578*25675Ssam untimeout(vd_int_timeout, (caddr_t)ctlr); 57924004Ssam #ifdef VDDCPERF 58024004Ssam scope_out(2); 58124004Ssam #endif 582*25675Ssam ci = &vdctlr_info[ctlr]; 583*25675Ssam if (!ci->int_expected) { 584*25675Ssam printf("vd%d: stray interrupt\n", ctlr); 58524004Ssam return; 58624004Ssam } 587*25675Ssam /* 588*25675Ssam * Take first request off controller's queue. 589*25675Ssam */ 590*25675Ssam cq = &vdminfo[ctlr]->um_tab; 591*25675Ssam uq = cq->b_forw; 592*25675Ssam bp = uq->av_forw; 59324004Ssam unit = VDUNIT(bp->b_dev); 594*25675Ssam dk_busy &= ~(1 << unit); 595*25675Ssam dk_xfer[unit]++; 596*25675Ssam ci->int_expected = 0; 597*25675Ssam /* find associated control blocks */ 598*25675Ssam mdcb = &ci->ctlr_mdcb, uncache(&mdcb->intdcb); 599*25675Ssam dcb = &ci->ctlr_dcb, uncache(&dcb->operrsta); 600*25675Ssam if (ci->ctlr_type == SMD_ECTLR) 601*25675Ssam uncache(&dcb->err_code); 602*25675Ssam slave = uq->b_dev; 603*25675Ssam switch (code = vddecode_error(dcb)) { 60424004Ssam 605*25675Ssam case CTLR_ERROR: 606*25675Ssam case DRIVE_ERROR: 607*25675Ssam if (cq->b_errcnt >= 2) 608*25675Ssam vdhard_error(ci, bp, dcb); 609*25675Ssam if (code == CTLR_ERROR) 610*25675Ssam vdreset_ctlr((cdr *)vdminfo[ctlr]->um_addr, ctlr); 611*25675Ssam else 612*25675Ssam reset_drive((cdr *)vdminfo[ctlr]->um_addr, ctlr, 613*25675Ssam slave, 2); 614*25675Ssam if (cq->b_errcnt++ < 2) { /* retry error */ 615*25675Ssam cq->b_forw = uq->b_back; 616*25675Ssam vdstart(vdminfo[ctlr]); 617*25675Ssam return; 618*25675Ssam } 619*25675Ssam bp->b_resid = bp->b_bcount; 620*25675Ssam break; 621*25675Ssam 622*25675Ssam case HARD_DATA_ERROR: 623*25675Ssam vdhard_error(ci, bp, dcb); 624*25675Ssam bp->b_resid = 0; 625*25675Ssam break; 626*25675Ssam 627*25675Ssam case SOFT_DATA_ERROR: 628*25675Ssam vdsoft_error(ci, bp, dcb); 629*25675Ssam /* fall thru... */ 630*25675Ssam 631*25675Ssam default: /* operation completed */ 632*25675Ssam bp->b_error = 0; 633*25675Ssam bp->b_resid = 0; 634*25675Ssam break; 63524004Ssam } 636*25675Ssam vbadone(bp, ci->rawbuf, (long *)ci->map, ci->utl); 637*25675Ssam /* 638*25675Ssam * Take next request on this unit q, or, if none, 639*25675Ssam * the next request on the next active unit q. 640*25675Ssam */ 641*25675Ssam s = spl7(); 642*25675Ssam uq->av_forw = bp->av_forw; 643*25675Ssam if (uq->av_back != bp) { 644*25675Ssam register struct buf *next; 64524004Ssam 646*25675Ssam unit = VDUNIT(uq->av_forw->b_dev); 647*25675Ssam slave = vddinfo[unit]->ui_slave; 648*25675Ssam next = uq->av_forw; 649*25675Ssam if (next->b_cylin != ci->cur_cyl[slave] || 650*25675Ssam (next->b_daddr & 0xff) != ci->cur_trk[slave]) 651*25675Ssam ci->off_cylinder |= 1 << slave; 652*25675Ssam } else 653*25675Ssam uq->av_back = NULL; 654*25675Ssam splx(s); 655*25675Ssam /* reset controller state */ 656*25675Ssam cq->b_errcnt = 0; 657*25675Ssam cq->b_active--; 65824004Ssam #ifdef VDDCPERF 65924004Ssam scope_out(3); 66024004Ssam #endif 661*25675Ssam if (bp->b_flags & B_ERROR) 662*25675Ssam bp->b_error = EIO; 66324004Ssam iodone(bp); 664*25675Ssam vdstart(vdminfo[ctlr]); 66524004Ssam } 66624004Ssam 667*25675Ssam /* 668*25675Ssam * Convert controller status to internal operation/error code. 669*25675Ssam */ 670*25675Ssam vddecode_error(dcb) 671*25675Ssam register fmt_dcb *dcb; 672*25675Ssam { 67324004Ssam 674*25675Ssam if (dcb->operrsta & HRDERR) { 675*25675Ssam if (dcb->operrsta & (HCRCERR | HCMPERR | UCDATERR | WPTERR | 676*25675Ssam DSEEKERR | NOTCYLERR |DRVNRDY | INVDADR)) 677*25675Ssam return (DRIVE_ERROR); 678*25675Ssam if (dcb->operrsta & (CTLRERR | OPABRT | INVCMD | DNEMEM)) 679*25675Ssam return (CTLR_ERROR); 680*25675Ssam return (HARD_DATA_ERROR); 681*25675Ssam } 682*25675Ssam if (dcb->operrsta & SFTERR) 683*25675Ssam return (SOFT_DATA_ERROR); 684*25675Ssam return (0); 685*25675Ssam } 686*25675Ssam 687*25675Ssam /* 688*25675Ssam * Report a hard error. 689*25675Ssam */ 690*25675Ssam vdhard_error(ci, bp, dcb) 691*25675Ssam ctlr_tab *ci; 692*25675Ssam register struct buf *bp; 693*25675Ssam register fmt_dcb *dcb; 694*25675Ssam { 695*25675Ssam unit_tab *ui = &vdunit_info[VDUNIT(bp->b_dev)]; 696*25675Ssam 697*25675Ssam bp->b_flags |= B_ERROR; 698*25675Ssam harderr(bp, ui->info.type_name); 699*25675Ssam printf("status %x", dcb->operrsta); 700*25675Ssam if (ci->ctlr_type == SMD_ECTLR) 701*25675Ssam printf(" ecode %x", dcb->err_code); 702*25675Ssam printf("\n"); 703*25675Ssam } 704*25675Ssam 705*25675Ssam /* 706*25675Ssam * Report a soft error. 707*25675Ssam */ 708*25675Ssam vdsoft_error(ci, bp, dcb) 709*25675Ssam ctlr_tab *ci; 710*25675Ssam register struct buf *bp; 711*25675Ssam register fmt_dcb *dcb; 712*25675Ssam { 713*25675Ssam unit_tab *ui = &vdunit_info[VDUNIT(bp->b_dev)]; 714*25675Ssam 715*25675Ssam printf("%s%d%c: soft error sn%d status %x", ui->info.type_name, 716*25675Ssam dkunit(bp), 'a'+(minor(bp->b_dev)&07), bp->b_blkno, 717*25675Ssam dcb->operrsta); 718*25675Ssam if (ci->ctlr_type == SMD_ECTLR) 719*25675Ssam printf(" ecode %x", dcb->err_code); 720*25675Ssam printf("\n"); 721*25675Ssam } 722*25675Ssam 723*25675Ssam /*ARGSUSED*/ 724*25675Ssam vdopen(dev, flag) 725*25675Ssam dev_t dev; 726*25675Ssam int flag; 727*25675Ssam { 728*25675Ssam register unit = VDUNIT(dev); 729*25675Ssam register struct vba_device *vi = vddinfo[unit]; 730*25675Ssam 731*25675Ssam if (vi == 0 || vi->ui_alive == 0 || vi->ui_type >= nvddrv) 732*25675Ssam return (ENXIO); 733*25675Ssam if (vdunit_info[unit].info.partition[FILSYS(dev)].par_len == 0) 734*25675Ssam return (ENXIO); 735*25675Ssam return (0); 736*25675Ssam } 737*25675Ssam 73824004Ssam vdread(dev, uio) 739*25675Ssam dev_t dev; 740*25675Ssam struct uio *uio; 74124004Ssam { 74224004Ssam register int unit = VDUNIT(dev); 743*25675Ssam register unit_tab *ui = &vdunit_info[unit]; 74424004Ssam 74524004Ssam if (unit >= NFSD) 746*25675Ssam return (ENXIO); 747*25675Ssam return (physio(vdstrategy, &ui->raw_q_element, dev, B_READ, 748*25675Ssam minphys, uio)); 74924004Ssam } 75024004Ssam 75124004Ssam vdwrite(dev, uio) 752*25675Ssam dev_t dev; 753*25675Ssam struct uio *uio; 75424004Ssam { 75524004Ssam register int unit = VDUNIT(dev); 756*25675Ssam register unit_tab *ui = &vdunit_info[unit]; 75724004Ssam 75824004Ssam if (unit >= NFSD) 759*25675Ssam return (ENXIO); 760*25675Ssam return (physio(vdstrategy, &ui->raw_q_element, dev, B_WRITE, 761*25675Ssam minphys, uio)); 76224004Ssam } 76324004Ssam 76424004Ssam /* 765*25675Ssam * Crash dump. 76624004Ssam */ 767*25675Ssam vddump(dev) 768*25675Ssam dev_t dev; 76924004Ssam { 770*25675Ssam register int unit = VDUNIT(dev); 771*25675Ssam register unit_tab *ui = &vdunit_info[unit]; 772*25675Ssam register fs_tab *fs = &ui->info; 773*25675Ssam register int ctlr = vddinfo[unit]->ui_ctlr; 774*25675Ssam register struct vba_ctlr *vba_vdctlr_info = vdminfo[ctlr]; 775*25675Ssam register int filsys = FILSYS(dev); 776*25675Ssam register cdr *addr = (cdr *)(vba_vdctlr_info->um_addr); 777*25675Ssam register int cur_blk, blkcount, blocks; 778*25675Ssam caddr_t memaddr; 77924004Ssam 780*25675Ssam vdreset_ctlr(addr, ctlr); 78124004Ssam blkcount = maxfree - 2; /* In 1k byte pages */ 782*25675Ssam if (dumplo + blkcount > fs->partition[filsys].par_len) { 783*25675Ssam blkcount = fs->partition[filsys].par_len - dumplo; 784*25675Ssam printf("vd%d: Dump truncated to %dMB\n", unit, blkcount/1024); 785*25675Ssam } 786*25675Ssam cur_blk = fs->partition[filsys].par_start + dumplo; 787*25675Ssam memaddr = 0; 78824004Ssam while (blkcount > 0) { 789*25675Ssam blocks = MIN(blkcount, DUMPSIZE); 790*25675Ssam if (!vdwrite_block(addr, ctlr, unit, memaddr, cur_blk, blocks)) 791*25675Ssam return (EIO); 792*25675Ssam blkcount -= blocks; 793*25675Ssam memaddr += blocks * NBPG; 794*25675Ssam cur_blk += blocks; 79524004Ssam } 796*25675Ssam return (0); 79724004Ssam } 79824004Ssam 799*25675Ssam /* 800*25675Ssam * Write a block to disk during a crash dump. 801*25675Ssam */ 802*25675Ssam vdwrite_block(caddr, ctlr, unit, addr, block, blocks) 803*25675Ssam register cdr *caddr; 804*25675Ssam register int ctlr, unit; 805*25675Ssam register caddr_t addr; 806*25675Ssam register int block, blocks; 80724004Ssam { 808*25675Ssam register fmt_mdcb *mdcb = &vdctlr_info[ctlr].ctlr_mdcb; 809*25675Ssam register fmt_dcb *dcb = &vdctlr_info[ctlr].ctlr_dcb; 810*25675Ssam register unit_tab *ui = &vdunit_info[unit]; 811*25675Ssam register fs_tab *fs = &ui->info; 81224004Ssam 813*25675Ssam block *= (int)ui->sec_per_blk; 814*25675Ssam blocks *= (int)ui->sec_per_blk; 815*25675Ssam mdcb->firstdcb = (fmt_dcb *)(PHYS(dcb)); 816*25675Ssam dcb->intflg = NOINT; 817*25675Ssam dcb->opcode = WD; 818*25675Ssam dcb->operrsta = 0; 819*25675Ssam dcb->devselect = (char)(vddinfo[unit])->ui_slave; 820*25675Ssam dcb->trailcnt = (char)(sizeof (trrw) / sizeof (long)); 821*25675Ssam dcb->trail.rwtrail.memadr = addr; 822*25675Ssam dcb->trail.rwtrail.wcount = (short) 823*25675Ssam ((blocks * fs->secsize)/ sizeof (short)); 824*25675Ssam dcb->trail.rwtrail.disk.cylinder = (short)(block / ui->sec_per_cyl); 825*25675Ssam dcb->trail.rwtrail.disk.track = (char)((block / fs->nsec) % fs->ntrak); 826*25675Ssam dcb->trail.rwtrail.disk.sector = (char)(block % fs->nsec); 827*25675Ssam VDDC_ATTENTION(caddr, (fmt_mdcb *)(PHYS(mdcb)), 828*25675Ssam vdctlr_info[ctlr].ctlr_type); 829*25675Ssam POLLTILLDONE(caddr, dcb, 5, vdctlr_info[ctlr].ctlr_type); 830*25675Ssam if (vdtimeout <= 0) { 831*25675Ssam printf(" during dump\n"); 832*25675Ssam return (0); 833*25675Ssam } 834*25675Ssam if (dcb->operrsta & HRDERR) { 835*25675Ssam printf("vd%d: hard error, status %x\n", unit, dcb->operrsta); 836*25675Ssam return (0); 837*25675Ssam } 838*25675Ssam return (1); 83924004Ssam } 84024004Ssam 84124004Ssam vdsize(dev) 842*25675Ssam dev_t dev; 84324004Ssam { 844*25675Ssam struct vba_device *vi = vddinfo[VDUNIT(dev)]; 84524004Ssam 846*25675Ssam if (vi == 0 || vi->ui_alive == 0 || vi->ui_type >= nvddrv) 847*25675Ssam return (-1); 848*25675Ssam return (vdunit_info[VDUNIT(dev)].info.partition[FILSYS(dev)].par_len); 84924004Ssam } 85024004Ssam 851*25675Ssam /* 852*25675Ssam * Perform a controller reset. 853*25675Ssam */ 854*25675Ssam vdreset_ctlr(addr, ctlr) 855*25675Ssam register cdr *addr; 856*25675Ssam register int ctlr; 85724004Ssam { 858*25675Ssam register struct buf *cq = &vdminfo[ctlr]->um_tab; 859*25675Ssam register struct buf *uq = cq->b_forw; 860*25675Ssam register ctlr_tab *ci = &vdctlr_info[ctlr]; 861*25675Ssam 862*25675Ssam VDDC_RESET(addr, ci->ctlr_type); 863*25675Ssam ci->ctlr_started = 0; 864*25675Ssam if (ci->ctlr_type == SMD_ECTLR) { 865*25675Ssam addr->cdr_csr = 0; 866*25675Ssam addr->mdcb_tcf = AM_ENPDA; 867*25675Ssam addr->dcb_tcf = AM_ENPDA; 868*25675Ssam addr->trail_tcf = AM_ENPDA; 869*25675Ssam addr->data_tcf = AM_ENPDA; 870*25675Ssam addr->cdr_ccf = CCF_STS | XMD_32BIT | BSZ_16WRD | 871*25675Ssam CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR; 872*25675Ssam } 873*25675Ssam if (vdnotrailer(addr, ctlr, 0, INIT, 10) & HRDERR) { 874*25675Ssam printf("failed to init\n"); 875*25675Ssam return (0); 876*25675Ssam } 877*25675Ssam if (vdnotrailer(addr, ctlr, 0, DIAG, 10) & HRDERR) { 878*25675Ssam printf("diagnostic error\n"); 879*25675Ssam return (0); 880*25675Ssam } 881*25675Ssam /* reset all units attached to controller */ 882*25675Ssam uq = cq->b_forw; 883*25675Ssam do { 884*25675Ssam reset_drive(addr, ctlr, uq->b_dev, 0); 885*25675Ssam uq = uq->b_forw; 886*25675Ssam } while (uq != cq->b_forw); 887*25675Ssam return (1); 888*25675Ssam } 88924004Ssam 890*25675Ssam /* 891*25675Ssam * Perform a reset on a drive. 892*25675Ssam */ 893*25675Ssam reset_drive(addr, ctlr, slave, start) 894*25675Ssam register cdr *addr; 895*25675Ssam register int ctlr, slave, start; 896*25675Ssam { 897*25675Ssam register int type = vdctlr_info[ctlr].unit_type[slave]; 898*25675Ssam 899*25675Ssam if (type == UNKNOWN) 900*25675Ssam return; 901*25675Ssam if (!vdconfigure_drive(addr, ctlr, slave, type, start)) 902*25675Ssam printf("vd%d: drive %d: couldn't reset\n", ctlr, slave); 903*25675Ssam } 904*25675Ssam 905*25675Ssam #ifdef notdef 906*25675Ssam /* 907*25675Ssam * Dump the mdcb and DCB for diagnostic purposes. 908*25675Ssam */ 909*25675Ssam vdprintdcb(lp) 910*25675Ssam register long *lp; 911*25675Ssam { 912*25675Ssam register int i, dcb, tc; 913*25675Ssam 914*25675Ssam for (dcb = 0; lp; lp = (long *)(*lp), dcb++) { 915*25675Ssam lp = (long *)((long)lp | 0xc0000000); 916*25675Ssam printf("\nDump of dcb%d@%x:", dcb, lp); 917*25675Ssam for (i = 0, tc = lp[3] & 0xff; i < tc+7; i++) 918*25675Ssam printf(" %lx", lp[i]); 919*25675Ssam printf("\n"); 92024004Ssam } 921*25675Ssam DELAY(1750000); 92224004Ssam } 92324004Ssam #endif 924*25675Ssam #endif 925