This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.1'.
[unix-history] / sys / i386 / isa / wd.c
index cca9c32..eb5a34b 100644 (file)
@@ -1,3 +1,4 @@
+#define WD_COUNT_RETRIES
 static int wdtest = 0;
 
 /*-
 static int wdtest = 0;
 
 /*-
@@ -35,12 +36,30 @@ static int wdtest = 0;
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *     from: @(#)wx.c  7.2 (Berkeley) 5/9/91
- *     $Id: wd.c,v 1.20 1994/01/04 20:20:38 nate Exp $
+ *     from: @(#)wd.c  7.2 (Berkeley) 5/9/91
+ *     $Id: wd.c,v 1.36 1994/03/06 03:10:58 jkh Exp $
  */
 
  */
 
-/* TODO:peel out buffer at low ipl, speed improvement */
-
+/* TODO:
+ *     o Bump error count after timeout.
+ *     o Satisfy ATA timing in all cases.
+ *     o Finish merging berry/sos timeout code (bump error count...).
+ *     o Merge/fix TIH/NetBSD bad144 code.
+ *     o Merge/fix Dyson/NetBSD clustering code.
+ *     o Don't use polling except for initialization.  Need to
+ *       reorganize the state machine.  Then "extra" interrupts
+ *       shouldn't happen (except maybe one for initialization).
+ *     o Fix disklabel, boot and driver inconsistencies with
+ *       bad144 in standard versions.
+ *     o Support extended DOS partitions.
+ *     o Support swapping to DOS partitions.
+ *     o Look at latest linux clustering methods.  Our disksort()
+ *       gets in the way of clustering.
+ *     o Handle bad sectors, clustering, disklabelling, DOS
+ *       partitions and swapping driver-independently.  Use
+ *       i386/dkbad.c for bad sectors.  Swapping will need new
+ *       driver entries for polled reinit and polled write).
+ */
 
 #include "wd.h"
 #if    NWDC > 0
 
 #include "wd.h"
 #if    NWDC > 0
@@ -60,45 +79,47 @@ static int wdtest = 0;
 #include "machine/cpu.h"
 #include "i386/isa/isa.h"
 #include "i386/isa/isa_device.h"
 #include "machine/cpu.h"
 #include "i386/isa/isa.h"
 #include "i386/isa/isa_device.h"
-#include "i386/isa/icu.h"
 #include "i386/isa/wdreg.h"
 #include "syslog.h"
 #include "vm/vm.h"
 
 #include "i386/isa/wdreg.h"
 #include "syslog.h"
 #include "vm/vm.h"
 
-#ifndef WDCTIMEOUT
-#define WDCTIMEOUT     10000000  /* arbitrary timeout for drive ready waits */
-#endif
+#define        TIMEOUT         10000   /* XXX? WDCC_DIAGNOSE can take > 1.1 sec */
 
 #define        RETRIES         5       /* number of retries before giving up */
 #define RECOVERYTIME   500000  /* usec for controller to recover after err */
 
 #define        RETRIES         5       /* number of retries before giving up */
 #define RECOVERYTIME   500000  /* usec for controller to recover after err */
-#define        MAXTRANSFER     32      /* max size of transfer in page clusters */
+#define        MAXTRANSFER     256     /* max size of transfer in sectors */
 
 
+#ifdef notyet
 #define wdnoreloc(dev) (minor(dev) & 0x80)     /* ignore partition table */
 #define wdnoreloc(dev) (minor(dev) & 0x80)     /* ignore partition table */
+#endif
 #define wddospart(dev) (minor(dev) & 0x40)     /* use dos partitions */
 #define wdunit(dev)    ((minor(dev) & 0x38) >> 3)
 #define wdpart(dev)    (minor(dev) & 0x7)
 #define makewddev(maj, unit, part)     (makedev(maj,((unit<<3)+part)))
 #define WDRAW  3               /* 'd' partition isn't a partition! */
 
 #define wddospart(dev) (minor(dev) & 0x40)     /* use dos partitions */
 #define wdunit(dev)    ((minor(dev) & 0x38) >> 3)
 #define wdpart(dev)    (minor(dev) & 0x7)
 #define makewddev(maj, unit, part)     (makedev(maj,((unit<<3)+part)))
 #define WDRAW  3               /* 'd' partition isn't a partition! */
 
-#define b_cylin        b_resid         /* cylinder number for doing IO to */
-                               /* shares an entry in the buf struct */
+/* Cylinder number for doing IO to.  Shares an entry in the buf struct. */
+#define b_cylin        b_resid
 
 
-#define        id_physid id_scsiid     /* this biotab field doubles as a field */
-                               /* for the physical unit number on the controller */
+/*
+ * This biotab field doubles as a field for the physical unit number on
+ * the controller.
+ */
+#define        id_physid id_scsiid
 
 /*
  * Drive states.  Used to initialize drive.
  */
 
 
 /*
  * Drive states.  Used to initialize drive.
  */
 
-#define        CLOSED          0               /* disk is closed. */
-#define        WANTOPEN        1               /* open requested, not started */
-#define        RECAL           2               /* doing restore */
-#define        OPEN            3               /* done with open */
+#define        CLOSED          0       /* disk is closed. */
+#define        WANTOPEN        1       /* open requested, not started */
+#define        RECAL           2       /* doing restore */
+#define        OPEN            3       /* done with open */
 
 /*
  * The structure of a disk drive.
  */
 
 /*
  * The structure of a disk drive.
  */
