This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.1'.
[unix-history] / sys / i386 / isa / wd.c
index 1f3794c..eb5a34b 100644 (file)
@@ -1,3 +1,6 @@
+#define WD_COUNT_RETRIES
+static int wdtest = 0;
+
 /*-
  * Copyright (c) 1990 The Regents of the University of California.
  * All rights reserved.
 /*-
  * Copyright (c) 1990 The Regents of the University of California.
  * All rights reserved.
  * 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:@(#)wd.c   7.2 (Berkeley) 5/9/91
- *
- * PATCHES MAGIC                LEVEL   PATCH THAT GOT US HERE
- * --------------------         -----   ----------------------
- * CURRENT PATCH LEVEL:         6       00155
- * --------------------         -----   ----------------------
- *
- * 17 Sep 92   Frank Maclachlan        Fixed I/O error reporting on raw device
- * 31 Jul 92   Christoph Robitschko    Fixed second disk recognition,
- *                                     bzero of malloced memory for warm
- *                                     boot problem.
- * 19 Aug 92    Frank Maclachlan       Fixed bug when first sector of a
- *                                     multisector read is in bad144 table.
- * 17 Jan 93   B. Evans & A.Chernov    Fixed bugs from previous patches,
- *                                     driver initialization, and cylinder
- *                                     boundary conditions.
- * 28 Mar 93   Charles Hannum          Add missing splx calls.
- * 20 Apr 93   Terry Lee               Always report disk errors
- * 20 Apr 93   Brett Lymn              Change infinite while loops to
- *                                     timeouts
- * 17 May 93   Rodney W. Grimes        Fixed all 1000000 to use WDCTIMEOUT,
- *                                     and increased to 1000000*10 for new
- *                                     intr-0.1 code.
+ *     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"
 
 #include "wd.h"
-#if    NWD > 0
+#if    NWDC > 0
 
 #include "param.h"
 #include "dkbad.h"
 #include "systm.h"
 
 #include "param.h"
 #include "dkbad.h"
 #include "systm.h"
+#include "kernel.h"
 #include "conf.h"
 #include "file.h"
 #include "stat.h"
 #include "conf.h"
 #include "file.h"
 #include "stat.h"
 #include "uio.h"
 #include "malloc.h"
 #include "machine/cpu.h"
 #include "uio.h"
 #include "malloc.h"
 #include "machine/cpu.h"
+#include "i386/isa/isa.h"
 #include "i386/isa/isa_device.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"
 
-#define _NWD  (NWD - 1)       /* One is for the controller XXX 31 Jul 92*/
-
-#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        RETRIES         5       /* number of retries before giving up */
-#define        MAXTRANSFER     32      /* max size of transfer in page clusters */
+#define RECOVERYTIME   500000  /* usec for controller to recover after err */
+#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
+
+/*
+ * 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 */
        long    dk_bc;          /* byte count left */
        short   dk_skip;        /* blocks already transferred */
+       char    dk_ctrlr;       /* physical controller number */
        char    dk_unit;        /* physical unit number */
        char    dk_unit;        /* physical unit number */
+       char    dk_lunit;       /* logical unit number */
        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_QUIET      0x00002  /* report errors back, but don't complain */
-#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_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 */
 };
 
 };
 
-struct disk    *wddrives[_NWD];                /* table of units */
-struct buf     wdtab;
-struct buf     wdutab[_NWD];           /* head of queue per drive */
-struct buf     rwdbuf[_NWD];           /* buffers for raw IO */
-long   wdxfer[_NWD];                   /* count of transfers */
-#ifdef WDDEBUG
-int    wddebug;
+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
+static struct buf rwdbuf[NWD]; /* buffers for raw IO */
 #endif
 #endif
-
-struct isa_driver wddriver = {
-       wdprobe, wdattach, "wd",
+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",
 };
 
 };
 
-static void wdustart(struct disk *);
-static void wdstart();
-static int wdcommand(struct disk *, int);
-static int wdcontrol(struct buf *);
-static int wdsetctlr(dev_t, struct disk *);
-static int wdgetctlr(int, struct disk *);
-
 /*
  * Probe for controller.
  */
 /*
  * Probe for controller.
  */
-int
+static int
 wdprobe(struct isa_device *dvp)
 {
 wdprobe(struct isa_device *dvp)
 {
-       int unit = dvp->id_unit;
+       int     unit = dvp->id_unit;
        struct disk *du;
        struct disk *du;
-       int wdc;
 
 
-       if (unit >= _NWD)                               /* 31 Jul 92*/
-               return(0);
+       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_port = dvp->id_iobase;
 
 
-       if ((du = wddrives[unit]) == 0) {
-               du = wddrives[unit] = (struct disk *)
-                       malloc (sizeof(struct disk), M_TEMP, M_NOWAIT);
-               bzero (du, sizeof(struct disk));        /* 31 Jul 92*/
-               du->dk_unit = unit;
-       }
-
-       wdc = 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;
 
                goto nodevice;
 
-       /* reset the device */
-       outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS));
-       DELAY(1000);
-       outb(wdc+wd_ctlr, WDCTL_IDS);
-       DELAY(1000);
+       if (wdreset(du) != 0 && (DELAY(RECOVERYTIME), wdreset(du)) != 0)
+               goto nodevice;
 
        /* execute a controller only command */
 
        /* execute a controller only command */
-       if (wdcommand(du, WDCC_DIAGNOSE) < 0)
+       if (wdcommand(du, 0, 0, 0, 0, WDCC_DIAGNOSE) != 0
+           || wdwait(du, 0, TIMEOUT) != 0)
                goto nodevice;
 
                goto nodevice;
 
-       (void) inb(wdc+wd_error);       /* XXX! */
-       outb(wdc+wd_ctlr, WDCTL_4BIT);
-       return (1);
+       free(du, M_TEMP);
+       return (IO_WDCSIZE);
 
 nodevice:
        free(du, M_TEMP);
 
 nodevice:
        free(du, M_TEMP);
-       wddrives[unit] = 0;
        return (0);
 }
 
 /*
  * Attach each drive if possible.
  */
        return (0);
 }
 
 /*
  * Attach each drive if possible.
  */
