home *** CD-ROM | disk | FTP | other *** search
- /*-
- * Copyright (c) 1990 The Regents of the University of California.
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * William Jolitz.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * @(#)wd.c 7.2 (Berkeley) 5/9/91
- */
-
- /* TODO:peel out buffer at low ipl,
- speed improvement, rewrite to clean code from garbage artifacts */
-
-
- #include "wd.h"
- #if NWD > 0
-
- #include "param.h"
- #include "dkbad.h"
- #include "systm.h"
- #include "conf.h"
- #include "file.h"
- #include "stat.h"
- #include "ioctl.h"
- #include "disklabel.h"
- #include "buf.h"
- #include "uio.h"
- #include "i386/isa/isa_device.h"
- #include "i386/isa/icu.h"
- #include "i386/isa/wdreg.h"
- #include "syslog.h"
- #include "vm/vm.h"
-
- #define RETRIES 5 /* number of retries before giving up */
- #define MAXTRANSFER 32 /* max size of transfer in page clusters */
-
- #define wdctlr(dev) ((minor(dev) & 0x80) >> 7)
- #define wdunit(dev) ((minor(dev) & 0x60) >> 5)
- #define wdpart(dev) ((minor(dev) & 0x1f))
-
- #define b_cylin b_resid /* cylinder number for doing IO to */
- /* shares an entry in the buf struct */
-
- /*
- * Drive states. Used for open and format operations.
- * States < OPEN (> 0) are transient, during an open operation.
- * OPENRAW is used for unlabeled disks, and for floppies, to inhibit
- * bad-sector forwarding.
- */
- #define RAWDISK 8 /* raw disk operation, no translation*/
- #define ISRAWSTATE(s) (RAWDISK&(s)) /* are we in a raw state? */
- #define DISKSTATE(s) (~RAWDISK&(s)) /* are we in a given state regardless
- of raw or cooked mode? */
-
- #define CLOSED 0 /* disk is closed. */
- /* "cooked" disk states */
- #define WANTOPEN 1 /* open requested, not started */
- #define RECAL 2 /* doing restore */
- #define RDLABEL 3 /* reading pack label */
- #define RDBADTBL 4 /* reading bad-sector table */
- #define OPEN 5 /* done with open */
-
- #define WANTOPENRAW (WANTOPEN|RAWDISK) /* raw WANTOPEN */
- #define RECALRAW (RECAL|RAWDISK) /* raw open, doing restore */
- #define OPENRAW (OPEN|RAWDISK) /* open, but unlabeled disk or floppy */
-
-
- /*
- * The structure of a disk drive.
- */
- struct disk {
- struct disklabel dk_dd; /* device configuration data */
- long dk_bc; /* byte count left */
- short dk_skip; /* blocks already transferred */
- char dk_unit; /* physical unit number */
- char dk_state; /* control state */
- u_char dk_status; /* copy of status reg. */
- u_char dk_error; /* copy of error reg. */
- short dk_open; /* open/closed refcnt */
- u_long dk_copenpart; /* character units open on this drive */
- u_long dk_bopenpart; /* block units open on this drive */
- u_long dk_openpart; /* all units open on this drive */
- short dk_wlabel; /* label writable? */
- };
-
- /*
- * This label is used as a default when initializing a new or raw disk.
- * It really only lets us access the first track until we know more.
- */
- struct disklabel dflt_sizes = {
- DISKMAGIC, DTYPE_ST506, 0, "default", "",
- 512, /* sector size */
- 17, /* # of sectors per track */
- 8, /* # of tracks per cylinder */
- 766, /* # of cylinders per unit */
- 17*8, /* # of sectors per cylinder */
- 766*8*17, /* # of sectors per unit */
- 0, /* # of spare sectors per track */
- 0, /* # of spare sectors per cylinder */
- 0, /* # of alt. cylinders per unit */
- 3600, /* rotational speed */
- 1, /* hardware sector interleave */
- 0, /* sector 0 skew, per track */
- 0, /* sector 0 skew, per cylinder */
- 0, /* head switch time, usec */
- 0, /* track-to-track seek, usec */
- 0, /* generic flags */
- 0,0,0,0,0,
- 0,0,0,0,0,
- DISKMAGIC,
- 0,
- 8,
- 8192,
- 8192,
-
- {{21600, 0, 0,0,0,0}, /* A=root filesystem */
- {21600, 40, 0,0,0,0},
- {660890, 0, 0,0,0,0}, /* C=whole disk */
- {216000, 80, 0,0,0,0},
- {0, 0, 0,0,0,0},
- {0, 0, 0,0,0,0},
- {0, 0, 0,0,0,0},
- {399600, 480, 0,0,0,0}}
- };
-
- static struct dkbad dkbad[NWD];
- struct disk wddrives[NWD] = {0}; /* table of units */
- struct buf wdtab = {0};
- struct buf wdutab[NWD] = {0}; /* head of queue per drive */
- struct buf rwdbuf[NWD] = {0}; /* buffers for raw IO */
- long wdxfer[NWD] = {0}; /* count of transfers */
- int writeprotected[NWD] = { 0 };
- int wdprobe(), wdattach(), wdintr();
- struct isa_driver wddriver = {
- wdprobe, wdattach, "wd",
- };
-
- static wdc;
- /*
- * Probe routine
- */
- wdprobe(dvp)
- struct isa_device *dvp;
- {
- wdc = dvp->id_iobase;
-
- #ifdef lint
- wdintr(0);
- #endif
- /* XXX sorry, needs to be better */
- outb(wdc+wd_error, 0x5a) ; /* error register not writable */
- outb(wdc+wd_cyl_lo, 0xa5) ; /* but all of cyllo are implemented */
- if(inb(wdc+wd_error) != 0x5a && inb(wdc+wd_cyl_lo) == 0xa5)
- return(1) ;
- return (0);
- }
-
- /*
- * attach each drive if possible.
- */
- wdattach(dvp)
- struct isa_device *dvp;
- {
- int unit = dvp->id_unit;
-
- outb(wdc+wd_ctlr,12);
- DELAY(1000);
- outb(wdc+wd_ctlr,8);
- }
-
- /* Read/write routine for a buffer. Finds the proper unit, range checks
- * arguments, and schedules the transfer. Does not wait for the transfer
- * to complete. Multi-page transfers are supported. All I/O requests must
- * be a multiple of a sector in length.
- */
- wdstrategy(bp)
- register struct buf *bp; /* IO operation to perform */
- {
- register struct buf *dp;
- register struct disk *du; /* Disk unit to do the IO. */
- register struct partition *p;
- long maxsz, sz;
- int unit = wdunit(bp->b_dev);
- int s;
-
- if ((unit >= NWD) || (bp->b_blkno < 0)) {
- printf("wdstrat: unit = %d, blkno = %d, bcount = %d\n",
- unit, bp->b_blkno, bp->b_bcount);
- pg("wd:error in wdstrategy");
- bp->b_flags |= B_ERROR;
- goto bad;
- }
- if (writeprotected[unit] && (bp->b_flags & B_READ) == 0) {
- printf("wd%d: write protected\n", unit);
- goto bad;
- }
- du = &wddrives[unit];
- if (DISKSTATE(du->dk_state) != OPEN)
- goto q;
- #ifdef old
- /*
- * Convert DEV_BSIZE "blocks" to sectors.
- * Note: doing the conversions this way limits the partition size
- * to about 8 million sectors (1-8 Gb).
- */
- blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.d_secsize;
- if (((u_long) bp->b_blkno * DEV_BSIZE % du->dk_dd.d_secsize != 0) ||
- bp->b_bcount >= MAXTRANSFER * CLBYTES) {
- bp->b_flags |= B_ERROR;
- goto bad;
- }
- nblocks = du->dk_dd.d_partitions[part].p_size;
- cyloff = du->dk_dd.d_partitions[part].p_offset;
- if (blknum + (bp->b_bcount / du->dk_dd.d_secsize) > nblocks) {
- if (blknum == nblocks)
- bp->b_resid = bp->b_bcount;
- else
- bp->b_flags |= B_ERROR;
- goto bad;
- }
- bp->b_cylin = blknum / du->dk_dd.d_secpercyl + cyloff;
- #else
- /*
- * Determine the size of the transfer, and make sure it is
- * within the boundaries of the partition.
- */
- p = &du->dk_dd.d_partitions[wdpart(bp->b_dev)];
- maxsz = p->p_size;
- sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT;
- if (bp->b_blkno + p->p_offset <= LABELSECTOR &&
- #if LABELSECTOR != 0
- bp->b_blkno + p->p_offset + sz > LABELSECTOR &&
- #endif
- (bp->b_flags & B_READ) == 0 && du->dk_wlabel == 0) {
- bp->b_error = EROFS;
- goto bad;
- }
- if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) {
- /* if exactly at end of disk, return an EOF */
- if (bp->b_blkno == maxsz) {
- bp->b_resid = bp->b_bcount;
- biodone(bp);
- return;
- }
- /* or truncate if part of it fits */
- sz = maxsz - bp->b_blkno;
- if (sz <= 0)
- goto bad;
- bp->b_bcount = sz << DEV_BSHIFT;
- }
- bp->b_cylin = (bp->b_blkno + p->p_offset) / du->dk_dd.d_secpercyl;
- #endif
- q:
- dp = &wdutab[unit];
- s = splhigh();
- disksort(dp, bp);
- if (dp->b_active == 0)
- wdustart(du); /* start drive if idle */
- if (wdtab.b_active == 0)
- wdstart(s); /* start IO if controller idle */
- splx(s);
- return;
-
- bad:
- bp->b_error = EINVAL;
- biodone(bp);
- }
-
- /* Routine to queue a read or write command to the controller. The request is
- * linked into the active list for the controller. If the controller is idle,
- * the transfer is started.
- */
- wdustart(du)
- register struct disk *du;
- {
- register struct buf *bp, *dp;
-
- dp = &wdutab[du->dk_unit];
- if (dp->b_active)
- return;
- bp = dp->b_actf;
- if (bp == NULL)
- return;
- dp->b_forw = NULL;
- if (wdtab.b_actf == NULL) /* link unit into active list */
- wdtab.b_actf = dp;
- else
- wdtab.b_actl->b_forw = dp;
- wdtab.b_actl = dp;
- dp->b_active = 1; /* mark the drive as busy */
- }
-
- /*
- * Controller startup routine. This does the calculation, and starts
- * a single-sector read or write operation. Called to start a transfer,
- * or from the interrupt routine to continue a multi-sector transfer.
- * RESTRICTIONS:
- * 1. The transfer length must be an exact multiple of the sector size.
- */
-
- static wd_sebyse;
-
- wdstart()
- {
- register struct disk *du; /* disk unit for IO */
- register struct buf *bp;
- struct buf *dp;
- register struct bt_bad *bt_ptr;
- long blknum, pagcnt, cylin, head, sector;
- long secpertrk, secpercyl, addr, i;
- int unit, s;
-
- loop:
- dp = wdtab.b_actf;
- if (dp == NULL)
- return;
- bp = dp->b_actf;
- if (bp == NULL) {
- wdtab.b_actf = dp->b_forw;
- goto loop;
- }
- unit = wdunit(bp->b_dev);
- du = &wddrives[unit];
- if (DISKSTATE(du->dk_state) <= RDLABEL) {
- if (wdcontrol(bp)) {
- dp->b_actf = bp->av_forw;
- goto loop; /* done */
- }
- return;
- }
- secpertrk = du->dk_dd.d_nsectors;
- secpercyl = du->dk_dd.d_secpercyl;
- /*
- * Convert DEV_BSIZE "blocks" to sectors.
- */
- blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.d_secsize
- + du->dk_skip;
- #ifdef WDDEBUG
- if (du->dk_skip == 0) {
- dprintf(DDSK,"\nwdstart %d: %s %d@%d; map ", unit,
- (bp->b_flags & B_READ) ? "read" : "write",
- bp->b_bcount, blknum);
- } else {
- dprintf(DDSK," %d)%x", du->dk_skip, inb(wdc+wd_altsts));
- }
- #endif
-
- addr = (int) bp->b_un.b_addr;
- if(du->dk_skip==0) du->dk_bc = bp->b_bcount;
- cylin = blknum / secpercyl;
- head = (blknum % secpercyl) / secpertrk;
- sector = blknum % secpertrk;
- if (DISKSTATE(du->dk_state) == OPEN)
- cylin += du->dk_dd.d_partitions[wdpart(bp->b_dev)].p_offset
- / secpercyl;
-
- /*
- * See if the current block is in the bad block list.
- * (If we have one, and not formatting.)
- */
- if (DISKSTATE(du->dk_state) == OPEN && wd_sebyse)
- for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) {
- if (bt_ptr->bt_cyl > cylin)
- /* Sorted list, and we passed our cylinder. quit. */
- break;
- if (bt_ptr->bt_cyl == cylin &&
- bt_ptr->bt_trksec == (head << 8) + sector) {
- /*
- * Found bad block. Calculate new block addr.
- * This starts at the end of the disk (skip the
- * last track which is used for the bad block list),
- * and works backwards to the front of the disk.
- */
- #ifdef WDDEBUG
- dprintf(DDSK,"--- badblock code -> Old = %d; ",
- blknum);
- #endif
- blknum = du->dk_dd.d_secperunit - du->dk_dd.d_nsectors
- - (bt_ptr - dkbad[unit].bt_bad) - 1;
- cylin = blknum / secpercyl;
- head = (blknum % secpercyl) / secpertrk;
- sector = blknum % secpertrk;
- #ifdef WDDEBUG
- dprintf(DDSK, "new = %d\n", blknum);
- #endif
- break;
- }
- }
- sector += 1; /* sectors begin with 1, not 0 */
-
- wdtab.b_active = 1; /* mark controller active */
-
- if(du->dk_skip==0 || wd_sebyse) {
- if(wdtab.b_errcnt && (bp->b_flags & B_READ) == 0) du->dk_bc += 512;
- while ((inb(wdc+wd_status) & WDCS_BUSY) != 0) ;
- /*while ((inb(wdc+wd_status) & WDCS_DRQ)) inb(wdc+wd_data);*/
- outb(wdc+wd_precomp, 0xff);
- /*wr(wdc+wd_precomp, du->dk_dd.dk_precompcyl / 4);*/
- /*if (bp->b_flags & B_FORMAT) {
- wr(wdc+wd_sector, du->dk_dd.dk_gap3);
- wr(wdc+wd_seccnt, du->dk_dd.dk_nsectors);
- } else {*/
- if(wd_sebyse)
- outb(wdc+wd_seccnt, 1);
- else
- outb(wdc+wd_seccnt, ((du->dk_bc +511) / 512));
- outb(wdc+wd_sector, sector);
-
- outb(wdc+wd_cyl_lo, cylin);
- outb(wdc+wd_cyl_hi, cylin >> 8);
-
- /* Set up the SDH register (select drive). */
- outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
- while ((inb(wdc+wd_status) & WDCS_READY) == 0) ;
-
- /*if (bp->b_flags & B_FORMAT)
- wr(wdc+wd_command, WDCC_FORMAT);
- else*/
- outb(wdc+wd_command,
- (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE);
- #ifdef WDDEBUG
- dprintf(DDSK,"sector %d cylin %d head %d addr %x sts %x\n",
- sector, cylin, head, addr, inb(wdc+wd_altsts));
- #endif
- }
-
- /* If this is a read operation, just go away until it's done. */
- if (bp->b_flags & B_READ) return;
-
- /* Ready to send data? */
- while ((inb(wdc+wd_status) & WDCS_DRQ) == 0);
-
- /* ASSUMES CONTIGUOUS MEMORY */
- outsw (wdc+wd_data, addr+du->dk_skip*512, 256);
- du->dk_bc -= 512;
- }
-
- /*
- * these are globally defined so they can be found
- * by the debugger easily in the case of a system crash
- */
- daddr_t wd_errsector;
- daddr_t wd_errbn;
- unsigned char wd_errstat;
-
- /* Interrupt routine for the controller. Acknowledge the interrupt, check for
- * errors on the current operation, mark it done if necessary, and start
- * the next request. Also check for a partially done transfer, and
- * continue with the next chunk if so.
- */
- wdintr(unit)
- {
- register struct disk *du;
- register struct buf *bp, *dp;
- int status;
- char partch ;
- static wd_haderror;
-
- /* Shouldn't need this, but it may be a slow controller. */
- while ((status = inb(wdc+wd_status)) & WDCS_BUSY) ;
- if (!wdtab.b_active) {
- printf("wd: extra interrupt\n");
- return;
- }
-
- #ifdef WDDEBUG
- dprintf(DDSK,"I ");
- #endif
- dp = wdtab.b_actf;
- bp = dp->b_actf;
- du = &wddrives[wdunit(bp->b_dev)];
- partch = wdpart(bp->b_dev) + 'a';
- if (DISKSTATE(du->dk_state) <= RDLABEL) {
- if (wdcontrol(bp))
- goto done;
- return;
- }
- if (status & (WDCS_ERR | WDCS_ECCCOR)) {
- wd_errstat = inb(wdc+wd_error); /* save error status */
- #ifdef WDDEBUG
- printf("status %x error %x\n", status, wd_errstat);
- #endif
- if(wd_sebyse == 0) {
- wd_haderror = 1;
- goto outt;
- }
- /*if (bp->b_flags & B_FORMAT) {
- du->dk_status = status;
- du->dk_error = wdp->wd_error;
- bp->b_flags |= B_ERROR;
- goto done;
- }*/
-
- wd_errsector = (bp->b_cylin * du->dk_dd.d_secpercyl) +
- (((unsigned long) bp->b_blkno * DEV_BSIZE /
- du->dk_dd.d_secsize) % du->dk_dd.d_secpercyl) +
- du->dk_skip;
- wd_errbn = bp->b_blkno
- + du->dk_skip * du->dk_dd.d_secsize / DEV_BSIZE ;
- if (status & WDCS_ERR) {
- if (++wdtab.b_errcnt < RETRIES) {
- wdtab.b_active = 0;
- } else {
- printf("wd%d%c: ", du->dk_unit, partch);
- printf(
- "hard %s error, sn %d bn %d status %b error %b\n",
- (bp->b_flags & B_READ)? "read":"write",
- wd_errsector, wd_errbn, status, WDCS_BITS,
- wd_errstat, WDERR_BITS);
- bp->b_flags |= B_ERROR; /* flag the error */
- }
- } else
- log(LOG_WARNING,"wd%d%c: soft ecc sn %d bn %d\n",
- du->dk_unit, partch, wd_errsector,
- wd_errbn);
- }
- outt:
-
- /*
- * If this was a successful read operation, fetch the data.
- */
- if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab.b_active) {
- int chk, dummy;
-
- chk = min(256,du->dk_bc/2);
- /* Ready to receive data? */
- while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
-
- /*dprintf(DDSK,"addr %x\n", (int)bp->b_un.b_addr + du->dk_skip * 512);*/
- insw(wdc+wd_data,(int)bp->b_un.b_addr + du->dk_skip * 512 ,chk);
- du->dk_bc -= 2*chk;
- while (chk++ < 256) insw (wdc+wd_data,&dummy,1);
- }
-
- wdxfer[du->dk_unit]++;
- if (wdtab.b_active) {
- if ((bp->b_flags & B_ERROR) == 0) {
- du->dk_skip++; /* Add to successful sectors. */
- if (wdtab.b_errcnt) {
- log(LOG_WARNING, "wd%d%c: ",
- du->dk_unit, partch);
- log(LOG_WARNING,
- "soft %s error, sn %d bn %d error %b retries %d\n",
- (bp->b_flags & B_READ) ? "read" : "write",
- wd_errsector, wd_errbn, wd_errstat,
- WDERR_BITS, wdtab.b_errcnt);
- }
- wdtab.b_errcnt = 0;
-
- /* see if more to transfer */
- /*if (du->dk_skip < (bp->b_bcount + 511) / 512) {*/
- if (du->dk_bc > 0 && wd_haderror == 0) {
- wdstart();
- return; /* next chunk is started */
- } else if (wd_haderror && wd_sebyse == 0) {
- du->dk_skip = 0;
- wd_haderror = 0;
- wd_sebyse = 1;
- wdstart();
- return; /* redo xfer sector by sector */
- }
- }
-
- done:
- wd_sebyse = 0;
- /* done with this transfer, with or without error */
- wdtab.b_actf = dp->b_forw;
- wdtab.b_errcnt = 0;
- du->dk_skip = 0;
- dp->b_active = 0;
- dp->b_actf = bp->av_forw;
- dp->b_errcnt = 0;
- bp->b_resid = 0;
- biodone(bp);
- }
- wdtab.b_active = 0;
- if (dp->b_actf)
- wdustart(du); /* requeue disk if more io to do */
- if (wdtab.b_actf)
- wdstart(); /* start IO on next drive */
- }
-
- /*
- * Initialize a drive.
- */
- wdopen(dev, flags, fmt)
- dev_t dev;
- int flags, fmt;
- {
- register unsigned int unit;
- register struct buf *bp;
- register struct disk *du;
- int part = wdpart(dev), mask = 1 << part;
- struct partition *pp;
- struct dkbad *db;
- int i, error = 0;
-
- unit = wdunit(dev);
- if (unit >= NWD) return (ENXIO) ;
- du = &wddrives[unit];
- #ifdef notdef
- if (du->dk_open){
- du->dk_open++ ;
- return(0); /* already is open, don't mess with it */
- }
- #endif
- du->dk_unit = unit;
- wdutab[unit].b_actf = NULL;
- /*if (flags & O_NDELAY)
- du->dk_state = WANTOPENRAW;
- else*/
- du->dk_state = WANTOPEN;
- /*
- * Use the default sizes until we've read the label,
- * or longer if there isn't one there.
- */
- du->dk_dd = dflt_sizes;
-
- /*
- * Recal, read of disk label will be done in wdcontrol
- * during first read operation.
- */
- bp = geteblk(512);
- bp->b_dev = dev & 0xff00;
- bp->b_bcount = 0;
- bp->b_blkno = LABELSECTOR;
- bp->b_flags = B_READ;
- wdstrategy(bp);
- biowait(bp);
- if (bp->b_flags & B_ERROR) {
- error = ENXIO;
- du->dk_state = CLOSED;
- goto done;
- }
- if (du->dk_state == OPENRAW) {
- du->dk_state = OPENRAW;
- goto done;
- }
- /*
- * Read bad sector table into memory.
- */
- i = 0;
- do {
- bp->b_flags = B_BUSY | B_READ;
- bp->b_blkno = du->dk_dd.d_secperunit - du->dk_dd.d_nsectors
- + i;
- if (du->dk_dd.d_secsize > DEV_BSIZE)
- bp->b_blkno *= du->dk_dd.d_secsize / DEV_BSIZE;
- else
- bp->b_blkno /= DEV_BSIZE / du->dk_dd.d_secsize;
- bp->b_bcount = du->dk_dd.d_secsize;
- bp->b_cylin = du->dk_dd.d_ncylinders - 1;
- wdstrategy(bp);
- biowait(bp);
- } while ((bp->b_flags & B_ERROR) && (i += 2) < 10 &&
- i < du->dk_dd.d_nsectors);
- db = (struct dkbad *)(bp->b_un.b_addr);
- #define DKBAD_MAGIC 0x4321
- if ((bp->b_flags & B_ERROR) == 0 && db->bt_mbz == 0 &&
- db->bt_flag == DKBAD_MAGIC) {
- dkbad[unit] = *db;
- du->dk_state = OPEN;
- } else {
- printf("wd%d: %s bad-sector file\n", unit,
- (bp->b_flags & B_ERROR) ? "can't read" : "format error in");
- error = ENXIO ;
- du->dk_state = OPENRAW;
- }
- done:
- bp->b_flags = B_INVAL | B_AGE;
- brelse(bp);
- if (error == 0)
- du->dk_open = 1;
-
- /*
- * Warn if a partion is opened
- * that overlaps another partition which is open
- * unless one is the "raw" partition (whole disk).
- */
- #define RAWPART 8 /* 'x' partition */ /* XXX */
- if ((du->dk_openpart & mask) == 0 && part != RAWPART) {
- int start, end;
-
- pp = &du->dk_dd.d_partitions[part];
- start = pp->p_offset;
- end = pp->p_offset + pp->p_size;
- for (pp = du->dk_dd.d_partitions;
- pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions];
- pp++) {
- if (pp->p_offset + pp->p_size <= start ||
- pp->p_offset >= end)
- continue;
- if (pp - du->dk_dd.d_partitions == RAWPART)
- continue;
- if (du->dk_openpart & (1 << (pp -
- du->dk_dd.d_partitions)))
- log(LOG_WARNING,
- "wd%d%c: overlaps open partition (%c)\n",
- unit, part + 'a',
- pp - du->dk_dd.d_partitions + 'a');
- }
- }
- if (part >= du->dk_dd.d_npartitions)
- return (ENXIO);
- du->dk_openpart |= mask;
- switch (fmt) {
- case S_IFCHR:
- du->dk_copenpart |= mask;
- break;
- case S_IFBLK:
- du->dk_bopenpart |= mask;
- break;
- }
- return (error);
- }
-
- /*
- * Implement operations other than read/write.
- * Called from wdstart or wdintr during opens and formats.
- * Uses finite-state-machine to track progress of operation in progress.
- * Returns 0 if operation still in progress, 1 if completed.
- */
- wdcontrol(bp)
- register struct buf *bp;
- {
- register struct disk *du;
- register unit;
- unsigned char stat;
- int s, cnt;
- extern int bootdev, cyloffset;
-
- du = &wddrives[wdunit(bp->b_dev)];
- unit = du->dk_unit;
- switch (DISKSTATE(du->dk_state)) {
-
- tryagainrecal:
- case WANTOPEN: /* set SDH, step rate, do restore */
- #ifdef WDDEBUG
- dprintf(DDSK,"wd%d: recal ", unit);
- #endif
- s = splbio(); /* not called from intr level ... */
- outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
- wdtab.b_active = 1;
- outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
- du->dk_state++;
- splx(s);
- return(0);
-
- case RECAL:
- if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
- printf("wd%d: recal", du->dk_unit);
- if (unit == 0) {
- printf(": status %b error %b\n",
- stat, WDCS_BITS,
- inb(wdc+wd_error), WDERR_BITS);
- if (++wdtab.b_errcnt < RETRIES)
- goto tryagainrecal;
- }
- goto badopen;
- }
-
- /* some compaq controllers require this ... */
- wdsetctlr(bp->b_dev, du);
-
- wdtab.b_errcnt = 0;
- if (ISRAWSTATE(du->dk_state)) {
- du->dk_state = OPENRAW;
- return(1);
- }
- retry:
- #ifdef WDDEBUG
- dprintf(DDSK,"rdlabel ");
- #endif
- if( cyloffset < 0 || cyloffset > 8192) cyloffset=0;
- /*
- * Read in sector LABELSECTOR to get the pack label
- * and geometry.
- */
- outb(wdc+wd_precomp, 0xff);/* sometimes this is head bit 3 */
- outb(wdc+wd_seccnt, 1);
- outb(wdc+wd_sector, LABELSECTOR+1);
- /*if (bp->b_dev == bootdev) {
- (wdc+wd_cyl_lo = cyloffset & 0xff;
- (wdc+wd_cyl_hi = cyloffset >> 8;
- } else {
- (wdc+wd_cyl_lo = 0;
- (wdc+wd_cyl_hi = 0;
- }*/
- outb(wdc+wd_cyl_lo, (cyloffset & 0xff));
- outb(wdc+wd_cyl_hi, (cyloffset >> 8));
- outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
- outb(wdc+wd_command, WDCC_READ);
- du->dk_state = RDLABEL;
- return(0);
-
- case RDLABEL:
- if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
- if (++wdtab.b_errcnt < RETRIES)
- goto retry;
- printf("wd%d: read label", unit);
- goto badopen;
- }
-
- insw(wdc+wd_data, bp->b_un.b_addr, 256);
-
- if (((struct disklabel *)
- (bp->b_un.b_addr + LABELOFFSET))->d_magic == DISKMAGIC) {
- du->dk_dd =
- * (struct disklabel *) (bp->b_un.b_addr + LABELOFFSET);
- } else {
- printf("wd%d: bad disk label\n", du->dk_unit);
- du->dk_state = OPENRAW;
- }
-
- s = splbio(); /* not called from intr level ... */
- while ((stat = inb(wdc+wd_status)) & WDCS_BUSY);
-
- wdsetctlr(bp->b_dev, du);
-
- outb(wdc+wd_seccnt, 0);
- splx(s);
-
- if (du->dk_state == RDLABEL)
- du->dk_state = RDBADTBL;
- /*
- * The rest of the initialization can be done
- * by normal means.
- */
- return(1);
-
- default:
- panic("wdcontrol");
- }
- /* NOTREACHED */
-
- badopen:
- printf(": status %b error %b\n",
- stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
- du->dk_state = OPENRAW;
- return(1);
- }
-
- wdsetctlr(dev, du) dev_t dev; struct disk *du; {
- int stat;
-
- outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders);
- outb(wdc+wd_cyl_hi, (du->dk_dd.d_ncylinders)>>8);
- outb(wdc+wd_sdh, WDSD_IBM | (wdunit(dev) << 4) + du->dk_dd.d_ntracks-1);
- outb(wdc+wd_seccnt, du->dk_dd.d_nsectors);
- outb(wdc+wd_command, 0x91);
-
- while ((stat = inb(wdc+wd_status)) & WDCS_BUSY) ;
- stat = inb(wdc+wd_error);
- return(stat);
- }
-
- /* ARGSUSED */
- wdclose(dev, flags, fmt)
- dev_t dev;
- int flags, fmt;
- {
- register struct disk *du;
-
- du = &wddrives[wdunit(dev)];
- du->dk_open-- ;
- /*if (du->dk_open == 0) du->dk_state = CLOSED ; does not work */
- }
-
- wdioctl(dev,cmd,addr,flag)
- dev_t dev;
- caddr_t addr;
- {
- int unit = wdunit(dev);
- register struct disk *du;
- int error = 0;
- struct uio auio;
- struct iovec aiov;
- /*int wdformat();*/
-
- du = &wddrives[unit];
-
- switch (cmd) {
-
- case DIOCGDINFO:
- *(struct disklabel *)addr = du->dk_dd;
- break;
-
- case DIOCGPART:
- ((struct partinfo *)addr)->disklab = &du->dk_dd;
- ((struct partinfo *)addr)->part =
- &du->dk_dd.d_partitions[wdpart(dev)];
- break;
-
- case DIOCSDINFO:
- if ((flag & FWRITE) == 0)
- error = EBADF;
- else
- error = setdisklabel(&du->dk_dd,
- (struct disklabel *)addr,
- 0 /*(dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart*/);
- /*if (error == 0 && dk->dk_state == OPENRAW &&
- vdreset_drive(vddinfo[unit]))
- dk->dk_state = OPEN;*/
- wdsetctlr(dev, du);
- break;
-
- case DIOCWLABEL:
- if ((flag & FWRITE) == 0)
- error = EBADF;
- else
- du->dk_wlabel = *(int *)addr;
- break;
-
- case DIOCWDINFO:
- if ((flag & FWRITE) == 0)
- error = EBADF;
- else if ((error = setdisklabel(&du->dk_dd, (struct disklabel *)addr,
- 0/*(dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart*/)) == 0) {
- int wlab;
-
- /*if (error == 0 && dk->dk_state == OPENRAW &&
- vdreset_drive(vddinfo[unit]))
- dk->dk_state = OPEN; */
- wdsetctlr(dev, du);
-
- /* simulate opening partition 0 so write succeeds */
- /* dk->dk_openpart |= (1 << 0); /* XXX */
- wlab = du->dk_wlabel;
- du->dk_wlabel = 1;
- error = writedisklabel(dev, wdstrategy, &du->dk_dd,wdpart(dev));
- /*dk->dk_openpart = dk->dk_copenpart | dk->dk_bopenpart;*/
- du->dk_wlabel = wlab;
- }
- break;
-
- #ifdef notyet
- case DIOCGDINFOP:
- *(struct disklabel **)addr = &(du->dk_dd);
- break;
-
- case DIOCWFORMAT:
- if ((flag & FWRITE) == 0)
- error = EBADF;
- else {
- register struct format_op *fop;
-
- fop = (struct format_op *)addr;
- aiov.iov_base = fop->df_buf;
- aiov.iov_len = fop->df_count;
- auio.uio_iov = &aiov;
- auio.uio_iovcnt = 1;
- auio.uio_resid = fop->df_count;
- auio.uio_segflg = 0;
- auio.uio_offset =
- fop->df_startblk * du->dk_dd.d_secsize;
- error = physio(wdformat, &rwdbuf[unit], dev, B_WRITE,
- minphys, &auio);
- fop->df_count -= auio.uio_resid;
- fop->df_reg[0] = du->dk_status;
- fop->df_reg[1] = du->dk_error;
- }
- break;
- #endif
-
- default:
- error = ENOTTY;
- break;
- }
- return (error);
- }
-
- /*wdformat(bp)
- struct buf *bp;
- {
-
- bp->b_flags |= B_FORMAT;
- return (wdstrategy(bp));
- }*/
-
- /*
- * Routines to do raw IO for a unit.
- */
- wdread(dev, uio) /* character read routine */
- dev_t dev;
- struct uio *uio;
- {
- int unit = wdunit(dev) ;
-
- if (unit >= NWD) return(ENXIO);
- return(physio(wdstrategy, &rwdbuf[unit], dev, B_READ, minphys, uio));
- }
-
-
- wdwrite(dev, uio) /* character write routine */
- dev_t dev;
- struct uio *uio;
- {
- int unit = wdunit(dev) ;
-
- if (unit >= NWD) return(ENXIO);
- return(physio(wdstrategy, &rwdbuf[unit], dev, B_WRITE, minphys, uio));
- }
-
- wdsize(dev)
- dev_t dev;
- {
- register unit = wdunit(dev);
- register part = wdpart(dev);
- register struct disk *du;
- register val ;
-
- if (unit >= NWD) return(-1);
- if (wddrives[unit].dk_state == 0) {
- val = wdopen (dev, 0);
- if (val < 0)
- return (-1);
- }
- du = &wddrives[unit];
- return((int)((u_long)du->dk_dd.d_partitions[part].p_size *
- du->dk_dd.d_secsize / 512));
- }
-
- extern char *vmmap; /* poor name! */
-
- wddump(dev) /* dump core after a system crash */
- dev_t dev;
- {
- register struct disk *du; /* disk unit to do the IO */
- register struct bt_bad *bt_ptr;
- long num; /* number of sectors to write */
- int unit, part;
- long cyloff, blknum, blkcnt;
- long cylin, head, sector, stat;
- long secpertrk, secpercyl, nblocks, i;
- char *addr;
- extern int Maxmem;
- static wddoingadump = 0 ;
- extern CMAP1;
- extern char CADDR1[];
-
-
- #ifdef ARGO
- outb(0x461,0); /* disable failsafe timer */
- #endif
- addr = (char *) 0; /* starting address */
- /* size of memory to dump */
- num = Maxmem;
- unit = wdunit(dev); /* eventually support floppies? */
- part = wdpart(dev); /* file system */
- /* check for acceptable drive number */
- if (unit >= NWD) return(ENXIO);
-
- du = &wddrives[unit];
- /* was it ever initialized ? */
- if (du->dk_state < OPEN) return (ENXIO) ;
-
- /* Convert to disk sectors */
- num = (u_long) num * NBPG / du->dk_dd.d_secsize;
-
- /* check if controller active */
- /*if (wdtab.b_active) return(EFAULT); */
- if (wddoingadump) return(EFAULT);
-
- secpertrk = du->dk_dd.d_nsectors;
- secpercyl = du->dk_dd.d_secpercyl;
- nblocks = du->dk_dd.d_partitions[part].p_size;
- cyloff = du->dk_dd.d_partitions[part].p_offset / secpercyl;
-
- /*pg("xunit %x, nblocks %d, dumplo %d num %d\n", part,nblocks,dumplo,num);*/
- /* check transfer bounds against partition size */
- if ((dumplo < 0) || ((dumplo + num) > nblocks))
- return(EINVAL);
-
- /*wdtab.b_active = 1; /* mark controller active for if we
- panic during the dump */
- wddoingadump = 1 ; i = 100000 ;
- while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0)) ;
- outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
- outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
- while (inb(wdc+wd_status) & WDCS_BUSY) ;
-
- /* some compaq controllers require this ... */
- wdsetctlr(dev, du);
-
- blknum = dumplo;
- while (num > 0) {
- #ifdef notdef
- if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER;
- if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl)
- blkcnt = secpercyl - (blknum % secpercyl);
- /* keep transfer within current cylinder */
- #endif
- pmap_enter(pmap_kernel(), vmmap, addr, VM_PROT_READ, TRUE);
-
- /* compute disk address */
- cylin = blknum / secpercyl;
- head = (blknum % secpercyl) / secpertrk;
- sector = blknum % secpertrk;
- cylin += cyloff;
-
- #ifdef notyet
- /*
- * See if the current block is in the bad block list.
- * (If we have one.)
- */
- for (bt_ptr = dkbad[unit].bt_bad;
- bt_ptr->bt_cyl != -1; bt_ptr++) {
- if (bt_ptr->bt_cyl > cylin)
- /* Sorted list, and we passed our cylinder.
- quit. */
- break;
- if (bt_ptr->bt_cyl == cylin &&
- bt_ptr->bt_trksec == (head << 8) + sector) {
- /*
- * Found bad block. Calculate new block addr.
- * This starts at the end of the disk (skip the
- * last track which is used for the bad block list),
- * and works backwards to the front of the disk.
- */
- blknum = (du->dk_dd.d_secperunit)
- - du->dk_dd.d_nsectors
- - (bt_ptr - dkbad[unit].bt_bad) - 1;
- cylin = blknum / secpercyl;
- head = (blknum % secpercyl) / secpertrk;
- sector = blknum % secpertrk;
- break;
- }
-
- #endif
- sector++; /* origin 1 */
-
- /* select drive. */
- outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
- while ((inb(wdc+wd_status) & WDCS_READY) == 0) ;
-
- /* transfer some blocks */
- outb(wdc+wd_sector, sector);
- outb(wdc+wd_seccnt,1);
- outb(wdc+wd_cyl_lo, cylin);
- outb(wdc+wd_cyl_hi, cylin >> 8);
- #ifdef notdef
- /* lets just talk about this first...*/
- pg ("sdh 0%o sector %d cyl %d addr 0x%x",
- inb(wdc+wd_sdh), inb(wdc+wd_sector),
- inb(wdc+wd_cyl_hi)*256+inb(wdc+wd_cyl_lo), addr) ;
- #endif
- #ifdef ODYSSEUS
- if(cylin < 46 || cylin > 91)pg("oops");
- #endif
- #ifdef PRIAM
- if(cylin < 40 || cylin > 79)pg("oops");
- #endif
- outb(wdc+wd_command, WDCC_WRITE);
-
- /* Ready to send data? */
- while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
- if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
-
- outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), 256);
- (int) addr += 512;
-
- if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
- /* Check data request (should be done). */
- if (inb(wdc+wd_status) & WDCS_DRQ) return(EIO) ;
-
- /* wait for completion */
- for ( i = 1000000 ; inb(wdc+wd_status) & WDCS_BUSY ; i--) {
- if (i < 0) return (EIO) ;
- }
- /* error check the xfer */
- if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
- /* update block count */
- num--;
- blknum++ ;
- if (num % 100 == 0) printf(".") ;
- }
- return(0);
- }
- #endif
-