fix to reeves fix of dgo handling (reset b_active to 1)
[unix-history] / usr / src / sys / vax / uba / up.c
index ac04685..44e1bdb 100644 (file)
-/*     %H%     3.16    %G%     */
+/*     up.c    4.53    82/05/27        */
 
 
+#include "up.h"
+#if NSC > 0
 /*
 /*
- * Emulex UNIBUS disk driver with overlapped seeks and ECC recovery.
+ * UNIBUS disk driver with overlapped seeks and ECC recovery.
  *
  *
- * NB: This driver works reliably only on an SC-11B controller with
- *     rev. level at least J (in particular rev. level H will not work well).
- *     If you have an older controller you may be able to get by if you
- *             #define OLDUCODE
- *     which implements larger delays for slow ucode.
- *
- * Controller switch settings:
- *     SW1-1   5/19 surfaces   (off, 19 surfaces on Ampex 9300)
- *     SW1-2   chksum enable   (off, checksum disabled)
- *     SW1-3   volume select   (off, 815 cylinders)
- *     SW1-4   sector select   (on, 32 sectors)
- *     SW1-5   unused          (off)
- *     SW1-6   port select     (on, single port)
- *     SW1-7   npr delay       (off, disable)
- *     SW1-8   ecc test mode   (off, disable)
- * and top mounted switches:
- *     SW2-1   extend opcodes  (off=open, disable)
- *     SW2-2   extend diag     (off=open, disable)
- *     SW2-3   4 wd dma burst  (on=closed, enable)
- *     SW2-4   unused          (off=open)
+ * TODO:
+ *     Add bad sector forwarding code
+ *     Check that offset recovery code works
  */
 
 #include "../h/param.h"
 #include "../h/systm.h"
  */
 
 #include "../h/param.h"
 #include "../h/systm.h"
+#include "../h/cpu.h"
+#include "../h/nexus.h"
 #include "../h/dk.h"
 #include "../h/buf.h"
 #include "../h/conf.h"
 #include "../h/dir.h"
 #include "../h/user.h"
 #include "../h/map.h"
 #include "../h/dk.h"
 #include "../h/buf.h"
 #include "../h/conf.h"
 #include "../h/dir.h"
 #include "../h/user.h"
 #include "../h/map.h"
-#include "../h/mba.h"
-#include "../h/mtpr.h"
 #include "../h/pte.h"
 #include "../h/pte.h"
-#include "../h/uba.h"
+#include "../h/mtpr.h"
 #include "../h/vm.h"
 #include "../h/vm.h"
+#include "../h/ubavar.h"
+#include "../h/ubareg.h"
+#include "../h/cmap.h"
 
 
-/*
- * Define number of drives, and range of sampling information to be used.
- *
- * Normally, DK_N .. DK_N+NUP-1 gather individual drive stats,
- * and DK_N+NUP gathers controller transferring stats.
- *
- * If DK_N+NUP > DK_NMAX, then transfer stats are divided per drive.
- * If DK_NMAX is yet smaller, some drives are not monitored.
- */
-#define        DK_N    2
-#define        DK_NMAX 3
-
-#define        ushort  unsigned short
-
-struct device
-{
-       ushort  upcs1;          /* control and status register 1 */
-       short   upwc;           /* word count register */
-       ushort  upba;           /* UNIBUS address register */
-       ushort  upda;           /* desired address register */
-       ushort  upcs2;          /* control and status register 2 */
-       ushort  upds;           /* drive Status */
-       ushort  uper1;          /* error register 1 */
-       ushort  upas;           /* attention summary */
-       ushort  upla;           /* look ahead */
-       ushort  updb;           /* data buffer */
-       ushort  upmr;           /* maintenance */ 
-       ushort  updt;           /* drive type */
-       ushort  upsn;           /* serial number */
-       ushort  upof;           /* offset register */
-       ushort  updc;           /* desired cylinder address register */
-       ushort  upcc;           /* current cylinder */
-       ushort  uper2;          /* error register 2 */
-       ushort  uper3;          /* error register 3 */
-       ushort  upec1;          /* burst error bit position */
-       ushort  upec2;          /* burst error bit pattern */
-};
-
-/*
- * Software extension to the upas register, so we can
- * postpone starting SEARCH commands until the controller
- * is not transferring.
- */
-int    upsoftas;
+#include "../h/upreg.h"
 
 
-/*
- * If upseek then we don't issue SEARCH commands but rather just
- * settle for a SEEK to the correct cylinder.
- */
-int    upseek;
-
-#define        UPADDR  ((struct device *)(UBA0_DEV + 0176700))
-
-#define        NUP     2               /* Number of drives this installation */
-
-#define        NSECT   32
-#define        NTRAC   19
-
-/*
- * Constants controlling on-cylinder SEARCH usage.
- *
- *     upSDIST/2 msec          time needed to start transfer
- *     upRDIST/2 msec          tolerable rotational latency when on-cylinder
- *
- * If we are no closer than upSDIST sectors and no further than upSDIST+upRDIST
- * and in the driver then we take it as it is.  Otherwise we do a SEARCH
- * requesting an interrupt upSDIST sectors in advance.
- */
-#define        _upSDIST        6               /* 3.0 msec */
-#define        _upRDIST        6               /* 3.0 msec */
-
-int    upSDIST = _upSDIST;
-int    upRDIST = _upRDIST;
+struct up_softc {
+       int     sc_softas;
+       int     sc_ndrive;
+       int     sc_wticks;
+       int     sc_recal;
+} up_softc[NSC];
 
 
-/*
- * To fill a 300M drive:
- *     A is designed to be used as a root.
- *     B is suitable for a swap area.
- *     H is the primary storage area.
- * On systems with RP06'es, we normally use only 291346 blocks of the H
- * area, and use DEF or G to cover the rest of the drive.  The C system
- * covers the whole drive and can be used for pack-pack copying.
- */
+/* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */
 struct size
 {
        daddr_t nblocks;
        int     cyloff;
 } up_sizes[8] = {
 struct size
 {
        daddr_t nblocks;
        int     cyloff;
 } up_sizes[8] = {
+#ifdef ERNIE
+       49324,  0,              /* A=cyl 0 thru 26 */
+#else
        15884,  0,              /* A=cyl 0 thru 26 */
        15884,  0,              /* A=cyl 0 thru 26 */
+#endif
        33440,  27,             /* B=cyl 27 thru 81 */
        495520, 0,              /* C=cyl 0 thru 814 */
        15884,  562,            /* D=cyl 562 thru 588 */
        55936,  589,            /* E=cyl 589 thru 680 */
        33440,  27,             /* B=cyl 27 thru 81 */
        495520, 0,              /* C=cyl 0 thru 814 */
        15884,  562,            /* D=cyl 562 thru 588 */
        55936,  589,            /* E=cyl 589 thru 680 */
-       81472,  681,            /* F=cyl 681 thru 814 */
-       153824, 562,            /* G=cyl 562 thru 814 */
-       445664, 82,             /* H=cyl 82 thru 814 */
-/* Later, and more safely for H area...
+#ifndef NOBADSECT
+       81376,  681,            /* F=cyl 681 thru 814 */
+       153728, 562,            /* G=cyl 562 thru 814 */
+#else
+       81472,  681,
+       153824, 562,
+#endif
        291346, 82,             /* H=cyl 82 thru 561 */
        291346, 82,             /* H=cyl 82 thru 561 */
+}, fj_sizes[8] = {
+       15884,  0,              /* A=cyl 0 thru 49 */
+       33440,  50,             /* B=cyl 50 thru 154 */
+       263360, 0,              /* C=cyl 0 thru 822 */
+       0,      0,
+       0,      0,
+       0,      0,
+       0,      0,
+#ifndef NOBADSECT
+       213664, 155,            /* H=cyl 155 thru 822 */
+#else
+       213760, 155,
+#endif
+}, upam_sizes[8] = {
+       15884,  0,              /* A=cyl 0 thru 31 */
+       33440,  32,             /* B=cyl 32 thru 97 */
+       524288, 0,              /* C=cyl 0 thru 1023 */
+       27786,  668,
+       27786,  723,
+       125440, 778,
+       181760, 668,            /* G=cyl 668 thru 1022 */
+       291346, 98,             /* H=cyl 98 thru 667 */
 };
 };