-int
+static int
 wdattach(struct isa_device *dvp)
 {
 wdattach(struct isa_device *dvp)
 {
-       int unit;
-/*     int unit = dvp->id_unit;*/
-
-       for (unit=0; unit< _NWD; unit++) {
-               struct disk *du;
-               if ((du = wddrives[unit]) == 0) {
-                       du = wddrives[unit] = (struct disk *)
-                               malloc (sizeof(struct disk), M_TEMP, M_NOWAIT);
-                       bzero (du, sizeof(struct disk));
-                       du->dk_unit = unit;
-                       du->dk_port = dvp->id_iobase;
-               }
+       int     unit, lunit;
+       struct isa_device *wdup;
+       struct disk *du;
 
 
-               /* print out description of drive, suppressing multiple blanks*/
-               if(wdgetctlr(unit, du) == 0)  {
-                       int i, blank;
-                       char c;
-                       printf(" %d:<", unit);
-                       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(">");
-                       du->dk_unit = unit;
-               }
-               else {
-                       /* old ST506 controller */
-                       printf(" %d:<wdgetctlr failed, assuming OK>",
-                              unit);
+       if (dvp->id_unit >= NWDC)
+               return (0);
+
+       for (wdup = isa_biotab_wdc; wdup->id_driver != 0; wdup++) {
+               if (wdup->id_iobase != dvp->id_iobase)
+                       continue;
+               lunit = wdup->id_unit;
+               if (lunit >= NWD)
+                       continue;
+               unit = wdup->id_physid;
+
+               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;
+
+               /*
+                * 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);
+                       wddrives[lunit] = NULL;
                }
        }
                }
        }
-       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
@@ -263,19 +302,16 @@ wdattach(struct isa_device *dvp)
  * to complete.  Multi-page transfers are supported.  All I/O requests must
  * be a multiple of a sector in length.
  */
  * to complete.  Multi-page transfers are supported.  All I/O requests must
  * be a multiple of a sector in length.
  */
-int
+void
 wdstrategy(register struct buf *bp)
 {
        register struct buf *dp;
 wdstrategy(register struct buf *bp)
 {
        register struct buf *dp;
-       struct disklabel *lp;
-       register struct partition *p;
-       struct disk *du;        /* Disk unit to do the IO.      */
-       long maxsz, sz;
-       int     unit = wdunit(bp->b_dev);
+       struct disk *du;
+       int     lunit = wdunit(bp->b_dev);
        int     s;
 
        /* valid unit, controller, and request?  */
        int     s;
 
        /* valid unit, controller, and request?  */
-       if (unit >= _NWD || bp->b_blkno < 0 || (du = wddrives[unit]) == 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;
@@ -289,27 +325,29 @@ 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;
 
 
-q:
        /* queue transfer on drive, activate drive and controller if idle */
        /* queue transfer on drive, activate drive and controller if idle */
-       dp = &wdutab[unit];
+       dp = &wdutab[lunit];
        s = splbio();
        disksort(dp, bp);
        if (dp->b_active == 0)
        s = splbio();
        disksort(dp, bp);
        if (dp->b_active == 0)
-               wdustart(du);           /* start drive */
-       if (wdtab.b_active == 0)
-               wdstart(s);             /* start controller */
+               wdustart(du);   /* start drive */
+
+       /* Pick up changes made by readdisklabel(). */
+       if (du->dk_flags & DKFL_LABELLING && du->dk_state > RECAL) {
+               wdsleep(du->dk_ctrlr, "wdlab");
+               du->dk_state = WANTOPEN;
+       }
+
+       if (wdtab[du->dk_ctrlr].b_active == 0)
+               wdstart(du->dk_ctrlr);  /* start controller */
        splx(s);
        return;
 
        splx(s);
        return;
 
@@ -326,7 +364,8 @@ done:
 static void
 wdustart(register struct disk *du)
 {
 static void
 wdustart(register struct disk *du)
 {
-       register struct buf *bp, *dp = &wdutab[du->dk_unit];
+       register struct buf *bp, *dp = &wdutab[du->dk_lunit];
+       int     ctrlr = du->dk_ctrlr;
 
        /* unit already active? */
        if (dp->b_active)
 
        /* unit already active? */
        if (dp->b_active)
@@ -335,15 +374,15 @@ wdustart(register struct disk *du)
        /* anything to start? */
        bp = dp->b_actf;
        if (bp == NULL)
        /* anything to start? */
        bp = dp->b_actf;
        if (bp == NULL)
-               return; 
+               return;
 
        /* link onto controller queue */
        dp->b_forw = NULL;
 
        /* link onto controller queue */
        dp->b_forw = NULL;
-       if (wdtab.b_actf  == NULL)
-               wdtab.b_actf = dp;
+       if (wdtab[ctrlr].b_actf == NULL)
+               wdtab[ctrlr].b_actf = dp;
        else
        else
-               wdtab.b_actl->b_forw = dp;
-       wdtab.b_actl = dp;
+               wdtab[ctrlr].b_actl->b_forw = dp;
+       wdtab[ctrlr].b_actl = dp;
 
        /* mark the drive unit as busy */
        dp->b_active = 1;
 
        /* mark the drive unit as busy */
        dp->b_active = 1;
@@ -354,64 +393,71 @@ wdustart(register struct disk *du)
  * a single-sector read or write operation.  Called to start a transfer,
  * or from the interrupt routine to continue a multi-sector transfer.
  * RESTRICTIONS:
  * a single-sector read or write operation.  Called to start a transfer,
  * or from the interrupt routine to continue a multi-sector transfer.
  * RESTRICTIONS:
- * 1.  The transfer length must be an exact multiple of the sector size.
+ * 1. The transfer length must be an exact multiple of the sector size.
  */
 
 static void
  */
 
 static void
-wdstart()
+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;
        register struct buf *bp;
        struct disklabel *lp;
        struct buf *dp;
        register struct bt_bad *bt_ptr;
-       long    blknum, pagcnt, cylin, head, sector;
-       long    secpertrk, secpercyl, addr, i, timeout;
-       int     unit, s, wdc;
+       long    blknum, cylin, head, sector;
+       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? */
-       dp = wdtab.b_actf;
+       dp = wdtab[ctrlr].b_actf;
        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) {
        bp = dp->b_actf;
        if (bp == NULL) {
-               wdtab.b_actf = dp->b_forw;
+               wdtab[ctrlr].b_actf = dp->b_forw;
                goto loop;
        }
 
        /* obtain controller and drive information */
                goto loop;
        }
 
        /* obtain controller and drive information */
-       unit = wdunit(bp->b_dev);
-       du = wddrives[unit];
+       lunit = wdunit(bp->b_dev);
+       du = wddrives[lunit];
 
        /* if not really a transfer, do control operations specially */
        if (du->dk_state < OPEN) {
 
        /* if not really a transfer, do control operations specially */
        if (du->dk_state < OPEN) {
-               (void) wdcontrol(bp);
+               if (du->dk_state != WANTOPEN)
+                       printf("wd%d: wdstart: weird dk_state %d\n",
+                              du->dk_lunit, du->dk_state);
+               if (wdcontrol(bp) != 0)
+                       printf("wd%d: wdstart: wdcontrol returned nonzero, state = %d\n",
+                              du->dk_lunit, du->dk_state);
                return;
        }
 
        /* calculate transfer details */
        blknum = bp->b_blkno + du->dk_skip;
                return;
        }
 
        /* calculate transfer details */
        blknum = bp->b_blkno + du->dk_skip;
-/*if(wddebug)printf("bn%d ", blknum);*/
-#ifdef WDDEBUG
+#ifdef WDDEBUG
        if (du->dk_skip == 0)
        if (du->dk_skip == 0)
-               printf("\nwdstart %d: %s %d@%d; map ", unit,
-                       (bp->b_flags & B_READ) ? "read" : "write",
-                       bp->b_bcount, blknum);
+               printf("wd%d: wdstart: %s %d@%d; map ", lunit,
+                      (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;
@@ -421,152 +467,141 @@ 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))
-           /* XXX
-            * BAD144END was done to clean up some old bad code that was
-            * attempting to compare a u_short to -1.  This makes the compilers
-            * happy and clearly shows what is going on.
-            * rgrimes 93/06/17
-            */
-#define BAD144END (u_short)(-1)
-           for (bt_ptr = du->dk_bad.bt_bad; bt_ptr->bt_cyl != BAD144END; bt_ptr++) {
+       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. */
                        break;
                if (bt_ptr->bt_cyl == cylin &&
                if (bt_ptr->bt_cyl > cylin)
                        /* Sorted list, and we passed our cylinder. quit. */
                        break;
                if (bt_ptr->bt_cyl == cylin &&
-                               bt_ptr->bt_trksec == (head << 8) + sector) {
+                   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
-                       blknum = lp->d_secperunit - lp->d_nsectors
-                               - (bt_ptr - du->dk_bad.bt_bad) - 1;
+
+                       /*
+                        * XXX the offset of the bad sector table ought
+                        * to be stored in the in-core copy of the table.
+                        */
+#define BAD144_PART    2       /* XXX scattered magic numbers */
+#define BSD_PART       0       /* XXX should be 2 but bad144.c uses 0 */
+                       if (lp->d_partitions[BSD_PART].p_offset != 0)
+                               blknum = lp->d_partitions[BAD144_PART].p_offset
+                                        + lp->d_partitions[BAD144_PART].p_size;
+                       else
+                               blknum = lp->d_secperunit;
+                       blknum -= lp->d_nsectors + (bt_ptr - du->dk_bad.bt_bad)
+                                 + 1;
+
                        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;
                }
        }
-/*if(wddebug)pg("c%d h%d s%d ", cylin, head, sector);*/
-       sector += 1;    /* sectors begin with 1, not 0 */
 
 
-       wdtab.b_active = 1;             /* mark controller active */
-       wdc = du->dk_port;
+       wdtab[ctrlr].b_active = 1;      /* mark controller active */
 
 
-RETRY:
        /* 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)) {
-               if (wdtab.b_errcnt && (bp->b_flags & B_READ) == 0)
-                       du->dk_bc += DEV_BSIZE;
+               u_int   command;
+               u_int   count;
 
 
-               /* controller idle? */
-               timeout = 0;
-               while (inb(wdc+wd_status) & WDCS_BUSY)
-               {
-                       if (++timeout > WDCTIMEOUT)
-                       {
-                               printf("wd.c: Controller busy too long!\n");
-                               /* reset the device */
-                               outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS));
-                               DELAY(1000);
-                               outb(wdc+wd_ctlr, WDCTL_IDS);
-                               DELAY(1000);
-                               (void) inb(wdc+wd_error);       /* XXX! */
-                               outb(wdc+wd_ctlr, WDCTL_4BIT);
-                               break;
-                       }
-               }
+               if (wdtab[ctrlr].b_errcnt && (bp->b_flags & B_READ) == 0)
+                       du->dk_bc += DEV_BSIZE;
 
 
-               /* stuff the task file */
-               outb(wdc+wd_precomp, lp->d_precompcyl / 4);
-#ifdef B_FORMAT
+#ifdef B_FORMAT
                if (bp->b_flags & B_FORMAT) {
                if (bp->b_flags & B_FORMAT) {
-                       outb(wdc+wd_sector, lp->d_gap3);
-                       outb(wdc+wd_seccnt, lp->d_nsectors);
-               } else {
+                       command = WDCC_FORMAT;
+                       count = lp->d_nsectors;
+                       sector = lp->d_gap3 - 1;        /* + 1 later */
+               } else
 #endif
 #endif
-               if (du->dk_flags & DKFL_SINGLE)
-                       outb(wdc+wd_seccnt, 1);
-               else
-                       outb(wdc+wd_seccnt, howmany(du->dk_bc, DEV_BSIZE));
-               outb(wdc+wd_sector, sector);
-
-#ifdef B_FORMAT
+               {
+                       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;
                }
                }
-#endif
-
-               outb(wdc+wd_cyl_lo, cylin);
-               outb(wdc+wd_cyl_hi, cylin >> 8);
 
 
-               /* set up the SDH register (select drive) */
-               outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
-
-               /* wait for drive to become ready */
-               timeout = 0;
-               while ((inb(wdc+wd_status) & WDCS_READY) == 0)
-               {
-                       if (++timeout > WDCTIMEOUT)
-                       {
-                               printf("wd.c: Drive busy too long!\n");
-                               /* reset the device */
-                               outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS));
-                               DELAY(1000);
-                               outb(wdc+wd_ctlr, WDCTL_IDS);
-                               DELAY(1000);
-                               (void) inb(wdc+wd_error);       /* XXX! */
-                               outb(wdc+wd_ctlr, WDCTL_4BIT);
-                               goto RETRY;
+               /*
+                * 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.
+                */
+               if (wdtest != 0) {
+                       if (--wdtest == 0) {
+                               wdtest = 100;
+                               printf("dummy wdunwedge\n");
+                               wdunwedge(du);
                        }
                }
                        }
                }
-
-               /* initiate command! */
-#ifdef B_FORMAT
-               if (bp->b_flags & B_FORMAT)
-                       outb(wdc+wd_command, WDCC_FORMAT);
-               else
-#endif
-               outb(wdc+wd_command,
-                       (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE);
-#ifdef WDDEBUG
-               printf("sector %d cylin %d head %d addr %x sts %x\n",
-                       sector, cylin, head, addr, inb(wdc+wd_altsts));
+               while (wdcommand(du, cylin, head, sector, count, command)
+                      != 0) {
+                       wderror(bp, du,
+                               "wdstart: timeout waiting to give command");
+                       wdunwedge(du);
+               }
+#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?  */
-       timeout = 0;
-       while ((inb(wdc+wd_status) & WDCS_DRQ) == 0)
-       {
-               if (++timeout > WDCTIMEOUT)
-               {
-                       printf("wd.c: Drive not ready for too long!\n");
-                       /* reset the device */
-                       outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS));
-                       DELAY(1000);
-                       outb(wdc+wd_ctlr, WDCTL_IDS);
-                       DELAY(1000);
-                       (void) inb(wdc+wd_error);       /* XXX! */
-                       outb(wdc+wd_ctlr, WDCTL_4BIT);
-                       goto RETRY;
-               }
+       /* 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,
+                * then we can treat this failure the same as a command
+                * failure.  But if we are continuing a multi-sector write,
+                * 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.
+                */
        }
 
        /* then send it! */
        }
 
        /* then send it! */
-       outsw (wdc+wd_data, 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;
 }
 
@@ -576,149 +611,148 @@ RETRY:
  * continue with the next chunk if so.
  */
 void
  * continue with the next chunk if so.
  */
 void
-wdintr(struct intrframe wdif)
+wdintr(int unit)
 {
        register struct disk *du;
        register struct buf *bp, *dp;
 {
        register struct disk *du;
        register struct buf *bp, *dp;
-       int status, wdc;
-       char partch ;
 
 
-       if (!wdtab.b_active) {
-#ifdef nyet
-               printf("wd: extra interrupt\n");
+       if (wdtab[unit].b_active == 2)
+               return;         /* intr in wdflushirq() */
+       if (!wdtab[unit].b_active) {
+#ifndef LAPTOP
+               printf("wdc%d: extra interrupt\n", unit);
 #endif
                return;
        }
 
 #endif
                return;
        }
 
-       dp = wdtab.b_actf;
+       dp = wdtab[unit].b_actf;
        bp = dp->b_actf;
        du = wddrives[wdunit(bp->b_dev)];
        bp = dp->b_actf;
        du = wddrives[wdunit(bp->b_dev)];
-       wdc = du->dk_port;
-
-#ifdef WDDEBUG
-       printf("I ");
-#endif
+       du->dk_timeout = 0;
 
 
-       while ((status = inb(wdc+wd_status)) & WDCS_BUSY) ;
+       if (wdwait(du, 0, TIMEOUT) < 0) {
+               wderror(bp, du, "wdintr: timeout waiting for status");
+               du->dk_status |= WDCS_ERR;      /* XXX */
+       }
 
        /* is it not a transfer, but a control operation? */
        if (du->dk_state < OPEN) {
 
        /* is it not a transfer, but a control operation? */
        if (du->dk_state < OPEN) {
-               if (wdcontrol(bp))
-                       wdstart();
+               wdtab[unit].b_active = 0;
+               switch (wdcontrol(bp)) {
+               case 0:
+                       return;
+               case 1:
+                       wdstart(unit);
                return;
                return;
+               case 2:
+                       goto done;
+               }
        }
 
        /* have we an error? */
        }
 
        /* have we an error? */
-       if (status & (WDCS_ERR | WDCS_ECCCOR)) {
-
-               du->dk_status = status;
-               du->dk_error = inb(wdc + wd_error);
-#ifdef WDDEBUG
-               printf("status %x error %x\n", status, du->dk_error);
+       if (du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) {
+oops:
+#ifdef WDDEBUG
+               wderror(bp, du, "wdintr");
 #endif
 #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;
                }
 #endif
                        bp->b_flags |= B_ERROR;
                        goto done;
                }
 #endif
-               
+
                /* error or error correction? */
                /* error or error correction? */
-               if (status & WDCS_ERR) {
-                       if (++wdtab.b_errcnt < RETRIES) {
-                               wdtab.b_active = 0;
+               if (du->dk_status & WDCS_ERR) {
+                       if (++wdtab[unit].b_errcnt < RETRIES) {
+                               wdtab[unit].b_active = 0;
                        } else {
                        } else {
-                               if((du->dk_flags&DKFL_QUIET) == 0) {
-                                       diskerr(bp, "wd", "hard error",
-                                               LOG_PRINTF, du->dk_skip,
-                                               &du->dk_dd);
-#ifdef WDDEBUG
-                                       printf( "status %b error %b\n",
-                                               status, WDCS_BITS,
-                                               inb(wdc+wd_error), WDERR_BITS);
-#endif
-                               }
-                               bp->b_error = EIO;      /* 17 Sep 92*/
+                               wderror(bp, du, "hard error");
+                               bp->b_error = EIO;
                                bp->b_flags |= B_ERROR; /* flag the error */
                        }
                                bp->b_flags |= B_ERROR; /* flag the error */
                        }
-               } else if((du->dk_flags&DKFL_QUIET) == 0) {
-                               diskerr(bp, "wd", "soft ecc", 0,
-                                       du->dk_skip, &du->dk_dd);
-               }
+               } else
+                       wderror(bp, du, "soft ecc");
        }
        }
-outt:
 
        /*
         * 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.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? */
-               while ((inb(wdc+wd_status) & 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 */
 
                /* suck in data */
-               insw (wdc+wd_data,
-                       (int)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, &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_unit]++;
-       if (wdtab.b_active) {
+       wdxfer[du->dk_lunit]++;
+outt:
+       if (wdtab[unit].b_active) {
                if ((bp->b_flags & B_ERROR) == 0) {
                if ((bp->b_flags & B_ERROR) == 0) {
-                       du->dk_skip++;          /* Add to successful sectors. */
-                       if (wdtab.b_errcnt && (du->dk_flags & DKFL_QUIET) == 0)
-                               diskerr(bp, "wd", "soft error", 0,
-                                       du->dk_skip, &du->dk_dd);
-                       wdtab.b_errcnt = 0;
+                       du->dk_skip++;  /* add to successful sectors */
+                       if (wdtab[unit].b_errcnt)
+                               wderror(bp, du, "soft error");
+                       wdtab[unit].b_errcnt = 0;
 
                        /* see if more to transfer */
                        if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) {
 
                        /* see if more to transfer */
                        if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) {
-                               wdstart();
-                               return;         /* next chunk is started */
-                       } else if ((du->dk_flags & (DKFL_SINGLE|DKFL_ERROR))
-                                       == DKFL_ERROR) {
+                               wdtab[unit].b_active = 0;
+                               wdstart(unit);
+                               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;
-                               wdstart();
-                               return;         /* redo xfer sector by sector */
+                               du->dk_flags |= DKFL_SINGLE;
+                               wdtab[unit].b_active = 0;
+                               wdstart(unit);
+                               return; /* redo xfer sector by sector */
                        }
                }
 
                        }
                }
 
-done:
+done: ;
                /* done with this transfer, with or without error */
                du->dk_flags &= ~DKFL_SINGLE;
                /* done with this transfer, with or without error */
                du->dk_flags &= ~DKFL_SINGLE;
-               wdtab.b_actf = dp->b_forw;
-               wdtab.b_errcnt = 0;
+               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);
        }
 
        /* controller idle */
                biodone(bp);
        }
 
        /* controller idle */
-       wdtab.b_active = 0;
+       wdtab[unit].b_active = 0;
 
        /* anything more on drive queue? */
        if (dp->b_actf)
                wdustart(du);
        /* anything more for controller to do? */
 
        /* anything more on drive queue? */
        if (dp->b_actf)
                wdustart(du);
        /* anything more for controller to do? */
-       if (wdtab.b_actf)
-               wdstart();
+       if (wdtab[unit].b_actf)
+               wdstart(unit);
 }
 
 /*
 }
 
 /*
@@ -727,106 +761,167 @@ done:
 int
 wdopen(dev_t dev, int flags, int fmt, struct proc *p)
 {
 int
 wdopen(dev_t dev, int flags, int fmt, struct proc *p)
 {
-       register unsigned int unit;
+       register unsigned int lunit;
        register struct disk *du;
        register struct disk *du;
-        int part = wdpart(dev), mask = 1 << part;
-        struct partition *pp;
-       struct dkbad *db;
-       int i, error = 0;
-       char *msg;
+       int     part = wdpart(dev), mask = 1 << part;
+       struct partition *pp;
+       char    *msg;
+       struct disklabel save_label;
+
+       lunit = wdunit(dev);
+       if (lunit >= NWD)
+               return (ENXIO);
+       du = wddrives[lunit];
+       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;
 
 
-       unit = wdunit(dev);
-       if (unit >= _NWD) return (ENXIO) ;
+       /*
+        * 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);
 
 
-       du = wddrives[unit];
-       if (du == 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) {
        if ((du->dk_flags & DKFL_BSDLABEL) == 0) {
-               du->dk_flags |= DKFL_WRITEPROT;
-               wdutab[unit].b_actf = NULL;
-
                /*
                /*
-                * Use the default sizes until we've read the label,
-                * or longer if there isn't one there.
+                * 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.
                 */
                 */
-               bzero(&du->dk_dd, sizeof(du->dk_dd));
-#undef d_type /* fix goddamn segments.h! XXX */
-               du->dk_dd.d_type = DTYPE_ST506;
-               du->dk_dd.d_ncylinders = 1024;
-               du->dk_dd.d_secsize = DEV_BSIZE;
-               du->dk_dd.d_ntracks = 8;
-               du->dk_dd.d_nsectors = 17;
-               du->dk_dd.d_secpercyl = 17*8;
+               wdsleep(du->dk_ctrlr, "wdopn1");
+
+               du->dk_flags |= DKFL_LABELLING | DKFL_WRITEPROT;
                du->dk_state = WANTOPEN;
                du->dk_state = WANTOPEN;
-               du->dk_unit = unit;
-               du->dk_flags &= ~DKFL_QUIET;
-
-               /* read label using "c" partition */
-               if (msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW),
-                               wdstrategy, &du->dk_dd, du->dk_dospartitions,
-                               &du->dk_bad, 0)) {
-                       if((du->dk_flags&DKFL_QUIET) == 0) {
-                               log(LOG_WARNING, "wd%d: cannot find label (%s)\n",
-                                       unit, msg);
-                               error = EINVAL;         /* XXX needs translation */
-                       }
-                       goto done;
+               wdutab[lunit].b_actf = NULL;
+
+               /*
+                * Read label using WDRAW partition.
+                *
+                * If the drive has an MBR, then the current geometry (from
+                * wdgetctlr()) is used to read it; then the BIOS/DOS
+                * geometry is inferred and used to read the label off the
+                * 'c' partition.  Otherwise the label is read using the
+                * current geometry.  The label gives the final geometry.
+                * If bad sector handling is enabled, then this geometry
+                * is used to read the bad sector table.  The geometry
+                * changes occur inside readdisklabel() and are propagated
+                * to the driver by resetting the state machine.
+                */
+               save_label = du->dk_dd;
+#define WDSTRATEGY     ((int (*)(struct buf *)) wdstrategy)    /* XXX */
+               msg = readdisklabel(makewddev(major(dev), lunit, WDRAW),
+                                   (d_strategy_t *) WDSTRATEGY, &du->dk_dd,
+                                   du->dk_dospartitions, &du->dk_bad,
+                                   (struct buf **)NULL);
+               du->dk_flags &= ~DKFL_LABELLING;
+               if (msg != NULL) {
+                       du->dk_dd = save_label;
+                       log(LOG_WARNING, "wd%d: cannot find label (%s)\n",
+                           lunit, msg);
+                       if (part != WDRAW)
+                               return (EINVAL);  /* XXX needs translation */
                } else {
                } else {
+                       int     dospart;
+                       unsigned long newsize, offset, size;
 
 
-                       wdsetctlr(dev, du);
                        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;
-               }
 
 
-done:
-               if (error)
-                       return(error);
+                       /*
+                        * 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(). */
+               wdsleep(du->dk_ctrlr, "wdopn2");
+               du->dk_state = WANTOPEN;
        }
        }