-struct disk {
+struct disk {
        long    dk_bc;          /* byte count left */
        short   dk_skip;        /* blocks already transferred */
        char    dk_ctrlr;       /* physical controller number */
        long    dk_bc;          /* byte count left */
        short   dk_skip;        /* blocks already transferred */
        char    dk_ctrlr;       /* physical controller number */
@@ -107,52 +128,56 @@ struct    disk {
        char    dk_state;       /* control state */
        u_char  dk_status;      /* copy of status reg. */
        u_char  dk_error;       /* copy of error reg. */
        char    dk_state;       /* control state */
        u_char  dk_status;      /* copy of status reg. */
        u_char  dk_error;       /* copy of error reg. */
+       u_char  dk_timeout;     /* countdown to next timeout */
        short   dk_port;        /* i/o port base */
 
        short   dk_port;        /* i/o port base */
 
-       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 */
+       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? */
        short   dk_flags;       /* drive characteistics found */
        short   dk_wlabel;      /* label writable? */
        short   dk_flags;       /* drive characteistics found */
-#define        DKFL_DOSPART    0x00001  /* has DOS partition table */
-#define        DKFL_SINGLE     0x00004  /* sector at a time mode */
-#define        DKFL_ERROR      0x00008  /* processing a disk error */
-#define        DKFL_BSDLABEL   0x00010  /* has a BSD disk label */
-#define        DKFL_BADSECT    0x00020  /* has a bad144 badsector table */
-#define        DKFL_WRITEPROT  0x00040  /* manual unit write protect */
-#define        DKFL_LABELLING  0x00080  /* readdisklabel() in progress */
+#define        DKFL_DOSPART    0x00001 /* has DOS partition table */
+#define        DKFL_SINGLE     0x00004 /* sector at a time mode */
+#define        DKFL_ERROR      0x00008 /* processing a disk error */
+#define        DKFL_BSDLABEL   0x00010 /* has a BSD disk label */
+#define        DKFL_BADSECT    0x00020 /* has a bad144 badsector table */
+#define        DKFL_WRITEPROT  0x00040 /* manual unit write protect */
+#define        DKFL_LABELLING  0x00080 /* readdisklabel() in progress */
        struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */
        struct disklabel dk_dd; /* device configuration data */
        struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */
        struct disklabel dk_dd; /* device configuration data */
-       struct  dos_partition
+       struct disklabel dk_dd2;        /* DOS view converted to label */
+       struct dos_partition
                dk_dospartitions[NDOSPART];     /* DOS view of disk */
                dk_dospartitions[NDOSPART];     /* DOS view of disk */
-       struct  dkbad   dk_bad; /* bad sector table */
+       struct dkbad dk_bad;    /* bad sector table */
 };
 
 };
 
-/*static*/     struct  disk    *wddrives[NWD]; /* table of units */
-static struct  buf     wdtab[NWDC];
-static struct  buf     wdutab[NWD];    /* head of queue per drive */
+static struct disk *wddrives[NWD];     /* table of units */
+static struct buf wdtab[NWDC];
+static struct buf wdutab[NWD]; /* head of queue per drive */
 #ifdef notyet
 #ifdef notyet
-static struct  buf     rwdbuf[NWD];    /* buffers for raw IO */
+static struct buf rwdbuf[NWD]; /* buffers for raw IO */
 #endif
 #endif
-static long    wdxfer[NWD];            /* count of transfers */
-
-static int     wdprobe(struct isa_device *dvp);
-static int     wdattach(struct isa_device *dvp);
-static void    wdustart(struct disk *du);
-static void    wdstart(int ctrlr);
-static int     wdcontrol(struct buf *bp);
-static int     wdcommand(struct disk *du, u_int cylinder, u_int head,
-                         u_int sector, u_int count, u_int command);
-static int     wdsetctlr(struct disk *du);
-static int     wdwsetctlr(struct disk *du);
-static int     wdgetctlr(struct disk *du);
-static void    wderror(struct buf *bp, struct disk *du, char *mesg);
-static int     wdreset(struct disk *du);
-static void    wdsleep(int ctrlr, char *wmesg);
-static int     wdunwedge(struct disk *du);
-static int     wdwait(struct disk *du, u_char bits_wanted);
-
-struct isa_driver wdcdriver = {
+static long wdxfer[NWD];       /* count of transfers */
+
+static int wdprobe(struct isa_device *dvp);
+static int wdattach(struct isa_device *dvp);
+static void wdustart(struct disk *du);
+static void wdstart(int ctrlr);
+static int wdcontrol(struct buf *bp);
+static int wdcommand(struct disk *du, u_int cylinder, u_int head,
+                    u_int sector, u_int count, u_int command);
+static int wdsetctlr(struct disk *du);
+static int wdwsetctlr(struct disk *du);
+static int wdgetctlr(struct disk *du);
+static void wderror(struct buf *bp, struct disk *du, char *mesg);
+static void wdflushirq(struct disk *du, int old_ipl);
+static int wdreset(struct disk *du);
+static void wdsleep(int ctrlr, char *wmesg);
+static void wdtimeout(caddr_t cdu, int ticks);
+static int wdunwedge(struct disk *du);
+static int wdwait(struct disk *du, u_char bits_wanted, int timeout);
+
+struct isa_driver wdcdriver = {
        wdprobe, wdattach, "wdc",
 };
 
        wdprobe, wdattach, "wdc",
 };
 
@@ -162,25 +187,21 @@ struct    isa_driver wdcdriver = {
 static int
 wdprobe(struct isa_device *dvp)
 {
 static int
 wdprobe(struct isa_device *dvp)
 {
-       int unit = dvp->id_unit;
-       int u;
+       int     unit = dvp->id_unit;
        struct disk *du;
        struct disk *du;
-       int wdc;
 
 
-       if (unit >= NWDC)                               /* 31 Jul 92*/
-               return(0);
-
-       du = malloc (sizeof(struct disk), M_TEMP, M_NOWAIT);
-       bzero (du, sizeof(struct disk));        /* 31 Jul 92*/
+       if (unit >= NWDC)
+               return (0);
+       du = malloc(sizeof *du, M_TEMP, M_NOWAIT);
+       if (du == NULL)
+               return (0);
+       bzero(du, sizeof *du);
        du->dk_ctrlr = dvp->id_unit;
        du->dk_ctrlr = dvp->id_unit;
-       du->dk_unit = 0;
-       du->dk_lunit = 0;
-
-       wdc = du->dk_port = dvp->id_iobase;
+       du->dk_port = dvp->id_iobase;
 
        /* check if we have registers that work */
 
        /* check if we have registers that work */
-       outb(wdc+wd_cyl_lo, 0xa5) ;     /* wd_cyl_lo is read/write */
-       if(inb(wdc+wd_cyl_lo) != 0xa5)
+       outb(du->dk_port + wd_cyl_lo, 0xa5);    /* wd_cyl_lo is read/write */
+       if (inb(du->dk_port + wd_cyl_lo) != 0xa5)
                goto nodevice;
 
        if (wdreset(du) != 0 && (DELAY(RECOVERYTIME), wdreset(du)) != 0)
                goto nodevice;
 
        if (wdreset(du) != 0 && (DELAY(RECOVERYTIME), wdreset(du)) != 0)
@@ -188,7 +209,7 @@ wdprobe(struct isa_device *dvp)
 
        /* execute a controller only command */
        if (wdcommand(du, 0, 0, 0, 0, WDCC_DIAGNOSE) != 0
 
        /* execute a controller only command */
        if (wdcommand(du, 0, 0, 0, 0, WDCC_DIAGNOSE) != 0
-           || wdwait(du, 0) != 0)
+           || wdwait(du, 0, TIMEOUT) != 0)
                goto nodevice;
 
        free(du, M_TEMP);
                goto nodevice;
 
        free(du, M_TEMP);
@@ -205,14 +226,12 @@ nodevice:
 static int
 wdattach(struct isa_device *dvp)
 {
 static int
 wdattach(struct isa_device *dvp)
 {
-       int unit, lunit;
+       int     unit, lunit;
        struct isa_device *wdup;
        struct disk *du;
 
        if (dvp->id_unit >= NWDC)
        struct isa_device *wdup;
        struct disk *du;
 
        if (dvp->id_unit >= NWDC)
-               return(0);
-
-       printf("wdc%d:", dvp->id_unit);
+               return (0);
 
        for (wdup = isa_biotab_wdc; wdup->id_driver != 0; wdup++) {
                if (wdup->id_iobase != dvp->id_iobase)
 
        for (wdup = isa_biotab_wdc; wdup->id_driver != 0; wdup++) {
                if (wdup->id_iobase != dvp->id_iobase)
@@ -222,50 +241,60 @@ wdattach(struct isa_device *dvp)
                        continue;
                unit = wdup->id_physid;
 
                        continue;
                unit = wdup->id_physid;
 
-               du = wddrives[lunit] = (struct disk *)
-                       malloc (sizeof(struct disk), M_TEMP, M_NOWAIT);
-               bzero (du, sizeof(struct disk));
-               bzero (&wdutab[lunit], sizeof(struct buf));
-#ifdef notyet
-               bzero (&rwdbuf[lunit], sizeof(struct buf));
-#endif
-               wdxfer[lunit] = 0;
-
+               du = malloc(sizeof *du, M_TEMP, M_NOWAIT);
+               if (du == NULL)
+                       continue;
+               if (wddrives[lunit] != NULL)
+                       panic("drive attached twice");
+               wddrives[lunit] = du;
+               bzero(du, sizeof *du);
                du->dk_ctrlr = dvp->id_unit;
                du->dk_unit = unit;
                du->dk_lunit = lunit;
                du->dk_port = dvp->id_iobase;
 
                du->dk_ctrlr = dvp->id_unit;
                du->dk_unit = unit;
                du->dk_lunit = lunit;
                du->dk_port = dvp->id_iobase;
 
-               /* print out description of drive, suppressing multiple blanks*/
-               if (wdgetctlr(du) == 0)  {
-                       int i, blank;
-
-                       printf(" [%d: wd%d: ", unit, lunit);
-                       for (i = blank = 0 ; i < sizeof(du->dk_params.wdp_model); i++) {
-                               char c = du->dk_params.wdp_model[i];
-
-                               if (blank && c == ' ')
-                                       continue;
-                               if (blank && c != ' ') {
-                                       printf(" %c", c);
-                                       blank = 0;
-                                       continue;
-                               }
-                               if (c == ' ')
-                                       blank = 1;
-                               else
-                                       printf("%c", c);
-                       }
-                       printf("]");
-               }
-               else {
+               /*
+                * Print out description of drive.
+                * wdp_model can be [0..40] bytes, thus \0 can be missing so
+                * so copy it and add a null before printing.
+                */
+               if (wdgetctlr(du) == 0) {
+                   char buf[sizeof(du->dk_params.wdp_model) + 1];
+                   bcopy(du->dk_params.wdp_model, buf, sizeof(buf)-1);
+                   buf[sizeof(buf)-1] = '\0';
+                   printf("wdc%d: unit %d (wd%d): <%s>\n",
+                       dvp->id_unit, unit, lunit, buf);
+                   if (du->dk_params.wdp_heads == 0)
+                       printf("wd%d: size unknown\n", lunit);
+                   else
+                       printf("wd%d: %luMB (%lu total sec), ",
+                               lunit,
+                               du->dk_dd.d_secperunit
+                               * du->dk_dd.d_secsize / (1024 * 1024),
+                               du->dk_dd.d_secperunit);
+                       printf("%lu cyl, %lu head, %lu sec, bytes/sec %lu\n",
+                               du->dk_dd.d_ncylinders,
+                               du->dk_dd.d_ntracks,
+                               du->dk_dd.d_nsectors,
+                               du->dk_dd.d_secsize);
+                       /*
+                        * Start timeout routine for this drive.
+                        * XXX timeout should be per controller.
+                        */
+                       wdtimeout((caddr_t)du, 0);
+               } else {
                        free(du, M_TEMP);
                        free(du, M_TEMP);
-                       wddrives[unit] = 0;
-                       printf(" [%d: wd%d: not found]", unit, lunit);
+                       wddrives[lunit] = NULL;
                }
        }
                }
        }
-       printf("\n");
-       return(1);
+
+       /*
+        * Discard any interrupts generated by wdgetctlr().  wdflushirq()
+        * doesn't work now because the ambient ipl is too high.
+        */
+       wdtab[dvp->id_unit].b_active = 2;
+
+       return (1);
 }
 
 /* Read/write routine for a buffer.  Finds the proper unit, range checks
 }
 
 /* Read/write routine for a buffer.  Finds the proper unit, range checks
@@ -277,12 +306,12 @@ void
 wdstrategy(register struct buf *bp)
 {
        register struct buf *dp;
 wdstrategy(register struct buf *bp)
 {
        register struct buf *dp;
-       struct disk *du;                        /* Disk unit to do the IO. */
+       struct disk *du;
        int     lunit = wdunit(bp->b_dev);
        int     s;
 
        /* valid unit, controller, and request?  */
        int     lunit = wdunit(bp->b_dev);
        int     s;
 
        /* valid unit, controller, and request?  */
-       if (lunit >= NWD || bp->b_blkno < 0 || (du = wddrives[lunit]) == 0) {
+       if (lunit >= NWD || bp->b_blkno < 0 || (du = wddrives[lunit]) == NULL) {
 
                bp->b_error = EINVAL;
                bp->b_flags |= B_ERROR;
 
                bp->b_error = EINVAL;
                bp->b_flags |= B_ERROR;
@@ -296,24 +325,20 @@ wdstrategy(register struct buf *bp)
                goto done;
        }
 
                goto done;
        }
 
-       /* have partitions and want to use them? */
-       if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW) {
-
-               /*
-                * do bounds checking, adjust transfer. if error, process.
-                * if end of partition, just return
-                */
-               if (bounds_check_with_label(bp, &du->dk_dd, du->dk_wlabel) <= 0)
-                       goto done;
-               /* otherwise, process transfer request */
-       }
+       /*
+        * Do bounds checking, adjust transfer, and set b_cylin.
+        */
+       if (bounds_check_with_label(bp, wddospart(bp->b_dev)
+                                       ? &du->dk_dd2 : &du->dk_dd,
+                                   du->dk_wlabel) <= 0)
+               goto done;
 
        /* queue transfer on drive, activate drive and controller if idle */
        dp = &wdutab[lunit];
        s = splbio();
        disksort(dp, bp);
        if (dp->b_active == 0)
 
        /* queue transfer on drive, activate drive and controller if idle */
        dp = &wdutab[lunit];
        s = splbio();
        disksort(dp, bp);
        if (dp->b_active == 0)
-               wdustart(du);                   /* start drive */
+               wdustart(du);   /* start drive */
 
        /* Pick up changes made by readdisklabel(). */
        if (du->dk_flags & DKFL_LABELLING && du->dk_state > RECAL) {
 
        /* Pick up changes made by readdisklabel(). */
        if (du->dk_flags & DKFL_LABELLING && du->dk_state > RECAL) {
@@ -322,7 +347,7 @@ wdstrategy(register struct buf *bp)
        }
 
        if (wdtab[du->dk_ctrlr].b_active == 0)
        }
 
        if (wdtab[du->dk_ctrlr].b_active == 0)
-               wdstart(du->dk_ctrlr);          /* start controller */
+               wdstart(du->dk_ctrlr);  /* start controller */
        splx(s);
        return;
 
        splx(s);
        return;
 
@@ -340,7 +365,7 @@ static void
 wdustart(register struct disk *du)
 {
        register struct buf *bp, *dp = &wdutab[du->dk_lunit];
 wdustart(register struct disk *du)
 {
        register struct buf *bp, *dp = &wdutab[du->dk_lunit];
-       int ctrlr = du->dk_ctrlr;
+       int     ctrlr = du->dk_ctrlr;
 
        /* unit already active? */
        if (dp->b_active)
 
        /* unit already active? */
        if (dp->b_active)
@@ -353,7 +378,7 @@ wdustart(register struct disk *du)
 
        /* link onto controller queue */
        dp->b_forw = NULL;
 
        /* link onto controller queue */
        dp->b_forw = NULL;
-       if (wdtab[ctrlr].b_actf  == NULL)
+       if (wdtab[ctrlr].b_actf == NULL)
                wdtab[ctrlr].b_actf = dp;
        else
                wdtab[ctrlr].b_actl->b_forw = dp;
                wdtab[ctrlr].b_actf = dp;
        else
                wdtab[ctrlr].b_actl->b_forw = dp;
@@ -374,14 +399,14 @@ wdustart(register struct disk *du)
 static void
 wdstart(int ctrlr)
 {
 static void
 wdstart(int ctrlr)
 {
-       register struct disk *du;       /* disk unit for IO */
+       register struct disk *du;
        register struct buf *bp;
        struct disklabel *lp;
        struct buf *dp;
        register struct bt_bad *bt_ptr;
        long    blknum, cylin, head, sector;
        register struct buf *bp;
        struct disklabel *lp;
        struct buf *dp;
        register struct bt_bad *bt_ptr;
        long    blknum, cylin, head, sector;
-       long    secpertrk, secpercyl, addr;
-       int     lunit, wdc;
+       long    secpertrk, secpercyl;
+       int     lunit;
 
 loop:
        /* is there a drive for the controller to do a transfer with? */
 
 loop:
        /* is there a drive for the controller to do a transfer with? */
@@ -389,8 +414,10 @@ loop:
        if (dp == NULL)
                return;
 
        if (dp == NULL)
                return;
 
-       /* is there a transfer to this drive? */
-       /* If so, link it on the controller's queue */
+       /*
+        * Is there a transfer to this drive?  If so, link it on the
+        * controller's queue.
+        */
        bp = dp->b_actf;
        if (bp == NULL) {
                wdtab[ctrlr].b_actf = dp->b_forw;
        bp = dp->b_actf;
        if (bp == NULL) {
                wdtab[ctrlr].b_actf = dp->b_forw;
@@ -400,16 +427,15 @@ loop:
        /* obtain controller and drive information */
        lunit = wdunit(bp->b_dev);
        du = wddrives[lunit];
        /* obtain controller and drive information */
        lunit = wdunit(bp->b_dev);
        du = wddrives[lunit];
-       wdc = du->dk_port;
 
        /* if not really a transfer, do control operations specially */
        if (du->dk_state < OPEN) {
                if (du->dk_state != WANTOPEN)
                        printf("wd%d: wdstart: weird dk_state %d\n",
 
        /* if not really a transfer, do control operations specially */
        if (du->dk_state < OPEN) {
                if (du->dk_state != WANTOPEN)
                        printf("wd%d: wdstart: weird dk_state %d\n",
-                               du->dk_lunit, du->dk_state);
+                              du->dk_lunit, du->dk_state);
                if (wdcontrol(bp) != 0)
                        printf("wd%d: wdstart: wdcontrol returned nonzero, state = %d\n",
                if (wdcontrol(bp) != 0)
                        printf("wd%d: wdstart: wdcontrol returned nonzero, state = %d\n",
-                               du->dk_lunit, du->dk_state);
+                              du->dk_lunit, du->dk_state);
                return;
        }
 
                return;
        }
 
@@ -418,19 +444,20 @@ loop:
 #ifdef WDDEBUG
        if (du->dk_skip == 0)
                printf("wd%d: wdstart: %s %d@%d; map ", lunit,
 #ifdef WDDEBUG
        if (du->dk_skip == 0)
                printf("wd%d: wdstart: %s %d@%d; map ", lunit,
-                       (bp->b_flags & B_READ) ? "read" : "write",
-                       bp->b_bcount, blknum);
+                      (bp->b_flags & B_READ) ? "read" : "write",
+                      bp->b_bcount, blknum);
        else
        else
-               printf(" %d)%x", du->dk_skip, inb(wdc+wd_altsts));
+               printf(" %d)%x", du->dk_skip, inb(du->dk_port + wd_altsts));
 #endif
 #endif
-       addr = (int) bp->b_un.b_addr;
        if (du->dk_skip == 0)
                du->dk_bc = bp->b_bcount;
 
        lp = &du->dk_dd;
        secpertrk = lp->d_nsectors;
        secpercyl = lp->d_secpercyl;
        if (du->dk_skip == 0)
                du->dk_bc = bp->b_bcount;
 
        lp = &du->dk_dd;
        secpertrk = lp->d_nsectors;
        secpercyl = lp->d_secpercyl;
-       if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW)
+       if (wddospart(bp->b_dev))
+               blknum += du->dk_dd2.d_partitions[wdpart(bp->b_dev)].p_offset;
+       else
                blknum += lp->d_partitions[wdpart(bp->b_dev)].p_offset;
        cylin = blknum / secpercyl;
        head = (blknum % secpercyl) / secpertrk;
                blknum += lp->d_partitions[wdpart(bp->b_dev)].p_offset;
        cylin = blknum / secpercyl;
        head = (blknum % secpercyl) / secpertrk;
@@ -440,9 +467,10 @@ loop:
         * See if the current block is in the bad block list.
         * (If we have one, and not formatting.)
         */
         * See if the current block is in the bad block list.
         * (If we have one, and not formatting.)
         */
-       if ((du->dk_flags & (DKFL_SINGLE|DKFL_BADSECT))         /* 19 Aug 92*/
-               == (DKFL_SINGLE|DKFL_BADSECT))
-           for (bt_ptr = du->dk_bad.bt_bad; bt_ptr->bt_cyl != 0xffff;
+       if ((du->dk_flags & (DKFL_SINGLE | DKFL_BADSECT))
+           == (DKFL_SINGLE | DKFL_BADSECT))
+#define BAD144_NO_CYL  0xffff  /* XXX should be in dkbad.h; bad144.c uses -1 */
+           for (bt_ptr = du->dk_bad.bt_bad; bt_ptr->bt_cyl != BAD144_NO_CYL;
                 bt_ptr++) {
                if (bt_ptr->bt_cyl > cylin)
                        /* Sorted list, and we passed our cylinder. quit. */
                 bt_ptr++) {
                if (bt_ptr->bt_cyl > cylin)
                        /* Sorted list, and we passed our cylinder. quit. */
@@ -450,13 +478,13 @@ loop:
                if (bt_ptr->bt_cyl == cylin &&
                    bt_ptr->bt_trksec == (head << 8) + sector) {
                        /*
                if (bt_ptr->bt_cyl == cylin &&
                    bt_ptr->bt_trksec == (head << 8) + sector) {
                        /*
-                        * Found bad block.  Calculate new block addr.
+                        * Found bad block.  Calculate new block number.
                         * 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.
                         */
                         * 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
-                       printf("--- badblock code -> Old = %d; ", blknum);
+#ifdef WDDEBUG
+                       printf("--- badblock code -> Old = %ld; ", blknum);
 #endif
 
                        /*
 #endif
 
                        /*
@@ -476,24 +504,24 @@ loop:
                        cylin = blknum / secpercyl;
                        head = (blknum % secpercyl) / secpertrk;
                        sector = blknum % secpertrk;
                        cylin = blknum / secpercyl;
                        head = (blknum % secpercyl) / secpertrk;
                        sector = blknum % secpertrk;
-#ifdef WDDEBUG
-                       printf("new = %d\n", blknum);
+#ifdef WDDEBUG
+                       printf("new = %ld\n", blknum);
 #endif
                        break;
                }
        }
 
 #endif
                        break;
                }
        }
 
-       wdtab[ctrlr].b_active = 1;              /* mark controller active */
+       wdtab[ctrlr].b_active = 1;      /* mark controller active */
 
        /* if starting a multisector transfer, or doing single transfers */
        if (du->dk_skip == 0 || (du->dk_flags & DKFL_SINGLE)) {
 
        /* if starting a multisector transfer, or doing single transfers */
        if (du->dk_skip == 0 || (du->dk_flags & DKFL_SINGLE)) {
-               int command;
-               u_int count;
+               u_int   command;
+               u_int   count;
 
                if (wdtab[ctrlr].b_errcnt && (bp->b_flags & B_READ) == 0)
                        du->dk_bc += DEV_BSIZE;
 
 
                if (wdtab[ctrlr].b_errcnt && (bp->b_flags & B_READ) == 0)
                        du->dk_bc += DEV_BSIZE;
 
-#ifdef B_FORMAT
+#ifdef B_FORMAT
                if (bp->b_flags & B_FORMAT) {
                        command = WDCC_FORMAT;
                        count = lp->d_nsectors;
                if (bp->b_flags & B_FORMAT) {
                        command = WDCC_FORMAT;
                        count = lp->d_nsectors;
@@ -501,17 +529,18 @@ loop:
                } else
 #endif
                {
                } else
 #endif
                {
-               if (du->dk_flags & DKFL_SINGLE)
-                       count = 1;
-               else
-                       count = howmany(du->dk_bc, DEV_BSIZE);
-               command = (bp->b_flags & B_READ) ? WDCC_READ : WDCC_WRITE;
+                       if (du->dk_flags & DKFL_SINGLE)
+                               count = 1;
+                       else
+                               count = howmany(du->dk_bc, DEV_BSIZE);
+                       command = (bp->b_flags & B_READ)
+                                 ? WDCC_READ : WDCC_WRITE;
                }
 
                /*
                 * XXX this loop may never terminate.  The code to handle
                }
 
                /*
                 * XXX this loop may never terminate.  The code to handle
-                * counting down of retries and eventually failing the i/o is
-                * in wdintr() and we can't get there from here.
+                * counting down of retries and eventually failing the i/o
+                * is in wdintr() and we can't get there from here.
                 */
                if (wdtest != 0) {
                        if (--wdtest == 0) {
                 */
                if (wdtest != 0) {
                        if (--wdtest == 0) {
@@ -520,23 +549,42 @@ loop:
                                wdunwedge(du);
                        }
                }
                                wdunwedge(du);
                        }
                }
-               while (wdcommand(du, cylin, head, sector, count, command) != 0)
-               {
+               while (wdcommand(du, cylin, head, sector, count, command)
+                      != 0) {
                        wderror(bp, du,
                        wderror(bp, du,
-                               "wdstart: timeout waiting to send command");
+                               "wdstart: timeout waiting to give command");
                        wdunwedge(du);
                }
                        wdunwedge(du);
                }
-#ifdef WDDEBUG
-               printf("sector %d cylin %d head %d addr %x sts %x\n",
-                       sector, cylin, head, addr, inb(wdc+wd_altsts));
+#ifdef WDDEBUG
+               printf("cylin %ld head %ld sector %ld addr %x sts %x\n",
+                      cylin, head, sector,
+                      (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE,
+                      inb(du->dk_port + wd_altsts));
 #endif
        }
 
 #endif
        }
 
-       /* if this is a read operation, just go away until it's done.   */
-       if (bp->b_flags & B_READ) return;
+       /*
+        * Schedule wdtimeout() to wake up after a few seconds.  Retrying
+        * unmarked bad blocks can take 3 seconds!  Then it is not good that
+        * we retry 5 times.
+        *
+        * XXX wdtimeout() doesn't increment the error count so we may loop
+        * forever.  More seriously, the loop isn't forever but causes a
+        * crash.
+        *
+        * TODO fix b_resid bug elsewhere (fd.c....).  Fix short but positive
+        * counts being discarded after there is an error (in physio I
+        * think).  Discarding them would be OK if the (special) file offset
+        * was not advanced.
+        */
+       du->dk_timeout = 1 + 3;
 
 
-       /* ready to send data?  */
-       if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ) != 0) {
+       /* If this is a read operation, just go away until it's done. */
+       if (bp->b_flags & B_READ)
+               return;
+
+       /* Ready to send data? */
+       if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) < 0) {
                wderror(bp, du, "wdstart: timeout waiting for DRQ");
                /*
                 * XXX what do we do now?  If we've just issued the command,
                wderror(bp, du, "wdstart: timeout waiting for DRQ");
                /*
                 * XXX what do we do now?  If we've just issued the command,
@@ -545,14 +593,15 @@ loop:
                 * the command was issued ages ago, so we can't simply
                 * restart it.
                 *
                 * the command was issued ages ago, so we can't simply
                 * restart it.
                 *
-                * XXX we waste a lot of time unnecessarily translating
-                * block numbers to cylin/head/sector for continued i/o's.
+                * XXX we waste a lot of time unnecessarily translating block
+                * numbers to cylin/head/sector for continued i/o's.
                 */
        }
 
        /* then send it! */
                 */
        }
 
        /* then send it! */
-       outsw (wdc + wd_data, (caddr_t)addr + du->dk_skip * DEV_BSIZE,
-               DEV_BSIZE/sizeof(short));
+       outsw(du->dk_port + wd_data,
+             (void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE),
+             DEV_BSIZE / sizeof(short));
        du->dk_bc -= DEV_BSIZE;
 }
 
        du->dk_bc -= DEV_BSIZE;
 }
 
@@ -566,19 +615,22 @@ wdintr(int unit)
 {
        register struct disk *du;
        register struct buf *bp, *dp;
 {
        register struct disk *du;
        register struct buf *bp, *dp;
-       int wdc;
 
 
+       if (wdtab[unit].b_active == 2)
+               return;         /* intr in wdflushirq() */
        if (!wdtab[unit].b_active) {
        if (!wdtab[unit].b_active) {
+#ifndef LAPTOP
                printf("wdc%d: extra interrupt\n", unit);
                printf("wdc%d: extra interrupt\n", unit);
+#endif
                return;
        }
 
        dp = wdtab[unit].b_actf;
        bp = dp->b_actf;
        du = wddrives[wdunit(bp->b_dev)];
                return;
        }
 
        dp = wdtab[unit].b_actf;
        bp = dp->b_actf;
        du = wddrives[wdunit(bp->b_dev)];
-       wdc = du->dk_port;
+       du->dk_timeout = 0;
 
 
-       if (wdwait(du, 0) < 0) {
+       if (wdwait(du, 0, TIMEOUT) < 0) {
                wderror(bp, du, "wdintr: timeout waiting for status");
                du->dk_status |= WDCS_ERR;      /* XXX */
        }
                wderror(bp, du, "wdintr: timeout waiting for status");
                du->dk_status |= WDCS_ERR;      /* XXX */
        }
@@ -586,24 +638,30 @@ wdintr(int unit)
        /* is it not a transfer, but a control operation? */
        if (du->dk_state < OPEN) {
                wdtab[unit].b_active = 0;
        /* is it not a transfer, but a control operation? */
        if (du->dk_state < OPEN) {
                wdtab[unit].b_active = 0;
-               if (wdcontrol(bp))
+               switch (wdcontrol(bp)) {
+               case 0:
+                       return;
+               case 1:
                        wdstart(unit);
                return;
                        wdstart(unit);
                return;
+               case 2:
+                       goto done;
+               }
        }
 
        /* have we an error? */
        if (du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) {
 oops:
        }
 
        /* have we an error? */
        if (du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) {
 oops:
-#ifdef WDDEBUG
+#ifdef WDDEBUG
                wderror(bp, du, "wdintr");
 #endif
                wderror(bp, du, "wdintr");
 #endif
-               if((du->dk_flags & DKFL_SINGLE) == 0) {
-                       du->dk_flags |=  DKFL_ERROR;
+               if ((du->dk_flags & DKFL_SINGLE) == 0) {
+                       du->dk_flags |= DKFL_ERROR;
                        goto outt;
                }
 #ifdef B_FORMAT
                if (bp->b_flags & B_FORMAT) {
                        goto outt;
                }
 #ifdef B_FORMAT
                if (bp->b_flags & B_FORMAT) {
-                       bp->b_error = EIO;              /* 17 Sep 92*/
+                       bp->b_error = EIO;
                        bp->b_flags |= B_ERROR;
                        goto done;
                }
                        bp->b_flags |= B_ERROR;
                        goto done;
                }
@@ -615,7 +673,7 @@ oops:
                                wdtab[unit].b_active = 0;
                        } else {
                                wderror(bp, du, "hard error");
                                wdtab[unit].b_active = 0;
                        } else {
                                wderror(bp, du, "hard error");
-                               bp->b_error = EIO;      /* 17 Sep 92*/
+                               bp->b_error = EIO;
                                bp->b_flags |= B_ERROR; /* flag the error */
                        }
                } else
                                bp->b_flags |= B_ERROR; /* flag the error */
                        }
                } else
@@ -625,34 +683,34 @@ oops:
        /*
         * If this was a successful read operation, fetch the data.
         */
        /*
         * If this was a successful read operation, fetch the data.
         */
-       if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab[unit].b_active) {
-               int chk, dummy;
+       if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ)
+           && wdtab[unit].b_active) {
+               int     chk, dummy;
 
                chk = min(DEV_BSIZE / sizeof(short), du->dk_bc / sizeof(short));
 
                /* ready to receive data? */
 
                chk = min(DEV_BSIZE / sizeof(short), du->dk_bc / sizeof(short));
 
                /* ready to receive data? */
-               if ((du->dk_status & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ))
-                   != (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ))
-                       wderror(bp, du, "wdintr: read intr arrived early");
-               if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ) != 0) {
+               if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) {
                        wderror(bp, du, "wdintr: read error detected late");
                        goto oops;
                }
 
                /* suck in data */
                        wderror(bp, du, "wdintr: read error detected late");
                        goto oops;
                }
 
                /* suck in data */
-               insw (wdc+wd_data,
-                     (caddr_t)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE, chk);
+               insw(du->dk_port + wd_data,
+                    (void *)((int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE),
+                               chk);
                du->dk_bc -= chk * sizeof(short);
 
                du->dk_bc -= chk * sizeof(short);
 
-               /* for obselete fractional sector reads */
-               while (chk++ < 256) insw (wdc + wd_data, (caddr_t)&dummy, 1);
+               /* XXX for obsolete fractional sector reads. */
+               while (chk++ < DEV_BSIZE / sizeof(short))
+                       insw(du->dk_port + wd_data, &dummy, 1);
        }
 
        wdxfer[du->dk_lunit]++;
 outt:
        if (wdtab[unit].b_active) {
                if ((bp->b_flags & B_ERROR) == 0) {
        }
 
        wdxfer[du->dk_lunit]++;
 outt:
        if (wdtab[unit].b_active) {
                if ((bp->b_flags & B_ERROR) == 0) {
-                       du->dk_skip++;          /* Add to successful sectors. */
+                       du->dk_skip++;  /* add to successful sectors */
                        if (wdtab[unit].b_errcnt)
                                wderror(bp, du, "soft error");
                        wdtab[unit].b_errcnt = 0;
                        if (wdtab[unit].b_errcnt)
                                wderror(bp, du, "soft error");
                        wdtab[unit].b_errcnt = 0;
@@ -661,30 +719,28 @@ outt:
                        if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) {
                                wdtab[unit].b_active = 0;
                                wdstart(unit);
                        if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) {
                                wdtab[unit].b_active = 0;
                                wdstart(unit);
-                               return;         /* next chunk is started */
-                       } else if ((du->dk_flags & (DKFL_SINGLE|DKFL_ERROR))
-                                       == DKFL_ERROR) {
+                               return; /* next chunk is started */
+                       } else if ((du->dk_flags & (DKFL_SINGLE | DKFL_ERROR))
+                                  == DKFL_ERROR) {
                                du->dk_skip = 0;
                                du->dk_flags &= ~DKFL_ERROR;
                                du->dk_skip = 0;
                                du->dk_flags &= ~DKFL_ERROR;
-                               du->dk_flags |=  DKFL_SINGLE;
+                               du->dk_flags |= DKFL_SINGLE;
                                wdtab[unit].b_active = 0;
                                wdstart(unit);
                                wdtab[unit].b_active = 0;
                                wdstart(unit);
-                               return;         /* redo xfer sector by sector */
+                               return; /* redo xfer sector by sector */
                        }
                }
 
                        }
                }
 
-#ifdef B_FORMAT
 done: ;
 done: ;
-#endif
                /* done with this transfer, with or without error */
                du->dk_flags &= ~DKFL_SINGLE;
                wdtab[unit].b_actf = dp->b_forw;
                wdtab[unit].b_errcnt = 0;
                /* done with this transfer, with or without error */
                du->dk_flags &= ~DKFL_SINGLE;
                wdtab[unit].b_actf = dp->b_forw;
                wdtab[unit].b_errcnt = 0;
+               bp->b_resid = bp->b_bcount - du->dk_skip * DEV_BSIZE;
                du->dk_skip = 0;
                dp->b_active = 0;
                dp->b_actf = bp->av_forw;
                dp->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);
        }
 
                biodone(bp);
        }
 
