Badblock reporting and timeout fixes for wd.c, interm hacks.
[unix-history] / usr / src / sys.386bsd / i386 / isa / wd.c
index e9b1a10..bbec45b 100644 (file)
  * SUCH DAMAGE.
  *
  *     from:@(#)wd.c   7.2 (Berkeley) 5/9/91
  * 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.
  */
 
 /* TODO:peel out buffer at low ipl, speed improvement */
  */
 
 /* TODO:peel out buffer at low ipl, speed improvement */
 #include "syslog.h"
 #include "vm/vm.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        RETRIES         5       /* number of retries before giving up */
 #define        MAXTRANSFER     32      /* max size of transfer in page clusters */
 
 #define        RETRIES         5       /* number of retries before giving up */
 #define        MAXTRANSFER     32      /* max size of transfer in page clusters */
 
@@ -113,11 +141,11 @@ struct    disk {
        struct  dkbad   dk_bad; /* bad sector table */
 };
 
        struct  dkbad   dk_bad; /* bad sector table */
 };
 
-struct disk    *wddrives[NWD];         /* table of units */
+struct disk    *wddrives[_NWD];                /* table of units */
 struct buf     wdtab;
 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 */
+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;
 #endif
 #ifdef WDDEBUG
 int    wddebug;
 #endif
@@ -143,21 +171,21 @@ wdprobe(struct isa_device *dvp)
        struct disk *du;
        int wdc;
 
        struct disk *du;
        int wdc;
 
-       if (unit > NWD)
+       if (unit >= _NWD)                               /* 31 Jul 92*/
                return(0);
 
        if ((du = wddrives[unit]) == 0) {
                du = wddrives[unit] = (struct disk *)
                        malloc (sizeof(struct disk), M_TEMP, M_NOWAIT);
                return(0);
 
        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 */
                du->dk_unit = unit;
        }
 
        wdc = du->dk_port = dvp->id_iobase;
        
        /* check if we have registers that work */
-       outb(wdc+wd_error, 0x5a) ;      /* error register not writable */
-       outb(wdc+wd_cyl_lo, 0xa5) ;     /* but all of cyllo are implemented */
-       if(inb(wdc+wd_error) == 0x5a || inb(wdc+wd_cyl_lo) != 0xa5)
+       outb(wdc+wd_cyl_lo, 0xa5) ;     /* wd_cyl_lo is read/write */
+       if(inb(wdc+wd_cyl_lo) != 0xa5)
                goto nodevice;
 
        /* reset the device */
                goto nodevice;
 
        /* reset the device */
@@ -186,33 +214,47 @@ nodevice:
 int
 wdattach(struct isa_device *dvp)
 {
 int
 wdattach(struct isa_device *dvp)
 {
-       int unit = dvp->id_unit;
-       struct disk *du = wddrives[unit];
-
-       if(wdgetctlr(unit, du) == 0)  {
-               int i, blank;
-               char c;
-
-               printf(" <");
-               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);
+       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;
+               }
+
+               /* 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);
                }
                }
-               printf(">");
        }
        }
-/* check for index pulses from each drive. if present, report and
-   allocate a bios drive position to it, which will be used by read disklabel */
-       du->dk_unit = unit;
        return(1);
 }
 
        return(1);
 }
 
@@ -233,7 +275,8 @@ wdstrategy(register struct buf *bp)
        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 (unit >= _NWD || bp->b_blkno < 0 || (du = wddrives[unit]) == 0) {
+
                bp->b_error = EINVAL;
                bp->b_flags |= B_ERROR;
                goto done;
                bp->b_error = EINVAL;
                bp->b_flags |= B_ERROR;
                goto done;
@@ -323,7 +366,7 @@ wdstart()
        struct buf *dp;
        register struct bt_bad *bt_ptr;
        long    blknum, pagcnt, cylin, head, sector;
        struct buf *dp;
        register struct bt_bad *bt_ptr;
        long    blknum, pagcnt, cylin, head, sector;
-       long    secpertrk, secpercyl, addr, i;
+       long    secpertrk, secpercyl, addr, i, timeout;
        int     unit, s, wdc;
 
 loop:
        int     unit, s, wdc;
 
 loop:
@@ -368,19 +411,18 @@ loop:
        lp = &du->dk_dd;
        secpertrk = lp->d_nsectors;
        secpercyl = lp->d_secpercyl;
        lp = &du->dk_dd;
        secpertrk = lp->d_nsectors;
        secpercyl = lp->d_secpercyl;
+       if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW)
+               blknum += lp->d_partitions[wdpart(bp->b_dev)].p_offset;
        cylin = blknum / secpercyl;
        head = (blknum % secpercyl) / secpertrk;
        sector = blknum % secpertrk;
        cylin = blknum / secpercyl;
        head = (blknum % secpercyl) / secpertrk;
        sector = blknum % secpertrk;