-        /*
-         * 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) {
+
+       /*
+        * 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 != WDRAW) {
                int     start, end;
 
                int     start, end;
 
-                pp = &du->dk_dd.d_partitions[part];
-                start = pp->p_offset;
-                end = pp->p_offset + pp->p_size;
-                for (pp = du->dk_dd.d_partitions;
-                     pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions];
-                       pp++) {
-                        if (pp->p_offset + pp->p_size <= start ||
-                            pp->p_offset >= end)
-                                continue;
-                        /*if (pp - du->dk_dd.d_partitions == RAWPART)
-                                continue; */
-                        if (pp - du->dk_dd.d_partitions == WDRAW)
-                                continue;
-                        if (du->dk_openpart & (1 << (pp -
-                                       du->dk_dd.d_partitions)))
-                                log(LOG_WARNING,
-                                    "wd%d%c: overlaps open partition (%c)\n",
-                                    unit, part + 'a',
-                                    pp - du->dk_dd.d_partitions + 'a');
-                }
-        }
-        if (part >= du->dk_dd.d_npartitions && part != WDRAW)
-                return (ENXIO);
-
-       /* insure only one open at a time */
-        du->dk_openpart |= mask;
-        switch (fmt) {
-        case S_IFCHR:
-                du->dk_copenpart |= mask;
-                break;
-        case S_IFBLK:
-                du->dk_bopenpart |= mask;
-                break;
-        }
+               pp = &du->dk_dd.d_partitions[part];
+               start = pp->p_offset;
+               end = pp->p_offset + pp->p_size;
+               for (pp = du->dk_dd.d_partitions;
+                    pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions];
+                    pp++) {
+                       if (pp->p_offset + pp->p_size <= start ||
+                           pp->p_offset >= end)
+                               continue;
+                       if (pp - du->dk_dd.d_partitions == WDRAW)
+                               continue;
+                       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',
+                                   pp - du->dk_dd.d_partitions + 'a');
+               }
+       }
+       if (part >= du->dk_dd.d_npartitions && part != WDRAW)
+               return (ENXIO);
+
+       switch (fmt) {
+       case S_IFCHR:
+               du->dk_copenpart |= mask;
+               break;
+       case S_IFBLK:
+               du->dk_bopenpart |= mask;
+               break;
+       }
+       du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
+
        return (0);
 }
 
        return (0);
 }
 