+/* END OF STUFF WHICH SHOULD BE READ IN PER DISK */
 
 /*
 
 /*
- * The following defines are used in offset positioning
- * when trying to recover disk errors, with the constants being
- * +/- microinches.  Note that header compare inhibit (HCI) is not
- * tried (this makes sense only during read, in any case.)
- *
- * NOT ALL OF THESE ARE IMPLEMENTED ON 9300!?!
+ * On a 780 upSDIST could be 2, but
+ * in the interest of 750's...
  */
  */
-#define        P400    020
-#define        M400    0220
-#define        P800    040
-#define        M800    0240
-#define        P1200   060
-#define        M1200   0260
-#define        HCI     020000
-
-int    up_offset[16] =
-{
-       P400, M400, P400, M400,
-       P800, M800, P800, M800,
-       P1200, M1200, P1200, M1200,
-       0, 0, 0, 0,
-};
+#define        _upSDIST        3               /* 1.5 msec */
+#define        _upRDIST        4               /* 2.0 msec */
 
 
-/*
- * Each drive has a table uputab[i].  On this table are sorted the
- * pending requests implementing an elevator algorithm (see dsort.c.)
- * In the upustart() routine, each drive is independently advanced
- * until it is on the desired cylinder for the next transfer and near
- * the desired sector.  The drive is then chained onto the uptab
- * table, and the transfer is initiated by the upstart() routine.
- * When the transfer is completed the driver reinvokes the upustart()
- * routine to set up the next transfer.
- */
-struct buf     uptab;
+int    upSDIST = _upSDIST;
+int    upRDIST = _upRDIST;
+
+int    upprobe(), upslave(), upattach(), updgo(), upintr();
+struct uba_ctlr *upminfo[NSC];
+struct uba_device *updinfo[NUP];
+#define        UPIPUNITS       8
+struct uba_device *upip[NSC][UPIPUNITS]; /* fuji w/fixed head gives n,n+4 */
+
+u_short        upstd[] = { 0776700, 0774400, 0776300, 0 };
+struct uba_driver scdriver =
+    { upprobe, upslave, upattach, updgo, upstd, "up", updinfo, "sc", upminfo };
 struct buf     uputab[NUP];
 
 struct buf     uputab[NUP];
 
-struct buf     rupbuf;                 /* Buffer for raw i/o */
-
-/* Drive commands, placed in upcs1 */
-#define        GO      01              /* Go bit, set in all commands */
-#define        PRESET  020             /* Preset drive at init or after errors */
-#define        OFFSET  014             /* Offset heads to try to recover error */
-#define        RTC     016             /* Return to center-line after OFFSET */
-#define        SEARCH  030             /* Search for cylinder+sector */
-#define        SEEK    04              /* Seek to cylinder */
-#define        RECAL   06              /* Recalibrate, needed after seek error */
-#define        DCLR    010             /* Drive clear, after error */
-#define        WCOM    060             /* Write */
-#define        RCOM    070             /* Read */
-
-/* Other bits of upcs1 */
-#define        IE      0100            /* Controller wide interrupt enable */
-#define        TRE     040000          /* Transfer error */
-#define        RDY     020             /* Transfer terminated */
-
-/* Drive status bits of upds */
-#define        PIP     020000          /* Positioning in progress */
-#define        ERR     040000          /* Error has occurred, DCLR necessary */
-#define        VV      0100            /* Volume is valid, set by PRESET */
-#define        DPR     0400            /* Drive has been preset */
-#define        MOL     010000          /* Drive is online, heads loaded, etc */
-#define        DRY     0200            /* Drive ready */
-
-/* Bits of upcs2 */
-#define        CLR     040             /* Controller clear */
-/* Bits of uper1 */
-#define        DCK     0100000         /* Ecc error occurred */
-#define        ECH     0100            /* Ecc error was unrecoverable */
-#define        WLE     04000           /* Attempt to write read-only drive */
-
-/* Bits of upof; the offset bits above are also in this register */
-#define        FMT22   010000          /* 16 bits/word, must be always set */
+struct upst {
+       short   nsect;
+       short   ntrak;
+       short   nspc;
+       short   ncyl;
+       struct  size *sizes;
+} upst[] = {
+       32,     19,     32*19,  823,    up_sizes,       /* 9300/cdc */
+/* 9300 actually has 815 cylinders... */
+       32,     10,     32*10,  823,    fj_sizes,       /* fujitsu 160m */
+       32,     16,     32*16,  1024,   upam_sizes,     /* ampex capricorn */
+};
+
+u_char up_offset[16] = {
+    UPOF_P400, UPOF_M400, UPOF_P400, UPOF_M400,
+    UPOF_P800, UPOF_M800, UPOF_P800, UPOF_M800, 
+    UPOF_P1200, UPOF_M1200, UPOF_P1200, UPOF_M1200,
+    0, 0, 0, 0
+};
+
+struct buf     rupbuf[NUP];
 
 #define        b_cylin b_resid
 
 
 #define        b_cylin b_resid
 
-int    up_ubinfo;              /* Information about UBA usage saved here */
-/*
- * The EMULEX controller balks if accessed quickly after
- * certain operations.  With rev J delays seem to be needed only
- * when selecting a new unit, and in drive initialization type
- * like PRESET and DCLR.  The following variables control the delay
- * DELAY(n) is approximately n usec.
- */
-int    idelay = 500;           /* Delay after PRESET or DCLR */
-#ifdef OLDUCODE
-int    sdelay = 150;           /* Delay after selecting drive in upcs2 */
-int    rdelay = 100;           /* Delay after SEARCH */
-int    asdel = 100;            /* Delay after clearing bit in upas */
-#else
-int    sdelay = 25;
+#ifdef INTRLVE
+daddr_t dkblock();
 #endif
 
 #endif
 