-       if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW)
-               cylin += lp->d_partitions[wdpart(bp->b_dev)].p_offset
-                               / secpercyl;
 
        /* 
         * See if the current block is in the bad block list.
         * (If we have one, and not formatting.)
         */
 
        /* 
         * 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))
-               == (/*DKFL_SINGLE|*/DKFL_BADSECT))
+       if ((du->dk_flags & (DKFL_SINGLE|DKFL_BADSECT))         /* 19 Aug 92*/
+               == (DKFL_SINGLE|DKFL_BADSECT))
            for (bt_ptr = du->dk_bad.bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) {
                if (bt_ptr->bt_cyl > cylin)
                        /* Sorted list, and we passed our cylinder. quit. */
            for (bt_ptr = du->dk_bad.bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) {
                if (bt_ptr->bt_cyl > cylin)
                        /* Sorted list, and we passed our cylinder. quit. */
@@ -414,14 +456,29 @@ loop:
        wdtab.b_active = 1;             /* mark controller active */
        wdc = du->dk_port;
 
        wdtab.b_active = 1;             /* mark controller active */
        wdc = du->dk_port;
 
+RETRY:
        /* 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;
 
                /* controller idle? */
        /* 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;
 
                /* controller idle? */
+               timeout = 0;
                while (inb(wdc+wd_status) & WDCS_BUSY)
                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;
+                       }
+               }
 
                /* stuff the task file */
                outb(wdc+wd_precomp, lp->d_precompcyl / 4);
 
                /* stuff the task file */
                outb(wdc+wd_precomp, lp->d_precompcyl / 4);
@@ -448,8 +505,22 @@ loop:
                outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
 
                /* wait for drive to become ready */
                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)
                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;
+                       }
+               }
 
                /* initiate command! */
 #ifdef B_FORMAT
 
                /* initiate command! */
 #ifdef B_FORMAT
@@ -469,8 +540,22 @@ loop:
        if (bp->b_flags & B_READ) return;
 
        /* ready to send data?  */
        if (bp->b_flags & B_READ) return;
 
        /* ready to send data?  */
+       timeout = 0;
        while ((inb(wdc+wd_status) & WDCS_DRQ) == 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;
+               }
+       }
 
        /* then send it! */
        outsw (wdc+wd_data, addr+du->dk_skip * DEV_BSIZE,
 
        /* then send it! */
        outsw (wdc+wd_data, addr+du->dk_skip * DEV_BSIZE,
@@ -530,6 +615,7 @@ wdintr(struct intrframe wdif)
                }
 #ifdef B_FORMAT
                if (bp->b_flags & B_FORMAT) {
                }
 #ifdef B_FORMAT
                if (bp->b_flags & B_FORMAT) {
+                       bp->b_error = EIO;              /* 17 Sep 92*/
                        bp->b_flags |= B_ERROR;
                        goto done;
                }
                        bp->b_flags |= B_ERROR;
                        goto done;
                }
@@ -550,6 +636,7 @@ wdintr(struct intrframe wdif)
                                                inb(wdc+wd_error), WDERR_BITS);
 #endif
                                }
                                                inb(wdc+wd_error), WDERR_BITS);
 #endif
                                }
+                               bp->b_error = EIO;      /* 17 Sep 92*/
                                bp->b_flags |= B_ERROR; /* flag the error */
                        }
                } else if((du->dk_flags&DKFL_QUIET) == 0) {
                                bp->b_flags |= B_ERROR; /* flag the error */
                        }
                } else if((du->dk_flags&DKFL_QUIET) == 0) {
@@ -642,14 +729,9 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p)
        char *msg;
 
        unit = wdunit(dev);
        char *msg;
 
        unit = wdunit(dev);
-       if (unit >= NWD) return (ENXIO) ;
+       if (unit >= _NWD) return (ENXIO) ;
 
        du = wddrives[unit];
 
        du = wddrives[unit];
-       if (du == 0 && (unit&1) && wddrives[unit&~1]) {                 /*XXX*/
-               du = wddrives[unit] = (struct disk *)                   /*XXX*/
-                       malloc (sizeof(struct disk), M_TEMP, M_NOWAIT); /*XXX*/
-               du->dk_port = wddrives[unit&~1]->dk_port;               /*XXX*/
-       }                                                               /*XXX*/
        if (du == 0) return (ENXIO) ;
 
        if ((du->dk_flags & DKFL_BSDLABEL) == 0) {
        if (du == 0) return (ENXIO) ;
 
        if ((du->dk_flags & DKFL_BSDLABEL) == 0) {
@@ -670,10 +752,7 @@ wdopen(dev_t dev, int flags, int fmt, struct proc *p)
                du->dk_dd.d_secpercyl = 17*8;
                du->dk_state = WANTOPEN;
                du->dk_unit = unit;
                du->dk_dd.d_secpercyl = 17*8;
                du->dk_state = WANTOPEN;
                du->dk_unit = unit;
-               if (part == WDRAW)
-                       du->dk_flags |= DKFL_QUIET;
-               else
-                       du->dk_flags &= ~DKFL_QUIET;
+               du->dk_flags &= ~DKFL_QUIET;
 
                /* read label using "c" partition */
                if (msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW),
 
                /* read label using "c" partition */
                if (msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW),
@@ -777,6 +856,11 @@ wdcontrol(register struct buf *bp)
 
                outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
                wdtab.b_active = 1;
 
                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);
                outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
                du->dk_state++;
                splx(s);
@@ -790,8 +874,10 @@ wdcontrol(register struct buf *bp)
                                        stat, WDCS_BITS, inb(wdc+wd_error),
                                        WDERR_BITS);
                        }
                                        stat, WDCS_BITS, inb(wdc+wd_error),
                                        WDERR_BITS);
                        }