@@ -834,206 +929,245 @@ done:
  * 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;
-       register unit;
-       unsigned char  stat;
-       int s, cnt;
-       extern int bootdev;
-       int cyl, trk, sec, i, wdc;
-       struct wdparams foo;
+       int     ctrlr;
 
        du = wddrives[wdunit(bp->b_dev)];
 
        du = wddrives[wdunit(bp->b_dev)];
-       unit = du->dk_unit;
-       wdc = du->dk_port;
-       
-       switch (du->dk_state) {
-
-       tryagainrecal:
-       case WANTOPEN:                  /* set SDH, step rate, do restore */
-#ifdef WDDEBUG
-               printf("wd%d: recal ", unit);
-#endif
-               s = splbio();           /* not called from intr level ... */
-               wdgetctlr(unit, du);
-
-               outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
-               wdtab.b_active = 1;
-
-               /* wait for drive and controller to become ready */
-               for (i = WDCTIMEOUT; (inb(wdc+wd_status) & (WDCS_READY|WDCS_BUSY))
-                                 != WDCS_READY && i-- != 0; )
-                       ;
-               outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
-               du->dk_state++;
-               splx(s);
-               return(0);
+       ctrlr = du->dk_ctrlr;
 
 
+       switch (du->dk_state) {
+       case WANTOPEN:
+tryagainrecal:
+               wdtab[ctrlr].b_active = 1;
+               if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0) {
+                       wderror(bp, du, "wdcontrol: wdcommand failed");
+                       goto maybe_retry;
+               }
+               du->dk_state = RECAL;
+               return (0);
        case RECAL:
        case RECAL:
-               if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
-                       if ((du->dk_flags & DKFL_QUIET) == 0) {
-                               printf("wd%d: recal", du->dk_unit);
-                               printf(": status %b error %b\n",
-                                       stat, WDCS_BITS, inb(wdc+wd_error),
-                                       WDERR_BITS);
-                       }
-                       if (++wdtab.b_errcnt < RETRIES) {
-                               du->dk_state = WANTOPEN;
+               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);
+                       du->dk_state = WANTOPEN;
+                       if (++wdtab[ctrlr].b_errcnt < RETRIES)
                                goto tryagainrecal;
                                goto tryagainrecal;
-                       }
                        bp->b_error = ENXIO;    /* XXX needs translation */
                        bp->b_error = ENXIO;    /* XXX needs translation */