@@ -707,22 +763,41 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p)
 {
        register unsigned int lunit;
        register struct disk *du;
 {
        register unsigned int lunit;
        register struct disk *du;
-       int part = wdpart(dev), mask = 1 << part;
+       int     part = wdpart(dev), mask = 1 << part;
        struct partition *pp;
        struct partition *pp;
-       char *msg;
+       char    *msg;
        struct disklabel save_label;
 
        lunit = wdunit(dev);
        struct disklabel save_label;
 
        lunit = wdunit(dev);
-       if (lunit >= NWD) return (ENXIO) ;
-
+       if (lunit >= NWD)
+               return (ENXIO);
        du = wddrives[lunit];
        du = wddrives[lunit];
-       if (du == 0) return (ENXIO) ;
+       if (du == NULL)
+               return (ENXIO);
+
+       /* Finish flushing IRQs left over from wdattach(). */
+       if (wdtab[du->dk_ctrlr].b_active == 2)
+               wdtab[du->dk_ctrlr].b_active = 0;
+
+       /*
+        * That's all for valid DOS partitions.  We don't need a BSD label.
+        * The openmask is only used for checking BSD partitions so we don't
+        * need to maintain it.
+        */
+       if (wddospart(dev)) {
+               /* XXX we do need a disklabel for now. */
+               if ((du->dk_flags & DKFL_BSDLABEL) == 0)
+                       return (ENXIO);
+
+               return (part > NDOSPART ? ENXIO : 0);
+       }
 
        while (du->dk_flags & DKFL_LABELLING)
                tsleep((caddr_t)&du->dk_flags, PZERO - 1, "wdopen", 1);
        if ((du->dk_flags & DKFL_BSDLABEL) == 0) {
                /*
 
        while (du->dk_flags & DKFL_LABELLING)
                tsleep((caddr_t)&du->dk_flags, PZERO - 1, "wdopen", 1);
        if ((du->dk_flags & DKFL_BSDLABEL) == 0) {
                /*
-                * wdtab[ctrlr].b_active != 0 implies wdutab[lunit].b_actf == NULL (?)
+                * wdtab[ctrlr].b_active != 0 implies
+                * wdutab[lunit].b_actf == NULL (?)
                 * so the following guards most things (until the next i/o).
                 * It doesn't guard against a new i/o starting and being
                 * affected by the label being changed.  Sigh.
                 * so the following guards most things (until the next i/o).
                 * It doesn't guard against a new i/o starting and being
                 * affected by the label being changed.  Sigh.
@@ -747,9 +822,9 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p)
                 * to the driver by resetting the state machine.
                 */
                save_label = du->dk_dd;
                 * to the driver by resetting the state machine.
                 */
                save_label = du->dk_dd;
-#define WDSTRATEGY     ((void (*)(struct buf *)) wdstrategy)   /* XXX */
+#define WDSTRATEGY     ((int (*)(struct buf *)) wdstrategy)    /* XXX */
                msg = readdisklabel(makewddev(major(dev), lunit, WDRAW),
                msg = readdisklabel(makewddev(major(dev), lunit, WDRAW),
-                                   WDSTRATEGY, &du->dk_dd,
+                                   (d_strategy_t *) WDSTRATEGY, &du->dk_dd,
                                    du->dk_dospartitions, &du->dk_bad,
                                    (struct buf **)NULL);
                du->dk_flags &= ~DKFL_LABELLING;
                                    du->dk_dospartitions, &du->dk_bad,
                                    (struct buf **)NULL);
                du->dk_flags &= ~DKFL_LABELLING;
@@ -760,10 +835,47 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p)
                        if (part != WDRAW)
                                return (EINVAL);  /* XXX needs translation */
                } else {
                        if (part != WDRAW)
                                return (EINVAL);  /* XXX needs translation */
                } else {
+                       int     dospart;
+                       unsigned long newsize, offset, size;
+
                        du->dk_flags |= DKFL_BSDLABEL;
                        du->dk_flags &= ~DKFL_WRITEPROT;
                        if (du->dk_dd.d_flags & D_BADSECT)
                                du->dk_flags |= DKFL_BADSECT;
                        du->dk_flags |= DKFL_BSDLABEL;
                        du->dk_flags &= ~DKFL_WRITEPROT;
                        if (du->dk_dd.d_flags & D_BADSECT)
                                du->dk_flags |= DKFL_BADSECT;
+
+                       /*
+                        * Force WDRAW partition to be the whole disk.
+                        */
+                       offset = du->dk_dd.d_partitions[WDRAW].p_offset;
+                       if (offset != 0) {
+                               printf(
+               "wd%d: changing offset of 'd' partition from %lu to 0\n",
+                                      du->dk_lunit, offset);
+                               du->dk_dd.d_partitions[WDRAW].p_offset = 0;
+                       }
+                       size = du->dk_dd.d_partitions[WDRAW].p_size;
+                       newsize = du->dk_dd.d_secperunit;       /* XXX */
+                       if (size != newsize) {
+                               printf(
+               "wd%d: changing size of 'd' partition from %lu to %lu\n",
+                                      du->dk_lunit, size, newsize);
+                               du->dk_dd.d_partitions[WDRAW].p_size = newsize;
+                       }
+
+                       /*
+                        * Convert DOS partition data to a label.
+                        */
+                       du->dk_dd2 = du->dk_dd;
+                       bzero(du->dk_dd2.d_partitions,
+                             sizeof du->dk_dd2.d_partitions);
+                       du->dk_dd2.d_partitions[0].p_size
+                           = du->dk_dd.d_secperunit;   /* XXX */
+                       for (dospart = 1; dospart <= NDOSPART; dospart++) {
+                               du->dk_dd2.d_partitions[dospart].p_offset =
+                                   du->dk_dospartitions[dospart - 1].dp_start;
+                               du->dk_dd2.d_partitions[dospart].p_size =
+                                   du->dk_dospartitions[dospart - 1].dp_size;
+                       }
                }
 
                /* Pick up changes made by readdisklabel(). */
                }
 
                /* Pick up changes made by readdisklabel(). */