-                       if (++wdtab.b_errcnt < RETRIES)
+                       if (++wdtab.b_errcnt < RETRIES) {
+                               du->dk_state = WANTOPEN;
                                goto tryagainrecal;
                                goto tryagainrecal;
+                       }
                        bp->b_error = ENXIO;    /* XXX needs translation */
                        goto badopen;
                }
                        bp->b_error = ENXIO;    /* XXX needs translation */
                        goto badopen;
                }
@@ -828,7 +914,7 @@ badopen:
  */
 static int
 wdcommand(struct disk *du, int cmd) {
  */
 static int
 wdcommand(struct disk *du, int cmd) {
-       int timeout = 1000000, stat, wdc;
+       int timeout = WDCTIMEOUT, stat, wdc;
 
        /* controller ready for command? */
        wdc = du->dk_port;
 
        /* controller ready for command? */
        wdc = du->dk_port;
@@ -874,8 +960,10 @@ wdsetctlr(dev_t dev, struct disk *du) {
        outb(wdc+wd_seccnt, du->dk_dd.d_nsectors);
        stat = wdcommand(du, WDCC_IDC);
 
        outb(wdc+wd_seccnt, du->dk_dd.d_nsectors);
        stat = wdcommand(du, WDCC_IDC);
 
-       if (stat < 0)
+       if (stat < 0) {
+               splx(x);
                return(stat);
                return(stat);
+       }
        if (stat & WDCS_ERR)
                printf("wdsetctlr: status %b error %b\n",
                        stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
        if (stat & WDCS_ERR)
                printf("wdsetctlr: status %b error %b\n",
                        stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
@@ -897,11 +985,14 @@ wdgetctlr(int u, struct disk *du) {
        outb(wdc+wd_sdh, WDSD_IBM | (u << 4));
        stat = wdcommand(du, WDCC_READP);
 
        outb(wdc+wd_sdh, WDSD_IBM | (u << 4));
        stat = wdcommand(du, WDCC_READP);
 
-       if (stat < 0)
+       if (stat < 0) {
+               splx(x);
                return(stat);
                return(stat);
+       }
        if (stat & WDCS_ERR) {
        if (stat & WDCS_ERR) {
+               stat = inb(wdc+wd_error);
                splx(x);
                splx(x);
-               return(inb(wdc+wd_error));
+               return(stat);
        }
 
        /* obtain parameters */
        }
 
        /* obtain parameters */
@@ -1089,7 +1180,7 @@ wdsize(dev_t dev)
        int unit = wdunit(dev), part = wdpart(dev), val;
        struct disk *du;
 
        int unit = wdunit(dev), part = wdpart(dev), val;
        struct disk *du;
 
-       if (unit >= NWD)
+       if (unit >= _NWD)       /* 31 Jul 92*/
                return(-1);
 
        du = wddrives[unit];
                return(-1);
 
        du = wddrives[unit];
@@ -1129,7 +1220,7 @@ wddump(dev_t dev)                 /* dump core after a system crash */
        unit = wdunit(dev);             /* eventually support floppies? */
        part = wdpart(dev);             /* file system */
        /* check for acceptable drive number */
        unit = wdunit(dev);             /* eventually support floppies? */
        part = wdpart(dev);             /* file system */
        /* check for acceptable drive number */
-       if (unit >= NWD) return(ENXIO);
+       if (unit >= _NWD) return(ENXIO);                /* 31 Jul 92*/
 
        du = wddrives[unit];
        if (du == 0) return(ENXIO);
 
        du = wddrives[unit];
        if (du == 0) return(ENXIO);
@@ -1240,7 +1331,7 @@ wddump(dev_t dev)                 /* dump core after a system crash */
                if (inb(wdc+wd_status) & WDCS_DRQ) return(EIO) ;
 
                /* wait for completion */
                if (inb(wdc+wd_status) & WDCS_DRQ) return(EIO) ;
 
                /* wait for completion */
-               for ( i = 1000000 ; inb(wdc+wd_status) & WDCS_BUSY ; i--) {
+               for ( i = WDCTIMEOUT ; inb(wdc+wd_status) & WDCS_BUSY ; i--) {
                                if (i < 0) return (EIO) ;
                }
                /* error check the xfer */
                                if (i < 0) return (EIO) ;
                }
                /* error check the xfer */