/* * Copyright (c) 1986 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. * * @(#)xp.c 1.2 (2.11BSD GTE) 1/2/93 */ /* $Header: xp.c,v 1.3 88/06/18 08:10:08 jbj Locked $ */ /* $Log: xp.c,v $ * Revision 1.3 88/06/18 08:10:08 jbj * Patch to fix bad sector forwarding (John Nelson, jack@cadre.dsl.pittsburgh.edu) * * Revision 1.2 88/06/18 08:02:25 jbj * ioctl calls for SI bad sector forwarding. * * Revision 1.1 88/06/18 07:54:42 jbj * Initial revision * */ /* * RM02/03/05, RP04/05/06, Ampex 9300, CDC 9766, DIVA, Fuji 160, and SI * Eagle. This driver will handle most variants of SMD drives on one or more * controllers. If XP_PROBE is defined, it includes a probe routine that * will determine the number and type of drives attached to each controller; * otherwise, the data structures must be initialized. * * For simplicity we use hpreg.h instead of an xpreg.h. * The bits are the same. */ #include "xp.h" #if NXPD > 0 #include "param.h" #include "../machine/seg.h" #include "systm.h" #include "buf.h" #include "conf.h" #include "user.h" #include "ioctl.h" #include "dkio.h" #include "hpreg.h" #include "dkbad.h" #include "dk.h" #include "map.h" #include "uba.h" #define XP_SDIST 2 #define XP_RDIST 6 #define xpunit(dev) ((minor(dev) >> 3) & 07) int xp_offset[] = { HPOF_P400, HPOF_M400, HPOF_P400, HPOF_M400, HPOF_P800, HPOF_M800, HPOF_P800, HPOF_M800, HPOF_P1200, HPOF_M1200, HPOF_P1200, HPOF_M1200, 0, 0, 0, 0, }; /* * xp_drive and xp_controller may be initialized here, or filled in at boot * time if XP_PROBE is enabled. xp_controller address fields must be * initialized for any boot devices, however. * * xp_controller structure: one line per controller. Only the address need * be initialized in the controller structure if XP_PROBE is defined (at least * the address for the root device); otherwise the flags must be here also. * The XP_NOCC flag is set for RM02/03/05's (with no current cylinder * register); XP_NOSEARCH is set for Diva's without the search command. The * XP_RH70 flag need not be set here, the driver will always check that. */ #define XPADDR ((struct hpdevice *)0176700) struct xp_controller xp_controller[NXPC] = { /* 0 0 addr flags 0 */ #ifdef XP_PROBE 0, 0, XPADDR, 0, 0 #else 0, 0, XPADDR, XP_NOCC|XP_NOSEARCH, 0 #endif }; /* * xp_drive structure: one entry per drive. The drive structure must be * initialized if XP_PROBE is not enabled. Macros are provided in hpreg.h * to initialize entries according to drive type, and controller and drive * numbers. See those for examples on how to set up other types of drives. * With XP_PROBE defined, xpslave will fill in this structure, and any * initialization will be overridden. There is one exception; if the * drive-type field is set, it will be used instead of the drive-type register * to determine the drive's type. */ struct xp_drive xp_drive[NXPD] #ifndef XP_PROBE = { RM02_INIT(0,0), /* RM02, controller 0, drive 0 */ RM02_INIT(0,1), /* RM02, controller 0, drive 1 */ RM2X_INIT(0,0) /* Fuji 160, controller 0, drive 0 */ RM2X_INIT(0,1) /* Fuji 160, controller 0, drive 1 */ RM05X_INIT(0,2) /* 815-cyl RM05, controller 0, drive 2 */ } #endif ; /* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */ struct size { daddr_t nblocks; int cyloff; } rm_sizes[8] = { /* RM02/03 */ #ifdef FOXNPUP 32000, 0, /* a: cyl 0 - 199 */ 32000, 200, /* b: cyl 200 - 399 */ 32000, 423, /* c: cyl 423 - 622 */ 31840, 623, /* d: cyl 623 - 821 */ 3520, 400, /* e: cyl 400 - 422 */ 63840, 423, /* f: cyl 423 - 821, overlaps c & d */ 99360, 200, /* g: cyl 200 - 821, overlaps b thru end */ /* CAUTION: Partition g should not be */ /* used on the root device if swap is */ /* defined on partition e. */ 131680, 0, /* h: cyl 0 - 822 */ #else 4800, 0, /* a: cyl 0 - 29 */ 4800, 30, /* b: cyl 30 - 59 */ 122080, 60, /* c: cyl 60 - 822 */ 62720, 60, /* d: cyl 60 - 451 */ 59360, 452, /* e: cyl 452 - 822 */ 9600, 0, /* f: cyl 0 - 59, overlaps a & b */ 0, 0, /* g: Not Defined */ 131680, 0, /* h: cyl 0 - 822 */ #endif }, rm5_sizes[8] = { /* RM05, CDC 9766 */ 9120, 0, /* a: cyl 0 - 14 */ 9120, 15, /* b: cyl 15 - 29 */ 234080, 30, /* c: cyl 30 - 414 */ 248064, 415, /* d: cyl 415 - 822 */ 164160, 30, /* e: cyl 30 - 299 */ 152000, 300, /* f: cyl 300 - 549 */ 165984, 550, /* g: cyl 550 - 822 */ 500384, 0, /* h: cyl 0 - 822 */ }, si5_sizes[8] = { /* SI9775, direct mapping */ 10240, 0, /* a: cyl 0 - 7 */ 10240, 8, /* b: cyl 8 - 15 */ 510720, 16, /* c: cyl 16 - 414 */ 547840, 415, /* d: cyl 415 - 842 */ 363520, 16, /* e: cyl 16 - 299 */ 320000, 300, /* f: cyl 300 - 549 */ 375040, 550, /* g: cyl 550 - 842 */ 1079040, 0, /* h: cyl 0 - 842 */ }, hp_sizes[8] = { /* RP04/05/06 */ 9614, 0, /* a: cyl 0 - 22 */ 8778, 23, /* b: cyl 23 - 43 */ 153406, 44, /* c: cyl 44 - 410, RP04/05 */ 168872, 411, /* d: cyl 411 - 814, RP06 */ 322278, 44, /* e: cyl 44 - 814, RP06 */ 0, 0, /* f: Not Defined */ 171798, 0, /* g: cyl 0 - 410, whole RP04/05 */ 340670, 0 /* h: cyl 0 - 814, whole RP06 */ }, dv_sizes[8] = { /* Diva Comp V, Ampex 9300 in direct mode */ 9405, 0, /* a: cyl 0 - 14 */ 9405, 15, /* b: cyl 15 - 29 */ 241395, 30, /* c: cyl 30 - 414 */ 250800, 415, /* d: cyl 415 - 814 */ 169290, 30, /* e: cyl 30 - 299 */ 156750, 300, /* f: cyl 300 - 549 */ 166155, 550, /* g: cyl 550 - 814 */ 511005, 0 /* h: cyl 0 - 814 */ }, rm2x_sizes[8] = { /* Fuji 160 */ 9600, 0, /* a: cyl 0 - 29 */ 9600, 30, /* b: cyl 30 - 59 */ 244160, 60, /* c: cyl 60 - 822 */ 125440, 60, /* d: cyl 60 - 451 */ 118720, 452, /* e: cyl 452 - 822 */ 59520, 452, /* f: cyl 452 - 637 */ 59200, 638, /* g: cyl 638 - 822 */ 263360, 0 /* h: cyl 0 - 822 */ }, cap_sizes[8] = { /* SI Capricorn */ 16384, 0, /* a: cyl 0 thru 31 */ 33792, 32, /* b: cyl 32 thru 97 */ 291840, 98, /* c: cyl 98 thru 667 */ 16384, 668, /* d: cyl 668 thru 699 */ 56320, 700, /* e: cyl 700 thru 809 */ 109568, 810, /* f: cyl 810 thru 1023 */ 182272, 668, /* g: cyl 668 thru 1023 */ 524288, 0, /* h: cyl 0 thru 1023 */ }, si_sizes[8] = { /* SI Eagle */ 11520, 0, /* a: cyl 0 - 11 */ 11520, 12, /* b: cyl 12 - 23 */ 474240, 24, /* c: cyl 24 - 517 */ 92160, 518, /* d: cyl 518 - 613 */ 218880, 614, /* e: cyl 614 - 841 */ 0, 0, /* f: Not Defined */ 0, 0, /* g: Not Defined */ 808320, 0 /* h: cyl 0 - 841 (everything) */ }; /* END OF STUFF WHICH SHOULD BE READ IN PER DISK */ #ifdef XP_PROBE struct xpst { short type; /* value from controller type register */ short nsect; /* number of sectors/track */ short ntrack; /* number of tracks/cylinder */ short ncyl; /* number of cylinders */ struct size *sizes; /* partition tables */ short flags; /* controller flags */ } xpst[] = { { RM02, RM_SECT, RM_TRAC, RM_CYL, rm_sizes, XP_NOCC }, { RM2X, RM2X_SECT, RM2X_TRAC, RM2X_CYL, rm2x_sizes, XP_NOCC }, { RM03, RM_SECT, RM_TRAC, RM_CYL, rm_sizes, XP_NOCC }, { RM05, RM5_SECT, RM5_TRAC, RM5_CYL, rm5_sizes, XP_NOCC }, { RM5X, RM5X_SECT, RM5X_TRAC, RM5X_CYL, rm5_sizes, XP_NOCC }, { SI5, SI5_SECT, SI5_TRAC, SI5_CYL, si5_sizes, XP_NOCC }, { RP04, HP_SECT, HP_TRAC, RP04_CYL, hp_sizes, 0 }, { RP05, HP_SECT, HP_TRAC, RP04_CYL, hp_sizes, 0 }, { RP06, HP_SECT, HP_TRAC, RP06_CYL, hp_sizes, 0 }, { DV, DV_SECT, DV_TRAC, DV_CYL, dv_sizes, XP_NOSEARCH }, { CAP, CAP_SECT, CAP_TRAC, CAP_CYL, cap_sizes, XP_NOCC }, { SI, SI_SECT, SI_TRAC, SI_CYL, si_sizes, XP_NOCC }, { 0, 0, 0, 0, 0 } }; #endif struct buf xptab; struct buf xputab[NXPD]; #ifdef BADSECT int maxbad = MAXBAD; /* KLUDGE: no. of bad blocks in table */ struct dkbad xpbad[NXPD]; /* replacement block number */ struct buf bxpbuf[NXPD]; bool_t xp_init[NXPD]; /* drive initialized */ bool_t xp_rwhdr[8*NXPD]; /* next i/o includes header */ #endif #ifdef UCB_METER static int xp_dkn = -1; /* number for iostat */ #endif /* * Attach controllers whose addresses are known at boot time. Stop at the * first not found, so that the drive numbering won't get confused. */ xproot() { register int i; register struct hpdevice *xpaddr; for (i = 0; i < NXPC; i++) if (((xpaddr = xp_controller[i].xp_addr) == 0) || (xpattach(xpaddr, i) == 0)) break; } /* * Attach controller at xpaddr. Mark as nonexistent if xpaddr is 0; otherwise * attach slaves if probing. NOTE: if probing for drives, this routine must * be called once per controller, in ascending controller numbers. */ xpattach(xpaddr, unit) register struct hpdevice *xpaddr; int unit; { register struct xp_controller *xc = &xp_controller[unit]; #ifdef XP_PROBE static int last_attached = -1; #endif #ifdef UCB_METER if (xp_dkn < 0) { dk_alloc(&xp_dkn, NXPD+NXPC, "xp", 0L); #ifndef XP_PROBE /* * Hard coded drive configuration - snag the number of * sectors per track for each drive and compute drive * transfer rate assuming 3600rpm (the Fujitsu Eagle 2351A * (SI Eagle) is actually 3961rpm; it's just not worth the * effort to fix the assumption.) If XP_PROBE is defined we * grab the number of sectors/track for each drive in * xpslave. */ if (xp_dkn >= 0) { register int i; register long *lp; for (i = 0, lp = &dk_wps[xp_dkn]; i < NXPD; i++) *lp++ = (long)xp_drive[i].xp_nsect * (60L * 256L); } #endif } #endif if ((unsigned)unit >= NXPC) return(0); if ((xpaddr != 0) && (fioword(xpaddr) != -1)) { xc->xp_addr = xpaddr; if (fioword(&xpaddr->hpbae) != -1) xc->xp_flags |= XP_RH70; #ifdef XP_PROBE /* * If already attached, ignore (don't want to renumber drives) */ if (unit > last_attached) { last_attached = unit; xpslave(xpaddr, xc); } #endif return(1); } xc->xp_addr = 0; return(0); } #ifdef XP_PROBE /* * Determine what drives are attached to a controller; guess their types and * fill in the drive structures. */ xpslave(xpaddr, xc) register struct hpdevice *xpaddr; struct xp_controller *xc; { register struct xp_drive *xd; register struct xpst *st; int j, dummy; static int nxp = 0; for (j = 0; j < 8; j++) { { int x = 6000; while (--x); /* delay */ } xpaddr->hpcs1.w = 0; xpaddr->hpcs2.w = j; xpaddr->hpcs1.w = HP_GO; /* testing... */ { int x = 6000; while (--x); /* delay */ } dummy = xpaddr->hpds; if (xpaddr->hpcs2.w & HPCS2_NED) { xpaddr->hpcs2.w = HPCS2_CLR; continue; } if (nxp < NXPD) { xd = &xp_drive[nxp++]; xd->xp_ctlr = xc; xd->xp_unit = j; /* If drive type is initialized, believe it. */ if (xd->xp_type == 0) { xd->xp_type = xpaddr->hpdt & 077; xd->xp_type = xpmaptype(xd, xpaddr->hpsn); } for (st = xpst; st->type; st++) if (st->type == xd->xp_type) { xd->xp_nsect = st->nsect; xd->xp_ntrack = st->ntrack; xd->xp_nspc = st->nsect * st->ntrack; #ifdef BADSECT xd->xp_ncyl = st->ncyl; #endif xd->xp_sizes = st->sizes; xd->xp_ctlr->xp_flags |= st->flags; break; } if (!st->type) { printf("xp%d: drive type %o unrecognized\n",nxp - 1, xd->xp_type); xd->xp_ctlr = NULL; } #ifdef UCB_METER if (xp_dkn >= 0 && xd->xp_ctlr) dk_wps[xd - &xp_drive[0]] = (long)xd->xp_nsect * (60L * 256L); #endif } } } static xpmaptype(xd, hpsn) register struct xp_drive *xd; register u_short hpsn; { register u_short type = xd->xp_type; /* * Model-byte processing for SI controllers. * NB: Only deals with RM02, RM03 and RM05 emulations. */ if ((type == RM02 || type == RM03 || type == RM05) && (hpsn & SIMB_LU) == xd->xp_unit) { switch (hpsn & (SIMB_MB & ~(SIMB_S6|SIMB_XX|SIRM03|SIRM05))) { case SI9775D: type = SI5; break; case SI9775M: type = RM05; break; case SI9730D: type = RM2X; break; case SI9766: type = RM05; break; case SI9762: type = RM03; break; case SICAPD: type = CAP; break; case SI9751D: type = SI; break; } } return(type); } #endif XP_PROBE xpopen(dev) dev_t dev; { register struct xp_drive *xd; register int unit = xpunit(dev); if (unit >= NXPD || !(xd = &xp_drive[unit])->xp_ctlr || !xd->xp_ctlr->xp_addr) return (ENXIO); return (0); } xpstrategy(bp) register struct buf *bp; { register struct xp_drive *xd; register unit; struct buf *dp; short pseudo_unit; int s; long bn; unit = dkunit(bp); pseudo_unit = minor(bp->b_dev) & 07; if ((unit >= NXPD) || ((xd = &xp_drive[unit])->xp_ctlr == 0) || (xd->xp_ctlr->xp_addr == 0)) { bp->b_error = ENXIO; goto errexit; } if ((bp->b_blkno < 0) || ((bn = dkblock(bp)) + ((bp->b_bcount + 511) >> 9) > xd->xp_sizes[pseudo_unit].nblocks)) { bp->b_error = EINVAL; errexit: bp->b_flags |= B_ERROR; iodone(bp); return; } if ((xd->xp_ctlr->xp_flags & XP_RH70) == 0) mapalloc(bp); bp->b_cylin = bn / xd->xp_nspc + xd->xp_sizes[pseudo_unit].cyloff; dp = &xputab[unit]; s = spl5(); disksort(dp, bp); if (dp->b_active == 0) { xpustart(unit); if (xd->xp_ctlr->xp_active == 0) xpstart(xd->xp_ctlr); } splx(s); } /* * Unit start routine. Seek the drive to where the data are and then generate * another interrupt to actually start the transfer. If there is only one * drive or we are very close to the data, don't bother with the search. If * called after searching once, don't bother to look where we are, just queue * for transfer (to avoid positioning forever without transferring). */ xpustart(unit) int unit; { register struct xp_drive *xd; register struct hpdevice *xpaddr; register struct buf *dp; struct buf *bp; daddr_t bn; int sn, cn, csn; xd = &xp_drive[unit]; xpaddr = xd->xp_ctlr->xp_addr; xpaddr->hpcs2.w = xd->xp_unit; xpaddr->hpcs1.c[0] = HP_IE; xpaddr->hpas = 1 << xd->xp_unit; if (unit >= NXPD) return; #ifdef UCB_METER if (xp_dkn >= 0) dk_busy &= ~(1 << (xp_dkn + unit)); #endif dp = &xputab[unit]; if ((bp=dp->b_actf) == NULL) return; /* * If we have already positioned this drive, * then just put it on the ready queue. */ if (dp->b_active) goto done; dp->b_active++; /* * If drive has just come up, set up the pack. */ #ifdef BADSECT if (((xpaddr->hpds & HPDS_VV) == 0) || (xp_init[unit] == 0)) { struct buf *bbp = &bxpbuf[unit]; #else if ((xpaddr->hpds & HPDS_VV) == 0) { #endif /* SHOULD WARN SYSTEM THAT THIS HAPPENED */ #ifdef XP_DEBUG printf("preset-unit=%d\n", unit); #endif xpaddr->hpcs1.c[0] = HP_IE | HP_PRESET | HP_GO; #ifdef XP_DEBUG printf("preset done\n"); #endif xpaddr->hpof = HPOF_FMT22; #ifdef BADSECT xp_init[unit] = 1; bbp->b_flags = B_READ | B_BUSY | B_PHYS; bbp->b_dev = bp->b_dev | 7; /* "h" partition whole disk */ bbp->b_bcount = sizeof(struct dkbad); bbp->b_un.b_addr = (caddr_t)&xpbad[unit]; bbp->b_blkno = (daddr_t)xd->xp_ncyl * xd->xp_nspc - xd->xp_nsect; bbp->b_cylin = xd->xp_ncyl - 1; if ((xd->xp_ctlr->xp_flags & XP_RH70) == 0) mapalloc(bbp); dp->b_actf = bbp; bbp->av_forw = bp; bp = bbp; #endif BADSECT } #if NXPD > 1 /* * If drive is offline, forget about positioning. */ if ((xpaddr->hpds & (HPDS_DPR | HPDS_MOL)) != (HPDS_DPR | HPDS_MOL)) goto done; /* * Figure out where this transfer is going to * and see if we are close enough to justify not searching. */ bn = dkblock(bp); cn = bp->b_cylin; sn = bn % xd->xp_nspc; sn += xd->xp_nsect - XP_SDIST; sn %= xd->xp_nsect; if (((xd->xp_ctlr->xp_flags & XP_NOCC) && (xd->xp_cc != cn)) || xpaddr->hpcc != cn) goto search; if (xd->xp_ctlr->xp_flags & XP_NOSEARCH) goto done; csn = (xpaddr->hpla >> 6) - sn + XP_SDIST - 1; if (csn < 0) csn += xd->xp_nsect; if (csn > xd->xp_nsect - XP_RDIST) goto done; search: xpaddr->hpdc = cn; xpaddr->hpda = sn; xpaddr->hpcs1.c[0] = (xd->xp_ctlr->xp_flags & XP_NOSEARCH) ? (HP_IE | HP_SEEK | HP_GO) : (HP_IE | HP_SEARCH | HP_GO); xd->xp_cc = cn; #ifdef UCB_METER /* * Mark unit busy for iostat. */ if (xp_dkn >= 0) { int dkn = xp_dkn + unit; dk_busy |= 1< 1 done: /* * Device is ready to go. * Put it on the ready queue for the controller. */ dp->b_forw = NULL; if (xd->xp_ctlr->xp_actf == NULL) xd->xp_ctlr->xp_actf = dp; else xd->xp_ctlr->xp_actl->b_forw = dp; xd->xp_ctlr->xp_actl = dp; } /* * Start up a transfer on a controller. */ xpstart(xc) register struct xp_controller *xc; { register struct hpdevice *xpaddr; register struct buf *bp; struct xp_drive *xd; struct buf *dp; short pseudo_unit; daddr_t bn; int unit, sn, tn, cn, cmd; xpaddr = xc->xp_addr; loop: /* * Pull a request off the controller queue. */ if ((dp = xc->xp_actf) == NULL) return; if ((bp = dp->b_actf) == NULL) { xc->xp_actf = dp->b_forw; goto loop; } /* * Mark controller busy and determine destination of this request. */ xc->xp_active++; pseudo_unit = minor(bp->b_dev) & 07; unit = dkunit(bp); xd = &xp_drive[unit]; bn = dkblock(bp); cn = xd->xp_sizes[pseudo_unit].cyloff; cn += bn / xd->xp_nspc; sn = bn % xd->xp_nspc; tn = sn / xd->xp_nsect; sn = sn % xd->xp_nsect; /* * Select drive. */ xpaddr->hpcs2.w = xd->xp_unit; /* * Check that it is ready and online. */ if ((xpaddr->hpds & (HPDS_DPR | HPDS_MOL)) != (HPDS_DPR | HPDS_MOL)) { xc->xp_active = 0; dp->b_errcnt = 0; dp->b_actf = bp->av_forw; bp->b_flags |= B_ERROR; iodone(bp); goto loop; } if (dp->b_errcnt >= 16 && (bp->b_flags & B_READ)) { xpaddr->hpof = xp_offset[dp->b_errcnt & 017] | HPOF_FMT22; xpaddr->hpcs1.w = HP_OFFSET | HP_GO; while ((xpaddr->hpds & (HPDS_PIP | HPDS_DRY)) != HPDS_DRY); } xpaddr->hpdc = cn; xpaddr->hpda = (tn << 8) + sn; xpaddr->hpba = bp->b_un.b_addr; if (xc->xp_flags & XP_RH70) xpaddr->hpbae = bp->b_xmem; xpaddr->hpwc = -(bp->b_bcount >> 1); /* * Initiate i/o command. */ cmd = ((bp->b_xmem & 3) << 8) | HP_IE | HP_GO; #ifdef XP_FORMAT if (minor(bp->b_dev) & 0200 || xp_rwhdr[unit]) { cmd |= bp->b_flags & B_READ ? HP_RHDR : HP_WHDR; } else #endif cmd |= bp->b_flags & B_READ ? HP_RCOM : HP_WCOM; xpaddr->hpcs1.w = cmd; #ifdef UCB_METER if (xp_dkn >= 0) { int dkn = xp_dkn + NXPD + (xc - &xp_controller[0]); dk_busy |= 1<b_bcount>>6; } #endif } /* * Handle a disk interrupt. */ xpintr(dev) int dev; { register struct hpdevice *xpaddr; register struct buf *dp; struct xp_controller *xc; struct xp_drive *xd; struct buf *bp; register int unit; int as; xc = &xp_controller[dev]; xpaddr = xc->xp_addr; as = xpaddr->hpas & 0377; if (xc->xp_active) { #ifdef UCB_METER if (xp_dkn >= 0) dk_busy &= ~(1 << (xp_dkn + NXPD + dev)); #endif /* * Get device and block structures. Select the drive. */ dp = xc->xp_actf; bp = dp->b_actf; #ifdef BADSECT if (bp->b_flags & B_BAD) if (xpecc(bp, CONT)) return; #endif unit = dkunit(bp); xd = &xp_drive[unit]; xpaddr->hpcs2.c[0] = xd->xp_unit; /* * Check for and process errors. */ if (xpaddr->hpcs1.w & HP_TRE) { while ((xpaddr->hpds & HPDS_DRY) == 0); if (xpaddr->hper1 & HPER1_WLE) { /* * Give up on write locked deviced immediately. */ printf("xp%d: write locked\n", unit); bp->b_flags |= B_ERROR; #ifdef BADSECT } else if ((xpaddr->rmer2 & RMER2_BSE) || (xpaddr->hper1 & HPER1_FER)) { #ifdef XP_FORMAT /* * Allow this error on format devices. */ if (minor(bp->b_dev) & 0200) goto errdone; #endif if (xpecc(bp, BSE)) return; else goto hard; #endif BADSECT } else { /* * After 28 retries (16 without offset and * 12 with offset positioning), give up. */ if (++dp->b_errcnt > 28) { hard: harderr(bp, "xp"); #ifdef XP_DEBUG /* * for RM drives */ printf("cs2=%b er1=%b er2=%b %s\n", xpaddr->hpcs2.w, HPCS2_BITS, xpaddr->hper1, HPER1_BITS, xpaddr->rmer2, RMER2_BITS, ((xp_rwhdr[unit]) ? " (hdr i/o)" : "")); #else printf("cs2=%b er1=%b %s\n", xpaddr->hpcs2.w, HPCS2_BITS, xpaddr->hper1, HPER1_BITS, ((xp_rwhdr[unit]) ? " (hdr i/o)" : "")); #endif bp->b_flags |= B_ERROR; bp->b_flags &= ~B_BAD; } else xc->xp_active = 0; } /* * If soft ecc, correct it (continuing by returning if * necessary). Otherwise, fall through and retry the * transfer. */ if((xpaddr->hper1 & (HPER1_DCK|HPER1_ECH)) == HPER1_DCK) if (xpecc(bp, ECC)) return; errdone: xpaddr->hpcs1.w = HP_TRE | HP_IE | HP_DCLR | HP_GO; if ((dp->b_errcnt & 07) == 4) { xpaddr->hpcs1.w = HP_RECAL | HP_IE | HP_GO; while ((xpaddr->hpds & (HPDS_PIP | HPDS_DRY)) != HPDS_DRY); } xd->xp_cc = -1; } if (xc->xp_active) { if (dp->b_errcnt) { xpaddr->hpcs1.w = HP_RTC | HP_GO; while ((xpaddr->hpds & (HPDS_PIP | HPDS_DRY)) != HPDS_DRY); } xc->xp_active = 0; xc->xp_actf = dp->b_forw; dp->b_active = 0; dp->b_errcnt = 0; dp->b_actf = bp->b_actf; xd->xp_cc = bp->b_cylin; bp->b_resid = - (xpaddr->hpwc << 1); xp_rwhdr[unit] = 0; iodone(bp); xpaddr->hpcs1.w = HP_IE; if (dp->b_actf) xpustart(unit); } as &= ~(1 << xp_drive[unit].xp_unit); } else { if (as == 0) xpaddr->hpcs1.w = HP_IE; xpaddr->hpcs1.c[1] = HP_TRE >> 8; } for (unit = 0; unit < NXPD; unit++) if ((xp_drive[unit].xp_ctlr == xc) && (as & (1 << xp_drive[unit].xp_unit))) xpustart(unit); xpstart(xc); } xpioctl(dev, cmd, data, flag) dev_t dev; u_int cmd; caddr_t data; { int unit = xpunit(dev); switch(cmd) { case DKIOCHDR: /* do header read/write */ xp_rwhdr[unit] = 1; return(0); case DKREINIT: /* reread BBF table */ xp_init[unit] = 0; return(0); case DKINFO: /* get drive info */ { struct drv_info *xo = (struct drv_info *)data; register struct xp_drive *xd = &xp_drive[unit]; xo->type = xd->xp_type; xo->model = 0; xo->trk = xd->xp_ntrack; xo->sec = xd->xp_nsect; xo->nspc = xd->xp_nspc; xo->ncyl = xd->xp_ncyl; xo->name[0] = '\0'; bcopy(xd->xp_sizes, xo->fs, 48); } return(0); } return(EINVAL); } #define exadr(x,y) (((long)(x) << 16) | (unsigned)(y)) /* * Correct an ECC error and restart the i/o to complete the transfer if * necessary. This is quite complicated because the correction may be going * to an odd memory address base and the transfer may cross a sector boundary. */ xpecc(bp, flag) register struct buf *bp; { register struct xp_drive *xd; register struct hpdevice *xpaddr; register unsigned byte; ubadr_t bb, addr; long wrong; int bit, wc; unsigned ndone, npx; int ocmd; int cn, tn, sn; daddr_t bn; struct ubmap *ubp; int unit; /* * ndone is #bytes including the error which is assumed to be in the * last disk page transferred. */ unit = dkunit(bp); xd = &xp_drive[unit]; xpaddr = xd->xp_ctlr->xp_addr; #ifdef BADSECT if (flag == CONT) { npx = bp->b_error; bp->b_error = 0; ndone = npx * NBPG; wc = -((short)(bp->b_bcount - ndone) / (short)NBPW); } else { #endif wc = xpaddr->hpwc; ndone = bp->b_bcount - ((unsigned)(-wc) * NBPW); npx = ndone / NBPG; #ifdef BADSECT } #endif ocmd = (xpaddr->hpcs1.w & ~HP_RDY) | HP_IE | HP_GO; bb = exadr(bp->b_xmem, bp->b_un.b_addr); bn = dkblock(bp); cn = bp->b_cylin - (bn / xd->xp_nspc); bn += npx; cn += bn / xd->xp_nspc; sn = bn % xd->xp_nspc; tn = sn; tn /= xd->xp_nsect; sn %= xd->xp_nsect; switch (flag) { case ECC: printf("xp%d%c: soft ecc sn%D\n",unit, 'a' + (minor(bp->b_dev) & 07), bp->b_blkno + (npx - 1)); wrong = xpaddr->hpec2; if (wrong == 0) { xpaddr->hpof = HPOF_FMT22; xpaddr->hpcs1.w |= HP_IE; return (0); } /* * Compute the byte/bit position of the err * within the last disk page transferred. * Hpec1 is origin-1. */ byte = xpaddr->hpec1 - 1; bit = byte & 07; byte >>= 3; byte += ndone - NBPG; wrong <<= bit; /* * Correct until mask is zero or until end of * transfer, whichever comes first. */ while (byte < bp->b_bcount && wrong != 0) { addr = bb + byte; if (bp->b_flags & (B_MAP|B_UBAREMAP)) { /* * Simulate UNIBUS map if UNIBUS * transfer. */ ubp = UBMAP + ((addr >> 13) & 037); addr = exadr(ubp->ub_hi, ubp->ub_lo) + (addr & 017777); } putmemc(addr, getmemc(addr) ^ (int) wrong); byte++; wrong >>= 8; } break; #ifdef BADSECT case BSE: if ((bn = isbad(&xpbad[unit], cn, tn, sn)) < 0) return(0); bp->b_flags |= B_BAD; bp->b_error = npx + 1; bn = (daddr_t)xd->xp_ncyl * xd->xp_nspc - xd->xp_nsect - 1 - bn; cn = bn/xd->xp_nspc; sn = bn%xd->xp_nspc; tn = sn; tn /= xd->xp_nsect; sn %= xd->xp_nsect; #ifdef XP_DEBUG printf("revector to cn %d tn %d sn %d\n", cn, tn, sn); #endif wc = -(512 / (short)NBPW); break; case CONT: bp->b_flags &= ~B_BAD; #ifdef XP_DEBUG printf("xpecc CONT: bn %D cn %d tn %d sn %d\n", bn, cn, tn, sn); #endif break; #endif BADSECT } xd->xp_ctlr->xp_active++; if (wc == 0) return (0); /* * Have to continue the transfer. Clear the drive and compute the * position where the transfer is to continue. We have completed * npx sectors of the transfer already. */ xpaddr->hpcs2.w = xd->xp_unit; xpaddr->hpcs1.w = HP_TRE | HP_DCLR | HP_GO; addr = bb + ndone; xpaddr->hpdc = cn; xpaddr->hpda = (tn << 8) + sn; xpaddr->hpwc = wc; xpaddr->hpba = (caddr_t)addr; if (xd->xp_ctlr->xp_flags & XP_RH70) xpaddr->hpbae = (int)(addr >> 16); xpaddr->hpcs1.w = ocmd; return (1); } #ifdef XP_DUMP /* * Dump routine. Dumps from dumplo to end of memory/end of disk section for * minor(dev). */ #define DBSIZE 16 /* unit of transfer, same number */ xpdump(dev) dev_t dev; { /* * ONLY USE 2 REGISTER VARIABLES, OR C COMPILER CHOKES */ register struct xp_drive *xd; register struct hpdevice *xpaddr; daddr_t bn, dumpsize; long paddr; int sn, count; struct ubmap *ubp; if ((bdevsw[major(dev)].d_strategy != xpstrategy) /* paranoia */ || ((dev=minor(dev)) > (NXPD << 3))) return(EINVAL); xd = &xp_drive[dev >> 3]; dev &= 07; if (xd->xp_ctlr == 0) return(EINVAL); xpaddr = xd->xp_ctlr->xp_addr; dumpsize = xd->xp_sizes[dev].nblocks; if ((dumplo < 0) || (dumplo >= dumpsize)) return(EINVAL); dumpsize -= dumplo; xpaddr->hpcs2.w = xd->xp_unit; if ((xpaddr->hpds & HPDS_VV) == 0) { xpaddr->hpcs1.w = HP_DCLR | HP_GO; xpaddr->hpcs1.w = HP_PRESET | HP_GO; xpaddr->hpof = HPOF_FMT22; } if ((xpaddr->hpds & (HPDS_DPR | HPDS_MOL)) != (HPDS_DPR | HPDS_MOL)) return(EFAULT); ubp = &UBMAP[0]; for (paddr = 0L; dumpsize > 0; dumpsize -= count) { count = dumpsize>DBSIZE? DBSIZE: dumpsize; bn = dumplo + (paddr >> PGSHIFT); xpaddr->hpdc = bn / xd->xp_nspc + xd->xp_sizes[dev].cyloff; sn = bn % xd->xp_nspc; xpaddr->hpda = ((sn / xd->xp_nsect) << 8) | (sn % xd->xp_nsect); xpaddr->hpwc = -(count << (PGSHIFT - 1)); if (ubmap && ((xd->xp_ctlr->xp_flags & XP_RH70) == 0)) { ubp->ub_lo = loint(paddr); ubp->ub_hi = hiint(paddr); xpaddr->hpba = 0; xpaddr->hpcs1.w = HP_WCOM | HP_GO; } else { /* * Non-UNIBUS map, or 11/70 RH70 (MASSBUS) */ xpaddr->hpba = loint(paddr); if (xd->xp_ctlr->xp_flags & XP_RH70) xpaddr->hpbae = hiint(paddr); xpaddr->hpcs1.w = HP_WCOM | HP_GO | ((paddr >> 8) & (03 << 8)); } while (xpaddr->hpcs1.w & HP_GO); if (xpaddr->hpcs1.w & HP_TRE) { if (xpaddr->hpcs2.w & HPCS2_NEM) return(0); /* made it to end of memory */ return(EIO); } paddr += (DBSIZE << PGSHIFT); } return(0); /* filled disk minor dev */ } #endif XP_DUMP #endif NXPD