@@ -772,11 +884,10 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p)
        }
 
        /*
        }
 
        /*
-        * Warn if a partion is opened
-        * that overlaps another partition which is open
-        * unless one is the "raw" partition (whole disk).
+        * Warn if a partion is opened that overlaps another partition which
+        * is open unless one is the "raw" partition (whole disk).
         */
         */
-       if ((du->dk_openpart & mask) == 0 /*&& part != RAWPART*/ && part != WDRAW) {
+       if ((du->dk_openpart & mask) == 0 && part != WDRAW) {
                int     start, end;
 
                pp = &du->dk_dd.d_partitions[part];
                int     start, end;
 
                pp = &du->dk_dd.d_partitions[part];
@@ -786,12 +897,12 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p)
                     pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions];
                     pp++) {
                        if (pp->p_offset + pp->p_size <= start ||
                     pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions];
                     pp++) {
                        if (pp->p_offset + pp->p_size <= start ||
-                           pp->p_offset >= end)
+                           pp->p_offset >= end)
                                continue;
                        if (pp - du->dk_dd.d_partitions == WDRAW)
                                continue;
                                continue;
                        if (pp - du->dk_dd.d_partitions == WDRAW)
                                continue;
-                       if (du->dk_openpart & (1 << (pp -
-                           du->dk_dd.d_partitions)))
+                       if (du->dk_openpart
+                           & (1 << (pp - du->dk_dd.d_partitions)))
                                log(LOG_WARNING,
                                    "wd%d%c: overlaps open partition (%c)\n",
                                    lunit, part + 'a',
                                log(LOG_WARNING,
                                    "wd%d%c: overlaps open partition (%c)\n",
                                    lunit, part + 'a',
@@ -801,8 +912,6 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p)
        if (part >= du->dk_dd.d_npartitions && part != WDRAW)
                return (ENXIO);
 
        if (part >= du->dk_dd.d_npartitions && part != WDRAW)
                return (ENXIO);
 
-       /* insure only one open at a time */
-       du->dk_openpart |= mask;
        switch (fmt) {
        case S_IFCHR:
                du->dk_copenpart |= mask;
        switch (fmt) {
        case S_IFCHR:
                du->dk_copenpart |= mask;
@@ -811,6 +920,8 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p)
                du->dk_bopenpart |= mask;
                break;
        }
                du->dk_bopenpart |= mask;
                break;
        }