-                       goto badopen;
+                       bp->b_flags |= B_ERROR;
+                       return (2);
                }
                }
-
-               /* some controllers require this ... */
-               wdsetctlr(bp->b_dev, du);
-
-               wdtab.b_errcnt = 0;
+               wdtab[ctrlr].b_errcnt = 0;
                du->dk_state = OPEN;
                /*
                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);
-
-       default:
-               panic("wdcontrol");
+               return (1);
        }
        }
-       /* NOTREACHED */
-
-badopen:
-       if ((du->dk_flags & DKFL_QUIET) == 0) 
-               printf(": status %b error %b\n",
-                       stat, WDCS_BITS, inb(wdc + wd_error), WDERR_BITS);
-       bp->b_flags |= B_ERROR;
-       return(1);
+       panic("wdcontrol");
+       return (2);
 }
 
 /*
 }
 
 /*
- * send a command and wait uninterruptibly until controller is finished.
- * return -1 if controller busy for too long, otherwise
- * return status. intended for brief controller commands at critical points.
- * assumes interrupts are blocked.
+ * Wait uninterruptibly until controller is not busy, then send it a command.
+ * The wait usually terminates immediately because we waited for the previous
+ * command to terminate.
  */
 static int
  */
 static int
-wdcommand(struct disk *du, int cmd) {
-       int timeout = WDCTIMEOUT, stat, wdc;
+wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector,
+         u_int count, u_int command)
+{
+       u_int   wdc;
 
 
-       /* controller ready for command? */
+       if (wdwait(du, 0, TIMEOUT) < 0)
+               return (1);
        wdc = du->dk_port;
        wdc = du->dk_port;
-       while (((stat = inb(wdc + wd_status)) & WDCS_BUSY) && timeout > 0)
-               timeout--;
-       if (timeout <= 0)
-               return(-1);
-
-       /* send command, await results */
-       outb(wdc+wd_command, cmd);
-       while (((stat = inb(wdc+wd_status)) & WDCS_BUSY) && timeout > 0)
-               timeout--;
-       if (timeout <= 0)
-               return(-1);
-       if (cmd != WDCC_READP)
-               return (stat);
-
-       /* is controller ready to return data? */
-       while (((stat = inb(wdc+wd_status)) & (WDCS_ERR|WDCS_DRQ)) == 0 &&
-               timeout > 0)
-               timeout--;
-       if (timeout <= 0)
-               return(-1);
-
-       return (stat);
+       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);
+       outb(wdc + wd_sector, sector + 1);
+       outb(wdc + wd_seccnt, count);
+       if (wdwait(du, command == WDCC_DIAGNOSE || command == WDCC_IDC
+                      ? 0 : WDCS_READY, TIMEOUT) < 0)
+               return (1);
+       outb(wdc + wd_command, command);
+       return (0);
 }
 
 /*
  * issue IDC to drive to tell it just what geometry it is to be.
  */
 static int
 }
 
 /*
  * issue IDC to drive to tell it just what geometry it is to be.
  */
 static int