-#define        DELAY(N)                { register int d; d = N; while (--d > 0); }
-int    nwaitcs2;               /* How many sdelay loops ? */
-int    neasycs2;               /* How many sdelay loops not needed ? */
+int    upwstart, upwatch();            /* Have started guardian */
+int    upseek;
+int    upwaitdry;
 
 
-int    up_wticks;              /* Ticks waiting for interrupt */
-int    upwstart;               /* Have started guardian */
-int    upwatch();
+/*ARGSUSED*/
+upprobe(reg)
+       caddr_t reg;
+{
+       register int br, cvec;
 
 
-#ifdef INTRLVE
-daddr_t dkblock();
+#ifdef lint    
+       br = 0; cvec = br; br = cvec;
 #endif
 #endif
-/*
- * Queue an i/o request for a drive, checking first that it is in range.
- *
- * A unit start is issued if the drive is inactive, causing
- * a SEARCH for the correct cylinder/sector.  If the drive is
- * already nearly on the money and the controller is not transferring
- * we kick it to start the transfer.
- */
-upstrategy(bp)
-register struct buf *bp;
+       ((struct updevice *)reg)->upcs1 = UP_IE|UP_RDY;
+       DELAY(10);
+       ((struct updevice *)reg)->upcs1 = 0;
+       return (1);
+}
+
+upslave(ui, reg)
+       struct uba_device *ui;
+       caddr_t reg;
 {
 {
-       register struct buf *dp;
-       register unit, xunit;
-       long sz, bn;
+       register struct updevice *upaddr = (struct updevice *)reg;
+
+       upaddr->upcs1 = 0;              /* conservative */
+       upaddr->upcs2 = ui->ui_slave;
+       upaddr->upcs1 = UP_NOP|UP_GO;
+       if (upaddr->upcs2&UPCS2_NED) {
+               upaddr->upcs1 = UP_DCLR|UP_GO;
+               return (0);
+       }
+       return (1);
+}
+
+upattach(ui)
+       register struct uba_device *ui;
+{
+       register struct updevice *upaddr;
 
        if (upwstart == 0) {
 
        if (upwstart == 0) {
-               timeout((caddr_t)upwatch, 0, HZ);
+               timeout(upwatch, (caddr_t)0, hz);
                upwstart++;
        }
                upwstart++;
        }
-       xunit = minor(bp->b_dev) & 077;
-       sz = bp->b_bcount;
-       sz = (sz+511) >> 9;             /* transfer size in 512 byte sectors */
+       if (ui->ui_dk >= 0)
+               dk_mspw[ui->ui_dk] = .0000020345;
+       upip[ui->ui_ctlr][ui->ui_slave] = ui;
+       up_softc[ui->ui_ctlr].sc_ndrive++;
+       upaddr = (struct updevice *)ui->ui_addr;
+       upaddr->upcs1 = 0;
+       upaddr->upcs2 = ui->ui_slave;
+       upaddr->uphr = UPHR_MAXTRAK;
+       if (upaddr->uphr == 9)
+               ui->ui_type = 1;                /* fujitsu hack */
+       else if (upaddr->uphr == 15)
+               ui->ui_type = 2;                /* ampex hack */
+       upaddr->upcs2 = UPCS2_CLR;
+/*
+       upaddr->uphr = UPHR_MAXCYL;
+       printf("maxcyl %d\n", upaddr->uphr);
+       upaddr->uphr = UPHR_MAXTRAK;
+       printf("maxtrak %d\n", upaddr->uphr);
+       upaddr->uphr = UPHR_MAXSECT;
+       printf("maxsect %d\n", upaddr->uphr);
+*/
+}
+upstrategy(bp)
+       register struct buf *bp;
+{
+       register struct uba_device *ui;
+       register struct upst *st;
+       register int unit;
+       register struct buf *dp;
+       int xunit = minor(bp->b_dev) & 07;
+       long bn, sz;
+
+       sz = (bp->b_bcount+511) >> 9;
        unit = dkunit(bp);
        unit = dkunit(bp);
-       if (unit >= NUP ||
-           bp->b_blkno < 0 ||
-           (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) {
-               bp->b_flags |= B_ERROR;
-               iodone(bp);
-               return;
-       }
-       bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff;
-       dp = &uputab[unit];
+       if (unit >= NUP)
+               goto bad;
+       ui = updinfo[unit];
+       if (ui == 0 || ui->ui_alive == 0)
+               goto bad;
+       st = &upst[ui->ui_type];
+       if (bp->b_blkno < 0 ||
+           (bn = dkblock(bp))+sz > st->sizes[xunit].nblocks)
+               goto bad;
+       bp->b_cylin = bn/st->nspc + st->sizes[xunit].cyloff;
        (void) spl5();
        (void) spl5();
+       dp = &uputab[ui->ui_unit];
        disksort(dp, bp);
        if (dp->b_active == 0) {
        disksort(dp, bp);
        if (dp->b_active == 0) {
-               (void) upustart(unit);
-               if (uptab.b_actf && uptab.b_active == 0)
-                       (void) upstart();
+               (void) upustart(ui);
+               bp = &ui->ui_mi->um_tab;
+               if (bp->b_actf && bp->b_active == 0)
+                       (void) upstart(ui->ui_mi);
        }
        (void) spl0();
        }
        (void) spl0();
+       return;
+
+bad:
+       bp->b_flags |= B_ERROR;
+       iodone(bp);
+       return;
 }
 
 /*
 }
 
 /*
- * Start activity on specified drive; called when drive is inactive
- * and new transfer request arrives and also when upas indicates that
- * a SEARCH command is complete.
+ * Unit start routine.
+ * Seek the drive to be where the data is
+ * and then generate another interrupt
+ * to actually start the transfer.
+ * If there is only one drive on the controller,
+ * or we are very close to the data, don't
+ * bother with the search.  If called after
+ * searching once, don't bother to look where
+ * we are, just queue for transfer (to avoid
+ * positioning forever without transferrring.)
  */
  */
-upustart(unit)
-register unit;
+upustart(ui)
+       register struct uba_device *ui;
 {
        register struct buf *bp, *dp;
 {
        register struct buf *bp, *dp;
-       register struct device *upaddr = UPADDR;
+       register struct uba_ctlr *um;
+       register struct updevice *upaddr;
+       register struct upst *st;
        daddr_t bn;
        daddr_t bn;
-       int sn, cn, csn;
-       int didie = 0;
-
+       int sn, csn;
        /*
        /*
-        * Other drivers tend to say something like
-        *      upaddr->upcs1 = IE;
-        *      upaddr->upas = 1<<unit;
-        * here, but the SC-11B will cancel a command which
-        * happens to be sitting in the cs1 if you clear the go
-        * bit by storing there (so the first is not safe),
-        * and it also does not like being bothered with operations
-        * such as clearing upas when a transfer is active (as
-        * it may well be.)
-        *
-        * Thus we keep careful track of when we re-enable IE
-        * after an interrupt and do it only if we didn't issue
-        * a command which re-enabled it as a matter of course.
-        * We clear bits in upas in the interrupt routine, when
-        * no transfers are active.
+        * The SC21 cancels commands if you just say
+        *      cs1 = UP_IE
+        * so we are cautious about handling of cs1.
+        * Also don't bother to clear as bits other than in upintr().
         */
         */
-       if (unit >= NUP)
-               goto out;
-       if (unit+DK_N <= DK_NMAX)
-               dk_busy &= ~(1<<(unit+DK_N));
-       dp = &uputab[unit];
+       int didie = 0;
+
+       if (ui == 0)
+               return (0);
+       um = ui->ui_mi;
+       dk_busy &= ~(1<<ui->ui_dk);
+       dp = &uputab[ui->ui_unit];
        if ((bp = dp->b_actf) == NULL)
                goto out;
        /*
        if ((bp = dp->b_actf) == NULL)
                goto out;
        /*
-        * The SC-11B doesn't start SEARCH commands when transfers are
-        * in progress.  In fact, it tends to get confused when given
-        * SEARCH'es during transfers, generating interrupts with neither
-        * RDY nor a bit in the upas register.  Thus we defer
-        * until an interrupt when a transfer is pending.
+        * If the controller is active, just remember
+        * that this device would like to be positioned...
+        * if we tried to position now we would confuse the SC21.
         */
         */
-       if (uptab.b_active) {
-               upsoftas |= 1<<unit;
+       if (um->um_tab.b_active) {
+               up_softc[um->um_ctlr].sc_softas |= 1<<ui->ui_slave;
                return (0);
        }
                return (0);
        }
+       /*
+        * If we have already positioned this drive,
+        * then just put it on the ready queue.
+        */
        if (dp->b_active)
                goto done;
        dp->b_active = 1;
        if (dp->b_active)
                goto done;
        dp->b_active = 1;
-       if ((upaddr->upcs2 & 07) != unit) {
-               upaddr->upcs2 = unit;
-               DELAY(sdelay);
-               nwaitcs2++;
-       } else
-               neasycs2++;
+       upaddr = (struct updevice *)um->um_addr;
+       upaddr->upcs2 = ui->ui_slave;
        /*
        /*
-        * If we have changed packs or just initialized,
-        * then the volume will not be valid; if so, clear
-        * the drive, preset it and put in 16bit/word mode.
+        * If drive has just come up,
+        * setup the pack.
         */
         */
-       if ((upaddr->upds & VV) == 0) {
-               upaddr->upcs1 = IE|DCLR|GO;
-               DELAY(idelay);
-               upaddr->upcs1 = IE|PRESET|GO;
-               DELAY(idelay);
-               upaddr->upof = FMT22;
+       if ((upaddr->upds & UPDS_VV) == 0) {
+               /* SHOULD WARN SYSTEM THAT THIS HAPPENED */
+               upaddr->upcs1 = UP_IE|UP_DCLR|UP_GO;
+               upaddr->upcs1 = UP_IE|UP_PRESET|UP_GO;
+               upaddr->upof = UPOF_FMT22;
                didie = 1;
        }
                didie = 1;
        }
-       if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL))
+       /*
+        * If drive is offline, forget about positioning.
+        */
+       if ((upaddr->upds & (UPDS_DPR|UPDS_MOL)) != (UPDS_DPR|UPDS_MOL))
                goto done;
        /*
                goto done;
        /*
-        * Do enough of the disk address decoding to determine
-        * which cylinder and sector the request is on.
-        * If we are on the correct cylinder and the desired sector
-        * lies between upSDIST and upSDIST+upRDIST sectors ahead of us, then
-        * we don't bother to SEARCH but just begin the transfer asap.
-        * Otherwise ask for a interrupt upSDIST sectors ahead.
+        * If there is only one drive,
+        * dont bother searching.
         */
         */
+       if (up_softc[um->um_ctlr].sc_ndrive == 1)
+               goto done;
+       /*
+        * Figure out where this transfer is going to
+        * and see if we are close enough to justify not searching.
+        */
+       st = &upst[ui->ui_type];
        bn = dkblock(bp);
        bn = dkblock(bp);
-       cn = bp->b_cylin;
-       sn = bn%(NSECT*NTRAC);
-       sn = (sn+NSECT-upSDIST)%NSECT;
-
-       if (cn - upaddr->updc)
+       sn = bn%st->nspc;
+       sn = (sn + st->nsect - upSDIST) % st->nsect;
+       if (bp->b_cylin - upaddr->updc)
                goto search;            /* Not on-cylinder */
        else if (upseek)
                goto done;              /* Ok just to be on-cylinder */
        csn = (upaddr->upla>>6) - sn - 1;
        if (csn < 0)
                goto search;            /* Not on-cylinder */
        else if (upseek)
                goto done;              /* Ok just to be on-cylinder */
        csn = (upaddr->upla>>6) - sn - 1;
        if (csn < 0)
-               csn += NSECT;
-       if (csn > NSECT-upRDIST)
+               csn += st->nsect;
+       if (csn > st->nsect - upRDIST)
                goto done;
                goto done;
-
 search:
 search:
-       upaddr->updc = cn;
+       upaddr->updc = bp->b_cylin;
+       /*
+        * Not on cylinder at correct position,
+        * seek/search.
+        */
        if (upseek)
        if (upseek)
-               upaddr->upcs1 = IE|SEEK|GO;
+               upaddr->upcs1 = UP_IE|UP_SEEK|UP_GO;
        else {
                upaddr->upda = sn;
        else {
                upaddr->upda = sn;
-               upaddr->upcs1 = IE|SEARCH|GO;
+               upaddr->upcs1 = UP_IE|UP_SEARCH|UP_GO;
        }
        didie = 1;
        /*
        }
        didie = 1;
        /*
-        * Mark this unit busy.
+        * Mark unit busy for iostat.
         */
         */
-       unit += DK_N;
-       if (unit <= DK_NMAX) {
-               dk_busy |= 1<<unit;
-               dk_numb[unit]++;
+       if (ui->ui_dk >= 0) {
+               dk_busy |= 1<<ui->ui_dk;
+               dk_seek[ui->ui_dk]++;
        }
        }
-#ifdef OLDUCODE
-       DELAY(rdelay);
-#endif
        goto out;
        goto out;
-
 done:
        /*
 done:
        /*
-        * This unit is ready to go so
-        * link it onto the chain of ready disks.
+        * Device is ready to go.
+        * Put it on the ready queue for the controller
+        * (unless its already there.)
         */
         */
-       dp->b_forw = NULL;
-       if (uptab.b_actf == NULL)
-               uptab.b_actf = dp;
-       else
-               uptab.b_actl->b_forw = dp;
-       uptab.b_actl = dp;
-
+       if (dp->b_active != 2) {
+               dp->b_forw = NULL;
+               if (um->um_tab.b_actf == NULL)
+                       um->um_tab.b_actf = dp;
+               else
+                       um->um_tab.b_actl->b_forw = dp;
+               um->um_tab.b_actl = dp;
+               dp->b_active = 2;
+       }
 out:
        return (didie);
 }
 
 /*
 out:
        return (didie);
 }
 
 /*
- * Start a transfer; call from top level at spl5() or on interrupt.
+ * Start up a transfer on a drive.
  */
  */
-upstart()
+upstart(um)
+       register struct uba_ctlr *um;
 {
        register struct buf *bp, *dp;
 {
        register struct buf *bp, *dp;
-       register unit;
-       register struct device *upaddr;
+       register struct uba_device *ui;
+       register struct updevice *upaddr;
+       struct upst *st;
        daddr_t bn;
        daddr_t bn;
-       int dn, sn, tn, cn, cmd;
+       int dn, sn, tn, cmd, waitdry;
 
 loop:
        /*
 
 loop:
        /*
-        * Pick a drive off the queue of ready drives, and
-        * perform the first transfer on its queue.
-        *
-        * Looping here is completely for the sake of drives which
-        * are not present and on-line, for which we completely clear the
-        * request queue.
+        * Pull a request off the controller queue
         */
         */
-       if ((dp = uptab.b_actf) == NULL)
+       if ((dp = um->um_tab.b_actf) == NULL)
                return (0);
        if ((bp = dp->b_actf) == NULL) {
                return (0);
        if ((bp = dp->b_actf) == NULL) {
-               uptab.b_actf = dp->b_forw;
+               um->um_tab.b_actf = dp->b_forw;
                goto loop;
        }
        /*
                goto loop;
        }
        /*
-        * Mark the controller busy, and multi-part disk address.
-        * Select the unit on which the i/o is to take place.
+        * Mark controller busy, and
+        * determine destination of this request.
         */
         */
-       uptab.b_active++;
-       unit = minor(bp->b_dev) & 077;
-       dn = dkunit(bp);
+       um->um_tab.b_active++;
+       ui = updinfo[dkunit(bp)];
        bn = dkblock(bp);
        bn = dkblock(bp);
-       cn = up_sizes[unit&07].cyloff;
-       cn += bn/(NSECT*NTRAC);
-       sn = bn%(NSECT*NTRAC);
-       tn = sn/NSECT;
-       sn %= NSECT;
-       upaddr = UPADDR;
-       if ((upaddr->upcs2 & 07) != dn) {
-               upaddr->upcs2 = dn;
-               /* DELAY(sdelay);               Provided by ubasetup() */
-               nwaitcs2++;
-       } else
-               neasycs2++;
-       up_ubinfo = ubasetup(bp, 1);    /* Providing delay */
+       dn = ui->ui_slave;
+       st = &upst[ui->ui_type];
+       sn = bn%st->nspc;
+       tn = sn/st->nsect;
+       sn %= st->nsect;
+       upaddr = (struct updevice *)ui->ui_addr;
        /*
        /*
-        * If drive is not present and on-line, then
-        * get rid of this with an error and loop to get
-        * rid of the rest of its queued requests.
-        * (Then on to any other ready drives.)
+        * Select drive if not selected already.
         */
         */
-       if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) {
-               printf("!DPR || !MOL, unit %d, ds %o\n", dn, upaddr->upds);
-               uptab.b_active = 0;
-               uptab.b_errcnt = 0;
-               dp->b_actf = bp->av_forw;
-               dp->b_active = 0;
-               bp->b_flags |= B_ERROR;
-               iodone(bp);
-               ubafree(up_ubinfo), up_ubinfo = 0;      /* A funny place ... */
-               goto loop;
-       }
+       if ((upaddr->upcs2&07) != dn)
+               upaddr->upcs2 = dn;
        /*
        /*
-        * If this is a retry, then with the 16'th retry we
-        * begin to try offsetting the heads to recover the data.
+        * Check that it is ready and online
         */
         */
-       if (uptab.b_errcnt >= 16) {
-               upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22;
-               upaddr->upcs1 = IE|OFFSET|GO;
-               DELAY(idelay);
-               while (upaddr->upds & PIP)
-                       DELAY(25);
+       waitdry = 0;
+       while ((upaddr->upds&UPDS_DRY) == 0) {
+               if (++waitdry > 512)
+                       break;
+               upwaitdry++;
+       }
+       if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) {
+               printf("up%d: not ready", dkunit(bp));
+               if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) {
+                       printf("\n");
+                       um->um_tab.b_active = 0;
+                       um->um_tab.b_errcnt = 0;
+                       dp->b_actf = bp->av_forw;
+                       dp->b_active = 0;
+                       bp->b_flags |= B_ERROR;
+                       iodone(bp);
+                       goto loop;
+               }
+               /*
+                * Oh, well, sometimes this
+                * happens, for reasons unknown.
+                */
+               printf(" (flakey)\n");
        }
        /*
        }
        /*
-        * Now set up the transfer, retrieving the high
-        * 2 bits of the UNIBUS address from the information
-        * returned by ubasetup() for the cs1 register bits 8 and 9.
+        * Setup for the transfer, and get in the
+        * UNIBUS adaptor queue.
         */
         */
-       upaddr->updc = cn;
+       upaddr->updc = bp->b_cylin;
        upaddr->upda = (tn << 8) + sn;
        upaddr->upda = (tn << 8) + sn;
-       upaddr->upba = up_ubinfo;
        upaddr->upwc = -bp->b_bcount / sizeof (short);
        upaddr->upwc = -bp->b_bcount / sizeof (short);
-       cmd = (up_ubinfo >> 8) & 0x300;
        if (bp->b_flags & B_READ)
        if (bp->b_flags & B_READ)
-               cmd |= IE|RCOM|GO;
+               cmd = UP_IE|UP_RCOM|UP_GO;
        else
        else
-               cmd |= IE|WCOM|GO;
-       upaddr->upcs1 = cmd;
-       /*
-        * This is a controller busy situation.
-        * Record in dk slot NUP+DK_N (after last drive)
-        * unless there aren't that many slots reserved for
-        * us in which case we record this as a drive busy
-        * (if there is room for that).
-        */
-       unit = dn+DK_N;
-       if (NUP+DK_N == DK_NMAX)
-               unit = NUP+DK_N;
-       if (unit <= DK_NMAX) {
-               dk_busy |= 1<<unit;
-               dk_numb[unit]++;
-               dk_wds[unit] += bp->b_bcount>>6;
-       }
+               cmd = UP_IE|UP_WCOM|UP_GO;
+       um->um_cmd = cmd;
+       (void) ubago(ui);
        return (1);
 }
 
 /*
        return (1);
 }
 
 /*
- * Handle a device interrupt.
- *
- * If the transferring drive needs attention, service it
- * retrying on error or beginning next transfer.
- * Service all other ready drives, calling ustart to transfer
- * their blocks to the ready queue in uptab, and then restart
- * the controller if there is anything to do.
+ * Now all ready to go, stuff the registers.
+ */
+updgo(um)
+       struct uba_ctlr *um;
+{
+       register struct updevice *upaddr = (struct updevice *)um->um_addr;
+
+       um->um_tab.b_active = 2;        /* should now be 2 */
+       upaddr->upba = um->um_ubinfo;
+       upaddr->upcs1 = um->um_cmd|((um->um_ubinfo>>8)&0x300);
+}
+
+/*
+ * Handle a disk interrupt.
  */
  */
-upintr()
+upintr(sc21)
+       register sc21;
 {
        register struct buf *bp, *dp;
 {
        register struct buf *bp, *dp;
+       register struct uba_ctlr *um = upminfo[sc21];
+       register struct uba_device *ui;
+       register struct updevice *upaddr = (struct updevice *)um->um_addr;
        register unit;
        register unit;
-       register struct device *upaddr = UPADDR;
-       int as = upaddr->upas & 0377;
-       int oupsoftas;
-       int needie = 1;
+       struct up_softc *sc = &up_softc[um->um_ctlr];
+       int as = (upaddr->upas & 0377) | sc->sc_softas;
+       int needie = 1, waitdry;
 
 
-#ifdef OLDUCODE
-       (void) spl6();
-#endif
-       up_wticks = 0;
-       if (uptab.b_active) {
-               /*
-                * The drive is transferring, thus the hardware
-                * (say the designers) will only interrupt when the transfer
-                * completes; check for it anyways.
-                */
-               if ((upaddr->upcs1 & RDY) == 0) {
-                       printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1,
-                           upaddr->upds, upaddr->upwc);
-                       printf("as=%d act %d %d %d\n", as, uptab.b_active,
-                           uputab[0].b_active, uputab[1].b_active);
+       sc->sc_wticks = 0;
+       sc->sc_softas = 0;
+       /*
+        * If controller wasn't transferring, then this is an
+        * interrupt for attention status on seeking drives.
+        * Just service them.
+        */
+       if (um->um_tab.b_active != 2 && !sc->sc_recal) {
+               if (upaddr->upcs1 & UP_TRE)
+                       upaddr->upcs1 = UP_TRE;
+               goto doattn;
+       }
+       um->um_tab.b_active = 1;
+       /*
+        * Get device and block structures, and a pointer
+        * to the uba_device for the drive.  Select the drive.
+        */
+       dp = um->um_tab.b_actf;
+       bp = dp->b_actf;
+       ui = updinfo[dkunit(bp)];
+       dk_busy &= ~(1 << ui->ui_dk);
+       if ((upaddr->upcs2&07) != ui->ui_slave)
+               upaddr->upcs2 = ui->ui_slave;
+       /*
+        * Check for and process errors on
+        * either the drive or the controller.
+        */
+       if ((upaddr->upds&UPDS_ERR) || (upaddr->upcs1&UP_TRE)) {
+               waitdry = 0;
+               while ((upaddr->upds & UPDS_DRY) == 0) {
+                       if (++waitdry > 512)
+                               break;
+                       upwaitdry++;
                }
                }
-               /*
-                * Mark controller or drive not busy, and check for an
-                * error condition which may have resulted from the transfer.
-                */
-               dp = uptab.b_actf;
-               bp = dp->b_actf;
-               unit = dkunit(bp);
-               if (DK_N+NUP == DK_NMAX)
-                       dk_busy &= ~(1<<(DK_N+NUP));
-               else if (DK_N+unit <= DK_NMAX)
-                       dk_busy &= ~(1<<(DK_N+unit));
-               if ((upaddr->upcs2 & 07) != unit) {
-                       upaddr->upcs2 = unit;
-                       DELAY(sdelay);
-                       nwaitcs2++;
-               } else
-                       neasycs2++;
-               if (upaddr->upds & ERR) {
+               if (upaddr->uper1&UPER1_WLE) {
                        /*
                        /*
-                        * An error occurred, indeed.  Select this unit
-                        * to get at the drive status (a SEARCH may have
-                        * intervened to change the selected unit), and
-                        * wait for the command which caused the interrupt
-                        * to complete (DRY).
+                        * Give up on write locked devices
+                        * immediately.
                         */
                         */
-                       while ((upaddr->upds & DRY) == 0)
-                               DELAY(25);
-                       /*
-                        * After 28 retries (16 w/o servo offsets, and then
-                        * 12 with servo offsets), or if we encountered
-                        * an error because the drive is write-protected,
-                        * give up.  Print an error message on the last 2
-                        * retries before a hard failure.
-                        */
-                       if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE)
-                               bp->b_flags |= B_ERROR;
-                       else
-                               uptab.b_active = 0;     /* To force retry */
-                       if (uptab.b_errcnt > 27)
-                               deverror(bp, upaddr->upcs2, upaddr->uper1);
+                       printf("up%d: write locked\n", dkunit(bp));
+                       bp->b_flags |= B_ERROR;
+               } else if (++um->um_tab.b_errcnt > 27) {
                        /*
                        /*
-                        * If this was a correctible ECC error, let upecc
-                        * do the dirty work to correct it.  If upecc
-                        * starts another READ for the rest of the data
-                        * then it returns 1 (having set uptab.b_active).
-                        * Otherwise we are done and fall through to
-                        * finish up.
+                        * After 28 retries (16 without offset, and
+                        * 12 with offset positioning) give up.
                         */
                         */
-                       if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp))
-                               return;
+                       harderr(bp, "up");
+                       printf("cs2=%b er1=%b er2=%b\n",
+                           upaddr->upcs2, UPCS2_BITS,
+                           upaddr->uper1, UPER1_BITS,
+                           upaddr->uper2, UPER2_BITS);
+                       bp->b_flags |= B_ERROR;
+               } else {
                        /*
                        /*
-                        * Clear the drive and, every 4 retries, recalibrate
-                        * to hopefully help clear up seek positioning problems.
+                        * Retriable error.
+                        * If a soft ecc, correct it (continuing
+                        * by returning if necessary.
+                        * Otherwise fall through and retry the transfer
                         */
                         */
-                       upaddr->upcs1 = TRE|IE|DCLR|GO;
-                       DELAY(idelay);
+                       um->um_tab.b_active = 0;         /* force retry */
+                       if ((upaddr->uper1&(UPER1_DCK|UPER1_ECH))==UPER1_DCK)
+                               if (upecc(ui))
+                                       return;
+               }
+               /*
+                * Clear drive error and, every eight attempts,
+                * (starting with the fourth)
+                * recalibrate to clear the slate.
+                */
+               upaddr->upcs1 = UP_TRE|UP_IE|UP_DCLR|UP_GO;
+               needie = 0;
+               if ((um->um_tab.b_errcnt&07) == 4 && um->um_tab.b_active == 0) {
+                       upaddr->upcs1 = UP_RECAL|UP_IE|UP_GO;
+                       sc->sc_recal = 0;
+                       goto nextrecal;
+               }
+       }
+       /*
+        * Advance recalibration finite state machine
+        * if recalibrate in progress, through
+        *      RECAL
+        *      SEEK
+        *      OFFSET (optional)
+        *      RETRY
+        */
+       switch (sc->sc_recal) {
+
+       case 1:
+               upaddr->updc = bp->b_cylin;
+               upaddr->upcs1 = UP_SEEK|UP_IE|UP_GO;
+               goto nextrecal;
+       case 2:
+               if (um->um_tab.b_errcnt < 16 || (bp->b_flags&B_READ) == 0)
+                       goto donerecal;
+               upaddr->upof = up_offset[um->um_tab.b_errcnt & 017] | UPOF_FMT22;
+               upaddr->upcs1 = UP_IE|UP_OFFSET|UP_GO;
+               goto nextrecal;
+       nextrecal:
+               sc->sc_recal++;
+               um->um_tab.b_active = 1;
+               return;
+       donerecal:
+       case 3:
+               sc->sc_recal = 0;
+               um->um_tab.b_active = 0;
+               break;
+       }
+       /*
+        * If still ``active'', then don't need any more retries.
+        */
+       if (um->um_tab.b_active) {
+               /*
+                * If we were offset positioning,
+                * return to centerline.
+                */
+               if (um->um_tab.b_errcnt >= 16) {
+                       upaddr->upof = UPOF_FMT22;
+                       upaddr->upcs1 = UP_RTC|UP_GO|UP_IE;
+                       while (upaddr->upds & UPDS_PIP)
+                               DELAY(25);
                        needie = 0;
                        needie = 0;
-                       if ((uptab.b_errcnt&07) == 4) {
-                               upaddr->upcs1 = RECAL|GO|IE;
-                               DELAY(idelay);
-                               while(upaddr->upds & PIP)
-                                       DELAY(25);
-                       }
                }
                }
+               um->um_tab.b_active = 0;
+               um->um_tab.b_errcnt = 0;
+               um->um_tab.b_actf = dp->b_forw;
+               dp->b_active = 0;
+               dp->b_errcnt = 0;
+               dp->b_actf = bp->av_forw;
+               bp->b_resid = (-upaddr->upwc * sizeof(short));
+               iodone(bp);
                /*
                /*
-                * If we are still noted as active, then no
-                * (further) retries are necessary.  
-                *
-                * Make sure the correct unit is selected,
-                * return it to centerline if necessary, and mark
-                * this i/o complete, starting the next transfer
-                * on this drive with the upustart routine (if any).
+                * If this unit has more work to do,
+                * then start it up right away.
                 */
                 */
-               if (uptab.b_active) {
-                       if (uptab.b_errcnt >= 16) {
-                               upaddr->upcs1 = RTC|GO|IE;
-                               DELAY(idelay);
-                               while (upaddr->upds & PIP)
-                                       DELAY(25);
+               if (dp->b_actf)
+                       if (upustart(ui))
                                needie = 0;
                                needie = 0;
-                       }
-                       uptab.b_active = 0;
-                       uptab.b_errcnt = 0;
-                       uptab.b_actf = dp->b_forw;
-                       dp->b_active = 0;
-                       dp->b_errcnt = 0;
-                       dp->b_actf = bp->av_forw;
-                       bp->b_resid = (-upaddr->upwc * sizeof(short));
-                       if (bp->b_resid)
-                               printf("resid %d ds %o er? %o %o %o\n",
-                                   bp->b_resid, upaddr->upds,
-                                   upaddr->uper1, upaddr->uper2, upaddr->uper3);
-                       iodone(bp);
-                       if(dp->b_actf)
-                               if (upustart(unit))
-                                       needie = 0;
-               }
+       }
+       as &= ~(1<<ui->ui_slave);
+       /*
+        * Release unibus resources and flush data paths.
+        */
+       ubadone(um);
+doattn:
+       /*
+        * Process other units which need attention.
+        * For each unit which needs attention, call
+        * the unit start routine to place the slave
+        * on the controller device queue.
+        */
+       while (unit = ffs(as)) {
+               unit--;         /* was 1 origin */
                as &= ~(1<<unit);
                as &= ~(1<<unit);
-               upsoftas &= ~(1<<unit);
-               ubafree(up_ubinfo), up_ubinfo = 0;
-       } else {
-               if (upaddr->upcs1 & TRE) {
-                       upaddr->upcs1 = TRE;
-                       DELAY(idelay);
-               }
+               upaddr->upas = 1<<unit;
+               if (unit < UPIPUNITS && upustart(upip[sc21][unit]))
+                       needie = 0;
        }
        /*
        }
        /*
-        * If we have a unit with an outstanding SEARCH,
-        * and the hardware indicates the unit requires attention,
-        * the bring the drive to the ready queue.
-        * Finally, if the controller is not transferring
-        * start it if any drives are now ready to transfer.
+        * If the controller is not transferring, but
+        * there are devices ready to transfer, start
+        * the controller.
         */
         */
-       as |= upsoftas;
-       oupsoftas = upsoftas;
-       upsoftas = 0;
-       for (unit = 0; unit < NUP; unit++)
-               if ((as|oupsoftas) & (1<<unit)) {
-                       if (as & (1<<unit)) {
-                               upaddr->upas = 1<<unit;
-#ifdef OLDUCODE
-                               DELAY(asdel);
-#endif
-                       }
-                       if (upustart(unit))
-                               needie = 0;
-               }
-       if (uptab.b_actf && uptab.b_active == 0)
-               if (upstart())
+       if (um->um_tab.b_actf && um->um_tab.b_active == 0)
+               if (upstart(um))
                        needie = 0;
                        needie = 0;
-out:
        if (needie)
        if (needie)
-               upaddr->upcs1 = IE;
+               upaddr->upcs1 = UP_IE;
 }
 
 upread(dev)
 }
 
 upread(dev)