+       du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
+
        return (0);
 }
 
        return (0);
 }
 
@@ -818,19 +929,19 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p)
  * 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.
  * 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.
+ * Returns 0 if operation still in progress, 1 if completed, 2 if error.
  */
 static int
 wdcontrol(register struct buf *bp)
 {
        register struct disk *du;
  */
 static int
 wdcontrol(register struct buf *bp)
 {
        register struct disk *du;
-       int ctrlr;
+       int     ctrlr;
 
        du = wddrives[wdunit(bp->b_dev)];
        ctrlr = du->dk_ctrlr;
 
        switch (du->dk_state) {
 
        du = wddrives[wdunit(bp->b_dev)];
        ctrlr = du->dk_ctrlr;
 
        switch (du->dk_state) {
-    case WANTOPEN:
+       case WANTOPEN:
 tryagainrecal:
                wdtab[ctrlr].b_active = 1;
                if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0) {
 tryagainrecal:
                wdtab[ctrlr].b_active = 1;
                if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0) {
@@ -838,31 +949,30 @@ tryagainrecal:
                        goto maybe_retry;
                }
                du->dk_state = RECAL;
                        goto maybe_retry;
                }
                du->dk_state = RECAL;
-               return(0);
+               return (0);
        case RECAL:
                if (du->dk_status & WDCS_ERR || wdsetctlr(du) != 0) {
                        wderror(bp, du, "wdcontrol: recal failed");
 maybe_retry:
                        if (du->dk_status & WDCS_ERR)
                                wdunwedge(du);
        case RECAL:
                if (du->dk_status & WDCS_ERR || wdsetctlr(du) != 0) {
                        wderror(bp, du, "wdcontrol: recal failed");
 maybe_retry:
                        if (du->dk_status & WDCS_ERR)
                                wdunwedge(du);
-                       if (++wdtab[ctrlr].b_errcnt < RETRIES) {
-                               du->dk_state = WANTOPEN;
+                       du->dk_state = WANTOPEN;
+                       if (++wdtab[ctrlr].b_errcnt < RETRIES)
                                goto tryagainrecal;
                                goto tryagainrecal;
-                       }
                        bp->b_error = ENXIO;    /* XXX needs translation */
                        bp->b_flags |= B_ERROR;
                        bp->b_error = ENXIO;    /* XXX needs translation */
                        bp->b_flags |= B_ERROR;
-                       return(1);
+                       return (2);
                }
                wdtab[ctrlr].b_errcnt = 0;
                du->dk_state = OPEN;
                /*
                }
                wdtab[ctrlr].b_errcnt = 0;
                du->dk_state = OPEN;
                /*
-                * The rest of the initialization can be done
-                * by normal means.
+                * The rest of the initialization can be done by normal
+                * means.
                 */
                 */
-               return(1);
+               return (1);
        }
        panic("wdcontrol");
        }
        panic("wdcontrol");
-       return (1);
+       return (2);
 }
 
 /*
 }
 
 /*
@@ -874,29 +984,21 @@ static int
 wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector,
          u_int count, u_int command)
 {
 wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector,
          u_int count, u_int command)
 {
-       u_int wdc;
+       u_int   wdc;
 
 
-       if (wdwait(du, 0) < 0)
+       if (wdwait(du, 0, TIMEOUT) < 0)
                return (1);
        wdc = du->dk_port;
        outb(wdc + wd_precomp, du->dk_dd.d_precompcyl / 4);
        outb(wdc + wd_cyl_lo, cylinder);
        outb(wdc + wd_cyl_hi, cylinder >> 8);
                return (1);
        wdc = du->dk_port;
        outb(wdc + wd_precomp, du->dk_dd.d_precompcyl / 4);
        outb(wdc + wd_cyl_lo, cylinder);
        outb(wdc + wd_cyl_hi, cylinder >> 8);
-       outb(wdc + wd_sdh, WDSD_IBM | (du->dk_unit << 4) | (head & 0x0f));
-       DELAY(10);                      /* XXX give drive time to see change */
-       if((command != WDCC_DIAGNOSE) && (command != WDCC_IDC)) {
-               if (wdwait(du, WDCS_READY) < 0) {
-                       return(1);
-               }
-       }
-       else {
-               if (wdwait(du, 0) < 0) {
-                       return(1);
-               }
-       }
+       outb(wdc + wd_sdh, WDSD_IBM | (du->dk_unit << 4) | head);
        outb(wdc + wd_sector, sector + 1);
        outb(wdc + wd_seccnt, count);
        outb(wdc + wd_sector, sector + 1);
        outb(wdc + wd_seccnt, count);
-       outb(du->dk_port + wd_command, command);
+       if (wdwait(du, command == WDCC_DIAGNOSE || command == WDCC_IDC
+                      ? 0 : WDCS_READY, TIMEOUT) < 0)
+               return (1);
+       outb(wdc + wd_command, command);
        return (0);
 }
 
        return (0);
 }
 
