1*34396Skarels /* vd.c 1.25 88/05/21 */ 224004Ssam 329564Ssam #include "dk.h" 424004Ssam #if NVD > 0 524004Ssam /* 630519Ssam * Versabus VDDC/SMDE driver. 725675Ssam */ 825675Ssam #include "param.h" 925675Ssam #include "buf.h" 1025675Ssam #include "cmap.h" 1125675Ssam #include "conf.h" 1225675Ssam #include "dir.h" 1329564Ssam #include "dkstat.h" 1430519Ssam #include "disklabel.h" 1525675Ssam #include "map.h" 1630519Ssam #include "file.h" 1725675Ssam #include "systm.h" 1825675Ssam #include "user.h" 1925675Ssam #include "vmmac.h" 2025675Ssam #include "proc.h" 2125675Ssam #include "uio.h" 2230370Skarels #include "syslog.h" 2330370Skarels #include "kernel.h" 2430519Ssam #include "ioctl.h" 2530756Skarels #include "stat.h" 2624004Ssam 2729951Skarels #include "../tahoe/cpu.h" 2829951Skarels #include "../tahoe/mtpr.h" 2929951Skarels #include "../tahoe/pte.h" 3029951Skarels 3125675Ssam #include "../tahoevba/vbavar.h" 3225928Ssam #include "../tahoevba/vdreg.h" 3324004Ssam 3432211Skarels #ifndef COMPAT_42 3530519Ssam #define COMPAT_42 3632211Skarels #endif 37*34396Skarels #define B_FORMAT B_XXX /* XXX */ 3830519Ssam 3930519Ssam #define vdunit(dev) (minor(dev) >> 3) 4030519Ssam #define vdpart(dev) (minor(dev) & 0x07) 4130519Ssam #define vdminor(unit,part) (((unit) << 3) | (part)) 4224004Ssam 4324004Ssam struct vba_ctlr *vdminfo[NVD]; 4429564Ssam struct vba_device *vddinfo[NDK]; 4530756Skarels int vdprobe(), vdslave(), vdattach(), vddgo(), vdstrategy(); 4630519Ssam long vdaddr[] = { 0xffff2000, 0xffff2100, 0xffff2200, 0xffff2300, 0 }; 4725675Ssam struct vba_driver vddriver = 4830519Ssam { vdprobe, vdslave, vdattach, vddgo, vdaddr, "dk", vddinfo, "vd", vdminfo }; 4924004Ssam 5024004Ssam /* 5130519Ssam * Per-controller state. 5230519Ssam */ 5330519Ssam struct vdsoftc { 5430519Ssam u_short vd_flags; 5530519Ssam #define VD_INIT 0x1 /* controller initialized */ 5630519Ssam #define VD_STARTED 0x2 /* start command issued */ 5730519Ssam #define VD_DOSEEKS 0x4 /* should overlap seeks */ 5830756Skarels #define VD_SCATGATH 0x8 /* can do scatter-gather commands (correctly) */ 5930519Ssam u_short vd_type; /* controller type */ 6030519Ssam u_short vd_wticks; /* timeout */ 6130519Ssam struct mdcb vd_mdcb; /* master command block */ 6230519Ssam u_long vd_mdcbphys; /* physical address of vd_mdcb */ 6330519Ssam struct dcb vd_dcb; /* i/o command block */ 6430519Ssam u_long vd_dcbphys; /* physical address of vd_dcb */ 6530601Skarels struct vb_buf vd_rbuf; /* vba resources */ 6630519Ssam } vdsoftc[NVD]; 6730519Ssam 68*34396Skarels #define VDMAXTIME 20 /* max time for operation, sec. */ 69*34396Skarels 7030519Ssam /* 7125675Ssam * Per-drive state. 7225675Ssam */ 7330519Ssam struct dksoftc { 7434076Skarels int dk_state; /* open fsm */ 7530756Skarels #ifndef SECSIZE 7630756Skarels u_short dk_bshift; /* shift for * (DEV_BSIZE / sectorsize) XXX */ 7730756Skarels #endif SECSIZE 7834076Skarels int dk_wlabel; /* label sector is currently writable */ 7932576Skarels u_long dk_copenpart; /* character units open on this drive */ 8032576Skarels u_long dk_bopenpart; /* block units open on this drive */ 8132576Skarels u_long dk_openpart; /* all units open on this drive */ 8230519Ssam u_int dk_curcyl; /* last selected cylinder */ 8330756Skarels struct skdcb dk_dcb; /* seek command block */ 8430519Ssam u_long dk_dcbphys; /* physical address of dk_dcb */ 85*34396Skarels int df_reg[3]; /* for formatting, in-out parameters */ 8630519Ssam } dksoftc[NDK]; 8724004Ssam 8824004Ssam /* 8930519Ssam * Drive states. Used during steps of open/initialization. 9030519Ssam * States < OPEN (> 0) are transient, during an open operation. 9134076Skarels * OPENRAW is used for unlabeled disks, to allow format operations. 9225675Ssam */ 9330519Ssam #define CLOSED 0 /* disk is closed */ 9430519Ssam #define WANTOPEN 1 /* open requested, not started */ 9530519Ssam #define WANTOPENRAW 2 /* open requested, no label */ 9630519Ssam #define RDLABEL 3 /* reading pack label */ 9730519Ssam #define OPEN 4 /* intialized and ready */ 9830519Ssam #define OPENRAW 5 /* open, no label */ 9924004Ssam 10030519Ssam struct buf dkutab[NDK]; /* i/o queue headers */ 10130519Ssam struct disklabel dklabel[NDK]; /* pack labels */ 10224004Ssam 10330519Ssam #define b_cylin b_resid 10430574Skarels #define b_track b_error /* used for seek commands */ 10530574Skarels #define b_seekf b_forw /* second queue on um_tab */ 10630574Skarels #define b_seekl b_back /* second queue on um_tab */ 10730519Ssam 10830519Ssam int vdwstart, vdwatch(); 10930519Ssam 11024004Ssam /* 11125675Ssam * See if the controller is really there; if so, initialize it. 11225675Ssam */ 11325857Ssam vdprobe(reg, vm) 11425857Ssam caddr_t reg; 11525857Ssam struct vba_ctlr *vm; 11625675Ssam { 11725857Ssam register br, cvec; /* must be r12, r11 */ 11830519Ssam register struct vddevice *vdaddr = (struct vddevice *)reg; 11930519Ssam struct vdsoftc *vd; 12030573Skarels int s; 12125857Ssam 12230370Skarels #ifdef lint 12330370Skarels br = 0; cvec = br; br = cvec; 12430370Skarels vdintr(0); 12530370Skarels #endif 12625857Ssam if (badaddr((caddr_t)reg, 2)) 12725675Ssam return (0); 12830519Ssam vd = &vdsoftc[vm->um_ctlr]; 12930519Ssam vdaddr->vdreset = 0xffffffff; 13025675Ssam DELAY(1000000); 13130519Ssam if (vdaddr->vdreset != (unsigned)0xffffffff) { 13230519Ssam vd->vd_type = VDTYPE_VDDC; 13330519Ssam vd->vd_flags &= ~VD_DOSEEKS; 13425675Ssam DELAY(1000000); 13525675Ssam } else { 13630519Ssam vd->vd_type = VDTYPE_SMDE; 13730519Ssam vd->vd_flags |= VD_DOSEEKS; 13830519Ssam vdaddr->vdrstclr = 0; 13925675Ssam DELAY(3000000); 14030519Ssam vdaddr->vdcsr = 0; 14130519Ssam vdaddr->vdtcf_mdcb = AM_ENPDA; 14230519Ssam vdaddr->vdtcf_dcb = AM_ENPDA; 14330519Ssam vdaddr->vdtcf_trail = AM_ENPDA; 14430519Ssam vdaddr->vdtcf_data = AM_ENPDA; 14530519Ssam vdaddr->vdccf = CCF_SEN | CCF_DER | CCF_STS | 14629921Skarels XMD_32BIT | BSZ_16WRD | 14725925Ssam CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR; 14825675Ssam } 14930519Ssam vd->vd_mdcbphys = vtoph((struct proc *)0, (unsigned)&vd->vd_mdcb); 15030519Ssam vd->vd_dcbphys = vtoph((struct proc *)0, (unsigned)&vd->vd_dcb); 15130519Ssam vm->um_addr = reg; /* XXX */ 15230573Skarels s = spl7(); 15330519Ssam if (!vdcmd(vm, VDOP_INIT, 10) || !vdcmd(vm, VDOP_DIAG, 10)) { 15430519Ssam printf("vd%d: %s cmd failed\n", vm->um_ctlr, 15530519Ssam vd->vd_dcb.opcode == VDOP_INIT ? "init" : "diag"); 15630573Skarels splx(s); 15730519Ssam return (0); 15830519Ssam } 15930756Skarels if (vd->vd_type == VDTYPE_SMDE) { 16030756Skarels vd->vd_dcb.trail.idtrail.date = 0; 16130756Skarels if (vdcmd(vm, VDOP_IDENT, 10)) { 16230756Skarels uncache(&vd->vd_dcb.trail.idtrail.date); 16330756Skarels if (vd->vd_dcb.trail.idtrail.date != 0) 16430756Skarels vd->vd_flags |= VD_SCATGATH; 16530756Skarels } 16630756Skarels } 16730573Skarels splx(s); 16825925Ssam /* 16925950Ssam * Allocate page tables and i/o buffer. 17025925Ssam */ 17132211Skarels if (vbainit(&vd->vd_rbuf, MAXPHYS, 17232211Skarels vd->vd_type == VDTYPE_VDDC ? VB_24BIT : VB_32BIT) == 0) { 17332211Skarels printf("vd%d: vbainit failed\n", vm->um_ctlr); 17432211Skarels return (0); 17532211Skarels } 17625857Ssam br = 0x17, cvec = 0xe0 + vm->um_ctlr; /* XXX */ 17730519Ssam return (sizeof (struct vddevice)); 17825675Ssam } 17924004Ssam 18024004Ssam /* 18130519Ssam * See if a drive is really there. 18230519Ssam * 18330519Ssam * Can't read pack label here as various data structures 18430519Ssam * aren't setup for doing a read in a straightforward 18530519Ssam * manner. Instead just probe for the drive and leave 18630519Ssam * the pack label stuff to the attach routine. 18725675Ssam */ 18834076Skarels /* ARGSUSED */ 18934076Skarels vdslave(vi, vdaddr) 19025675Ssam register struct vba_device *vi; 19130519Ssam struct vddevice *vdaddr; 19225675Ssam { 19330519Ssam register struct disklabel *lp = &dklabel[vi->ui_unit]; 19432211Skarels register struct dksoftc *dk = &dksoftc[vi->ui_unit]; 19530519Ssam struct vdsoftc *vd = &vdsoftc[vi->ui_ctlr]; 19624004Ssam 19730519Ssam if ((vd->vd_flags&VD_INIT) == 0) { 19830756Skarels printf("vd%d: %s controller%s\n", vi->ui_ctlr, 19930756Skarels vd->vd_type == VDTYPE_VDDC ? "VDDC" : "SMDE", 20030756Skarels (vd->vd_flags & VD_SCATGATH) ? " with scatter-gather" : ""); 20130519Ssam vd->vd_flags |= VD_INIT; 20225675Ssam } 20330519Ssam 20425675Ssam /* 20530519Ssam * Initialize label enough to do a reset on 20630519Ssam * the drive. The remainder of the default 20730519Ssam * label values will be filled in in vdinit 20830519Ssam * at attach time. 20925675Ssam */ 21032211Skarels if (vd->vd_type == VDTYPE_SMDE) 21132211Skarels lp->d_secsize = VD_MAXSECSIZE; 21232211Skarels else 21332211Skarels lp->d_secsize = VDDC_SECSIZE; 214*34396Skarels lp->d_nsectors = 66; /* only used on smd-e */ 21534076Skarels lp->d_ntracks = 23; 216*34396Skarels lp->d_ncylinders = 850; 217*34396Skarels lp->d_secpercyl = 66*23; 21824004Ssam 21930519Ssam /* 22030519Ssam * Initialize invariant portion of 22130519Ssam * dcb used for overlapped seeks. 22230519Ssam */ 22330519Ssam dk->dk_dcb.opcode = VDOP_SEEK; 22430519Ssam dk->dk_dcb.intflg = DCBINT_NONE | DCBINT_PBA; 22530519Ssam dk->dk_dcb.devselect = vi->ui_slave; 22630756Skarels dk->dk_dcb.trailcnt = sizeof (struct trseek) / sizeof (long); 22730519Ssam dk->dk_dcb.trail.sktrail.skaddr.sector = 0; 22830519Ssam dk->dk_dcbphys = vtoph((struct proc *)0, (unsigned)&dk->dk_dcb); 22932211Skarels #ifndef SECSIZE 23032211Skarels vd_setsecsize(dk, lp); 23132211Skarels #endif 23232211Skarels return (vdreset_drive(vi)); 23332211Skarels } 23432211Skarels 23532211Skarels vdattach(vi) 23632211Skarels register struct vba_device *vi; 23732211Skarels { 23832211Skarels register int unit = vi->ui_unit; 23932211Skarels register struct disklabel *lp = &dklabel[unit]; 24032211Skarels 24130601Skarels /* 24230601Skarels * Try to initialize device and read pack label. 24330601Skarels */ 24430601Skarels if (vdinit(vdminor(unit, 0), 0) != 0) { 24530601Skarels printf(": unknown drive type"); 24630601Skarels return; 24730601Skarels } 24832211Skarels if (dksoftc[unit].dk_state == OPEN) 24932211Skarels printf(": %s <secsize %d, ntrak %d, ncyl %d, nsec %d>", 25032211Skarels lp->d_typename, lp->d_secsize, 25132211Skarels lp->d_ntracks, lp->d_ncylinders, lp->d_nsectors); 25230519Ssam /* 25330519Ssam * (60 / rpm) / (sectors per track * (bytes per sector / 2)) 25430519Ssam */ 25530519Ssam if (vi->ui_dk >= 0) 25630519Ssam dk_mspw[vi->ui_dk] = 120.0 / 25730519Ssam (lp->d_rpm * lp->d_nsectors * lp->d_secsize); 25830519Ssam #ifdef notyet 25930573Skarels addswap(makedev(VDMAJOR, vdminor(unit, 0)), lp); 26030519Ssam #endif 26124004Ssam } 26224004Ssam 26330756Skarels vdopen(dev, flags, fmt) 26430519Ssam dev_t dev; 26530756Skarels int flags, fmt; 26624004Ssam { 26730519Ssam register unit = vdunit(dev); 26830519Ssam register struct disklabel *lp; 26930519Ssam register struct dksoftc *dk; 27030519Ssam register struct partition *pp; 27130519Ssam struct vba_device *vi; 27230756Skarels int s, error, part = vdpart(dev), mask = 1 << part; 27330519Ssam daddr_t start, end; 27424004Ssam 27530519Ssam if (unit >= NDK || (vi = vddinfo[unit]) == 0 || vi->ui_alive == 0) 27630519Ssam return (ENXIO); 27730519Ssam lp = &dklabel[unit]; 27830519Ssam dk = &dksoftc[unit]; 27930519Ssam 28030519Ssam s = spl7(); 28130519Ssam while (dk->dk_state != OPEN && dk->dk_state != OPENRAW && 28230519Ssam dk->dk_state != CLOSED) 28330519Ssam sleep((caddr_t)dk, PZERO+1); 28430519Ssam splx(s); 28530519Ssam if (dk->dk_state != OPEN && dk->dk_state != OPENRAW) 28630519Ssam if (error = vdinit(dev, flags)) 28730519Ssam return (error); 28830573Skarels 28930573Skarels if (vdwstart == 0) { 29030573Skarels timeout(vdwatch, (caddr_t)0, hz); 29130573Skarels vdwstart++; 29230573Skarels } 29330519Ssam /* 29430519Ssam * Warn if a partion is opened 29530519Ssam * that overlaps another partition which is open 29630519Ssam * unless one is the "raw" partition (whole disk). 29730519Ssam */ 29832211Skarels #define RAWPART 8 /* 'x' partition */ /* XXX */ 29932576Skarels if ((dk->dk_openpart & mask) == 0 && part != RAWPART) { 30030519Ssam pp = &lp->d_partitions[part]; 30130519Ssam start = pp->p_offset; 30230519Ssam end = pp->p_offset + pp->p_size; 30330519Ssam for (pp = lp->d_partitions; 30430519Ssam pp < &lp->d_partitions[lp->d_npartitions]; pp++) { 30530519Ssam if (pp->p_offset + pp->p_size <= start || 30630519Ssam pp->p_offset >= end) 30730519Ssam continue; 30830519Ssam if (pp - lp->d_partitions == RAWPART) 30930519Ssam continue; 31030519Ssam if (dk->dk_openpart & (1 << (pp - lp->d_partitions))) 31130519Ssam log(LOG_WARNING, 31230519Ssam "dk%d%c: overlaps open partition (%c)\n", 31330519Ssam unit, part + 'a', 31430519Ssam pp - lp->d_partitions + 'a'); 31530519Ssam } 31624004Ssam } 31730519Ssam if (part >= lp->d_npartitions) 31830519Ssam return (ENXIO); 31930756Skarels dk->dk_openpart |= mask; 32030756Skarels switch (fmt) { 32130756Skarels case S_IFCHR: 32230756Skarels dk->dk_copenpart |= mask; 32330756Skarels break; 32430756Skarels case S_IFBLK: 32530756Skarels dk->dk_bopenpart |= mask; 32630756Skarels break; 32730756Skarels } 32830519Ssam return (0); 32925675Ssam } 33024004Ssam 33130756Skarels vdclose(dev, flags, fmt) 33230519Ssam dev_t dev; 33330756Skarels int flags, fmt; 33424004Ssam { 33530519Ssam register int unit = vdunit(dev); 33630519Ssam register struct dksoftc *dk = &dksoftc[unit]; 33730756Skarels int part = vdpart(dev), mask = 1 << part; 33824004Ssam 33930756Skarels switch (fmt) { 34030756Skarels case S_IFCHR: 34130756Skarels dk->dk_copenpart &= ~mask; 34230756Skarels break; 34330756Skarels case S_IFBLK: 34430756Skarels dk->dk_bopenpart &= ~mask; 34530756Skarels break; 34630756Skarels } 34730756Skarels if (((dk->dk_copenpart | dk->dk_bopenpart) & mask) == 0) 34830756Skarels dk->dk_openpart &= ~mask; 34930519Ssam /* 35030519Ssam * Should wait for i/o to complete on this partition 35130519Ssam * even if others are open, but wait for work on blkflush(). 35230519Ssam */ 35330519Ssam if (dk->dk_openpart == 0) { 35430573Skarels int s = spl7(); 35530573Skarels while (dkutab[unit].b_actf) 35630573Skarels sleep((caddr_t)dk, PZERO-1); 35730519Ssam splx(s); 35830519Ssam dk->dk_state = CLOSED; 35934076Skarels dk->dk_wlabel = 0; 36024004Ssam } 36130756Skarels return (0); 36225675Ssam } 36324004Ssam 36430519Ssam vdinit(dev, flags) 36530519Ssam dev_t dev; 36630519Ssam int flags; 36725675Ssam { 36830519Ssam register struct disklabel *lp; 36930519Ssam register struct dksoftc *dk; 37030519Ssam struct vba_device *vi; 37130519Ssam int unit = vdunit(dev), error = 0; 37230756Skarels char *msg, *readdisklabel(); 37330519Ssam extern int cold; 37425675Ssam 37530519Ssam dk = &dksoftc[unit]; 37630519Ssam if (flags & O_NDELAY) { 37730519Ssam dk->dk_state = OPENRAW; 37830756Skarels return; 37930519Ssam } 38030519Ssam dk->dk_state = RDLABEL; 38130519Ssam lp = &dklabel[unit]; 38230519Ssam vi = vddinfo[unit]; 38330756Skarels if (msg = readdisklabel(dev, vdstrategy, lp)) { 38434076Skarels if (cold) { 38530601Skarels printf(": %s", msg); 38634076Skarels dk->dk_state = CLOSED; 38734076Skarels } else { 38832211Skarels log(LOG_ERR, "dk%d: %s\n", unit, msg); 38934076Skarels dk->dk_state = OPENRAW; 39034076Skarels } 39130519Ssam #ifdef COMPAT_42 39234076Skarels if (vdmaptype(vi, lp)) 39330519Ssam dk->dk_state = OPEN; 39430519Ssam #endif 39530756Skarels } else { 39630756Skarels /* 39730756Skarels * Now that we have the label, configure 39830756Skarels * the correct drive parameters. 39930756Skarels */ 40032211Skarels if (vdreset_drive(vi)) 40132211Skarels dk->dk_state = OPEN; 40232211Skarels else { 40330756Skarels dk->dk_state = CLOSED; 40430756Skarels error = ENXIO; 40532211Skarels } 40625675Ssam } 40730756Skarels #ifndef SECSIZE 40832211Skarels vd_setsecsize(dk, lp); 40932211Skarels #endif 41030519Ssam wakeup((caddr_t)dk); 41130519Ssam return (error); 41224004Ssam } 41324004Ssam 41432211Skarels #ifndef SECSIZE 41532211Skarels vd_setsecsize(dk, lp) 41632211Skarels register struct dksoftc *dk; 41732211Skarels register struct disklabel *lp; 41832211Skarels { 41932211Skarels int mul; 42032211Skarels 42132211Skarels /* 42232211Skarels * Calculate scaling shift for mapping 42332211Skarels * DEV_BSIZE blocks to drive sectors. 42432211Skarels */ 42532211Skarels mul = DEV_BSIZE / lp->d_secsize; 42632211Skarels dk->dk_bshift = 0; 42732211Skarels while ((mul >>= 1) > 0) 42832211Skarels dk->dk_bshift++; 42932211Skarels } 43032211Skarels #endif SECSIZE 43132211Skarels 43225675Ssam /*ARGSUSED*/ 43330519Ssam vddgo(vm) 43430519Ssam struct vba_device *vm; 43524004Ssam { 43624004Ssam 43724004Ssam } 43824004Ssam 43924004Ssam vdstrategy(bp) 44025675Ssam register struct buf *bp; 44124004Ssam { 44230519Ssam register struct vba_device *vi; 44330519Ssam register struct disklabel *lp; 44430519Ssam register struct dksoftc *dk; 44530519Ssam register int unit; 44630573Skarels register daddr_t sn; 44730519Ssam struct buf *dp; 44830573Skarels daddr_t sz, maxsz; 44930519Ssam int part, s; 45024004Ssam 45130519Ssam unit = vdunit(bp->b_dev); 45232211Skarels if (unit >= NDK) { 45329954Skarels bp->b_error = ENXIO; 45425675Ssam goto bad; 45529954Skarels } 45630519Ssam vi = vddinfo[unit]; 45730519Ssam lp = &dklabel[unit]; 45830519Ssam if (vi == 0 || vi->ui_alive == 0) { 45930519Ssam bp->b_error = ENXIO; 46030519Ssam goto bad; 46130519Ssam } 46230519Ssam dk = &dksoftc[unit]; 46330519Ssam if (dk->dk_state < OPEN) 46430519Ssam goto q; 46534076Skarels if (dk->dk_state != OPEN && (bp->b_flags & B_READ) == 0) { 46634076Skarels bp->b_error = EROFS; 46734076Skarels goto bad; 46834076Skarels } 46930519Ssam part = vdpart(bp->b_dev); 47030519Ssam if ((dk->dk_openpart & (1 << part)) == 0) { 47130519Ssam bp->b_error = ENODEV; 47230519Ssam goto bad; 47330519Ssam } 47432211Skarels sz = (bp->b_bcount + lp->d_secsize - 1) / lp->d_secsize; 47530519Ssam maxsz = lp->d_partitions[part].p_size; 47630756Skarels #ifndef SECSIZE 47730756Skarels sn = bp->b_blkno << dk->dk_bshift; 47830756Skarels #else SECSIZE 47930573Skarels sn = bp->b_blkno; 48030756Skarels #endif SECSIZE 48134076Skarels if (sn + lp->d_partitions[part].p_offset <= LABELSECTOR && 48234076Skarels #if LABELSECTOR != 0 48334076Skarels sn + lp->d_partitions[part].p_offset + sz > LABELSECTOR && 48434076Skarels #endif 48534076Skarels (bp->b_flags & B_READ) == 0 && dk->dk_wlabel == 0) { 48634076Skarels bp->b_error = EROFS; 48734076Skarels goto bad; 48834076Skarels } 48930519Ssam if (sn < 0 || sn + sz > maxsz) { 49030519Ssam if (sn == maxsz) { 49129954Skarels bp->b_resid = bp->b_bcount; 49229954Skarels goto done; 49329954Skarels } 49430756Skarels sz = maxsz - sn; 49530573Skarels if (sz <= 0) { 49630573Skarels bp->b_error = EINVAL; 49730573Skarels goto bad; 49830573Skarels } 49930573Skarels bp->b_bcount = sz * lp->d_secsize; 50025675Ssam } 50130519Ssam bp->b_cylin = (sn + lp->d_partitions[part].p_offset) / lp->d_secpercyl; 50230756Skarels #ifdef SECSIZE 50330756Skarels if (bp->b_blksize != lp->d_secsize && (bp->b_flags & B_PGIN) == 0) 50430756Skarels panic("vdstrat blksize"); 50530756Skarels #endif SECSIZE 50630519Ssam q: 50725675Ssam s = spl7(); 50830519Ssam dp = &dkutab[vi->ui_unit]; 50930519Ssam disksort(dp, bp); 51030519Ssam if (!dp->b_active) { 51130519Ssam (void) vdustart(vi); 51230573Skarels if (!vi->ui_mi->um_tab.b_active) 51330519Ssam vdstart(vi->ui_mi); 51424004Ssam } 51530519Ssam splx(s); 51624004Ssam return; 51725675Ssam bad: 51829954Skarels bp->b_flags |= B_ERROR; 51929954Skarels done: 52030519Ssam biodone(bp); 52130519Ssam return; 52224004Ssam } 52324004Ssam 52430519Ssam vdustart(vi) 52530519Ssam register struct vba_device *vi; 52624004Ssam { 52730519Ssam register struct buf *bp, *dp; 52830519Ssam register struct vba_ctlr *vm; 52930519Ssam register int unit = vi->ui_unit; 53030519Ssam register struct dksoftc *dk; 53130519Ssam register struct vdsoftc *vd; 53230519Ssam struct disklabel *lp; 53324004Ssam 53430519Ssam dp = &dkutab[unit]; 53530519Ssam /* 53630519Ssam * If queue empty, nothing to do. 53730519Ssam */ 53830519Ssam if ((bp = dp->b_actf) == NULL) 53930519Ssam return; 54030519Ssam /* 54130574Skarels * If drive is off-cylinder and controller supports seeks, 54230574Skarels * place drive on seek queue for controller. 54330574Skarels * Otherwise, place on transfer queue. 54430519Ssam */ 54530519Ssam vd = &vdsoftc[vi->ui_ctlr]; 54630519Ssam dk = &dksoftc[unit]; 54730574Skarels vm = vi->ui_mi; 54830519Ssam if (bp->b_cylin != dk->dk_curcyl && vd->vd_flags&VD_DOSEEKS) { 54930519Ssam lp = &dklabel[unit]; 55030574Skarels bp->b_track = (bp->b_blkno % lp->d_secpercyl) / lp->d_nsectors; 55130574Skarels if (vm->um_tab.b_seekf == NULL) 55230574Skarels vm->um_tab.b_seekf = dp; 55330574Skarels else 55430574Skarels vm->um_tab.b_seekl->b_forw = dp; 55530574Skarels vm->um_tab.b_seekl = dp; 55630574Skarels } else { 55730574Skarels if (vm->um_tab.b_actf == NULL) 55830574Skarels vm->um_tab.b_actf = dp; 55930574Skarels else 56030574Skarels vm->um_tab.b_actl->b_forw = dp; 56130574Skarels vm->um_tab.b_actl = dp; 56230519Ssam } 56330573Skarels dp->b_forw = NULL; 56430573Skarels dp->b_active++; 56525675Ssam } 56625675Ssam 56725675Ssam /* 56830519Ssam * Start next transfer on a controller. 56930574Skarels * There are two queues of drives, the first on-cylinder 57030574Skarels * and the second off-cylinder from their next transfers. 57130574Skarels * Perform the first transfer for the first drive on the on-cylinder 57230574Skarels * queue, if any, otherwise the first transfer for the first drive 57330574Skarels * on the second queue. Initiate seeks on remaining drives on the 57430574Skarels * off-cylinder queue, then move them all to the on-cylinder queue. 57525675Ssam */ 57630519Ssam vdstart(vm) 57730519Ssam register struct vba_ctlr *vm; 57825675Ssam { 57925675Ssam register struct buf *bp; 58030519Ssam register struct vba_device *vi; 58130519Ssam register struct vdsoftc *vd; 58230519Ssam register struct dksoftc *dk; 58330519Ssam register struct disklabel *lp; 58430519Ssam register struct dcb **dcbp; 58530519Ssam struct mdcb *mdcb; 58630519Ssam struct buf *dp; 58730519Ssam int sn, tn; 58825675Ssam 58930519Ssam loop: 59030519Ssam /* 59130519Ssam * Pull a request off the controller queue. 59230519Ssam */ 59330574Skarels if ((dp = vm->um_tab.b_actf) == NULL && 59430574Skarels (dp = vm->um_tab.b_seekf) == NULL) 59530519Ssam return; 59630519Ssam if ((bp = dp->b_actf) == NULL) { 59730601Skarels if (dp == vm->um_tab.b_actf) 59830601Skarels vm->um_tab.b_actf = dp->b_forw; 59930601Skarels else 60030601Skarels vm->um_tab.b_seekf = dp->b_forw; 60130519Ssam goto loop; 60230519Ssam } 60325675Ssam 60424004Ssam /* 60530519Ssam * Mark controller busy, and determine 60630519Ssam * destination of this request. 60724004Ssam */ 60830519Ssam vm->um_tab.b_active++; 60930519Ssam vi = vddinfo[vdunit(bp->b_dev)]; 61030519Ssam dk = &dksoftc[vi->ui_unit]; 61130756Skarels #ifndef SECSIZE 61230756Skarels sn = bp->b_blkno << dk->dk_bshift; 61330756Skarels #else SECSIZE 61430573Skarels sn = bp->b_blkno; 61530756Skarels #endif SECSIZE 61630519Ssam lp = &dklabel[vi->ui_unit]; 61730519Ssam sn %= lp->d_secpercyl; 61830519Ssam tn = sn / lp->d_nsectors; 61930519Ssam sn %= lp->d_nsectors; 62030519Ssam 62130519Ssam /* 62230519Ssam * Construct dcb for read/write command. 62330519Ssam */ 62430519Ssam vd = &vdsoftc[vm->um_ctlr]; 62530519Ssam vd->vd_dcb.intflg = DCBINT_DONE; 62632211Skarels vd->vd_dcb.devselect = dk->dk_dcb.devselect; 62730519Ssam vd->vd_dcb.operrsta = 0; 62830519Ssam vd->vd_dcb.nxtdcb = (struct dcb *)0; /* end of chain */ 62930519Ssam vd->vd_dcb.trail.rwtrail.disk.cylinder = bp->b_cylin; 63030519Ssam vd->vd_dcb.trail.rwtrail.disk.track = tn; 63130519Ssam vd->vd_dcb.trail.rwtrail.disk.sector = sn; 63230574Skarels dk->dk_curcyl = bp->b_cylin; 63330574Skarels bp->b_track = 0; /* init overloaded field */ 63430756Skarels vd->vd_dcb.trailcnt = sizeof (struct trrw) / sizeof (long); 635*34396Skarels if (bp->b_flags & B_FORMAT) 636*34396Skarels vd->vd_dcb.opcode = dk->dk_op; 637*34396Skarels else if (vd->vd_flags & VD_SCATGATH && 638*34396Skarels ((int)bp->b_un.b_addr & (sizeof(long) - 1)) == 0) 63930756Skarels vd->vd_dcb.opcode = (bp->b_flags & B_READ)? VDOP_RAS : VDOP_GAW; 640*34396Skarels else 64130756Skarels vd->vd_dcb.opcode = (bp->b_flags & B_READ)? VDOP_RD : VDOP_WD; 642*34396Skarels 643*34396Skarels switch (vd->vd_dcb.opcode) { 644*34396Skarels case VDOP_FSECT: 645*34396Skarels vd->vd_dcb.trailcnt = sizeof (struct trrw) / sizeof (long); 646*34396Skarels vd->vd_dcb.trail.fmtrail.nsectors = bp->b_bcount / 647*34396Skarels lp->d_secsize; 648*34396Skarels vd->vd_dcb.trail.fmtrail.hdr = *(dskadr *)&dk->dk_althdr; 649*34396Skarels vd->vd_dcb.trail.rwtrail.disk.cylinder |= dk->dk_fmtflags; 650*34396Skarels goto setupaddr; 651*34396Skarels 652*34396Skarels case VDOP_RDRAW: 653*34396Skarels case VDOP_RD: 654*34396Skarels case VDOP_WD: 655*34396Skarels vd->vd_dcb.trail.rwtrail.wcount = (bp->b_bcount+1) >> 1; 656*34396Skarels setupaddr: 65730756Skarels vd->vd_dcb.trail.rwtrail.memadr = 65830756Skarels vbasetup(bp, &vd->vd_rbuf, lp->d_secsize); 659*34396Skarels break; 660*34396Skarels 661*34396Skarels case VDOP_RAS: 662*34396Skarels case VDOP_GAW: 663*34396Skarels vd->vd_dcb.trailcnt += vba_sgsetup(bp, &vd->vd_rbuf, 664*34396Skarels &vd->vd_dcb.trail.sgtrail); 665*34396Skarels break; 66630756Skarels } 66730574Skarels if (vi->ui_dk >= 0) { 66830574Skarels dk_busy |= 1<<vi->ui_dk; 66930574Skarels dk_xfer[vi->ui_dk]++; 67030574Skarels dk_wds[vi->ui_dk] += bp->b_bcount>>6; 67130574Skarels } 67230519Ssam 67330519Ssam /* 67430519Ssam * Look for any seeks to be performed on other drives on this 67530519Ssam * controller. If overlapped seeks exist, insert seek commands 67630519Ssam * on the controller's command queue before the transfer. 67730519Ssam */ 67830519Ssam dcbp = &vd->vd_mdcb.mdcb_head; 67930519Ssam 68030574Skarels if (dp == vm->um_tab.b_seekf) 68130574Skarels dp = dp->b_forw; 68230574Skarels else 68330574Skarels dp = vm->um_tab.b_seekf; 68430574Skarels for (; dp != NULL; dp = dp->b_forw) { 68530574Skarels if ((bp = dp->b_actf) == NULL) 68630574Skarels continue; 68730574Skarels vi = vddinfo[vdunit(bp->b_dev)]; 68830574Skarels dk = &dksoftc[vi->ui_unit]; 68930519Ssam dk->dk_curcyl = bp->b_cylin; 69030574Skarels if (vi->ui_dk >= 0) 69130574Skarels dk_seek[vi->ui_dk]++; 69230574Skarels dk->dk_dcb.operrsta = 0; 69330574Skarels dk->dk_dcb.trail.sktrail.skaddr.cylinder = bp->b_cylin; 69430574Skarels dk->dk_dcb.trail.sktrail.skaddr.track = bp->b_track; 69530574Skarels *dcbp = (struct dcb *)dk->dk_dcbphys; 69630574Skarels dcbp = &dk->dk_dcb.nxtdcb; 69724004Ssam } 69830519Ssam *dcbp = (struct dcb *)vd->vd_dcbphys; 69930574Skarels if (vm->um_tab.b_actf) 70030574Skarels vm->um_tab.b_actl->b_forw = vm->um_tab.b_seekf; 70130574Skarels else 70230574Skarels vm->um_tab.b_actf = vm->um_tab.b_seekf; 70330601Skarels if (vm->um_tab.b_seekf) 70430601Skarels vm->um_tab.b_actl = vm->um_tab.b_seekl; 70530574Skarels vm->um_tab.b_seekf = 0; 70624004Ssam 70730519Ssam /* 70830519Ssam * Initiate operation. 70930519Ssam */ 71030519Ssam vd->vd_mdcb.mdcb_status = 0; 71130519Ssam VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type); 71224004Ssam } 71324004Ssam 71430519Ssam #define DONTCARE (DCBS_DSE|DCBS_DSL|DCBS_TOP|DCBS_TOM|DCBS_FAIL|DCBS_DONE) 71524004Ssam /* 71624004Ssam * Handle a disk interrupt. 71724004Ssam */ 71825675Ssam vdintr(ctlr) 71930519Ssam register ctlr; 72024004Ssam { 72130519Ssam register struct buf *bp, *dp; 72230519Ssam register struct vba_ctlr *vm = vdminfo[ctlr]; 72330519Ssam register struct vba_device *vi; 72430519Ssam register struct vdsoftc *vd = &vdsoftc[ctlr]; 72530519Ssam register status; 726*34396Skarels int ecode, timedout; 72730573Skarels struct dksoftc *dk; 72824004Ssam 72930519Ssam if (!vm->um_tab.b_active) { 73025675Ssam printf("vd%d: stray interrupt\n", ctlr); 73124004Ssam return; 73224004Ssam } 73325675Ssam /* 73430519Ssam * Get device and block structures, and a pointer 73530519Ssam * to the vba_device for the drive. 73625675Ssam */ 73730519Ssam dp = vm->um_tab.b_actf; 73830519Ssam bp = dp->b_actf; 73930519Ssam vi = vddinfo[vdunit(bp->b_dev)]; 74030574Skarels if (vi->ui_dk >= 0) 74130574Skarels dk_busy &= ~(1<<vi->ui_dk); 742*34396Skarels timedout = (vd->vd_wticks >= VDMAXTIME); 74330519Ssam /* 74430519Ssam * Check for and process errors on 74530519Ssam * either the drive or the controller. 74630519Ssam */ 74730519Ssam uncache(&vd->vd_dcb.operrsta); 74830519Ssam status = vd->vd_dcb.operrsta; 749*34396Skarels if (bp->b_flags & B_FORMAT) { 750*34396Skarels dk->dk_operrsta = status; 751*34396Skarels uncache(&vd->vd_dcb.err_code); 752*34396Skarels dk->dk_ecode = vd->vd_dcb.err_code; 753*34396Skarels } 754*34396Skarels if (status & VDERR_HARD || timedout) { 75530601Skarels if (vd->vd_type == VDTYPE_SMDE) { 75630601Skarels uncache(&vd->vd_dcb.err_code); 75730601Skarels ecode = vd->vd_dcb.err_code; 75830601Skarels } 75930519Ssam if (status & DCBS_WPT) { 76030519Ssam /* 76130519Ssam * Give up on write locked devices immediately. 76230519Ssam */ 76330573Skarels printf("dk%d: write locked\n", vi->ui_unit); 76430519Ssam bp->b_flags |= B_ERROR; 765*34396Skarels } else if (status & VDERR_RETRY || timedout) { 76632211Skarels int endline = 1; 76732211Skarels 768*34396Skarels if (status & VDERR_CTLR || timedout) { 769*34396Skarels vdharderr("controller err", 770*34396Skarels vd, bp, &vd->vd_dcb); 771*34396Skarels printf("; resetting controller..."); 772*34396Skarels vdreset_ctlr(vm); 773*34396Skarels } else if (status & VDERR_DRIVE) { 774*34396Skarels vdharderr("drive err", vd, bp, &vd->vd_dcb); 775*34396Skarels printf("; resetting drive..."); 77630519Ssam if (!vdreset_drive(vi)) 77730519Ssam vi->ui_alive = 0; 77832211Skarels } else 77932211Skarels endline = 0; 78030519Ssam /* 78130519Ssam * Retry transfer once, unless reset failed. 78230519Ssam */ 783*34396Skarels if (!vi->ui_alive || dp->b_errcnt++ >= 2 || 784*34396Skarels bp->b_flags & B_FORMAT) { 78532211Skarels if (endline) 78632211Skarels printf("\n"); 78730519Ssam goto hard; 78832211Skarels } 78932211Skarels 79032211Skarels if (endline) 79132211Skarels printf(" retrying\n"); 79230519Ssam vm->um_tab.b_active = 0; /* force retry */ 79330519Ssam } else { 79430519Ssam hard: 79530519Ssam bp->b_flags |= B_ERROR; 796*34396Skarels vdharderr("hard error", vd, bp, &vd->vd_dcb); 79730519Ssam printf("\n"); 79830519Ssam } 79930519Ssam } else if (status & DCBS_SOFT) 80030519Ssam vdsofterr(vd, bp, &vd->vd_dcb); 801*34396Skarels vd->vd_wticks = 0; 80230519Ssam if (vm->um_tab.b_active) { 80330519Ssam vm->um_tab.b_active = 0; 80430519Ssam vm->um_tab.b_actf = dp->b_forw; 80530519Ssam dp->b_active = 0; 80630519Ssam dp->b_errcnt = 0; 80730519Ssam dp->b_actf = bp->av_forw; 80830519Ssam bp->b_resid = 0; 80930601Skarels vbadone(bp, &vd->vd_rbuf); 81030519Ssam biodone(bp); 81130370Skarels /* 81230519Ssam * If this unit has more work to do, 81330519Ssam * then start it up right away. 81430370Skarels */ 81530519Ssam if (dp->b_actf) 81630519Ssam vdustart(vi); 81730573Skarels else if ((dk = &dksoftc[vi->ui_unit])->dk_openpart == 0) 81830573Skarels wakeup((caddr_t)dk); 81924004Ssam } 82025675Ssam /* 82130519Ssam * If there are devices ready to 82230519Ssam * transfer, start the controller. 82325675Ssam */ 82430601Skarels if (vm->um_tab.b_actf || vm->um_tab.b_seekf) 82530519Ssam vdstart(vm); 82624004Ssam } 82724004Ssam 828*34396Skarels vdharderr(what, vd, bp, dcb) 829*34396Skarels char *what; 830*34396Skarels struct vdsoftc *vd; 831*34396Skarels register struct buf *bp; 832*34396Skarels register struct dcb *dcb; 833*34396Skarels { 834*34396Skarels int unit = vdunit(bp->b_dev), status = dcb->operrsta; 835*34396Skarels int part = vdpart(bp->b_dev); 836*34396Skarels register struct disklabel *lp = &dklabel[unit]; 837*34396Skarels char partname = 'a' + part; 838*34396Skarels int sn; 839*34396Skarels 840*34396Skarels if (vd->vd_wticks < VDMAXTIME) 841*34396Skarels status &= ~DONTCARE; 842*34396Skarels /* generic 843*34396Skarels sn = bp->b_blkno + lp->d_partitions[part].p_offset; 844*34396Skarels */ 845*34396Skarels sn = ((dcb->err_cyl & 0xfff) * lp->d_ntracks + dcb->err_trk) 846*34396Skarels * lp->d_nsectors + dcb->err_sec; 847*34396Skarels printf("dk%d%c: %s bn [%d-%d) (sn %d), status %b", 848*34396Skarels unit, partname, what, bp->b_blkno, 849*34396Skarels bp->b_blkno + (bp->b_bcount - 1)/DEV_BSIZE, sn, status, VDERRBITS); 850*34396Skarels if (vd->vd_type == VDTYPE_SMDE) 851*34396Skarels printf(" ecode %x", dcb->err_code); 852*34396Skarels printf("\n(error sec %d trk %d cyl %d wcount %d)", dcb->err_sec, 853*34396Skarels dcb->err_trk, dcb->err_cyl, dcb->err_wcount); 854*34396Skarels } 855*34396Skarels 85630519Ssam vdsofterr(vd, bp, dcb) 85730519Ssam struct vdsoftc *vd; 85825675Ssam register struct buf *bp; 85930519Ssam register struct dcb *dcb; 86025675Ssam { 86130519Ssam int unit = vdunit(bp->b_dev), status = dcb->operrsta; 862*34396Skarels int part = vdpart(bp->b_dev); 863*34396Skarels char partname = 'a' + part; 864*34396Skarels int sn = (bp->b_blkno << dksoftc[unit].dk_bshift) + 865*34396Skarels dklabel[unit].d_partitions[part].p_offset; 86625675Ssam 86732211Skarels if (status != (DCBS_CCD|DCBS_SOFT|DCBS_ERR|DCBS_DONE)) 868*34396Skarels log(LOG_WARNING, 869*34396Skarels "dk%d%c: soft error sn %d bn %d, status %b ecode %x\n", 870*34396Skarels unit, partname, sn, bp->b_blkno, status, VDERRBITS, 871*34396Skarels dcb->err_code); 87232211Skarels else 873*34396Skarels log(LOG_WARNING, "dk%d%c: soft ecc sn %d bn %d\n", 874*34396Skarels unit, part, sn, bp->b_blkno); 87525675Ssam } 87625675Ssam 87730519Ssam vdioctl(dev, cmd, data, flag) 87825675Ssam dev_t dev; 87930519Ssam int cmd; 88030519Ssam caddr_t data; 88130519Ssam int flag; 88224004Ssam { 88332576Skarels register int unit = vdunit(dev); 88430519Ssam register struct disklabel *lp = &dklabel[unit]; 88534076Skarels register struct dksoftc *dk = &dksoftc[unit]; 886*34396Skarels int error = 0, wlab, vdformat(); 88724004Ssam 88830519Ssam switch (cmd) { 88930519Ssam 89030519Ssam case DIOCGDINFO: 89130519Ssam *(struct disklabel *)data = *lp; 89230519Ssam break; 89330519Ssam 89430573Skarels case DIOCGPART: 89530573Skarels ((struct partinfo *)data)->disklab = lp; 89630573Skarels ((struct partinfo *)data)->part = 89730573Skarels &lp->d_partitions[vdpart(dev)]; 89830519Ssam break; 89930519Ssam 90030519Ssam case DIOCSDINFO: 90130519Ssam if ((flag & FWRITE) == 0) 90230519Ssam error = EBADF; 90330519Ssam else 90432576Skarels error = setdisklabel(lp, (struct disklabel *)data, 90534076Skarels (dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart); 906*34396Skarels if (error == 0 && dk->dk_state == OPENRAW && 907*34396Skarels vdreset_drive(vddinfo[unit])) 90834076Skarels dk->dk_state = OPEN; 90930519Ssam break; 91030519Ssam 91134076Skarels case DIOCWLABEL: 91234076Skarels if ((flag & FWRITE) == 0) 91334076Skarels error = EBADF; 91434076Skarels else 91534076Skarels dk->dk_wlabel = *(int *)data; 91634076Skarels break; 91734076Skarels 91832576Skarels case DIOCWDINFO: 91934076Skarels /* simulate opening partition 0 so write succeeds */ 92034076Skarels dk->dk_openpart |= (1 << 0); /* XXX */ 92134076Skarels wlab = dk->dk_wlabel; 92234076Skarels dk->dk_wlabel = 1; 92332576Skarels if ((flag & FWRITE) == 0) 92430519Ssam error = EBADF; 92532576Skarels else if ((error = setdisklabel(lp, (struct disklabel *)data, 92634076Skarels (dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart)) == 0) { 92734076Skarels dk->dk_state = OPEN; 92832576Skarels error = writedisklabel(dev, vdstrategy, lp); 92934076Skarels } 93034076Skarels dk->dk_openpart = dk->dk_copenpart | dk->dk_bopenpart; 93134076Skarels dk->dk_wlabel = wlab; 93230519Ssam break; 93330519Ssam 934*34396Skarels case DIOCWFORMAT: 935*34396Skarels { 936*34396Skarels register struct format_op *fop; 937*34396Skarels struct uio auio; 938*34396Skarels struct iovec aiov; 939*34396Skarels 940*34396Skarels if ((flag & FWRITE) == 0) { 941*34396Skarels error = EBADF; 942*34396Skarels break; 943*34396Skarels } 944*34396Skarels fop = (struct format_op *)data; 945*34396Skarels aiov.iov_base = fop->df_buf; 946*34396Skarels aiov.iov_len = fop->df_count; 947*34396Skarels auio.uio_iov = &aiov; 948*34396Skarels auio.uio_iovcnt = 1; 949*34396Skarels auio.uio_resid = fop->df_count; 950*34396Skarels auio.uio_segflg = UIO_USERSPACE; 951*34396Skarels auio.uio_offset = fop->df_startblk * lp->d_secsize; 952*34396Skarels dk->dk_operrsta = fop->dk_operrsta; 953*34396Skarels dk->dk_ecode = fop->dk_ecode; 954*34396Skarels /* 955*34396Skarels * Don't return errors, as the format op won't get copied 956*34396Skarels * out if we return nonzero. Callers must check the returned 957*34396Skarels * count. 958*34396Skarels */ 959*34396Skarels (void) physio(vdformat, (struct buf *)NULL, dev, 960*34396Skarels (cmd == DIOCWFORMAT ? B_WRITE : B_READ), minphys, &auio); 961*34396Skarels fop->df_count -= auio.uio_resid; 962*34396Skarels fop->dk_operrsta = dk->dk_operrsta; 963*34396Skarels fop->dk_ecode = dk->dk_ecode; 964*34396Skarels break; 965*34396Skarels } 966*34396Skarels 96730519Ssam default: 96830519Ssam error = ENOTTY; 96930519Ssam break; 97024004Ssam } 97132606Skarels return (error); 97224004Ssam } 97324004Ssam 974*34396Skarels vdformat(bp) 975*34396Skarels struct buf *bp; 976*34396Skarels { 977*34396Skarels bp->b_flags |= B_FORMAT; 978*34396Skarels vdstrategy(bp); 979*34396Skarels } 980*34396Skarels 98125675Ssam /* 98230519Ssam * Watch for lost interrupts. 98325675Ssam */ 98430519Ssam vdwatch() 98530519Ssam { 98630519Ssam register struct vdsoftc *vd; 98730519Ssam register struct vba_ctlr *vm; 98825675Ssam register int ctlr, unit; 989*34396Skarels int s; 99030519Ssam 99130519Ssam timeout(vdwatch, (caddr_t)0, hz); 99230519Ssam for (ctlr = 0; ctlr < NVD; ctlr++) { 99330519Ssam vm = vdminfo[ctlr]; 99430519Ssam if (vm == 0 || vm->um_alive == 0) 99530519Ssam continue; 99630519Ssam vd = &vdsoftc[ctlr]; 997*34396Skarels s = spl7(); 998*34396Skarels if (vm->um_tab.b_active && vd->vd_wticks++ >= VDMAXTIME) { 99930519Ssam printf("vd%d: lost interrupt\n", ctlr); 1000*34396Skarels #ifdef maybe 1001*34396Skarels VDABORT((struct vddevice *)vm->um_addr, vd->vd_type); 1002*34396Skarels #endif 1003*34396Skarels vdintr(ctlr); 100430519Ssam } 1005*34396Skarels splx(s); 100630519Ssam } 100730519Ssam } 100830519Ssam 100930519Ssam #define DBSIZE 64 /* controller limit with 1K sectors */ 101030519Ssam /* 101130519Ssam * Crash dump. 101230519Ssam */ 101330519Ssam vddump(dev) 101430519Ssam dev_t dev; 101524004Ssam { 101630519Ssam register struct vba_device *vi; 101730519Ssam register struct vba_ctlr *vm; 101830519Ssam register struct disklabel *lp; 101930519Ssam register struct vdsoftc *vd; 102030519Ssam struct dksoftc *dk; 102130519Ssam int part, unit, num; 102230601Skarels u_long start; 102324004Ssam 102430519Ssam start = 0; 102530519Ssam unit = vdunit(dev); 102630519Ssam if (unit > NDK || (vi = vddinfo[unit]) == 0 || vi->ui_alive == 0) 102730519Ssam return (ENXIO); 102830519Ssam dk = &dksoftc[unit]; 102934076Skarels if (dk->dk_state != OPEN && dk->dk_state != OPENRAW && 103034076Skarels vdinit(vdminor(unit, 0), 0) != 0) 103130519Ssam return (ENXIO); 103230519Ssam lp = &dklabel[unit]; 103330519Ssam part = vdpart(dev); 103430519Ssam if (part >= lp->d_npartitions) 103530519Ssam return (ENXIO); 103632211Skarels vm = vi->ui_mi; 103730519Ssam vdreset_ctlr(vm); 103830519Ssam if (dumplo < 0) 103930519Ssam return (EINVAL); 104030519Ssam /* 104130756Skarels * Maxfree is in pages, dumplo is in DEV_BSIZE units. 104230519Ssam */ 104330519Ssam num = maxfree * (NBPG / lp->d_secsize); 104430756Skarels dumplo *= DEV_BSIZE / lp->d_secsize; 104530519Ssam if (dumplo + num >= lp->d_partitions[vdpart(dev)].p_size) 104630519Ssam num = lp->d_partitions[vdpart(dev)].p_size - dumplo; 104730519Ssam vd = &vdsoftc[vm->um_ctlr]; 104830519Ssam vd->vd_dcb.intflg = DCBINT_NONE; 104930519Ssam vd->vd_dcb.opcode = VDOP_WD; 105032211Skarels vd->vd_dcb.devselect = dk->dk_dcb.devselect; 105130756Skarels vd->vd_dcb.trailcnt = sizeof (struct trrw) / sizeof (long); 105230519Ssam while (num > 0) { 105330519Ssam int nsec, cn, sn, tn; 105430519Ssam 105530519Ssam nsec = MIN(num, DBSIZE); 105630601Skarels sn = dumplo + start / lp->d_secsize; 105730519Ssam cn = (sn + lp->d_partitions[vdpart(dev)].p_offset) / 105830519Ssam lp->d_secpercyl; 105930519Ssam sn %= lp->d_secpercyl; 106030519Ssam tn = sn / lp->d_nsectors; 106130519Ssam sn %= lp->d_nsectors; 106230519Ssam vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys; 106330519Ssam vd->vd_dcb.trail.rwtrail.memadr = start; 106430519Ssam vd->vd_dcb.trail.rwtrail.wcount = (nsec * lp->d_secsize) >> 1; 106530519Ssam vd->vd_dcb.trail.rwtrail.disk.cylinder = cn; 106630519Ssam vd->vd_dcb.trail.rwtrail.disk.track = tn; 106730519Ssam vd->vd_dcb.trail.rwtrail.disk.sector = sn; 106830519Ssam vd->vd_dcb.operrsta = 0; 106930519Ssam VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type); 107030519Ssam if (!vdpoll(vm, 5)) { 107130519Ssam printf(" during dump\n"); 107230519Ssam return (EIO); 107330519Ssam } 107430519Ssam if (vd->vd_dcb.operrsta & VDERR_HARD) { 107530519Ssam printf("dk%d: hard error, status=%b\n", unit, 107630519Ssam vd->vd_dcb.operrsta, VDERRBITS); 107730519Ssam return (EIO); 107830519Ssam } 107930519Ssam start += nsec * lp->d_secsize; 108030519Ssam num -= nsec; 108125675Ssam } 108230519Ssam return (0); 108324004Ssam } 108424004Ssam 108524004Ssam vdsize(dev) 108625675Ssam dev_t dev; 108724004Ssam { 108830519Ssam register int unit = vdunit(dev); 108930519Ssam register struct dksoftc *dk; 109030519Ssam struct vba_device *vi; 109130519Ssam struct disklabel *lp; 109224004Ssam 109330519Ssam if (unit >= NDK || (vi = vddinfo[unit]) == 0 || vi->ui_alive == 0 || 109430519Ssam (dk = &dksoftc[unit])->dk_state != OPEN) 109525675Ssam return (-1); 109630519Ssam lp = &dklabel[unit]; 109730756Skarels #ifdef SECSIZE 109830573Skarels return ((int)lp->d_partitions[vdpart(dev)].p_size); 109930756Skarels #else SECSIZE 110030756Skarels return ((int)lp->d_partitions[vdpart(dev)].p_size >> dk->dk_bshift); 110130756Skarels #endif SECSIZE 110224004Ssam } 110324004Ssam 110425675Ssam /* 110525675Ssam * Perform a controller reset. 110625675Ssam */ 110730519Ssam vdreset_ctlr(vm) 110830519Ssam register struct vba_ctlr *vm; 110924004Ssam { 111030519Ssam register struct vddevice *vdaddr = (struct vddevice *)vm->um_addr; 111130519Ssam register struct vdsoftc *vd = &vdsoftc[vm->um_ctlr]; 111230519Ssam register int unit; 111330519Ssam struct vba_device *vi; 111425675Ssam 111530519Ssam VDRESET(vdaddr, vd->vd_type); 111630519Ssam if (vd->vd_type == VDTYPE_SMDE) { 111730519Ssam vdaddr->vdcsr = 0; 111830519Ssam vdaddr->vdtcf_mdcb = AM_ENPDA; 111930519Ssam vdaddr->vdtcf_dcb = AM_ENPDA; 112030519Ssam vdaddr->vdtcf_trail = AM_ENPDA; 112130519Ssam vdaddr->vdtcf_data = AM_ENPDA; 112230519Ssam vdaddr->vdccf = CCF_STS | XMD_32BIT | BSZ_16WRD | 112325675Ssam CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR; 112425675Ssam } 112530519Ssam if (!vdcmd(vm, VDOP_INIT, 10) || !vdcmd(vm, VDOP_DIAG, 10)) { 112630519Ssam printf("%s cmd failed\n", 112730519Ssam vd->vd_dcb.opcode == VDOP_INIT ? "init" : "diag"); 112830370Skarels return; 112925675Ssam } 113030519Ssam for (unit = 0; unit < NDK; unit++) 113130519Ssam if ((vi = vddinfo[unit])->ui_mi == vm && vi->ui_alive) 113230519Ssam (void) vdreset_drive(vi); 113330519Ssam } 113430519Ssam 113530519Ssam vdreset_drive(vi) 113630519Ssam register struct vba_device *vi; 113730519Ssam { 113830519Ssam register struct disklabel *lp = &dklabel[vi->ui_unit]; 113930519Ssam struct vba_ctlr *vm = vdminfo[vi->ui_ctlr]; 114030519Ssam struct vddevice *vdaddr = (struct vddevice *)vm->um_addr; 114132211Skarels register struct vdsoftc *vd = &vdsoftc[vi->ui_ctlr]; 114232211Skarels register struct dksoftc *dk = &dksoftc[vi->ui_unit]; 114330519Ssam 114430519Ssam top: 114530519Ssam vd->vd_dcb.opcode = VDOP_CONFIG; /* command */ 114630519Ssam vd->vd_dcb.intflg = DCBINT_NONE; 114730519Ssam vd->vd_dcb.nxtdcb = (struct dcb *)0; /* end of chain */ 114830519Ssam vd->vd_dcb.operrsta = 0; 114932211Skarels vd->vd_dcb.devselect = vi->ui_slave | lp->d_devflags; 115030519Ssam vd->vd_dcb.trail.rstrail.ncyl = lp->d_ncylinders; 115130519Ssam vd->vd_dcb.trail.rstrail.nsurfaces = lp->d_ntracks; 115230519Ssam if (vd->vd_type == VDTYPE_SMDE) { 115330756Skarels vd->vd_dcb.trailcnt = sizeof (struct treset) / sizeof (long); 115430519Ssam vd->vd_dcb.trail.rstrail.nsectors = lp->d_nsectors; 115530601Skarels vd->vd_dcb.trail.rstrail.slip_sec = lp->d_sparespertrack; 115632211Skarels vd->vd_dcb.trail.rstrail.recovery = VDRF_NORMAL; 115730519Ssam } else 115830519Ssam vd->vd_dcb.trailcnt = 2; /* XXX */ 115930519Ssam vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys; 116030519Ssam vd->vd_mdcb.mdcb_status = 0; 116130519Ssam VDGO(vdaddr, vd->vd_mdcbphys, vd->vd_type); 116230519Ssam if (!vdpoll(vm, 5)) { 116330519Ssam printf(" during config\n"); 116430519Ssam return (0); 116525675Ssam } 116630519Ssam if (vd->vd_dcb.operrsta & VDERR_HARD) { 116732211Skarels if (vd->vd_type == VDTYPE_SMDE) { 116832211Skarels if (lp->d_devflags == 0) { 116932211Skarels lp->d_devflags = VD_ESDI; 117032211Skarels goto top; 117132211Skarels } 117232211Skarels #ifdef notdef 117332211Skarels /* this doesn't work, STA_US isn't set(?) */ 117432211Skarels if ((vdaddr->vdstatus[vi->ui_slave] & STA_US) == 0) 117532211Skarels return (0); 117632211Skarels #endif 117732211Skarels } 117830519Ssam if ((vd->vd_dcb.operrsta & (DCBS_OCYL|DCBS_NRDY)) == 0) 117932211Skarels printf("dk%d: config error %b ecode %x\n", vi->ui_unit, 1180*34396Skarels vd->vd_dcb.operrsta, VDERRBITS, 1181*34396Skarels (u_char) vd->vd_dcb.err_code); 118232211Skarels else if ((vd->vd_flags & VD_STARTED) == 0) { 118330519Ssam int started; 118430519Ssam 118532211Skarels printf(" starting drives, wait ... "); 118630519Ssam vd->vd_flags |= VD_STARTED; 118730519Ssam started = (vdcmd(vm, VDOP_START, 10) == 1); 118830519Ssam DELAY(62000000); 118932211Skarels printf("done"); 119032211Skarels lp->d_devflags = 0; 119130519Ssam if (started) 119230519Ssam goto top; 119330519Ssam } 119430519Ssam return (0); 119530519Ssam } 119632211Skarels dk->dk_dcb.devselect |= lp->d_devflags; 119730519Ssam return (1); 119825675Ssam } 119924004Ssam 120025675Ssam /* 120130519Ssam * Perform a command w/o trailer. 120225675Ssam */ 120330519Ssam vdcmd(vm, cmd, t) 120430519Ssam register struct vba_ctlr *vm; 120525675Ssam { 120630519Ssam register struct vdsoftc *vd = &vdsoftc[vm->um_ctlr]; 120725675Ssam 120830519Ssam vd->vd_dcb.opcode = cmd; /* command */ 120930519Ssam vd->vd_dcb.intflg = DCBINT_NONE; 121030519Ssam vd->vd_dcb.nxtdcb = (struct dcb *)0; /* end of chain */ 121130519Ssam vd->vd_dcb.operrsta = 0; 121230519Ssam vd->vd_dcb.devselect = 0; 121330519Ssam vd->vd_dcb.trailcnt = 0; 121430519Ssam vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys; 121530519Ssam vd->vd_mdcb.mdcb_status = 0; 121630519Ssam VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type); 121730519Ssam if (!vdpoll(vm, t)) { 121830519Ssam printf(" during init\n"); 121930370Skarels return (0); 122030370Skarels } 122130519Ssam return ((vd->vd_dcb.operrsta&VDERR_HARD) == 0); 122225675Ssam } 122325675Ssam 122425925Ssam /* 122530519Ssam * Poll controller until operation 122630519Ssam * completes or timeout expires. 122725925Ssam */ 122830519Ssam vdpoll(vm, t) 122930519Ssam register struct vba_ctlr *vm; 123025925Ssam register int t; 123125925Ssam { 123230519Ssam register struct vdsoftc *vd = &vdsoftc[vm->um_ctlr]; 123330519Ssam register struct vddevice *vdaddr = (struct vddevice *)vm->um_addr; 123425925Ssam 123525925Ssam t *= 1000; 123630370Skarels for (;;) { 123730519Ssam uncache(&vd->vd_dcb.operrsta); 123830519Ssam if (vd->vd_dcb.operrsta & (DCBS_DONE|DCBS_ABORT)) 123930370Skarels break; 124025925Ssam if (--t <= 0) { 124130519Ssam printf("vd%d: controller timeout", vm->um_ctlr); 124230519Ssam VDABORT(vdaddr, vd->vd_type); 124325925Ssam return (0); 124425925Ssam } 124530370Skarels DELAY(1000); 124625925Ssam } 124730519Ssam if (vd->vd_type == VDTYPE_SMDE) { 124830519Ssam do { 124925925Ssam DELAY(50); 125030519Ssam uncache(&vdaddr->vdcsr); 125130519Ssam } while (vdaddr->vdcsr & CS_GO); 125232211Skarels DELAY(300); 125332211Skarels uncache(&vd->vd_dcb.err_code); 125425925Ssam } 125525925Ssam DELAY(200); 125630519Ssam uncache(&vd->vd_dcb.operrsta); 125725925Ssam return (1); 125825925Ssam } 125925925Ssam 126030519Ssam #ifdef COMPAT_42 126130519Ssam struct vdst { 126230519Ssam int nsec; /* sectors/track */ 126330519Ssam int ntrack; /* tracks/cylinder */ 126430519Ssam int ncyl; /* cylinders */ 126532211Skarels int secsize; /* sector size */ 126630519Ssam char *name; /* type name */ 126730519Ssam struct { 126830519Ssam int off; /* partition offset in sectors */ 126930519Ssam int size; /* partition size in sectors */ 127030573Skarels } parts[8]; 127130519Ssam } vdst[] = { 127232211Skarels { 66, 23, 850, 512, "NEC 800", 127332211Skarels {0, 1290300}, /* a cyl 0 - 849 */ 127432211Skarels }, 127532211Skarels { 48, 24, 711, 512, "xsd", 127631039Skarels {0, 61056}, /* a cyl 0 - 52 */ 127731039Skarels {61056, 61056}, /* b cyl 53 - 105 */ 127831039Skarels {122112, 691200}, /* c cyl 106 - 705 */ 127931039Skarels {237312, 576000}, /* d cyl 206 - 705 */ 128031039Skarels {352512, 460800}, /* e cyl 306 - 705 */ 128131039Skarels {467712, 345600}, /* f cyl 406 - 705 */ 128231039Skarels {582912, 230400}, /* g cyl 506 - 705 */ 128331039Skarels {698112, 115200} /* h cyl 606 - 705 */ 128430573Skarels }, 128532211Skarels { 44, 20, 842, 512, "eagle", 128630601Skarels {0, 52800}, /* egl0a cyl 0 - 59 */ 128730601Skarels {52800, 66000}, /* egl0b cyl 60 - 134 */ 128830601Skarels {118800, 617760}, /* egl0c cyl 135 - 836 */ 128930756Skarels {736560, 4400}, /* egl0d cyl 837 - 841 */ 129031039Skarels {0, 736560}, /* egl0e cyl 0 - 836 */ 129131039Skarels {0, 740960}, /* egl0f cyl 0 - 841 */ 129230601Skarels {118800, 310640}, /* egl0g cyl 135 - 487 */ 129330601Skarels {429440, 307120} /* egl0h cyl 488 - 836 */ 129430573Skarels }, 129532211Skarels { 64, 10, 823, 512, "fuj", 129631039Skarels {0, 38400}, /* fuj0a cyl 0 - 59 */ 129731039Skarels {38400, 48000}, /* fuj0b cyl 60 - 134 */ 129831039Skarels {86400, 437120}, /* fuj0c cyl 135 - 817 */ 129931039Skarels {159360, 364160}, /* fuj0d cyl 249 - 817 */ 130031039Skarels {232320, 291200}, /* fuj0e cyl 363 - 817 */ 130131039Skarels {305280, 218240}, /* fuj0f cyl 477 - 817 */ 130231039Skarels {378240, 145280}, /* fuj0g cyl 591 - 817 */ 130331039Skarels {451200, 72320} /* fug0h cyl 705 - 817 */ 130430573Skarels }, 130532211Skarels { 32, 24, 711, 512, "xfd", 130630756Skarels { 0, 40704 }, /* a cyl 0 - 52 */ 130730756Skarels { 40704, 40704 }, /* b cyl 53 - 105 */ 130830756Skarels { 81408, 460800 }, /* c cyl 106 - 705 */ 130930756Skarels { 0, 81408 }, /* d cyl 709 - 710 (a & b) */ 131030756Skarels { 0, 542208 }, /* e cyl 0 - 705 */ 131130756Skarels { 40704, 501504 }, /* f cyl 53 - 705 (b & c) */ 131230756Skarels { 81408, 230400 }, /* g cyl 106 - 405 (1/2 of c) */ 131330756Skarels { 311808,230400 } /* h cyl 406 - 705 (1/2 of c) */ 131430573Skarels }, 131532211Skarels { 32, 19, 823, 512, "smd", 131631039Skarels {0, 40128}, /* a cyl 0-65 */ 131731039Skarels {40128, 27360}, /* b cyl 66-110 */ 131831039Skarels {67488, 429856}, /* c cyl 111-817 */ 131931039Skarels {139232, 358112}, /* d cyl 229 - 817 */ 132031039Skarels {210976, 286368}, /* e cyl 347 - 817 */ 132131039Skarels {282720, 214624}, /* f cyl 465 - 817 */ 132231039Skarels {354464, 142880}, /* g cyl 583 - 817 */ 132331039Skarels {426208, 71136} /* h cyl 701 - 817 */ 132430573Skarels }, 132532211Skarels { 18, 15, 1224, 1024, "mxd", 132632211Skarels {0, 21600}, /* a cyl 0-79 */ 132732211Skarels {21600, 22410}, /* b cyl 80-162 */ 132832211Skarels {44010, 285120}, /* c cyl 163-1217 */ 132932211Skarels #ifdef notyet 133032211Skarels {x, 237600}, /* d cyl y - 1217 */ 133132211Skarels {x, 190080}, /* e cyl y - 1217 */ 133232211Skarels {x, 142560}, /* f cyl y - 1217 */ 133332211Skarels {x, 95040}, /* g cyl y - 1217 */ 133432211Skarels {x, 47520} /* h cyl 701 - 817 */ 133532211Skarels #endif 133632211Skarels }, 133732211Skarels { 32, 10, 823, 512, "fsd", 133830756Skarels {0, 19200}, /* a cyl 0 - 59 */ 133930756Skarels {19200, 24000}, /* b cyl 60 - 134 */ 134030756Skarels {43200, 218560}, /* c cyl 135 - 817 */ 134130573Skarels } 134230519Ssam }; 134330519Ssam #define NVDST (sizeof (vdst) / sizeof (vdst[0])) 134430519Ssam 134525675Ssam /* 134630519Ssam * Construct a label for an unlabeled pack. We 134730519Ssam * deduce the drive type by reading from the last 134830519Ssam * track on successively smaller drives until we 134930519Ssam * don't get an error. 135025675Ssam */ 135130519Ssam vdmaptype(vi, lp) 135230519Ssam register struct vba_device *vi; 135330519Ssam register struct disklabel *lp; 135425675Ssam { 135530519Ssam register struct vdsoftc *vd; 135630519Ssam register struct vdst *p; 135732211Skarels struct vba_ctlr *vm = vi->ui_mi; 135830519Ssam int i; 135925675Ssam 136030519Ssam vd = &vdsoftc[vi->ui_ctlr]; 136130519Ssam for (p = vdst; p < &vdst[NVDST]; p++) { 136230519Ssam if (vd->vd_type == VDTYPE_VDDC && p->nsec != 32) 136330519Ssam continue; 136430519Ssam lp->d_nsectors = p->nsec; 136530519Ssam lp->d_ntracks = p->ntrack; 136630519Ssam lp->d_ncylinders = p->ncyl; 136732211Skarels lp->d_secsize = p->secsize; 136830519Ssam if (!vdreset_drive(vi)) 136930519Ssam return (0); 137030519Ssam vd->vd_dcb.opcode = VDOP_RD; 137130519Ssam vd->vd_dcb.intflg = DCBINT_NONE; 137230519Ssam vd->vd_dcb.nxtdcb = (struct dcb *)0; /* end of chain */ 137332211Skarels vd->vd_dcb.devselect = dksoftc[vi->ui_unit].dk_dcb.devselect; 137430756Skarels vd->vd_dcb.trailcnt = sizeof (struct trrw) / sizeof (long); 137530601Skarels vd->vd_dcb.trail.rwtrail.memadr = 137630601Skarels vtoph((struct proc *)0, (unsigned)vd->vd_rbuf.vb_rawbuf); 137732211Skarels vd->vd_dcb.trail.rwtrail.wcount = lp->d_secsize / sizeof(short); 137830519Ssam vd->vd_dcb.operrsta = 0; 137930519Ssam vd->vd_dcb.trail.rwtrail.disk.cylinder = p->ncyl - 2; 138030519Ssam vd->vd_dcb.trail.rwtrail.disk.track = p->ntrack - 1; 138130519Ssam vd->vd_dcb.trail.rwtrail.disk.sector = p->nsec - 1; 138230519Ssam vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys; 138330519Ssam vd->vd_mdcb.mdcb_status = 0; 138430519Ssam VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type); 138530519Ssam if (!vdpoll(vm, 60)) 138630519Ssam printf(" during probe\n"); 138730519Ssam if ((vd->vd_dcb.operrsta & VDERR_HARD) == 0) 138830519Ssam break; 138924004Ssam } 139032211Skarels if (p >= &vdst[NVDST]) 139130519Ssam return (0); 139232211Skarels 139330573Skarels for (i = 0; i < 8; i++) { 139430519Ssam lp->d_partitions[i].p_offset = p->parts[i].off; 139530519Ssam lp->d_partitions[i].p_size = p->parts[i].size; 139630519Ssam } 139730573Skarels lp->d_npartitions = 8; 139830519Ssam lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; 139930519Ssam lp->d_rpm = 3600; 140030519Ssam bcopy(p->name, lp->d_typename, 4); 140130519Ssam return (1); 140224004Ssam } 140330519Ssam #endif COMPAT_42 140424004Ssam #endif 1405