-wdsetctlr(dev_t dev, struct disk *du) {
-       int stat, x, wdc;
+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);
+#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
+           || wdwait(du, WDCS_READY, TIMEOUT) < 0) {
+               wderror((struct buf *)NULL, du, "wdsetctlr failed");
+               return (1);
+       }
+       return (0);
+}
 
 
-/*printf("C%dH%dS%d ", du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks,
-       du->dk_dd.d_nsectors);*/
+/*
+ * Wait until driver is inactive, then set up controller.
+ */
+static int
+wdwsetctlr(struct disk *du)
+{
+       int     stat;
+       int     x;
 
 
+       wdsleep(du->dk_ctrlr, "wdwset");
        x = splbio();
        x = splbio();
-       wdc = du->dk_port;
-       outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders+1);
-       outb(wdc+wd_cyl_hi, (du->dk_dd.d_ncylinders+1)>>8);
-       outb(wdc+wd_sdh, WDSD_IBM | (wdunit(dev) << 4) + du->dk_dd.d_ntracks-1);
-       outb(wdc+wd_seccnt, du->dk_dd.d_nsectors);
-       stat = wdcommand(du, WDCC_IDC);
-
-       if (stat < 0) {
-               splx(x);
-               return(stat);
-       }
-       if (stat & WDCS_ERR)
-               printf("wdsetctlr: status %b error %b\n",
-                       stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
+       stat = wdsetctlr(du);
+       wdflushirq(du, x);
        splx(x);
        splx(x);
-       return(stat);
+       return (stat);
 }
 
 /*
  * 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(int u, struct disk *du) {
-       int stat, x, i, wdc;
-       char tb[DEV_BSIZE];
+wdgetctlr(struct disk *du)
+{
+       int     i;
+       char    tb[DEV_BSIZE];
        struct wdparams *wp;
 
        struct wdparams *wp;
 
-       x = splbio();           /* not called from intr level ... */
-       wdc = du->dk_port;
-       outb(wdc+wd_sdh, WDSD_IBM | (u << 4));
-       stat = wdcommand(du, WDCC_READP);
+       if (wdcommand(du, 0, 0, 0, 0, WDCC_READP) != 0
+           || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) {
+               /* XXX need to check error status after final transfer. */
+               /*
+                * 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.
+                */
+               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);
 
 
-       if (stat < 0) {
-               splx(x);
-               return(stat);
-       }
-       if (stat & WDCS_ERR) {
-               stat = inb(wdc+wd_error);
-               splx(x);
-               return(stat);
+               /*
+                * 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_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)
+                * in case there is no real label.
+                */
+               du->dk_dd.d_type = DTYPE_ST506;
+               du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
+               strncpy(du->dk_dd.d_typename, "Fake geometry",
+                       sizeof du->dk_dd.d_typename);
+
+               /* Fake the model name for printing by wdattach(). */
+               strncpy(du->dk_params.wdp_model, "unknown",
+                       sizeof du->dk_params.wdp_model);
+
+               return (0);
        }
 
        /* obtain parameters */
        wp = &du->dk_params;
        }
 
        /* obtain parameters */
        wp = &du->dk_params;