@@ -906,14 +1008,32 @@ wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector,
 static int
 wdsetctlr(struct disk *du)
 {
 static int
 wdsetctlr(struct disk *du)
 {
-#ifdef WDDEBUG
-       printf("wd(%d,%d): wdsetctlr C %lu H %lu S %lu\n", du->dk_ctrlr, du->dk_unit,
-               du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks,
-               du->dk_dd.d_nsectors);
+#ifdef WDDEBUG
+       printf("wd(%d,%d): wdsetctlr: C %lu H %lu S %lu\n",
+              du->dk_ctrlr, du->dk_unit,
+              du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks,
+              du->dk_dd.d_nsectors);
 #endif
 #endif
+       if (du->dk_dd.d_ntracks == 0 || du->dk_dd.d_ntracks > 16) {
+               struct wdparams *wp;
+
+               printf("wd%d: can't handle %lu heads from partition table ",
+                      du->dk_lunit, du->dk_dd.d_ntracks);
+               /* obtain parameters */
+               wp = &du->dk_params;
+               if (wp->wdp_heads > 0 && wp->wdp_heads <= 16) {
+                       printf("(controller value %lu restored)\n",
+                               wp->wdp_heads);
+                       du->dk_dd.d_ntracks = wp->wdp_heads;
+               }
+               else {
+                       printf("(truncating to 16)\n");
+               du->dk_dd.d_ntracks = 16;
+       }
+       }
        if (wdcommand(du, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks - 1, 0,
                      du->dk_dd.d_nsectors, WDCC_IDC) != 0
        if (wdcommand(du, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks - 1, 0,
                      du->dk_dd.d_nsectors, WDCC_IDC) != 0
-           || wdwait(du, WDCS_READY) != 0) {
+           || wdwait(du, WDCS_READY, TIMEOUT) < 0) {
                wderror((struct buf *)NULL, du, "wdsetctlr failed");
                return (1);
        }
                wderror((struct buf *)NULL, du, "wdsetctlr failed");
                return (1);
        }
@@ -926,12 +1046,13 @@ wdsetctlr(struct disk *du)
 static int
 wdwsetctlr(struct disk *du)
 {
 static int
 wdwsetctlr(struct disk *du)
 {
-       int stat;
-       int x;
+       int     stat;
+       int     x;
 
        wdsleep(du->dk_ctrlr, "wdwset");
        x = splbio();
        stat = wdsetctlr(du);
 
        wdsleep(du->dk_ctrlr, "wdwset");
        x = splbio();
        stat = wdsetctlr(du);
+       wdflushirq(du, x);
        splx(x);
        return (stat);
 }
        splx(x);
        return (stat);
 }
@@ -940,39 +1061,47 @@ wdwsetctlr(struct disk *du)
  * issue READP to drive to ask it what it is.
  */
 static int
  * issue READP to drive to ask it what it is.
  */
 static int
-wdgetctlr(struct disk *du) {
-       int i;
-       char tb[DEV_BSIZE];
+wdgetctlr(struct disk *du)
+{
+       int     i;
+       char    tb[DEV_BSIZE];
        struct wdparams *wp;
 
        if (wdcommand(du, 0, 0, 0, 0, WDCC_READP) != 0
        struct wdparams *wp;
 
        if (wdcommand(du, 0, 0, 0, 0, WDCC_READP) != 0
-           || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ) != 0) {
-
-#ifndef        MFM
-               /* Old drives don't support WDCC_READP.  Try a seek to 0. */
-               if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0
-                   || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT) != 0)
-                       return (1);
-#else  /* IDE */
+           || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) {
+               /* XXX need to check error status after final transfer. */
                /*
                /*
-                * Some IDE drives return trash if there is not a unit 1
-                * out there, need to make sure that it is READY and not BUSY
-                * before you assume it is there !
+                * Old drives don't support WDCC_READP.  Try a seek to 0.
+                * Some IDE controllers return trash if there is no drive
+                * attached, so first test that the drive can be selected.
+                * This also avoids long waits for nonexistent drives.
                 */
                 */
-               outb(du->dk_port+wd_sdh, WDSD_IBM | (du->dk_unit<<4));
-               DELAY(5000);
-               if ((inb(du->dk_port+wd_status) & (WDCS_READY|WDCS_BUSY)) !=
-                       WDCS_READY) {
+               if (wdwait(du, 0, TIMEOUT) < 0)
+                       return (1);
+               outb(du->dk_port + wd_sdh, WDSD_IBM | (du->dk_unit << 4));
+               DELAY(5000);    /* usually unnecessary; drive select is fast */
+               if ((inb(du->dk_port + wd_status) & (WDCS_BUSY | WDCS_READY))
+                   != WDCS_READY
+                   || wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0
+                   || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0)
                        return (1);
                        return (1);
-               }
-#endif /* MFM */
 
 
-               /* Fake minimal drive geometry for reading the MBR or label. */
+               /*
+                * Fake minimal drive geometry for reading the MBR.
+                * readdisklabel() may enlarge it to read the label and the
+                * bad sector table.
+                */
                du->dk_dd.d_secsize = DEV_BSIZE;
                du->dk_dd.d_nsectors = 17;
                du->dk_dd.d_ntracks = 1;
                du->dk_dd.d_ncylinders = 1;
                du->dk_dd.d_secpercyl = 17;
                du->dk_dd.d_secsize = DEV_BSIZE;
                du->dk_dd.d_nsectors = 17;
                du->dk_dd.d_ntracks = 1;
                du->dk_dd.d_ncylinders = 1;
                du->dk_dd.d_secpercyl = 17;
+               du->dk_dd.d_secperunit = 17;
+
+               /*
+                * Fake maximal drive size for writing the label.
+                */
+               du->dk_dd.d_partitions[WDRAW].p_size = 64 * 16 * 1024;
 
                /*
                 * Fake some more of the label for printing by disklabel(1)
 
                /*
                 * Fake some more of the label for printing by disklabel(1)
@@ -984,7 +1113,7 @@ wdgetctlr(struct disk *du) {
                        sizeof du->dk_dd.d_typename);
 
                /* Fake the model name for printing by wdattach(). */
                        sizeof du->dk_dd.d_typename);
 
                /* Fake the model name for printing by wdattach(). */
-               strncpy(du->dk_params.wdp_model, "Unknown Type",
+               strncpy(du->dk_params.wdp_model, "unknown",
                        sizeof du->dk_params.wdp_model);
 
                return (0);
                        sizeof du->dk_params.wdp_model);
 
                return (0);
@@ -992,38 +1121,53 @@ wdgetctlr(struct disk *du) {
 
        /* obtain parameters */
        wp = &du->dk_params;
 
        /* obtain parameters */
        wp = &du->dk_params;
-       insw(du->dk_port + wd_data, tb, sizeof(tb)/sizeof(short));
+       insw(du->dk_port + wd_data, tb, sizeof(tb) / sizeof(short));
        bcopy(tb, wp, sizeof(struct wdparams));
 
        /* shuffle string byte order */
        bcopy(tb, wp, sizeof(struct wdparams));
 
        /* shuffle string byte order */
-       for (i=0; i < sizeof(wp->wdp_model) ;i+=2) {
+       for (i = 0; i < sizeof(wp->wdp_model); i += 2) {
                u_short *p;
                u_short *p;
+
                p = (u_short *) (wp->wdp_model + i);
                *p = ntohs(*p);
        }
                p = (u_short *) (wp->wdp_model + i);
                *p = ntohs(*p);
        }
-#ifdef WDDEBUG
+       /*
+        * Clean up the wdp_model by converting nulls to spaces, and
+        * then removing the trailing spaces.
+        */
+       for (i=0; i < sizeof(wp->wdp_model); i++) {
+               if (wp->wdp_model[i] == '\0') {
+                       wp->wdp_model[i] = ' ';
+               }
+       }
+       for (i=sizeof(wp->wdp_model)-1; i>=0 && wp->wdp_model[i]==' '; i--) {
+               wp->wdp_model[i] = '\0';
+       }
+
+#ifdef WDDEBUG
        printf(
        printf(
-    "\nwdgetctlr wd(%d,%d): gc %x cyl %d trk %d sec %d type %d sz %d model %s\n",
-               du->dk_ctrlr, du->dk_unit, wp->wdp_config,
-               wp->wdp_fixedcyl + wp->wdp_removcyl, wp->wdp_heads,
-               wp->wdp_sectors, wp->wdp_cntype, wp->wdp_cnsbsz,
-               wp->wdp_model);
+"\nwd(%d,%d): wdgetctlr: gc %x cyl %d trk %d sec %d type %d sz %d model %s\n",
+              du->dk_ctrlr, du->dk_unit, wp->wdp_config,
+              wp->wdp_fixedcyl + wp->wdp_removcyl, wp->wdp_heads,
+              wp->wdp_sectors, wp->wdp_cntype, wp->wdp_cnsbsz,
+              wp->wdp_model);
 #endif
 
        /* update disklabel given drive information */
        du->dk_dd.d_secsize = DEV_BSIZE;
 #endif
 
        /* update disklabel given drive information */
        du->dk_dd.d_secsize = DEV_BSIZE;
-       du->dk_dd.d_ncylinders = wp->wdp_fixedcyl + wp->wdp_removcyl /*+- 1*/;
+       du->dk_dd.d_ncylinders = wp->wdp_fixedcyl + wp->wdp_removcyl /*+- 1*/ ;
        du->dk_dd.d_ntracks = wp->wdp_heads;
        du->dk_dd.d_nsectors = wp->wdp_sectors;
        du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors;
        du->dk_dd.d_ntracks = wp->wdp_heads;
        du->dk_dd.d_nsectors = wp->wdp_sectors;
        du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors;
+       du->dk_dd.d_partitions[WDRAW].p_size = du->dk_dd.d_secperunit
+           = du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders;
        /* dubious ... */
        bcopy("ESDI/IDE", du->dk_dd.d_typename, 9);
        /* dubious ... */
        bcopy("ESDI/IDE", du->dk_dd.d_typename, 9);
-       bcopy(wp->wdp_model+20, du->dk_dd.d_packname, 14-1);
+       bcopy(wp->wdp_model + 20, du->dk_dd.d_packname, 14 - 1);
        /* better ... */
        du->dk_dd.d_type = DTYPE_ESDI;
        du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
        /* better ... */
        du->dk_dd.d_type = DTYPE_ESDI;
        du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
-       
-       
+
        return (0);
 }
 
        return (0);
 }
 
@@ -1033,12 +1177,13 @@ int
 wdclose(dev_t dev, int flags, int fmt)
 {
        register struct disk *du;
 wdclose(dev_t dev, int flags, int fmt)
 {
        register struct disk *du;
-       int part = wdpart(dev), mask = 1 << part;
+       int     part = wdpart(dev), mask = 1 << part;
+
+       if (wddospart(dev))
+               return (0);
 
        du = wddrives[wdunit(dev)];
 
 
        du = wddrives[wdunit(dev)];
 
-       /* insure only one open at a time */
-       du->dk_openpart &= ~mask;
        switch (fmt) {
        case S_IFCHR:
                du->dk_copenpart &= ~mask;
        switch (fmt) {
        case S_IFCHR:
                du->dk_copenpart &= ~mask;
@@ -1047,15 +1192,17 @@ wdclose(dev_t dev, int flags, int fmt)
                du->dk_bopenpart &= ~mask;
                break;
        }
                du->dk_bopenpart &= ~mask;
                break;
        }
-       return(0);
+       du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
+
+       return (0);
 }
 
 int
 wdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
 {
 }
 
 int
 wdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
 {
-       int lunit = wdunit(dev);
+       int     lunit = wdunit(dev);
        register struct disk *du;
        register struct disk *du;
-       int error = 0;
+       int     error = 0;
 #ifdef notyet
        struct uio auio;
        struct iovec aiov;
 #ifdef notyet
        struct uio auio;
        struct iovec aiov;
@@ -1077,6 +1224,8 @@ wdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
                break;
 
        case DIOCGPART:
                break;
 
        case DIOCGPART:
+               if (wddospart(dev))
+                       return (EINVAL);
                ((struct partinfo *)addr)->disklab = &du->dk_dd;
                ((struct partinfo *)addr)->part =
                    &du->dk_dd.d_partitions[wdpart(dev)];
                ((struct partinfo *)addr)->disklab = &du->dk_dd;
                ((struct partinfo *)addr)->part =
                    &du->dk_dd.d_partitions[wdpart(dev)];
@@ -1087,9 +1236,20 @@ wdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
                        error = EBADF;
                else
                        error = setdisklabel(&du->dk_dd,
                        error = EBADF;
                else
                        error = setdisklabel(&du->dk_dd,
-                                       (struct disklabel *)addr,
-                       /* du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart : */0,
-                               du->dk_dospartitions);
+                                            (struct disklabel *)addr,
+#if 0
+                                           /*
+                                            * XXX setdisklabel() uses the
+                                            * openmask to allow it to reject
+                                            * changing open partitions.  Why
+                                            * are we pretending nothing is
+                                            * open?
+                                            */
+                                            du->dk_flags & DKFL_BSDLABEL
+                                            ? du->dk_openpart :
+#endif
+                                            0,
+                                            du->dk_dospartitions);
                if (error == 0) {
                        du->dk_flags |= DKFL_BSDLABEL;
                        wdwsetctlr(du); /* XXX - check */
                if (error == 0) {
                        du->dk_flags |= DKFL_BSDLABEL;
                        wdwsetctlr(du); /* XXX - check */
@@ -1108,20 +1268,25 @@ wdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
                du->dk_flags &= ~DKFL_WRITEPROT;
                if ((flag & FWRITE) == 0)
                        error = EBADF;
                du->dk_flags &= ~DKFL_WRITEPROT;
                if ((flag & FWRITE) == 0)
                        error = EBADF;
-                else if ((error = setdisklabel(&du->dk_dd, (struct disklabel *)addr,
-                               /*du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart :*/ 0,
-                               du->dk_dospartitions)) == 0) {
-                       int wlab;
+               else if ((error = setdisklabel(&du->dk_dd,
+                                              (struct disklabel *)addr,
+#if 0
+                                              du->dk_flags & DKFL_BSDLABEL
+                                              ? du->dk_openpart :
+#endif
+                                              0,
+                                              du->dk_dospartitions)) == 0) {
+                       int     wlab;
 
                        du->dk_flags |= DKFL_BSDLABEL;
 
                        du->dk_flags |= DKFL_BSDLABEL;
-                       wdwsetctlr(du);                 /* XXX - check */
+                       wdwsetctlr(du); /* XXX - check */
 
                        /* simulate opening partition 0 so write succeeds */
                        du->dk_openpart |= (1 << 0);    /* XXX */
                        wlab = du->dk_wlabel;
                        du->dk_wlabel = 1;
 
                        /* simulate opening partition 0 so write succeeds */
                        du->dk_openpart |= (1 << 0);    /* XXX */
                        wlab = du->dk_wlabel;
                        du->dk_wlabel = 1;
-                       error = writedisklabel(dev, WDSTRATEGY,
-                               &du->dk_dd, du->dk_dospartitions);
+                       error = writedisklabel(dev, (d_strategy_t *) WDSTRATEGY,
+                                       &du->dk_dd, du->dk_dospartitions);
                        du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
                        du->dk_wlabel = wlab;
                }
                        du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
                        du->dk_wlabel = wlab;
                }
@@ -1146,9 +1311,10 @@ wdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
                        auio.uio_resid = fop->df_count;
                        auio.uio_segflg = 0;
                        auio.uio_offset =
                        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[lunit], 0, dev, B_WRITE,
-                               minphys, &auio);
+                           fop->df_startblk * du->dk_dd.d_secsize;
+#error /* XXX the 386BSD interface is different */
+                       error = physio(wdformat, &rwdbuf[lunit], 0, 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;
                        fop->df_count -= auio.uio_resid;
                        fop->df_reg[0] = du->dk_status;
                        fop->df_reg[1] = du->dk_error;
@@ -1163,7 +1329,7 @@ wdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
        return (error);
 }
 
        return (error);
 }
 
-#ifdef B_FORMAT
+#ifdef B_FORMAT
 int
 wdformat(struct buf *bp)
 {
 int
 wdformat(struct buf *bp)
 {
@@ -1176,136 +1342,145 @@ wdformat(struct buf *bp)
 int
 wdsize(dev_t dev)
 {
 int
 wdsize(dev_t dev)
 {
-       int lunit = wdunit(dev), part = wdpart(dev), val;
+       int     lunit = wdunit(dev), part = wdpart(dev), val;
        struct disk *du;
 
        struct disk *du;
 
-       if (lunit >= NWD)       /* 31 Jul 92*/
-               return(-1);
-
-       du = wddrives[lunit];
+       if (lunit >= NWD || wddospart(dev) || (du = wddrives[lunit]) == NULL)
+               return (-1);
        val = 0;
        val = 0;
-       if (du == 0 || du->dk_state == CLOSED)
-               val = wdopen (makewddev(major(dev), lunit, WDRAW), FREAD, S_IFBLK, 0);
-       if (du == 0 || val != 0 || du->dk_flags & DKFL_WRITEPROT)
+       if (du->dk_state == CLOSED)
+               val = wdopen(makewddev(major(dev), lunit, WDRAW),
+                            FREAD, S_IFBLK, 0);
+       if (val != 0 || du->dk_flags & DKFL_WRITEPROT)
                return (-1);
                return (-1);
-
-       return((int)du->dk_dd.d_partitions[part].p_size);
+       return ((int)du->dk_dd.d_partitions[part].p_size);
 }
 
 }
 
-extern char    *vmmap;                 /* poor name! */
+extern char *vmmap;            /* poor name! */
 
 
+/*
+ * Dump core after a system crash.
+ */
 int
 int
-wddump(dev_t dev)                      /* dump core after a system crash */
+wddump(dev_t dev)
 {
 {
-       register struct disk *du;       /* disk unit to do the IO */
-#ifdef notyet
+       register struct disk *du;
        register struct bt_bad *bt_ptr;
        register struct bt_bad *bt_ptr;
-#endif
-       long    num;                    /* number of sectors to write */
-       int     ctrlr, lunit, part, wdc;
+       struct disklabel *lp;
+       long    num;            /* number of sectors to write */
+       int     lunit, part;
        long    blkoff, blknum;
        long    blkoff, blknum;
-#ifdef notdef
-       long    blkcnt;
-#endif
+       long    blkchk, blkcnt, blknext;
        long    cylin, head, sector;
        long    cylin, head, sector;
-       long    secpertrk, secpercyl, nblocks, i;
-       char *addr;
-       extern  int Maxmem;
-       static  wddoingadump = 0 ;
+       long    secpertrk, secpercyl, nblocks;
+       char   *addr;
+       extern int Maxmem;
+       static int wddoingadump = 0;
        extern caddr_t CADDR1;
 
        extern caddr_t CADDR1;
 
-       addr = (char *) 0;              /* starting address */
-
-       /* toss any characters present prior to dump */
+       /* Toss any characters present prior to dump. */
        while (sgetc(1))
                ;
 
        while (sgetc(1))
                ;
 
-       /* size of memory to dump */
-       num = Maxmem;
-       lunit = wdunit(dev);            /* eventually support floppies? */
-       part = wdpart(dev);             /* file system */
-       /* check for acceptable drive number */
-       if (lunit >= NWD) return(ENXIO);                /* 31 Jul 92*/
-
-       du = wddrives[lunit];
-       if (du == 0) return(ENXIO);
-       /* was it ever initialized ? */
-       if (du->dk_state < OPEN) return (ENXIO) ;
-       if (du->dk_flags & DKFL_WRITEPROT) return(ENXIO);
-       wdc = du->dk_port;
-       ctrlr = du->dk_ctrlr;
-
-       /* Convert to disk sectors */
-       num = (u_long) num * NBPG / du->dk_dd.d_secsize;
+       /* Check for acceptable device. */
+       /* XXX should reset to maybe allow du->dk_state < OPEN. */
+       lunit = wdunit(dev);    /* eventually support floppies? */
+       part = wdpart(dev);
+       if (lunit >= NWD || wddospart(dev) || (du = wddrives[lunit]) == NULL
+           || du->dk_state < OPEN || du->dk_flags & DKFL_WRITEPROT)
+               return (ENXIO);
 
 
-       /* check if controller active */
-       /*if (wdtab[ctrlr].b_active) return(EFAULT); */
-       if (wddoingadump) return(EFAULT);
+       /* Size of memory to dump, in disk sectors. */
+       num = (u_long)Maxmem * NBPG / du->dk_dd.d_secsize;
 
        secpertrk = du->dk_dd.d_nsectors;
        secpercyl = du->dk_dd.d_secpercyl;
        nblocks = du->dk_dd.d_partitions[part].p_size;
        blkoff = du->dk_dd.d_partitions[part].p_offset;
 
 
        secpertrk = du->dk_dd.d_nsectors;
        secpercyl = du->dk_dd.d_secpercyl;
        nblocks = du->dk_dd.d_partitions[part].p_size;
        blkoff = du->dk_dd.d_partitions[part].p_offset;
 
-/*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);
+#if 0
+       pg("part %x, nblocks %d, dumplo %d num %d\n",
+          part, nblocks, dumplo, num);
+#endif
+
+       /* Check transfer bounds against partition size. */
+       if (dumplo < 0 || dumplo + num > nblocks)
+               return (EINVAL);
+
+       /* Check if we are being called recursively. */
+       if (wddoingadump)
+               return (EFAULT);
 
 #if 0
 
 #if 0
-       wdtab[ctrlr].b_active = 1;      /* mark controller active for if we */
-                                       /* panic during the dump */
+       /* Mark controller active for if we panic during the dump. */
+       wdtab[du->dk_ctrlr].b_active = 1;
 #endif
 #endif
-       wddoingadump = 1  ;
-       i = 100000;                     /* WHY NOT TIMEOUT */
-       /* must delay 5us to conform to ATA spec */
-       DELAY(5);
-       while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0)) ;
-       outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit << 4));
-       outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
-       /* must delay 5us to conform to ATA spec */
-       DELAY(5);
-       while (inb(wdc+wd_status) & WDCS_BUSY) ;
-
-       /* some compaq controllers require this ... */
-       wdsetctlr(du);
+       wddoingadump = 1;
+
+       /* Recalibrate the drive. */
+       DELAY(5);               /* ATA spec XXX NOT */
+       if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0
+           || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0
+           || wdsetctlr(du) != 0) {
+               wderror((struct buf *)NULL, du, "wddump: recalibrate failed");
+               return (EIO);
+       }
 
 
+       du->dk_flags |= DKFL_SINGLE;
+       addr = (char *) 0;
        blknum = dumplo + blkoff;
        while (num > 0) {
        blknum = dumplo + blkoff;
        while (num > 0) {
-#ifdef notdef
-               if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER;
+               blkcnt = num;
+               if (blkcnt > MAXTRANSFER)
+                       blkcnt = MAXTRANSFER;
+               /* Keep transfer within current cylinder. */
                if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl)
                        blkcnt = secpercyl - (blknum % secpercyl);
                if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl)
                        blkcnt = secpercyl - (blknum % secpercyl);
-                           /* keep transfer within current cylinder */
-#endif
-               pmap_enter(kernel_pmap, (vm_offset_t)CADDR1,
-                          trunc_page(addr), VM_PROT_READ, TRUE);
+               blknext = blknum + blkcnt;
 
 
-               /* compute disk address */
-               cylin = blknum / secpercyl;
-               head = (blknum % secpercyl) / secpertrk;
-               sector = blknum % secpertrk;
-
-#ifdef notyet
                /* 
                /* 
-                * See if the current block is in the bad block list.
-                * (If we have one.)
+                * See if one of the sectors is in the bad sector list
+                * (if we have one).  If the first sector is bad, then
+                * reduce the transfer to this one bad sector; if another
+                * sector is bad, then reduce reduce the transfer to
+                * avoid any bad sectors.
                 */
                 */
-                       for (bt_ptr = du->dk_bad.bt_bad;
-                               bt_ptr->bt_cyl != -1; bt_ptr++) {
+               if ((du->dk_flags & (DKFL_SINGLE | DKFL_BADSECT))
+                   == (DKFL_SINGLE | DKFL_BADSECT))
+                 for (blkchk = blknum; blkchk < blknum + blkcnt; blkchk++) {
+                   cylin = blkchk / secpercyl;
+                   head = (blkchk % secpercyl) / secpertrk;
+                   sector = blkchk % secpertrk;
+                   for (bt_ptr = du->dk_bad.bt_bad;
+                        bt_ptr->bt_cyl != BAD144_NO_CYL; bt_ptr++) {
                        if (bt_ptr->bt_cyl > cylin)
                        if (bt_ptr->bt_cyl > cylin)
-                               /* Sorted list, and we passed our cylinder.
-                                       quit. */
+                               /*
+                                * Sorted list, and we passed our cylinder.
+                                * quit.
+                                */
                                break;
                        if (bt_ptr->bt_cyl == cylin &&
                                break;
                        if (bt_ptr->bt_cyl == cylin &&
-                               bt_ptr->bt_trksec == (head << 8) + sector) {
+                           bt_ptr->bt_trksec == (head << 8) + sector) {
+                               /* Found bad block. */
+                               blkcnt = blkchk - blknum;
+                               if (blkcnt > 0) {
+                                       blknext = blknum + blkcnt;
+                                       goto out;
+                               }
+                               blkcnt = 1;
+                               blknext = blknum + blkcnt;
                        /*
                        /*
-                        * Found bad block.  Calculate new block addr.
+                        * Found bad block.  Calculate new block number.
                         * 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.
                         */
                         * 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.
                         */
-                               /* XXX as usual */
+                               /* XXX as usual. */
+#ifdef WDDEBUG
+                               printf("--- badblock code -> Old = %ld; ",
+                                      blknum);
+#endif
+                               lp = &du->dk_dd;
                                if (lp->d_partitions[BSD_PART].p_offset != 0)
                                        blknum = lp->d_partitions[BAD144_PART]
                                                     .p_offset
                                if (lp->d_partitions[BSD_PART].p_offset != 0)
                                        blknum = lp->d_partitions[BAD144_PART]
                                                     .p_offset
@@ -1313,75 +1488,106 @@ wddump(dev_t dev)                      /* dump core after a system crash */
                                                       .p_size;
                                else
                                        blknum = lp->d_secperunit;
                                                       .p_size;
                                else
                                        blknum = lp->d_secperunit;
-                               blknum -= du->dk_dd.d_nsectors
+                               blknum -= lp->d_nsectors
                                          + (bt_ptr - du->dk_bad.bt_bad) + 1;
                                          + (bt_ptr - du->dk_bad.bt_bad) + 1;
-                               cylin = blknum / secpercyl;
-                               head = (blknum % secpercyl) / secpertrk;
-                               sector = blknum % secpertrk;
+#ifdef WDDEBUG
+                               printf("new = %ld\n", blknum);
+#endif
                                break;
                        }
                                break;
                        }
-               }
-#endif
-               sector++;               /* origin 1 */
-
-               /* select drive. */
-               outb(wdc+wd_sdh, WDSD_IBM | (du->dk_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
-               outb(wdc+wd_command, WDCC_WRITE);
+                   }
+                 }
+out:
+
+               /* Compute disk address. */
+               cylin = blknum / secpercyl;
+               head = (blknum % secpercyl) / secpertrk;
+               sector = blknum % secpertrk;
 
 
-               /* Ready to send data?  */
-               while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
-               if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
+#if 0
+               /* Let's just talk about this first... */
+               pg("cylin l%d head %ld sector %ld addr 0x%x count %ld",
+                  cylin, head, sector, addr, blkcnt);
+#endif
 
 
-               outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), 256);
+               /* Do the write. */
+               if (wdcommand(du, cylin, head, sector, blkcnt, WDCC_WRITE)
+                   != 0) {
+                       wderror((struct buf *)NULL, du,
+                               "wddump: timeout waiting to to give command");
+                       return (EIO);
+               }
+               while (blkcnt != 0) {
+                       pmap_enter(kernel_pmap, CADDR1, trunc_page(addr),
+                                  VM_PROT_READ, TRUE);
+
+                       /* Ready to send data? */
+                       DELAY(5);       /* ATA spec */
+                       if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT)
+                           < 0) {
+                               wderror((struct buf *)NULL, du,
+                                       "wddump: timeout waiting for DRQ");
+                               return (EIO);
+                       }
+                       outsw(du->dk_port + wd_data,
+                             CADDR1 + ((int)addr & (NBPG - 1)),
+                             DEV_BSIZE / sizeof(short));
+                       addr += DEV_BSIZE;
+                       if ((unsigned)addr % (1024 * 1024) == 0)
+                               printf("%ld ", num / (1024 * 1024 / DEV_BSIZE));
+                       num--;
+                       blkcnt--;
+               }
 
 
-               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. */
+               DELAY(5);       /* ATA spec XXX NOT */
+               if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) < 0) {
+                       wderror((struct buf *)NULL, du,
+                               "wddump: timeout waiting for status");
+                       return (EIO);
+               }
 
 
-               /* wait for completion */
-               /* must delay 5us to conform to ATA spec */
-               DELAY(5);
-               for ( i = WDCTIMEOUT ; inb(wdc+wd_status) & WDCS_BUSY ; i--) {
-                               if (i < 0) return (EIO) ;
+               /* Check final status. */
+               if (du->dk_status
+                   & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ | WDCS_ERR)
+                   != (WDCS_READY | WDCS_SEEKCMPLT)) {
+                       wderror((struct buf *)NULL, du,
+                               "wddump: extra DRQ, or error");
+                       return (EIO);
                }
                }
