Subject: Re: wd
To: None <current-users@sun-lamp.cs.berkeley.edu, mycroft@sun-lamp.cs.berkeley.edu,>
From: Charles Hannum <mycroft>
List: current-users
Date: 03/04/1994 15:48:41
I got an endless loop of timeout errors on wdc0, [...]
This version should work on your machine.
# This is a shell archive. Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file". Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
# wd.c
# wdreg.h
#
echo x - wd.c
sed 's/^X//' >wd.c << 'END-of-wd.c'
X/*
X * Copyright (c) 1994 Charles Hannum.
X * Copyright (c) 1990 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * William Jolitz.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X * notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X * notice, this list of conditions and the following disclaimer in the
X * documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X * must display the following acknowledgement:
X * This product includes software developed by the University of
X * California, Berkeley and its contributors.
X * 4. Neither the name of the University nor the names of its contributors
X * may be used to endorse or promote products derived from this software
X * without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X * from: @(#)wd.c 7.2 (Berkeley) 5/9/91
X * $Id: wd.c,v 1.59 1994/03/04 23:43:14 mycroft Exp $
X */
X
X#define QUIETWORKS /* define this to make wdopen() set DKFL_QUIET */
X#define INSTRUMENT /* instrumentation stuff by Brad Parker */
X
X#include "wd.h"
X#if NWDC > 0
X
X#include <sys/param.h>
X#include <sys/dkbad.h>
X#include <sys/systm.h>
X#include <sys/kernel.h>
X#include <sys/conf.h>
X#include <sys/file.h>
X#include <sys/stat.h>
X#include <sys/ioctl.h>
X#include <sys/disklabel.h>
X#include <sys/buf.h>
X#include <sys/uio.h>
X#include <sys/malloc.h>
X#include <sys/syslog.h>
X#ifdef INSTRUMENT
X#include <sys/dkstat.h>
X#endif
X
X#include <vm/vm.h>
X
X#include <machine/cpu.h>
X#include <machine/cpufunc.h>
X#include <machine/pio.h>
X
X#include <i386/isa/isa.h>
X#include <i386/isa/isa_device.h>
X#include <i386/isa/icu.h>
X#include <i386/isa/wdreg.h>
X
X#define WDCNDELAY 100000 /* delay = 100us; so 10s for a controller state change */
X#define WDCDELAY 100
X
X#if 0
X/* if you enable this, it will report any delays more than 100us * N long */
X#define WDCNDELAY_DEBUG 100
X#endif
X
X#define WDIORETRIES 5 /* number of retries before giving up */
X
X#define WDUNIT(dev) ((minor(dev) & 0xf8) >> 3)
X#define WDPART(dev) ((minor(dev) & 0x07) )
X#define makewddev(maj, unit, part) (makedev(maj, ((unit << 3) + part)))
X#define WDRAW 3 /* 'd' partition isn't a partition! */
X
X#define b_cylin b_resid /* cylinder number for doing IO to */
X /* shares an entry in the buf struct */
X
X/*
X * Drive states. Used to initialize drive.
X */
X#define CLOSED 0 /* disk is closed. */
X#define WANTOPEN 1 /* open requested, not started */
X#define RECAL 2 /* doing restore */
X#define OPEN 3 /* done with open */
X
X/*
X * Drive status.
X */
Xstruct disk {
X long dk_bc; /* byte count left */
X long dk_bct; /* total byte count left */
X short dk_skip; /* blocks already transferred */
X short dk_skipm; /* blocks already transferred for multi */
X char dk_ctrlr; /* physical controller number */
X char dk_unit; /* physical unit number */
X char dk_lunit; /* logical unit number */
X char dk_state; /* control state */
X int dk_timeout; /* timeout counter */
X u_char dk_status; /* copy of status reg. */
X u_char dk_error; /* copy of error reg. */
X short dk_port; /* i/o port base */
X
X u_long dk_copenpart; /* character units open on this drive */
X u_long dk_bopenpart; /* block units open on this drive */
X u_long dk_openpart; /* all units open on this drive */
X short dk_wlabel; /* label writable? */
X short dk_flags; /* drive characteistics found */
X#define DKFL_QUIET 0x00002 /* report errors back, but don't complain */
X#define DKFL_SINGLE 0x00004 /* sector at a time mode */
X#define DKFL_ERROR 0x00008 /* processing a disk error */
X#define DKFL_BSDLABEL 0x00010 /* has a BSD disk label */
X#define DKFL_BADSECT 0x00020 /* has a bad144 badsector table */
X#define DKFL_WRITEPROT 0x00040 /* manual unit write protect */
X struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */
X struct disklabel dk_dd; /* device configuration data */
X struct cpu_disklabel dk_cpd;
X long dk_badsect[127]; /* 126 plus trailing -1 marker */
X};
X
Xstruct board {
X short dkc_port;
X};
X
Xstruct board wdcontroller[NWDC];
Xstruct disk *wddrives[NWD]; /* table of units */
Xstruct buf wdtab[NWDC]; /* various per-controller info */
Xstruct buf wdutab[NWD]; /* head of queue per drive */
Xstruct buf rwdbuf[NWD]; /* buffers for raw IO */
Xlong wdxfer[NWD]; /* count of transfers */
X
Xint wdprobe(), wdattach();
X
Xstruct isa_driver wdcdriver = {
X wdprobe, wdattach, "wdc",
X};
X
Xstatic void wdustart __P((struct disk *));
Xstatic void wdstart __P((int));
Xstatic int wdcommand __P((struct disk *, int));
Xstatic int wdcontrol __P((struct buf *));
Xstatic int wdsetctlr __P((struct disk *));
Xstatic int wdgetctlr __P((struct disk *));
Xstatic void bad144intern __P((struct disk *));
Xstatic int wdreset __P((struct disk *, int));
Xstatic int wdtimeout __P((caddr_t));
Xvoid wddisksort __P((struct buf *dp, struct buf *bp));
Xint wdc_wait __P((struct disk *, int));
X#define wait_for_drq(d) wdc_wait(d, WDCS_DRQ)
X#define wait_for_ready(d) wdc_wait(d, WDCS_READY | WDCS_SEEKCMPLT)
X#define wait_for_unbusy(d) wdc_wait(d, 0)
X
X/*
X * Probe for controller.
X */
Xint
Xwdprobe(isa_dev)
X struct isa_device *isa_dev;
X{
X struct disk *du;
X int wdc;
X
X if (isa_dev->id_unit >= NWDC)
X return 0;
X
X du = (struct disk *)malloc(sizeof(struct disk), M_TEMP, M_NOWAIT);
X bzero(du, sizeof(struct disk));
X
X du->dk_ctrlr = isa_dev->id_unit;
X du->dk_unit = 0;
X du->dk_lunit = 0;
X wdcontroller[isa_dev->id_unit].dkc_port = isa_dev->id_iobase;
X
X wdc = du->dk_port = isa_dev->id_iobase;
X
X /* check if we have registers that work */
X outb(wdc+wd_error, 0x5a); /* error register not writable */
X outb(wdc+wd_cyl_lo, 0xa5); /* but all of cyllo are implemented */
X if (inb(wdc+wd_error) == 0x5a || inb(wdc+wd_cyl_lo) != 0xa5)
X goto nodevice;
X
X wdreset(du, 0);
X
X /* execute a controller only command */
X if (wdcommand(du, WDCC_DIAGNOSE) < 0)
X goto nodevice;
X
X free(du, M_TEMP);
X return 8;
X
Xnodevice:
X free(du, M_TEMP);
X return 0;
X}
X
X/*
X * Called for the controller too
X * Attach each drive if possible.
X */
Xint
Xwdattach(isa_dev)
X struct isa_device *isa_dev;
X{
X int unit, lunit;
X struct disk *du;
X int i, blank;
X
X if (isa_dev->id_masunit == -1)
X return 0;
X if (isa_dev->id_masunit >= NWDC)
X return 0;
X
X lunit = isa_dev->id_unit;
X if (lunit == -1) {
X printf("wdc%d: cannot support unit ?\n", isa_dev->id_masunit);
X return 0;
X }
X if (lunit >= NWD)
X return 0;
X unit = isa_dev->id_physid;
X
X du = wddrives[lunit] =
X (struct disk *)malloc(sizeof(struct disk), M_TEMP, M_NOWAIT);
X bzero(du, sizeof(struct disk));
X bzero(&wdutab[lunit], sizeof(struct buf));
X bzero(&rwdbuf[lunit], sizeof(struct buf));
X wdxfer[lunit] = 0;
X du->dk_ctrlr = isa_dev->id_masunit;
X du->dk_unit = unit;
X du->dk_lunit = lunit;
X du->dk_port = wdcontroller[isa_dev->id_masunit].dkc_port;
X
X if (wdgetctlr(du) != 0) {
X /*printf("wd%d at wdc%d slave %d -- error\n", lunit,
X isa_dev->id_masunit, unit);*/
X wddrives[lunit] = 0;
X free(du, M_TEMP);
X return 0;
X }
X
X printf("wd%d at wdc%d targ %d: ", isa_dev->id_unit,
X isa_dev->id_masunit, isa_dev->id_physid);
X if (du->dk_params.wdp_heads == 0)
X printf("(unknown size) <");
X else
X printf("%dMB %d cyl, %d head, %d sec <",
X du->dk_dd.d_ncylinders * du->dk_dd.d_secpercyl /
X (1048576 / DEV_BSIZE),
X du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks,
X du->dk_dd.d_nsectors);
X for (i = blank = 0; i < sizeof(du->dk_params.wdp_model); i++) {
X char c = du->dk_params.wdp_model[i];
X if (c == '\0')
X break;
X if (c != ' ') {
X if (blank)
X printf(" %c", c);
X else
X printf("%c", c);
X blank = 0;
X } else
X blank = 1;
X }
X printf(">\n");
X
X wdtimeout((caddr_t)du);
X
X return 1;
X}
X
X/* Read/write routine for a buffer. Finds the proper unit, range checks
X * arguments, and schedules the transfer. Does not wait for the transfer
X * to complete. Multi-page transfers are supported. All I/O requests must
X * be a multiple of a sector in length.
X */
Xint
Xwdstrategy(bp)
X struct buf *bp;
X{
X struct buf *dp;
X struct disk *du; /* Disk unit to do the IO. */
X int lunit = WDUNIT(bp->b_dev);
X int s;
X
X /* valid unit, controller, and request? */
X if (lunit >= NWD || bp->b_blkno < 0 ||
X howmany(bp->b_bcount, DEV_BSIZE) >= (1 << NBBY) ||
X (du = wddrives[lunit]) == 0) {
X bp->b_error = EINVAL;
X bp->b_flags |= B_ERROR;
X goto done;
X }
X
X /* "soft" write protect check */
X if ((du->dk_flags & DKFL_WRITEPROT) && (bp->b_flags & B_READ) == 0) {
X bp->b_error = EROFS;
X bp->b_flags |= B_ERROR;
X goto done;
X }
X
X /* have partitions and want to use them? */
X if ((du->dk_flags & DKFL_BSDLABEL) != 0 && WDPART(bp->b_dev) != WDRAW) {
X /*
X * do bounds checking, adjust transfer. if error, process.
X * if end of partition, just return
X */
X if (bounds_check_with_label(bp, &du->dk_dd, du->dk_wlabel) <= 0)
X goto done;
X /* otherwise, process transfer request */
X }
X
X /* don't bother */
X bp->b_cylin = 0;
X
X /* queue transfer on drive, activate drive and controller if idle */
X dp = &wdutab[lunit];
X s = splbio();
X wddisksort(dp, bp);
X if (dp->b_active == 0)
X wdustart(du); /* start drive */
X if (wdtab[du->dk_ctrlr].b_active == 0)
X wdstart(du->dk_ctrlr); /* start controller */
X splx(s);
X return 0;
X
Xdone:
X /* toss transfer, we're done early */
X biodone(bp);
X}
X
X/*
X * Need to skip over multitransfer bufs.
X */
Xvoid
Xwddisksort(dp, bp)
X struct buf *dp, *bp;
X{
X struct buf *ap;
X
X while ((ap = dp->b_actf) && ap->b_flags & B_XXX)
X dp = ap;
X disksort(dp, bp);
X}
X
X/*
X * Routine to queue a command to the controller. The unit's
X * request is linked into the active list for the controller.
X * If the controller is idle, the transfer is started.
X */
Xstatic void
Xwdustart(du)
X struct disk *du;
X{
X struct buf *dp = &wdutab[du->dk_lunit];
X int ctrlr = du->dk_ctrlr;
X
X /* unit already active? */
X if (dp->b_active)
X return;
X
X /* anything to start? */
X if (dp->b_actf == NULL)
X return;
X
X /* link onto controller queue */
X dp->b_forw = NULL;
X if (wdtab[ctrlr].b_forw == NULL)
X wdtab[ctrlr].b_forw = dp;
X else
X wdtab[ctrlr].b_actl->b_forw = dp;
X wdtab[ctrlr].b_actl = dp;
X
X /* mark the drive unit as busy */
X dp->b_active = 1;
X}
X
X/*
X * Controller startup routine. This does the calculation, and starts
X * a single-sector read or write operation. Called to start a transfer,
X * or from the interrupt routine to continue a multi-sector transfer.
X * RESTRICTIONS:
X * 1. The transfer length must be an exact multiple of the sector size.
X */
Xstatic void
Xwdstart(ctrlr)
X int ctrlr;
X{
X register struct disk *du; /* disk unit for IO */
X register struct buf *bp;
X struct disklabel *lp;
X struct buf *dp;
X long blknum, cylin, head, sector;
X long secpertrk, secpercyl, addr;
X int lunit, wdc;
X int xfrblknum;
X
Xloop:
X /* is there a drive for the controller to do a transfer with? */
X dp = wdtab[ctrlr].b_forw;
X if (dp == NULL) {
X wdtab[ctrlr].b_active = 0;
X return;
X }
X
X /* is there a transfer to this drive ? if so, link it on
X the controller's queue */
X bp = dp->b_actf;
X if (bp == NULL) {
X dp->b_active = 0;
X wdtab[ctrlr].b_forw = dp->b_forw;
X goto loop;
X }
X
X /* obtain controller and drive information */
X lunit = WDUNIT(bp->b_dev);
X du = wddrives[lunit];
X
X /* clear any pending timeout, just in case */
X du->dk_timeout = 0;
X
X /* if not really a transfer, do control operations specially */
X if (du->dk_state < OPEN) {
X (void) wdcontrol(bp);
X return;
X }
X
X /* calculate transfer details */
X blknum = bp->b_blkno + du->dk_skip;
X#ifdef WDDEBUG
X if (du->dk_skip == 0)
X printf("\nwdstart %d: %s %d@%d; map ", lunit,
X (bp->b_flags & B_READ) ? "read" : "write", bp->b_bcount,
X blknum);
X else
X printf(" %d)%x", du->dk_skip, inb(du->dk_port+wd_altsts));
X#endif
X addr = (int)bp->b_un.b_addr;
X if (du->dk_skip == 0)
X du->dk_bc = bp->b_bcount;
X if (du->dk_skipm == 0) {
X struct buf *oldbp, *nextbp;
X oldbp = bp;
X nextbp = bp->b_actf;
X du->dk_bct = du->dk_bc;
X oldbp->b_flags |= B_XXX;
X while (nextbp && (oldbp->b_flags & DKFL_SINGLE) == 0 &&
X oldbp->b_dev == nextbp->b_dev && nextbp->b_blkno ==
X (oldbp->b_blkno + (oldbp->b_bcount / DEV_BSIZE)) &&
X (oldbp->b_flags & B_READ) == (nextbp->b_flags & B_READ)) {
X if ((du->dk_bct + nextbp->b_bcount) / DEV_BSIZE >= 240)
X break;
X du->dk_bct += nextbp->b_bcount;
X nextbp->b_flags |= B_XXX;
X oldbp = nextbp;
X nextbp = nextbp->b_actf;
X }
X }
X
X lp = &du->dk_dd;
X secpertrk = lp->d_nsectors;
X secpercyl = lp->d_secpercyl;
X if ((du->dk_flags & DKFL_BSDLABEL) != 0 && WDPART(bp->b_dev) != WDRAW)
X blknum += lp->d_partitions[WDPART(bp->b_dev)].p_offset;
X cylin = blknum / secpercyl;
X head = (blknum % secpercyl) / secpertrk;
X sector = blknum % secpertrk;
X
X /* Check for bad sectors if we have them, and not formatting */
X /* Only do this in single-sector mode, or when starting a */
X /* multiple-sector transfer. */
X if ((du->dk_flags & DKFL_BADSECT) &&
X#ifdef B_FORMAT
X (bp->b_flags & B_FORMAT) == 0 &&
X#endif
X (du->dk_skipm == 0 || (du->dk_flags & DKFL_SINGLE))) {
X
X long blkchk, blkend, blknew;
X int i;
X
X blkend = blknum + howmany(du->dk_bct, DEV_BSIZE) - 1;
X for (i = 0; (blkchk = du->dk_badsect[i]) != -1; i++) {
X if (blkchk > blkend)
X break; /* transfer is completely OK; done */
X if (blkchk == blknum) {
X blknew =
X lp->d_secperunit - lp->d_nsectors - i - 1;
X cylin = blknew / secpercyl;
X head = (blknew % secpercyl) / secpertrk;
X sector = blknew % secpertrk;
X du->dk_flags |= DKFL_SINGLE;
X /* found and replaced first blk of transfer; done */
X break;
X } else if (blkchk > blknum) {
X du->dk_flags |= DKFL_SINGLE;
X break; /* bad block inside transfer; done */
X }
X }
X }
X if (du->dk_flags & DKFL_SINGLE) {
X du->dk_bct = du->dk_bc;
X du->dk_skipm = du->dk_skip;
X }
X
X#ifdef WDDEBUG
X printf("c%d h%d s%d ", cylin, head, sector);
X#endif
X
X sector++; /* sectors begin with 1, not 0 */
X
X wdtab[ctrlr].b_active = 1; /* mark controller active */
X wdc = du->dk_port;
X
X#ifdef INSTRUMENT
X /* instrumentation */
X if (du->dk_unit >= 0 && du->dk_skip == 0) {
X dk_busy |= 1 << du->dk_lunit;
X dk_wds[du->dk_lunit] += bp->b_bcount >> 6;
X }
X if (du->dk_unit >= 0 && du->dk_skipm == 0) {
X ++dk_seek[du->dk_lunit];
X ++dk_xfer[du->dk_lunit];
X }
X#endif
X
Xretry:
X /* if starting a multisector transfer, or doing single transfers */
X if (du->dk_skipm == 0 || (du->dk_flags & DKFL_SINGLE)) {
X int command;
X
X if (wdtab[ctrlr].b_errcnt && (bp->b_flags & B_READ) == 0) {
X du->dk_bc += DEV_BSIZE;
X du->dk_bct += DEV_BSIZE;
X }
X
X /* controller idle? */
X if (wait_for_unbusy(du) < 0) {
X wdreset(du, 1);
X goto retry;
X }
X
X /* stuff the task file */
X outb(wdc+wd_precomp, lp->d_precompcyl / 4);
X#ifdef B_FORMAT
X if (bp->b_flags & B_FORMAT) {
X outb(wdc+wd_sector, lp->d_gap3);
X outb(wdc+wd_seccnt, lp->d_nsectors);
X } else {
X if (du->dk_flags & DKFL_SINGLE)
X outb(wdc+wd_seccnt, 1);
X else
X outb(wdc+wd_seccnt,
X howmany(du->dk_bct, DEV_BSIZE));
X outb(wdc+wd_sector, sector);
X }
X#else
X if (du->dk_flags & DKFL_SINGLE)
X outb(wdc+wd_seccnt, 1);
X else
X outb(wdc+wd_seccnt, howmany(du->dk_bct, DEV_BSIZE));
X outb(wdc+wd_sector, sector);
X#endif
X outb(wdc+wd_cyl_lo, cylin);
X outb(wdc+wd_cyl_hi, cylin >> 8);
X
X /* set up the SDH register (select drive) */
X outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit<<4) | (head & 0xf));
X /* wait for drive to become ready */
X if (wait_for_ready(du) < 0) {
X wdreset(du, 1);
X goto retry;
X }
X
X /* initiate command! */
X#ifdef B_FORMAT
X if (bp->b_flags & B_FORMAT)
X command = WDCC_FORMAT;
X else
X command =
X (bp->b_flags & B_READ) ? WDCC_READ : WDCC_WRITE;
X#else
X command = (bp->b_flags & B_READ) ? WDCC_READ : WDCC_WRITE;
X#endif
X if (wdcommand(du, command) < 0) {
X wdreset(du, 1);
X goto retry;
X }
X#ifdef WDDEBUG
X printf("sector %d cylin %d head %d addr %x sts %x\n", sector,
X cylin, head, addr, inb(wdc+wd_altsts));
X#endif
X }
X
X /* if this is a read operation, just go away until it's done. */
X if (bp->b_flags & B_READ) {
X du->dk_timeout = 2;
X return;
X }
X
X if (wait_for_drq(du) < 0) {
X wdreset(du, 1);
X goto retry;
X }
X /* then send it! */
X outsw(wdc+wd_data, addr + du->dk_skip * DEV_BSIZE,
X DEV_BSIZE / sizeof(short));
X
X du->dk_bc -= DEV_BSIZE;
X du->dk_bct -= DEV_BSIZE;
X
X du->dk_timeout = 2;
X}
X
X/* Interrupt routine for the controller. Acknowledge the interrupt, check for
X * errors on the current operation, mark it done if necessary, and start
X * the next request. Also check for a partially done transfer, and
X * continue with the next chunk if so.
X */
Xvoid
Xwdintr(ctrlr)
X int ctrlr;
X{
X register struct disk *du;
X register struct buf *bp, *dp;
X int stat, wdc;
X
X /* clear the pending interrupt */
X (void) inb(wdcontroller[ctrlr].dkc_port+wd_status);
X
X if (!wdtab[ctrlr].b_active) {
X printf("wdc%d: extra interrupt\n", ctrlr);
X return;
X }
X
X dp = wdtab[ctrlr].b_forw;
X bp = dp->b_actf;
X du = wddrives[WDUNIT(bp->b_dev)];
X wdc = du->dk_port;
X du->dk_timeout = 0;
X
X#ifdef WDDEBUG
X printf("I%d ", ctrlr);
X#endif
X
X stat = wait_for_unbusy(du);
X if (stat < 0) {
X printf("wdc%d: timeout waiting for unbusy\n", ctrlr);
X goto lose;
X }
X
X /* is it not a transfer, but a control operation? */
X if (du->dk_state < OPEN) {
X wdtab[ctrlr].b_active = 0;
X if (wdcontrol(bp))
X wdstart(ctrlr);
X return;
X }
X
X /* have we an error? */
X if (stat & (WDCS_ERR | WDCS_ECCCOR)) {
X lose:
X du->dk_status = stat;
X du->dk_error = inb(wdc+wd_error);
X#ifdef WDDEBUG
X printf("stat %x error %x\n", stat, du->dk_error);
X#endif
X if ((du->dk_flags & DKFL_SINGLE) == 0) {
X du->dk_flags |= DKFL_ERROR;
X goto outt;
X }
X#ifdef B_FORMAT
X if (bp->b_flags & B_FORMAT) {
X bp->b_error = EIO;
X bp->b_flags |= B_ERROR;
X goto done;
X }
X#endif
X
X /* error or error correction? */
X if (stat & WDCS_ERR) {
X if (++wdtab[ctrlr].b_errcnt < WDIORETRIES)
X wdtab[ctrlr].b_active = 0;
X else {
X if ((du->dk_flags & DKFL_QUIET) == 0) {
X diskerr(bp, "wd", "hard error",
X LOG_PRINTF, du->dk_skip,
X &du->dk_dd);
X#ifdef WDDEBUG
X printf("stat %b error %b\n", stat,
X WDCS_BITS, inb(wdc+wd_error),
X WDERR_BITS);
X#endif
X }
X bp->b_error = EIO;
X bp->b_flags |= B_ERROR; /* flag the error */
X }
X } else if ((du->dk_flags & DKFL_QUIET) == 0)
X diskerr(bp, "wd", "soft ecc", 0, du->dk_skip,
X &du->dk_dd);
X }
X
X /*
X * If this was a successful read operation, fetch the data.
X */
X if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) &&
X wdtab[ctrlr].b_active) {
X int chk;
X
X chk = min(DEV_BSIZE / sizeof(short), du->dk_bc / sizeof(short));
X
X /* ready to receive data? */
X if (wait_for_drq(du) < 0) {
X printf("wdc%d: timeout waiting for drq\n", ctrlr);
X goto lose;
X }
X
X /* suck in data */
X insw(wdc+wd_data,
X (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE, chk);
X du->dk_bc -= chk * sizeof(short);
X du->dk_bct -= chk * sizeof(short);
X
X /* for obselete fractional sector reads */
X while (chk++ < (DEV_BSIZE / sizeof(short)))
X (void) inw(wdc+wd_data);
X }
X
X wdxfer[du->dk_lunit]++;
Xoutt:
X if (wdtab[ctrlr].b_active) {
X#ifdef INSTRUMENT
X if (du->dk_unit >= 0)
X dk_busy &= ~(1 << du->dk_unit);
X#endif
X if ((bp->b_flags & B_ERROR) == 0) {
X du->dk_skip++; /* Add to succ. sect */
X du->dk_skipm++; /* Add to succ. sect for multitransfer */
X if (wdtab[ctrlr].b_errcnt &&
X (du->dk_flags & DKFL_QUIET) == 0)
X diskerr(bp, "wd", "soft error", 0, du->dk_skip,
X &du->dk_dd);
X wdtab[ctrlr].b_errcnt = 0;
X
X /* see if more to transfer */
X if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) {
X wdtab[ctrlr].b_active = 0;
X wdstart(ctrlr);
X return; /* next chunk is started */
X } else if ((du->dk_flags & (DKFL_SINGLE | DKFL_ERROR)) == DKFL_ERROR) {
X du->dk_skip = 0;
X du->dk_skipm = 0;
X du->dk_flags &= ~DKFL_ERROR;
X du->dk_flags |= DKFL_SINGLE;
X wdtab[ctrlr].b_active = 0;
X wdstart(ctrlr);
X return; /* redo xfer sector by sector */
X }
X }
X
Xdone:
X /* done with this transfer, with or without error */
X du->dk_flags &= ~DKFL_SINGLE;
X wdtab[ctrlr].b_errcnt = 0;
X if (du->dk_bct == 0) {
X wdtab[ctrlr].b_forw = dp->b_forw;
X du->dk_skipm = 0;
X dp->b_active = 0;
X }
X bp->b_resid = bp->b_bcount - du->dk_skip * DEV_BSIZE;
X bp->b_flags &= ~B_XXX;
X du->dk_skip = 0;
X dp->b_actf = bp->b_actf;
X dp->b_errcnt = 0;
X biodone(bp);
X }
X
X /* controller now idle */
X wdtab[ctrlr].b_active = 0;
X
X /* anything more on drive queue? */
X if (dp->b_actf && du->dk_bct == 0)
X wdustart(du);
X
X /* anything more for controller to do? */
X if (wdtab[ctrlr].b_forw)
X wdstart(ctrlr);
X}
X
X/*
X * Initialize a drive.
X */
Xint
Xwdopen(dev, flag, fmt, p)
X dev_t dev;
X int flag;
X int fmt;
X struct proc *p;
X{
X int lunit;
X struct disk *du;
X int part = WDPART(dev), mask = 1 << part;
X struct partition *pp;
X char *msg;
X
X lunit = WDUNIT(dev);
X if (lunit >= NWD)
X return ENXIO;
X du = wddrives[lunit];
X if (du == 0)
X return ENXIO;
X
X#ifdef QUIETWORKS
X if (part == WDRAW)
X du->dk_flags |= DKFL_QUIET;
X else
X du->dk_flags &= ~DKFL_QUIET;
X#else
X du->dk_flags &= ~DKFL_QUIET;
X#endif
X
X if ((du->dk_flags & DKFL_BSDLABEL) == 0) {
X du->dk_flags |= DKFL_WRITEPROT;
X wdutab[lunit].b_actf = NULL;
X
X /*
X * Use the default sizes until we've read the label,
X * or longer if there isn't one there.
X */
X bzero(&du->dk_dd, sizeof(du->dk_dd));
X du->dk_dd.d_type = DTYPE_ST506;
X du->dk_dd.d_ncylinders = 1024;
X du->dk_dd.d_secsize = DEV_BSIZE;
X du->dk_dd.d_ntracks = 8;
X du->dk_dd.d_nsectors = 17;
X du->dk_dd.d_secpercyl = 17*8;
X du->dk_dd.d_secperunit = 17*8*1024;
X du->dk_state = WANTOPEN;
X
X /* read label using "raw" partition */
X#ifdef notdef
X /* wdsetctlr(du); */ /* Maybe do this TIH */
X msg = readdisklabel(makewddev(major(dev), WDUNIT(dev), WDRAW),
X wdstrategy, &du->dk_dd, &du->dk_cpd);
X wdsetctlr(du);
X#endif
X if (msg = readdisklabel(makewddev(major(dev), WDUNIT(dev),
X WDRAW), wdstrategy, &du->dk_dd, &du->dk_cpd)) {
X if ((du->dk_flags & DKFL_QUIET) == 0) {
X log(LOG_WARNING,
X "wd%d: cannot find label (%s)\n",
X lunit, msg);
X return EINVAL; /* XXX needs translation */
X }
X } else {
X wdsetctlr(du);
X du->dk_flags |= DKFL_BSDLABEL;
X du->dk_flags &= ~DKFL_WRITEPROT;
X if (du->dk_dd.d_flags & D_BADSECT)
X du->dk_flags |= DKFL_BADSECT;
X }
X }
X
X if (du->dk_flags & DKFL_BADSECT)
X bad144intern(du);
X
X /*
X * Warn if a partion is opened
X * that overlaps another partition which is open
X * unless one is the "raw" partition (whole disk).
X */
X if ((du->dk_openpart & mask) == 0 && part != WDRAW) {
X int start, end;
X
X pp = &du->dk_dd.d_partitions[part];
X start = pp->p_offset;
X end = pp->p_offset + pp->p_size;
X for (pp = du->dk_dd.d_partitions;
X pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions];
X pp++) {
X if (pp->p_offset + pp->p_size <= start ||
X pp->p_offset >= end)
X continue;
X if (pp - du->dk_dd.d_partitions == WDRAW)
X continue;
X if (du->dk_openpart & (1 << (pp - du->dk_dd.d_partitions)))
X log(LOG_WARNING,
X "wd%d%c: overlaps open partition (%c)\n",
X lunit, part + 'a',
X pp - du->dk_dd.d_partitions + 'a');
X }
X }
X if (part >= du->dk_dd.d_npartitions && part != WDRAW)
X return ENXIO;
X
X /* insure only one open at a time */
X du->dk_openpart |= mask;
X switch (fmt) {
X case S_IFCHR:
X du->dk_copenpart |= mask;
X break;
X case S_IFBLK:
X du->dk_bopenpart |= mask;
X break;
X }
X return 0;
X}
X
X/*
X * Implement operations other than read/write.
X * Called from wdstart or wdintr during opens and formats.
X * Uses finite-state-machine to track progress of operation in progress.
X * Returns 0 if operation still in progress, 1 if completed.
X */
Xstatic int
Xwdcontrol(bp)
X struct buf *bp;
X{
X struct disk *du;
X int unit, lunit;
X int stat;
X int s, ctrlr;
X int wdc;
X
X du = wddrives[WDUNIT(bp->b_dev)];
X ctrlr = du->dk_ctrlr;
X unit = du->dk_unit;
X lunit = du->dk_lunit;
X wdc = du->dk_port;
X
X switch (du->dk_state) {
X case WANTOPEN: /* set SDH, step rate, do restore */
X tryagainrecal:
X#ifdef WDDEBUG
X printf("wd%d: recal ", lunit);
X#endif
X s = splbio(); /* not called from intr level ... */
X wdgetctlr(du);
X
X if (wait_for_unbusy(du) < 0) {
X lose:
X wdreset(du, 1);
X splx(s);
X goto tryagainrecal;
X }
X outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
X if (wait_for_ready(du) < 0)
X goto lose;
X wdtab[ctrlr].b_active = 1;
X if (wdcommand(du, WDCC_RESTORE | WD_STEP) < 0)
X goto lose;
X du->dk_state = RECAL;
X splx(s);
X return 0;
X
X case RECAL:
X if ((stat = inb(wdc+wd_altsts)) & WDCS_ERR) {
X if ((du->dk_flags & DKFL_QUIET) == 0) {
X printf("wd%d: recal", du->dk_lunit);
X printf(": status %b error %b\n", stat,
X WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
X }
X if (++wdtab[ctrlr].b_errcnt < WDIORETRIES)
X goto tryagainrecal;
X bp->b_error = ENXIO; /* XXX needs translation */
X goto badopen;
X }
X
X /* some controllers require this ... */
X wdsetctlr(du);
X
X wdtab[ctrlr].b_errcnt = 0;
X du->dk_state = OPEN;
X /*
X * The rest of the initialization can be done
X * by normal means.
X */
X return 1;
X
X default:
X panic("wdcontrol");
X }
X /* NOTREACHED */
X
Xbadopen:
X if ((du->dk_flags & DKFL_QUIET) == 0)
X printf(": status %b error %b\n", stat, WDCS_BITS,
X inb(wdc+wd_error), WDERR_BITS);
X bp->b_flags |= B_ERROR;
X return 1;
X}
X
X/*
X * send a command and wait uninterruptibly until controller is finished.
X * return -1 if controller busy for too long, otherwise
X * return status. intended for brief controller commands at critical points.
X * assumes interrupts are blocked.
X */
Xstatic int
Xwdcommand(du, cmd)
X struct disk *du;
X int cmd;
X{
X int stat, wdc;
X
X wdc = du->dk_port;
X
X /* controller ready for command? */
X if (wait_for_unbusy(du) < 0)
X return -1;
X
X /* send command, await results */
X outb(wdc+wd_command, cmd);
X
X switch (cmd) {
X default:
X#if 0
X case WDCC_FORMAT:
X case WDCC_RESTORE | WD_STEP:
X#endif
X return wait_for_unbusy(du);
X case WDCC_READ:
X case WDCC_WRITE:
X return 0;
X case WDCC_IDC:
X case WDCC_DIAGNOSE:
X return wdc_wait(du, WDCS_READY);
X case WDCC_READP:
X return wait_for_drq(du);
X }
X}
X
X/*
X * issue IDC to drive to tell it just what geometry it is to be.
X */
Xstatic int
Xwdsetctlr(du)
X struct disk *du;
X{
X int stat, s, wdc;
X
X#ifdef WDDEBUG
X printf("wd(%d,%d) C%dH%dS%d\n", du->dk_ctrlr, du->dk_unit,
X du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, du->dk_dd.d_nsectors);
X#endif
X
X wdc = du->dk_port;
X
X s = splbio();
X if (wait_for_unbusy(du) < 0) {
X lose:
X splx(s);
X return -1;
X }
X outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders); /* TIH: was ...ders+1 */
X outb(wdc+wd_cyl_hi, du->dk_dd.d_ncylinders>>8); /* TIH: was ...ders+1 */
X outb(wdc+wd_sdh,
X WDSD_IBM | (du->dk_unit << 4) + du->dk_dd.d_ntracks - 1);
X outb(wdc+wd_seccnt, du->dk_dd.d_nsectors);
X if (wait_for_ready(du) < 0)
X goto lose;
X stat = wdcommand(du, WDCC_IDC);
X if (stat < 0 || stat & WDCS_ERR)
X printf("wdsetctlr: stat %b error %b\n", stat, WDCS_BITS,
X inb(wdc+wd_error), WDERR_BITS);
X splx(s);
X return stat;
X}
X
X/*
X * issue READP to drive to ask it what it is.
X */
Xstatic int
Xwdgetctlr(du)
X struct disk *du;
X{
X int stat, s, i, wdc;
X char tb[DEV_BSIZE];
X struct wdparams *wp;
X
X s = splbio(); /* not called from intr level ... */
X wdc = du->dk_port;
X if (wait_for_unbusy(du) < 0) {
X lose:
X splx(s);
X return -1;
X }
X outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit << 4));
X if (wait_for_ready(du) < 0)
X goto lose;
X stat = wdcommand(du, WDCC_READP);
X if (stat < 0)
X goto lose;
X
X if ((stat & WDCS_ERR) == 0) {
X /* obtain parameters */
X wp = &du->dk_params;
X insw(wdc+wd_data, tb, sizeof(tb)/sizeof(short));
X bcopy(tb, wp, sizeof(struct wdparams));
X
X /* shuffle string byte order */
X for (i = 0; i < sizeof(wp->wdp_model); i += 2) {
X u_short *p;
X p = (u_short *)(wp->wdp_model + i);
X *p = ntohs(*p);
X }
X
X strncpy(du->dk_dd.d_typename, "ESDI/IDE",
X sizeof du->dk_dd.d_typename);
X du->dk_dd.d_type = DTYPE_ESDI;
X bcopy(wp->wdp_model+20, du->dk_dd.d_packname, 14-1);
X
X /* update disklabel given drive information */
X du->dk_dd.d_ncylinders =
X wp->wdp_fixedcyl + wp->wdp_removcyl /*+- 1*/;
X du->dk_dd.d_ntracks = wp->wdp_heads;
X du->dk_dd.d_nsectors = wp->wdp_sectors;
X du->dk_dd.d_secpercyl =
X du->dk_dd.d_ntracks * du->dk_dd.d_nsectors;
X du->dk_dd.d_partitions[1].p_size =
X du->dk_dd.d_secpercyl * wp->wdp_sectors;
X du->dk_dd.d_partitions[1].p_offset = 0;
X } else {
X /*
X * If WDCC_READP fails then we might have an old drive
X * so we try a seek to 0; if that passes then the
X * drive is there but it's OLD AND KRUSTY.
X */
X stat = wdcommand(du, WDCC_RESTORE | WD_STEP);
X if (stat < 0 || stat & WDCS_ERR)
X goto lose;
X
X strncpy(du->dk_dd.d_typename, "ST506",
X sizeof du->dk_dd.d_typename);
X strncpy(du->dk_params.wdp_model, "Unknown Type",
X sizeof du->dk_params.wdp_model);
X du->dk_dd.d_type = DTYPE_ST506;
X }
X
X#if 0
X printf("gc %x cyl %d trk %d sec %d type %d sz %d model %s\n",
X wp->wdp_config, wp->wdp_fixedcyl + wp->wdp_removcyl, wp->wdp_heads,
X wp->wdp_sectors, wp->wdp_cntype, wp->wdp_cnsbsz, wp->wdp_model);
X#endif
X
X /* better ... */
X du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
X
X /* XXX sometimes possibly needed */
X (void) inb(wdc+wd_status);
X splx(s);
X return 0;
X}
X
Xint
Xwdclose(dev, flag, fmt)
X dev_t dev;
X int flag;
X int fmt;
X{
X struct disk *du;
X int part = WDPART(dev), mask = 1 << part;
X
X du = wddrives[WDUNIT(dev)];
X
X /* insure only one open at a time */
X du->dk_openpart &= ~mask;
X switch (fmt) {
X case S_IFCHR:
X du->dk_copenpart &= ~mask;
X break;
X case S_IFBLK:
X du->dk_bopenpart &= ~mask;
X break;
X }
X return 0;
X}
X
Xint
Xwdioctl(dev, cmd, addr, flag, p)
X dev_t dev;
X int cmd;
X caddr_t addr;
X int flag;
X struct proc *p;
X{
X int lunit = WDUNIT(dev);
X struct disk *du = wddrives[lunit];
X int error;
X
X switch (cmd) {
X case DIOCSBAD:
X if ((flag & FWRITE) == 0)
X return EBADF;
X du->dk_cpd.bad = *(struct dkbad *)addr;
X bad144intern(du);
X return 0;
X
X case DIOCGDINFO:
X *(struct disklabel *)addr = du->dk_dd;
X return 0;
X
X case DIOCGPART:
X ((struct partinfo *)addr)->disklab = &du->dk_dd;
X ((struct partinfo *)addr)->part =
X &du->dk_dd.d_partitions[WDPART(dev)];
X return 0;
X
X case DIOCSDINFO:
X if ((flag & FWRITE) == 0)
X return EBADF;
X error = setdisklabel(&du->dk_dd,
X (struct disklabel *)addr,
X /*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart : */0,
X &du->dk_cpd);
X if (error == 0) {
X du->dk_flags |= DKFL_BSDLABEL;
X wdsetctlr(du);
X }
X return error;
X
X case DIOCWLABEL:
X du->dk_flags &= ~DKFL_WRITEPROT;
X if ((flag & FWRITE) == 0)
X return EBADF;
X du->dk_wlabel = *(int *)addr;
X return 0;
X
X case DIOCWDINFO:
X du->dk_flags &= ~DKFL_WRITEPROT;
X if ((flag & FWRITE) == 0)
X return EBADF;
X error = setdisklabel(&du->dk_dd,
X (struct disklabel *)addr,
X /*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart :*/0,
X &du->dk_cpd);
X if (error == 0) {
X int wlab;
X
X du->dk_flags |= DKFL_BSDLABEL;
X wdsetctlr(du);
X
X /* simulate opening partition 0 so write succeeds */
X du->dk_openpart |= (1 << 0); /* XXX */
X wlab = du->dk_wlabel;
X du->dk_wlabel = 1;
X error = writedisklabel(dev, wdstrategy, &du->dk_dd, &du->dk_cpd);
X du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
X du->dk_wlabel = wlab;
X }
X return error;
X
X#ifdef notyet
X case DIOCGDINFOP:
X *(struct disklabel **)addr = &du->dk_dd;
X return 0;
X
X case DIOCWFORMAT:
X if ((flag & FWRITE) == 0)
X return EBADF;
X {
X register struct format_op *fop;
X struct iovec aiov;
X struct uio auio;
X
X fop = (struct format_op *)addr;
X aiov.iov_base = fop->df_buf;
X aiov.iov_len = fop->df_count;
X auio.uio_iov = &aiov;
X auio.uio_iovcnt = 1;
X auio.uio_resid = fop->df_count;
X auio.uio_segflg = 0;
X auio.uio_offset =
X fop->df_startblk * du->dk_dd.d_secsize;
X error = physio(wdformat, &rwdbuf[lunit], dev, B_WRITE,
X minphys, &auio);
X fop->df_count -= auio.uio_resid;
X fop->df_reg[0] = du->dk_status;
X fop->df_reg[1] = du->dk_error;
X return error;
X }
X#endif
X
X default:
X return ENOTTY;
X }
X
X#ifdef DIAGNOSTIC
X panic("wdioctl: impossible");
X#endif
X}
X
X#ifdef B_FORMAT
Xint
Xwdformat(struct buf *bp)
X{
X
X bp->b_flags |= B_FORMAT;
X return wdstrategy(bp);
X}
X#endif
X
Xint
Xwdsize(dev)
X dev_t dev;
X{
X int lunit = WDUNIT(dev), part = WDPART(dev);
X struct disk *du;
X
X if (lunit >= NWD)
X return -1;
X du = wddrives[lunit];
X if (du == 0)
X return -1;
X
X if (du->dk_state < OPEN || (du->dk_flags & DKFL_BSDLABEL) == 0) {
X int val;
X val = wdopen(makewddev(major(dev), lunit, WDRAW), FREAD,
X S_IFBLK, 0);
X /* XXX Clear the open flag? */
X if (val != 0)
X return -1;
X }
X
X if ((du->dk_flags & (DKFL_WRITEPROT | DKFL_BSDLABEL)) != DKFL_BSDLABEL)
X return -1;
X
X return (int)du->dk_dd.d_partitions[part].p_size;
X}
X
X/* dump core after a system crash */
Xint
Xwddump(dev)
X dev_t dev;
X{
X register struct disk *du; /* disk unit to do the IO */
X long num; /* number of sectors to write */
X int ctrlr, lunit, part, wdc;
X long blkoff, blknum;
X long cylin, head, sector, stat;
X long secpertrk, secpercyl, nblocks, i;
X char *addr;
X static wddoingadump = 0;
X extern caddr_t CADDR1;
X extern struct pte *CMAP1;
X
X addr = (char *)0; /* starting address */
X
X#if DO_NOT_KNOW_HOW
X /* toss any characters present prior to dump, ie. non-blocking getc */
X while (cngetc())
X ;
X#endif
X
X /* size of memory to dump */
X lunit = WDUNIT(dev);
X part = WDPART(dev); /* file system */
X /* check for acceptable drive number */
X if (lunit >= NWD)
X return ENXIO;
X du = wddrives[lunit];
X /* was it ever initialized ? */
X if (du == 0 || du->dk_state < OPEN || du->dk_flags & DKFL_WRITEPROT)
X return ENXIO;
X
X wdc = du->dk_port;
X ctrlr = du->dk_ctrlr;
X
X /* Convert to disk sectors */
X num = ctob(physmem) / du->dk_dd.d_secsize;
X
X /* check if controller active */
X /*if (wdtab[ctrlr].b_active)
X return EFAULT;*/
X if (wddoingadump)
X return EFAULT;
X
X secpertrk = du->dk_dd.d_nsectors;
X secpercyl = du->dk_dd.d_secpercyl;
X nblocks = du->dk_dd.d_partitions[part].p_size;
X blkoff = du->dk_dd.d_partitions[part].p_offset;
X
X /*printf("xunit %x, nblocks %d, dumplo %d num %d\n", part, nblocks,
X dumplo, num);*/
X /* check transfer bounds against partition size */
X if (dumplo < 0 || dumplo + num > nblocks)
X return EINVAL;
X
X /* mark controller active for if we panic during the dump */
X /*wdtab[ctrlr].b_active = 1;*/
X wddoingadump = 1;
X i = 200000000;
X if (wait_for_unbusy(du) < 0)
X return EIO;
X outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit << 4));
X if (wait_for_ready(du) < 0)
X return EIO;
X if (wdcommand(du, WDCC_RESTORE | WD_STEP) < 0)
X return EIO;
X
X /* some compaq controllers require this ... */
X wdsetctlr(du);
X
X blknum = dumplo + blkoff;
X while (num > 0) {
X /* compute disk address */
X cylin = blknum / secpercyl;
X head = (blknum % secpercyl) / secpertrk;
X sector = blknum % secpertrk;
X
X if (du->dk_flags & DKFL_BADSECT) {
X long newblk;
X int i;
X
X for (i = 0; du->dk_badsect[i] != -1; i++) {
X if (blknum < du->dk_badsect[i]) {
X /* sorted list, passed our block by */
X break;
X } else if (blknum == du->dk_badsect[i]) {
X newblk = du->dk_dd.d_secperunit -
X du->dk_dd.d_nsectors - i - 1;
X cylin = newblk / secpercyl;
X head = (newblk % secpercyl) / secpertrk;
X sector = newblk % secpertrk;
X /* found and replaced; done */
X break;
X }
X }
X }
X sector++; /* origin 1 */
X
X if (wait_for_unbusy(du) < 0)
X return EIO;
X /* select drive. */
X outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit << 4) | (head & 0xf));
X if (wait_for_ready(du) < 0)
X return EIO;
X
X /* transfer some blocks */
X outb(wdc+wd_sector, sector);
X outb(wdc+wd_seccnt, 1);
X outb(wdc+wd_cyl_lo, cylin);
X outb(wdc+wd_cyl_hi, cylin >> 8);
X#ifdef notdef
X /* lets just talk about this first...*/
X printf("sdh 0%o sector %d cyl %d addr 0x%x", inb(wdc+wd_sdh),
X inb(wdc+wd_sector),
X (inb(wdc+wd_cyl_hi) << 8) + inb(wdc+wd_cyl_lo), addr);
X#endif
X stat = wdcommand(du, WDCC_WRITE);
X if (stat < 0 || stat & WDCS_ERR)
X return EIO;
X
X#ifdef notdef /* cannot use this since this address was mapped differently */
X pmap_enter(kernel_pmap, CADDR1, trunc_page(addr), VM_PROT_READ, TRUE);
X#else
X *(int *)CMAP1 = PG_V | PG_KW | ctob((long)addr);
X tlbflush();
X#endif
X
X outsw(wdc+wd_data, CADDR1 + ((int)addr & PGOFSET),
X DEV_BSIZE / sizeof(short));
X
X /* Check data request (should be done). */
X if (inb(wdc+wd_status) & (WDCS_ERR | WDCS_DRQ))
X return EIO;
X
X if (wait_for_unbusy(du) < 0)
X return EIO;
X
X /* error check the xfer */
X if (inb(wdc+wd_status) & WDCS_ERR)
X return EIO;
X
X if ((unsigned)addr % 1048576 == 0)
X printf("%d ", num / (1048576 / DEV_BSIZE));
X
X /* update block count */
X num--;
X blknum++;
X (int)addr += DEV_BSIZE;
X
X#if DO_NOT_KNOW_HOW
X /* operator aborting dump? non-blocking getc() */
X if (cngetc())
X return EINTR;
X#endif
X }
X return 0;
X}
X
X/*
X * Internalize the bad sector table.
X */
Xvoid
Xbad144intern(du)
X struct disk *du;
X{
X int i;
X
X if ((du->dk_flags & DKFL_BADSECT) == 0)
X return;
X
X for (i = 0; i < 127; i++)
X du->dk_badsect[i] = -1;
X for (i = 0; i < 126; i++) {
X if (du->dk_cpd.bad.bt_bad[i].bt_cyl == 0xffff)
X break;
X du->dk_badsect[i] =
X du->dk_cpd.bad.bt_bad[i].bt_cyl * du->dk_dd.d_secpercyl +
X (du->dk_cpd.bad.bt_bad[i].bt_trksec >> 8) *
X du->dk_dd.d_nsectors +
X (du->dk_cpd.bad.bt_bad[i].bt_trksec & 0x00ff);
X }
X}
X
Xstatic int
Xwdreset(du, err)
X struct disk *du;
X int err;
X{
X int wdc = du->dk_port;
X int stat;
X
X if (err)
X printf("wdc%d: busy too long, resetting\n", du->dk_ctrlr);
X
X /* reset the device */
X outb(wdc+wd_ctlr, WDCTL_RST | WDCTL_IDS);
X DELAY(1000);
X outb(wdc+wd_ctlr, WDCTL_IDS);
X DELAY(1000);
X (void) inb(wdc+wd_error);
X outb(wdc+wd_ctlr, WDCTL_4BIT);
X
X if (wait_for_unbusy(du) < 0)
X printf("wdc%d: failed to reset controller\n", du->dk_ctrlr);
X}
X
Xint
Xwdc_wait(du, mask)
X struct disk *du;
X int mask;
X{
X int wdc = du->dk_port;
X int timeout = 0;
X int stat;
X
X for (;;) {
X stat = inb(wdc+wd_altsts);
X if ((stat & WDCS_BUSY) == 0 && (stat & mask) == mask)
X break;
X if (++timeout > WDCNDELAY)
X return -1;
X DELAY(WDCDELAY);
X }
X#ifdef WDCNDELAY_DEBUG
X if (timeout > WDCNDELAY_DEBUG)
X printf("wdc%d: timeout took %dus\n", du->dk_ctrlr,
X WDCDELAY * timeout);
X#endif
X return stat;
X}
X
Xstatic int
Xwdtimeout(arg)
X caddr_t arg;
X{
X int s = splbio();
X struct disk *du = (void *)arg;
X
X if (du->dk_timeout && --du->dk_timeout == 0) {
X int wdc = du->dk_port;
X
X printf("wd%d: lost interrupt - status %x, error %x\n",
X du->dk_lunit, inb(wdc+wd_status), inb(wdc+wd_error));
X wdreset(du, 0);
X du->dk_skip = 0;
X du->dk_flags |= DKFL_SINGLE;
X wdstart(du->dk_ctrlr); /* start controller */
X }
X timeout((timeout_t)wdtimeout, arg, hz);
X splx(s);
X return 0;
X}
X
X#endif /* NWDC > 0 */
END-of-wd.c
echo x - wdreg.h
sed 's/^X//' >wdreg.h << 'END-of-wdreg.h'
X/*-
X * Copyright (c) 1991 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * William Jolitz.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X * notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X * notice, this list of conditions and the following disclaimer in the
X * documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X * must display the following acknowledgement:
X * This product includes software developed by the University of
X * California, Berkeley and its contributors.
X * 4. Neither the name of the University nor the names of its contributors
X * may be used to endorse or promote products derived from this software
X * without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X * from: @(#)wdreg.h 7.1 (Berkeley) 5/9/91
X * $Id: wdreg.h,v 1.4 1994/03/02 21:43:42 mycroft Exp $
X */
X
X/*
X * Disk Controller register definitions.
X */
X#define wd_data 0x0 /* data register (R/W - 16 bits) */
X#define wd_error 0x1 /* error register (R) */
X#define wd_precomp wd_error /* write precompensation (W) */
X#define wd_seccnt 0x2 /* sector count (R/W) */
X#define wd_sector 0x3 /* first sector number (R/W) */
X#define wd_cyl_lo 0x4 /* cylinder address, low byte (R/W) */
X#define wd_cyl_hi 0x5 /* cylinder address, high byte (R/W)*/
X#define wd_sdh 0x6 /* sector size/drive/head (R/W)*/
X#define wd_command 0x7 /* command register (W) */
X#define wd_status wd_command /* immediate status (R) */
X
X#define wd_altsts 0x206 /*alternate fixed disk status(via 1015) (R)*/
X#define wd_ctlr 0x206 /*fixed disk controller control(via 1015) (W)*/
X#define WDCTL_4BIT 0x8 /* use four head bits (wd1003) */
X#define WDCTL_RST 0x4 /* reset the controller */
X#define WDCTL_IDS 0x2 /* disable controller interrupts */
X#define wd_digin 0x207 /* disk controller input(via 1015) (R)*/
X
X/*
X * Status Bits.
X */
X#define WDCS_BUSY 0x80 /* Controller busy bit. */
X#define WDCS_READY 0x40 /* Selected drive is ready */
X#define WDCS_WRTFLT 0x20 /* Write fault */
X#define WDCS_SEEKCMPLT 0x10 /* Seek complete */
X#define WDCS_DRQ 0x08 /* Data request bit. */
X#define WDCS_ECCCOR 0x04 /* ECC correction made in data */
X#define WDCS_INDEX 0x02 /* Index pulse from selected drive */
X#define WDCS_ERR 0x01 /* Error detect bit. */
X
X#define WDCS_BITS "\020\010busy\006rdy\006wrtflt\005seekdone\004drq\003ecc_cor\002index\001err"
X
X#define WDERR_BITS "\020\010badblk\007uncorr\006id_crc\005no_id\003abort\002tr000\001no_dam"
X
X/*
X * Commands for Disk Controller.
X */
X#define WDCC_RESTORE 0x10 /* disk restore code -- resets cntlr */
X
X#define WDCC_READ 0x20 /* disk read code */
X#define WDCC_WRITE 0x30 /* disk write code */
X#define WDCC__LONG 0x02 /* modifier -- access ecc bytes */
X#define WDCC__NORETRY 0x01 /* modifier -- no retrys */
X
X#define WDCC_FORMAT 0x50 /* disk format code */
X#define WDCC_DIAGNOSE 0x90 /* controller diagnostic */
X#define WDCC_IDC 0x91 /* initialize drive command */
X
X#define WDCC_EXTDCMD 0xE0 /* send extended command */
X#define WDCC_READP 0xEC /* read parameters from controller */
X#define WDCC_CACHEC 0xEF /* cache control */
X
X#define WD_STEP 0 /* winchester- default 35us step */
X
X#define WDSD_IBM 0xa0 /* forced to 512 byte sector, ecc */
X
X
X#ifdef KERNEL
X/*
X * read parameters command returns this:
X */
Xstruct wdparams {
X /* drive info */
X short wdp_config; /* general configuration */
X short wdp_fixedcyl; /* number of non-removable cylinders */
X short wdp_removcyl; /* number of removable cylinders */
X short wdp_heads; /* number of heads */
X short wdp_unfbytespertrk; /* number of unformatted bytes/track */
X short wdp_unfbytes; /* number of unformatted bytes/sector */
X short wdp_sectors; /* number of sectors */
X short wdp_minisg; /* minimum bytes in inter-sector gap*/
X short wdp_minplo; /* minimum bytes in postamble */
X short wdp_vendstat; /* number of words of vendor status */
X /* controller info */
X char wdp_cnsn[20]; /* controller serial number */
X short wdp_cntype; /* controller type */
X#define WDTYPE_SINGLEPORTSECTOR 1 /* single port, single sector buffer */
X#define WDTYPE_DUALPORTMULTI 2 /* dual port, multiple sector buffer */
X#define WDTYPE_DUALPORTMULTICACHE 3 /* above plus track cache */
X short wdp_cnsbsz; /* sector buffer size, in sectors */
X short wdp_necc; /* ecc bytes appended */
X char wdp_rev[8]; /* firmware revision */
X char wdp_model[40]; /* model name */
X short wdp_nsecperint; /* sectors per interrupt */
X short wdp_usedmovsd; /* can use double word read/write? */
X};
X
X/*
X * wd driver entry points
X */
Xint wdprobe(struct isa_device *);
Xint wdattach(struct isa_device *);
Xint wdstrategy(struct buf *);
Xvoid wdintr(int);
Xint wdopen(dev_t, int, int, struct proc *);
Xint wdclose(dev_t, int, int);
Xint wdioctl(dev_t, int, caddr_t, int, struct proc *);
X/* int wdformat(struct buf *bp); */
Xint wdsize(dev_t);
Xint wddump(dev_t);
X
X#endif KERNEL
END-of-wdreg.h
exit
------------------------------------------------------------------------------