-       insw(wdc+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);
        }
-/*printf("gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", wp->wdp_config,
-wp->wdp_fixedcyl+wp->wdp_removcyl, wp->wdp_heads, wp->wdp_sectors,
-wp->wdp_cntype, wp->wdp_cnsbsz, wp->wdp_model);*/
+       /*
+        * 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(
+"\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 */
 
        /* update disklabel given drive information */
-       du->dk_dd.d_ncylinders = wp->wdp_fixedcyl + wp->wdp_removcyl /*+- 1*/;
+       du->dk_dd.d_secsize = DEV_BSIZE;
+       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[1].p_size = du->dk_dd.d_secpercyl *
-                       wp->wdp_sectors;
-       du->dk_dd.d_partitions[1].p_offset = 0;
+       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;
 
-       /* XXX sometimes possibly needed */
-       (void) inb(wdc+wd_status);
        return (0);
 }
 
        return (0);
 }
 
@@ -1043,39 +1177,44 @@ 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;
-                break;
-        case S_IFBLK:
-                du->dk_bopenpart &= ~mask;
-                break;
-        }
-       return(0);
+       switch (fmt) {
+       case S_IFCHR:
+               du->dk_copenpart &= ~mask;
+               break;
+       case S_IFBLK:
+               du->dk_bopenpart &= ~mask;
+               break;
+       }
+       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 unit = 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;
        struct uio auio;
        struct iovec aiov;
+#endif
 
 
-       du = wddrives[unit];
+       du = wddrives[lunit];
 
        switch (cmd) {
 
        case DIOCSBAD:
 
        switch (cmd) {
 
        case DIOCSBAD:
-                if ((flag & FWRITE) == 0)
-                        error = EBADF;
+               if ((flag & FWRITE) == 0)
+                       error = EBADF;
                else
                        du->dk_bad = *(struct dkbad *)addr;
                break;
                else
                        du->dk_bad = *(struct dkbad *)addr;
                break;
@@ -1084,56 +1223,74 @@ wdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
                *(struct disklabel *)addr = du->dk_dd;
                break;
 
                *(struct disklabel *)addr = du->dk_dd;
                break;
 
-        case DIOCGPART:
-                ((struct partinfo *)addr)->disklab = &du->dk_dd;
-                ((struct partinfo *)addr)->part =
-                    &du->dk_dd.d_partitions[wdpart(dev)];
-                break;
-
-        case DIOCSDINFO:
-                if ((flag & FWRITE) == 0)
-                        error = EBADF;
-                else
-                        error = setdisklabel(&du->dk_dd,
-                                       (struct disklabel *)addr,
-                         /*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart : */0,
-                               du->dk_dospartitions);
-                if (error == 0) {
+       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)];
+               break;
+
+       case DIOCSDINFO:
+               if ((flag & FWRITE) == 0)
+                       error = EBADF;
+               else
+                       error = setdisklabel(&du->dk_dd,
+                                            (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;
                        du->dk_flags |= DKFL_BSDLABEL;
-                       wdsetctlr(dev, du);
+                       wdwsetctlr(du); /* XXX - check */
                }
                }
-                break;
+               break;
 
 
-        case DIOCWLABEL:
+       case DIOCWLABEL:
                du->dk_flags &= ~DKFL_WRITEPROT;
                du->dk_flags &= ~DKFL_WRITEPROT;
-                if ((flag & FWRITE) == 0)
-                        error = EBADF;
-                else
-                        du->dk_wlabel = *(int *)addr;
-                break;
+               if ((flag & FWRITE) == 0)
+                       error = EBADF;
+               else
+                       du->dk_wlabel = *(int *)addr;
+               break;
 
 
-        case DIOCWDINFO:
+       case DIOCWDINFO:
                du->dk_flags &= ~DKFL_WRITEPROT;
                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;
+               if ((flag & FWRITE) == 0)
+                       error = EBADF;
+               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;
-                       wdsetctlr(dev, du);
-
-                        /* 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);
-                        du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
-                        du->dk_wlabel = wlab;
-                }
-                break;
+                       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;
+                       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;
+               }
+               break;
 
 #ifdef notyet
        case DIOCGDINFOP:
 
 #ifdef notyet
        case DIOCGDINFOP:
@@ -1154,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[unit], 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;
@@ -1171,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)
 {
@@ -1184,176 +1342,397 @@ wdformat(struct buf *bp)
 int
 wdsize(dev_t dev)
 {
 int
 wdsize(dev_t dev)
 {
-       int unit = wdunit(dev), part = wdpart(dev), val;
+       int     lunit = wdunit(dev), part = wdpart(dev), val;
        struct disk *du;
 
        struct disk *du;
 
-       if (unit >= _NWD)       /* 31 Jul 92*/
-               return(-1);
-
-       du = wddrives[unit];
-       if (du == 0 || du->dk_state == 0)
-               val = wdopen (makewddev(major(dev), unit, WDRAW), FREAD, S_IFBLK, 0);
-       if (du == 0 || val != 0 || du->dk_flags & DKFL_WRITEPROT)
+       if (lunit >= NWD || wddospart(dev) || (du = wddrives[lunit]) == NULL)
                return (-1);
                return (-1);
-
-       return((int)du->dk_dd.d_partitions[part].p_size);
+       val = 0;
+       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 ((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 */
+       register struct disk *du;
        register struct bt_bad *bt_ptr;
        register struct bt_bad *bt_ptr;
-       long    num;                    /* number of sectors to write */
-       int     unit, part, wdc;
-       long    blkoff, blknum, blkcnt;
-       long    cylin, head, sector, stat;
-       long    secpertrk, secpercyl, nblocks, i;
-       char *addr;
-       extern  int Maxmem;
-       static  wddoingadump = 0 ;
+       struct disklabel *lp;
+       long    num;            /* number of sectors to write */
+       int     lunit, part;
+       long    blkoff, blknum;
+       long    blkchk, blkcnt, blknext;
+       long    cylin, head, sector;
+       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;
-       unit = wdunit(dev);             /* eventually support floppies? */
-       part = wdpart(dev);             /* file system */
-       /* check for acceptable drive number */
-       if (unit >= _NWD) return(ENXIO);                /* 31 Jul 92*/
-
-       du = wddrives[unit];
-       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;
-
-       /* 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.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);
-
-       /*wdtab.b_active = 1;           /* mark controller active for if we
-                                          panic during the dump */
-       wddoingadump = 1  ;  i = 100000 ;
-       while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0)) ;
-       outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
-       outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
-       while (inb(wdc+wd_status) & WDCS_BUSY) ;
-
-       /* some compaq controllers require this ... */
-       wdsetctlr(dev, du);
-       
+#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
+       /* Mark controller active for if we panic during the dump. */
+       wdtab[du->dk_ctrlr].b_active = 1;
+#endif
+       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, CADDR1, trunc_page(addr), VM_PROT_READ, TRUE);
-
-               /* compute disk address */
-               cylin = blknum / secpercyl;
-               head = (blknum % secpercyl) / secpertrk;
-               sector = blknum % secpertrk;
+               blknext = blknum + blkcnt;
 
 
-#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.
                         */