-               /* error check the xfer */
-               if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
-
-               if ((unsigned)addr % (1024*1024) == 0) printf("%ld ", num/2048);
-               /* update block count */
-               num--;
-               blknum++ ;
-               (int) addr += 512;
-
-               /* operator aborting dump? */
-               if (sgetc(1))
-                       return(EINTR);
+
+               /* Update block count. */
+               blknum = blknext;
+
+               /* Operator aborting dump? */
+               if (sgetc(1) & 0xff)    /* EWS: A hack to work with syscons */
+                       return (EINTR);
        }
        }
-       return(0);
+       return (0);
 }
 
 static void
 wderror(struct buf *bp, struct disk *du, char *mesg)
 {
        if (bp == NULL)
 }
 
 static void
 wderror(struct buf *bp, struct disk *du, char *mesg)
 {
        if (bp == NULL)
-               printf("wd%d: %s:", du->dk_lunit, mesg);
+               printf("wd%d: %s:\n", du->dk_lunit, mesg);
        else
                diskerr(bp, "wd", mesg, LOG_PRINTF, du->dk_skip, &du->dk_dd);
        else
                diskerr(bp, "wd", mesg, LOG_PRINTF, du->dk_skip, &du->dk_dd);
-       printf(" status %b error %b\n",
-               du->dk_status, WDCS_BITS, du->dk_error, WDERR_BITS);
+       printf("wd%d: status %b error %b\n", du->dk_lunit,
+              du->dk_status, WDCS_BITS, du->dk_error, WDERR_BITS);
+}
+
+/*
+ * Discard any interrupts that were latched by the interrupt system while
+ * we were doing polled i/o.
+ */
+static void
+wdflushirq(struct disk *du, int old_ipl)
+{
+       wdtab[du->dk_ctrlr].b_active = 2;
+       splx(old_ipl);
+       (void)splbio();
+       wdtab[du->dk_ctrlr].b_active = 0;
 }
 
 /*
 }
 
 /*
@@ -1390,14 +1596,14 @@ wderror(struct buf *bp, struct disk *du, char *mesg)
 static int
 wdreset(struct disk *du)
 {
 static int
 wdreset(struct disk *du)
 {
-       int wdc;
+       int     wdc;
 
        wdc = du->dk_port;
 
        wdc = du->dk_port;
-       (void)wdwait(du, 0);
+       (void)wdwait(du, 0, TIMEOUT);
        outb(wdc + wd_ctlr, WDCTL_IDS | WDCTL_RST);
        DELAY(10 * 1000);
        outb(wdc + wd_ctlr, WDCTL_IDS);
        outb(wdc + wd_ctlr, WDCTL_IDS | WDCTL_RST);
        DELAY(10 * 1000);
        outb(wdc + wd_ctlr, WDCTL_IDS);
-       if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT) != 0
+       if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0
            || (du->dk_error = inb(wdc + wd_error)) != 0x01)
                return (1);
        outb(wdc + wd_ctlr, WDCTL_4BIT);
            || (du->dk_error = inb(wdc + wd_error)) != 0x01)
                return (1);
        outb(wdc + wd_ctlr, WDCTL_4BIT);
@@ -1416,6 +1622,26 @@ wdsleep(int ctrlr, char *wmesg)
                tsleep((caddr_t)&wdtab[ctrlr].b_active, PZERO - 1, wmesg, 1);
 }
 
                tsleep((caddr_t)&wdtab[ctrlr].b_active, PZERO - 1, wmesg, 1);
 }
 
+static void
+wdtimeout(caddr_t cdu, int ticks)
+{
+       struct disk *du;
+       int     x;
+
+       du = (struct disk *)cdu;
+       x = splbio();
+       if (du->dk_timeout != 0 && --du->dk_timeout == 0) {
+               wderror((struct buf *)NULL, du, "interrupt timeout");
+               wdunwedge(du);
+               wdflushirq(du, x);
+               du->dk_skip = 0;
+               du->dk_flags |= DKFL_SINGLE;
+               wdstart(du->dk_ctrlr);
+       }
+       timeout(wdtimeout, cdu, hz);
+       splx(x);
+}
+
 /*
  * Reset the controller after it has become wedged.  This is different from
  * wdreset() so that wdreset() can be used in the probe and so that this
 /*
  * Reset the controller after it has become wedged.  This is different from
  * wdreset() so that wdreset() can be used in the probe and so that this
@@ -1425,7 +1651,7 @@ static int
 wdunwedge(struct disk *du)
 {
        struct disk *du1;
 wdunwedge(struct disk *du)
 {
        struct disk *du1;
-       int lunit;
+       int     lunit;
 
        /* Schedule other drives for recalibration. */
        for (lunit = 0; lunit < NWD; lunit++)
 
        /* Schedule other drives for recalibration. */
        for (lunit = 0; lunit < NWD; lunit++)