+       dev_t dev;
 {
 {
+       register int unit = minor(dev) >> 3;
 
 
-       physio(upstrategy, &rupbuf, dev, B_READ, minphys);
+       if (unit >= NUP)
+               u.u_error = ENXIO;
+       else
+               physio(upstrategy, &rupbuf[unit], dev, B_READ, minphys);
 }
 
 upwrite(dev)
 }
 
 upwrite(dev)
+       dev_t dev;
 {
 {
+       register int unit = minor(dev) >> 3;
 
 
-       physio(upstrategy, &rupbuf, dev, B_WRITE, minphys);
+       if (unit >= NUP)
+               u.u_error = ENXIO;
+       else
+               physio(upstrategy, &rupbuf[unit], dev, B_WRITE, minphys);
 }
 
 /*
 }
 
 /*
@@ -716,11 +682,14 @@ upwrite(dev)
  * the transfer may be going to an odd memory address base and/or
  * across a page boundary.
  */
  * the transfer may be going to an odd memory address base and/or
  * across a page boundary.
  */
-upecc(up, bp)
-register struct device *up;
-register struct buf *bp;
+upecc(ui)
+       register struct uba_device *ui;
 {
 {
-       struct uba_regs *ubp = (struct uba_regs *)UBA0;
+       register struct updevice *up = (struct updevice *)ui->ui_addr;
+       register struct buf *bp = uputab[ui->ui_unit].b_actf;
+       register struct uba_ctlr *um = ui->ui_mi;
+       register struct upst *st;
+       struct uba_regs *ubp = ui->ui_hd->uh_uba;
        register int i;
        caddr_t addr;
        int reg, bit, byte, npf, mask, o, cmd, ubaddr;
        register int i;
        caddr_t addr;
        int reg, bit, byte, npf, mask, o, cmd, ubaddr;
@@ -733,23 +702,22 @@ register struct buf *bp;
         * O is offset within a memory page of the first byte transferred.
         */
        npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1;
         * O is offset within a memory page of the first byte transferred.
         */
        npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1;
-       reg = btop(up_ubinfo&0x3ffff) + npf;
+       reg = btop(um->um_ubinfo&0x3ffff) + npf;
        o = (int)bp->b_un.b_addr & PGOFSET;
        o = (int)bp->b_un.b_addr & PGOFSET;
-       printf("%D ", bp->b_blkno+npf);
-       prdev("ECC", bp->b_dev);
+       printf("up%d%c: soft ecc sn%d\n", dkunit(bp),
+           'a'+(minor(bp->b_dev)&07), bp->b_blkno + npf);
        mask = up->upec2;
        mask = up->upec2;
-       if (mask == 0) {
-               up->upof = FMT22;               /* == RTC ???? */
-               DELAY(idelay);
-               return (0);
-       }
+#ifdef UPECCDEBUG
+       printf("npf %d reg %x o %d mask %o pos %d\n", npf, reg, o, mask,
+           up->upec1);
+#endif
        /*
         * Flush the buffered data path, and compute the
         * byte and bit position of the error.  The variable i
         * is the byte offset in the transfer, the variable byte
         * is the offset from a page boundary in main memory.
         */
        /*
         * Flush the buffered data path, and compute the
         * byte and bit position of the error.  The variable i
         * is the byte offset in the transfer, the variable byte
         * is the offset from a page boundary in main memory.
         */
-       ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE;
+       ubapurge(um);
        i = up->upec1 - 1;              /* -1 makes 0 origin */
        bit = i&07;
        i = (i&~07)>>3;
        i = up->upec1 - 1;              /* -1 makes 0 origin */
        bit = i&07;
        i = (i&~07)>>3;
@@ -763,12 +731,19 @@ register struct buf *bp;
        while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) {
                addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+
                    (byte & PGOFSET);
        while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) {
                addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+
                    (byte & PGOFSET);
+#ifdef UPECCDEBUG
+               printf("addr %x map reg %x\n",
+                   addr, *(int *)(&ubp->uba_map[reg+btop(byte)]));
+               printf("old: %x, ", getmemc(addr));
+#endif
                putmemc(addr, getmemc(addr)^(mask<<bit));
                putmemc(addr, getmemc(addr)^(mask<<bit));
+#ifdef UPECCDEBUG
+               printf("new: %x\n", getmemc(addr));
+#endif
                byte++;
                i++;
                bit -= 8;
        }
                byte++;
                i++;
                bit -= 8;
        }
-       uptab.b_active++;       /* Either complete or continuing... */
        if (up->upwc == 0)
                return (0);
        /*
        if (up->upwc == 0)
                return (0);
        /*
@@ -777,22 +752,28 @@ register struct buf *bp;
         * We have completed npf+1 sectors of the transfer already;
         * restart at offset o of next sector (i.e. in UBA register reg+1).
         */
         * We have completed npf+1 sectors of the transfer already;
         * restart at offset o of next sector (i.e. in UBA register reg+1).
         */
-       up->upcs1 = TRE|IE|DCLR|GO;
-       DELAY(idelay);
+#ifdef notdef
+       up->uper1 = 0;
+       up->upcs1 |= UP_GO;
+#else
+       up->upcs1 = UP_TRE|UP_IE|UP_DCLR|UP_GO;
        bn = dkblock(bp);
        bn = dkblock(bp);
+       st = &upst[ui->ui_type];
        cn = bp->b_cylin;
        cn = bp->b_cylin;
-       sn = bn%(NSECT*NTRAC) + npf + 1;
-       tn = sn/NSECT;
-       sn %= NSECT;
-       cn += tn/NTRAC;
-       tn %= NTRAC;
+       sn = bn%st->nspc + npf + 1;
+       tn = sn/st->nsect;
+       sn %= st->nsect;
+       cn += tn/st->ntrak;
+       tn %= st->ntrak;
        up->updc = cn;
        up->upda = (tn << 8) | sn;
        ubaddr = (int)ptob(reg+1) + o;
        up->upba = ubaddr;
        cmd = (ubaddr >> 8) & 0x300;
        up->updc = cn;
        up->upda = (tn << 8) | sn;
        ubaddr = (int)ptob(reg+1) + o;
        up->upba = ubaddr;
        cmd = (ubaddr >> 8) & 0x300;
-       cmd |= IE|GO|RCOM;
+       cmd |= UP_IE|UP_GO|UP_RCOM;
+       um->um_tab.b_active = 2;        /* continuing transfer ... */
        up->upcs1 = cmd;
        up->upcs1 = cmd;
+#endif
        return (1);
 }
 
        return (1);
 }
 