-                               blknum = (du->dk_dd.d_secperunit)
-                                       - du->dk_dd.d_nsectors
-                                       - (bt_ptr - du->dk_bad.bt_bad) - 1;
-                               cylin = blknum / secpercyl;
-                               head = (blknum % secpercyl) / secpertrk;
-                               sector = blknum % secpertrk;
+                               /* 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
+                                                + lp->d_partitions[BAD144_PART]
+                                                      .p_size;
+                               else
+                                       blknum = lp->d_secperunit;
+                               blknum -= lp->d_nsectors
+                                         + (bt_ptr - du->dk_bad.bt_bad) + 1;
+#ifdef WDDEBUG
+                               printf("new = %ld\n", blknum);
+#endif
                                break;
                        }
                                break;
                        }
+                   }
+                 }
+out:
 
 
+               /* Compute disk address. */
+               cylin = blknum / secpercyl;
+               head = (blknum % secpercyl) / secpertrk;
+               sector = blknum % secpertrk;
+
+#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
 #endif
-               sector++;               /* origin 1 */
-
-               /* select drive.     */
-               outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
-               while ((inb(wdc+wd_status) & WDCS_READY) == 0) ;
-
-               /* transfer some blocks */
-               outb(wdc+wd_sector, sector);
-               outb(wdc+wd_seccnt,1);
-               outb(wdc+wd_cyl_lo, cylin);
-               outb(wdc+wd_cyl_hi, cylin >> 8);
-#ifdef notdef
-               /* lets just talk about this first...*/
-               pg ("sdh 0%o sector %d cyl %d addr 0x%x",
-                       inb(wdc+wd_sdh), inb(wdc+wd_sector),
-                       inb(wdc+wd_cyl_hi)*256+inb(wdc+wd_cyl_lo), addr) ;
-#endif
-               outb(wdc+wd_command, WDCC_WRITE);
-               
-               /* Ready to send data?  */
-               while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
-               if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
 
 
-               outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), 256);
+               /* 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 */
-               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("%d ", 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);
+}
+
+static void
+wderror(struct buf *bp, struct disk *du, char *mesg)
+{
+       if (bp == NULL)
+               printf("wd%d: %s:\n", du->dk_lunit, mesg);
+       else
+               diskerr(bp, "wd", mesg, LOG_PRINTF, du->dk_skip, &du->dk_dd);
+       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;
+}
+
+/*
+ * Reset the controller.
+ */
+static int
+wdreset(struct disk *du)
+{
+       int     wdc;
+
+       wdc = du->dk_port;
+       (void)wdwait(du, 0, TIMEOUT);
+       outb(wdc + wd_ctlr, WDCTL_IDS | WDCTL_RST);
+       DELAY(10 * 1000);
+       outb(wdc + wd_ctlr, WDCTL_IDS);
+       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);
+       return (0);
+}
+
+/*
+ * Sleep until driver is inactive.
+ * This is used only for avoiding rare race conditions, so it is unimportant
+ * that the sleep may be far too short or too long.
+ */
+static void
+wdsleep(int ctrlr, char *wmesg)
+{
+       while (wdtab[ctrlr].b_active)
+               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
+ * can restore the geometry .
+ */
+static int
+wdunwedge(struct disk *du)
+{
+       struct disk *du1;
+       int     lunit;
+
+       /* Schedule other drives for recalibration. */
+       for (lunit = 0; lunit < NWD; lunit++)
+               if ((du1 = wddrives[lunit]) != NULL && du1 != du
+                   && du1->dk_ctrlr == du->dk_ctrlr
+                   && du1->dk_state > WANTOPEN)
+                       du1->dk_state = WANTOPEN;
+
+       DELAY(RECOVERYTIME);
+       if (wdreset(du) == 0) {
+               /*
+                * XXX - recalibrate current drive now because some callers
+                * 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, TIMEOUT) == 0
+                   && wdsetctlr(du) == 0)
+                       return (0);
        }
        }
-       return(0);
+       wderror((struct buf *)NULL, du, "wdunwedge failed");
+       return (1);
 }
 }
+
+/*
+ * Wait uninterruptibly until controller is not busy and either certain
+ * status bits are set or an error has occurred.
+ * The wait is usually short unless it is for the controller to process
+ * an entire critical command.
+ * Return 1 for (possibly stale) controller errors, -1 for timeout errors,
+ * or 0 for no errors.
+ * 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];
+#endif
+
+static int
+wdwait(struct disk *du, u_char bits_wanted, int timeout)
+{
+       int     wdc;
+       u_char  status;
+
+#define        POLLING         1000
+
+       wdc = du->dk_port;
+       timeout += POLLING;
+       do {
+#ifdef WD_COUNT_RETRIES
+               if (min_retries[du->dk_ctrlr] > timeout
+                   || min_retries[du->dk_ctrlr] == 0)
+                       min_retries[du->dk_ctrlr] = timeout;
 #endif
 #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_error = inb(wdc + wd_error);
+                               /*
+                                * We once returned here.  This is wrong
+                                * because the error bit is apparently only
+                                * valid after the controller has interrupted
+                                * (e.g., the error bit is stale when we wait
+                                * for DRQ for writes).  So we can't depend
+                                * on the error bit at all when polling for
+                                * command completion.
+                                */
+                       }
+                       if ((status & bits_wanted) == bits_wanted)
+                               return (status & WDCS_ERR);
+               }
+               if (timeout < TIMEOUT)
+                       /*
+                        * Switch to a polling rate of about 1 KHz so that
+                        * the timeout is almost machine-independent.  The
+                        * controller is taking a long time to respond, so
+                        * an extra msec won't matter.
+                        */
+                       DELAY(1000);
+       } while (--timeout != 0);
+       return (-1);
+}
+
+#endif /* NWDC > 0 */