@@ -1441,7 +1667,7 @@ wdunwedge(struct disk *du)
                 * aren't prepared to have its state change.
                 */
                if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) == 0
                 * aren't prepared to have its state change.
                 */
                if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) == 0
-                   && wdwait(du, WDCS_READY | WDCS_SEEKCMPLT) == 0
+                   && wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) == 0
                    && wdsetctlr(du) == 0)
                        return (0);
        }
                    && wdsetctlr(du) == 0)
                        return (0);
        }
@@ -1459,24 +1685,27 @@ wdunwedge(struct disk *du)
  * Return controller status in du->dk_status and, if there was a controller
  * error, return the error code in du->dk_error.
  */
  * Return controller status in du->dk_status and, if there was a controller
  * error, return the error code in du->dk_error.
  */
+#ifdef WD_COUNT_RETRIES
 static int min_retries[NWDC];
 static int min_retries[NWDC];
+#endif
+
 static int
 static int
-wdwait(struct disk *du, u_char bits_wanted)
+wdwait(struct disk *du, u_char bits_wanted, int timeout)
 {
 {
-       int retries;
-       int wdc;
-       int ctrlr = du->dk_ctrlr;
-       u_char status;
+       int     wdc;
+       u_char  status;
+
 #define        POLLING         1000
 #define        POLLING         1000
-#define        TIMEOUT         2000    /* WDCC_DIAGNOSE can take > 300 msec */
 
        wdc = du->dk_port;
 
        wdc = du->dk_port;
-       retries = POLLING + TIMEOUT;
+       timeout += POLLING;
        do {
        do {
-               if (min_retries[ctrlr] > retries || min_retries[ctrlr] == 0)
-                       min_retries[ctrlr] = retries;
-               /* must delay 5us to conform to ATA spec */
-               DELAY(5);
+#ifdef WD_COUNT_RETRIES
+               if (min_retries[du->dk_ctrlr] > timeout
+                   || min_retries[du->dk_ctrlr] == 0)
+                       min_retries[du->dk_ctrlr] = timeout;
+#endif
+               DELAY(5);       /* ATA spec XXX NOT */
                du->dk_status = status = inb(wdc + wd_status);
                if (!(status & WDCS_BUSY)) {
                        if (status & WDCS_ERR) {
                du->dk_status = status = inb(wdc + wd_status);
                if (!(status & WDCS_BUSY)) {
                        if (status & WDCS_ERR) {
@@ -1494,7 +1723,7 @@ wdwait(struct disk *du, u_char bits_wanted)
                        if ((status & bits_wanted) == bits_wanted)
                                return (status & WDCS_ERR);
                }
                        if ((status & bits_wanted) == bits_wanted)
                                return (status & WDCS_ERR);
                }
-               if (retries < TIMEOUT)
+               if (timeout < TIMEOUT)
                        /*
                         * Switch to a polling rate of about 1 KHz so that
                         * the timeout is almost machine-independent.  The
                        /*
                         * Switch to a polling rate of about 1 KHz so that
                         * the timeout is almost machine-independent.  The
@@ -1502,8 +1731,8 @@ wdwait(struct disk *du, u_char bits_wanted)
                         * an extra msec won't matter.
                         */
                        DELAY(1000);
                         * an extra msec won't matter.
                         */
                        DELAY(1000);
-       } while (--retries != 0);
+       } while (--timeout != 0);
        return (-1);
 }
 
        return (-1);
 }
 
-#endif /* NWDC > 0 */
+#endif /* NWDC > 0 */