@@ -801,52 +782,161 @@ register struct buf *bp;
  * Cancel software state of all pending transfers
  * and restart all units and the controller.
  */
  * Cancel software state of all pending transfers
  * and restart all units and the controller.
  */
-upreset()
+upreset(uban)
+       int uban;
 {
 {
-       int unit;
-
-       printf(" up");
-       uptab.b_active = 0;
-       uptab.b_actf = uptab.b_actl = 0;
-       if (DK_N+NUP == DK_NMAX)
-               dk_busy &= ~(1<<(DK_N+NUP));
-       if (up_ubinfo) {
-               printf("<%d>", (up_ubinfo>>28)&0xf);
-               ubafree(up_ubinfo), up_ubinfo = 0;
-       }
-       UPADDR->upcs2 = CLR;            /* clear controller */
-       DELAY(idelay);
-       for (unit = 0; unit < NUP; unit++) {
-               uputab[unit].b_active = 0;
-               (void) upustart(unit);
+       register struct uba_ctlr *um;
+       register struct uba_device *ui;
+       register sc21, unit;
+
+       for (sc21 = 0; sc21 < NSC; sc21++) {
+               if ((um = upminfo[sc21]) == 0 || um->um_ubanum != uban ||
+                   um->um_alive == 0)
+                       continue;
+               printf(" sc%d", sc21);
+               um->um_tab.b_active = 0;
+               um->um_tab.b_actf = um->um_tab.b_actl = 0;
+               up_softc[sc21].sc_recal = 0;
+               up_softc[sc21].sc_wticks = 0;
+               if (um->um_ubinfo) {
+                       printf("<%d>", (um->um_ubinfo>>28)&0xf);
+                       ubadone(um);
+               }
+               ((struct updevice *)(um->um_addr))->upcs2 = UPCS2_CLR;
+               for (unit = 0; unit < NUP; unit++) {
+                       if ((ui = updinfo[unit]) == 0)
+                               continue;
+                       if (ui->ui_alive == 0 || ui->ui_mi != um)
+                               continue;
+                       uputab[unit].b_active = 0;
+                       (void) upustart(ui);
+               }
+               (void) upstart(um);
        }
        }
-       (void) upstart();
 }
 
 /*
  * Wake up every second and if an interrupt is pending
  * but nothing has happened increment a counter.
 }
 
 /*
  * Wake up every second and if an interrupt is pending
  * but nothing has happened increment a counter.
- * If nothing happens for 20 seconds, reset the controller
+ * If nothing happens for 20 seconds, reset the UNIBUS
  * and begin anew.
  */
 upwatch()
 {
  * and begin anew.
  */
 upwatch()
 {
-       int i;
-
-       timeout((caddr_t)upwatch, 0, HZ);
-       if (uptab.b_active == 0) {
-               for (i = 0; i < NUP; i++)
-                       if (uputab[i].b_active)
-                               goto active;
-               up_wticks = 0;          /* idling */
-               return;
-       }
+       register struct uba_ctlr *um;
+       register sc21, unit;
+       register struct up_softc *sc;
+
+       timeout(upwatch, (caddr_t)0, hz);
+       for (sc21 = 0; sc21 < NSC; sc21++) {
+               um = upminfo[sc21];
+               if (um == 0 || um->um_alive == 0)
+                       continue;
+               sc = &up_softc[sc21];
+               if (um->um_tab.b_active == 0) {
+                       for (unit = 0; unit < NUP; unit++)
+                               if (uputab[unit].b_active &&
+                                   updinfo[unit]->ui_mi == um)
+                                       goto active;
+                       sc->sc_wticks = 0;
+                       continue;
+               }
 active:
 active:
-       up_wticks++;
-       if (up_wticks >= 20) {
-               up_wticks = 0;
-               printf("LOST INTERRUPT RESET");
-               upreset();
-               printf("\n");
+               sc->sc_wticks++;
+               if (sc->sc_wticks >= 20) {
+                       sc->sc_wticks = 0;
+                       printf("sc%d: lost interrupt\n", sc21);
+                       ubareset(um->um_ubanum);
+               }
        }
 }
        }
 }
+
+#define        DBSIZE  20
+
+updump(dev)
+       dev_t dev;
+{
+       struct updevice *upaddr;
+       char *start;
+       int num, blk, unit;
+       struct size *sizes;
+       register struct uba_regs *uba;
+       register struct uba_device *ui;
+       register short *rp;
+       struct upst *st;
+       register int retry;
+
+       unit = minor(dev) >> 3;
+       if (unit >= NUP)
+               return (ENXIO);
+#define        phys(cast, addr) ((cast)((int)addr & 0x7fffffff))
+       ui = phys(struct uba_device *, updinfo[unit]);
+       if (ui->ui_alive == 0)
+               return (ENXIO);
+       uba = phys(struct uba_hd *, ui->ui_hd)->uh_physuba;
+       ubainit(uba);
+       upaddr = (struct updevice *)ui->ui_physaddr;
+       DELAY(5000000);
+       num = maxfree;
+       upaddr->upcs2 = unit;
+       DELAY(100);
+       upaddr->upcs1 = UP_DCLR|UP_GO;
+       upaddr->upcs1 = UP_PRESET|UP_GO;
+       upaddr->upof = UPOF_FMT22;
+       retry = 0;
+       do {
+               DELAY(25);
+               if (++retry > 527)
+                       break;
+       } while ((upaddr->upds & UP_RDY) == 0);
+       if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY)
+               return (EFAULT);
+       start = 0;
+       st = &upst[ui->ui_type];
+       sizes = phys(struct size *, st->sizes);
+       if (dumplo < 0 || dumplo + num >= sizes[minor(dev)&07].nblocks)
+               return (EINVAL);
+       while (num > 0) {
+               register struct pte *io;
+               register int i;
+               int cn, sn, tn;
+               daddr_t bn;
+
+               blk = num > DBSIZE ? DBSIZE : num;
+               io = uba->uba_map;
+               for (i = 0; i < blk; i++)
+                       *(int *)io++ = (btop(start)+i) | (1<<21) | UBAMR_MRV;
+               *(int *)io = 0;
+               bn = dumplo + btop(start);
+               cn = bn/st->nspc + sizes[minor(dev)&07].cyloff;
+               sn = bn%st->nspc;
+               tn = sn/st->nsect;
+               sn = sn%st->nsect;
+               upaddr->updc = cn;
+               rp = (short *) &upaddr->upda;
+               *rp = (tn << 8) + sn;
+               *--rp = 0;
+               *--rp = -blk*NBPG / sizeof (short);
+               *--rp = UP_GO|UP_WCOM;
+               retry = 0;
+               do {
+                       DELAY(25);
+                       if (++retry > 527)
+                               break;
+               } while ((upaddr->upcs1 & UP_RDY) == 0);
+               if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) {
+                       printf("up%d: not ready", unit);
+                       if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) {
+                               printf("\n");
+                               return (EIO);
+                       }
+                       printf(" (flakey)\n");
+               }
+               if (upaddr->upds&UPDS_ERR)
+                       return (EIO);
+               start += blk*NBPG;
+               num -= blk;
+       }
+       return (0);
+}
+#endif