+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ *
+ * %W% (Berkeley) %D%
+ */
+
+/*
+ * up.c, driver for Emulex SC21, SC31 disk controllers
+ * with support for disklabels.
+ * UNTESTED.
+ */
+
+#ifdef UPDEBUG
+int updebug;
+#endif
+#ifdef UPBDEBUG
+int upbdebug;
+#endif
+
+#include "up.h"
+#if NSC > 0
+/*
+ * UNIBUS disk driver with:
+ * overlapped seeks,
+ * ECC recovery, and
+ * bad sector forwarding.
+ *
+ * TODO:
+ * Check that offset recovery code works
+ */
+#include "../machine/pte.h"
+
+#include "param.h"
+#include "systm.h"
+#include "dkstat.h"
+#include "dkbad.h"
+#include "ioctl.h"
+#include "disklabel.h"
+#include "buf.h"
+#include "conf.h"
+#include "dir.h"
+#include "file.h"
+#include "user.h"
+#include "map.h"
+#include "vm.h"
+#include "cmap.h"
+#include "uio.h"
+#include "kernel.h"
+#include "syslog.h"
+#include "stat.h"
+
+#include "../vax/mtpr.h"
+#include "../vax/cpu.h"
+#include "../vax/nexus.h"
+#include "ubavar.h"
+#include "ubareg.h"
+#include "upreg.h"
+
+#define COMPAT_42
+#define B_FORMAT B_XXX
+
+#define upunit(dev) (minor(dev) >> 3)
+#define uppart(dev) (minor(dev) & 07)
+#define upminor(unit, part) (((unit) << 3) | (part))
+
+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];
+char upinit[NUP];
+
+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 bupbuf[NUP];
+struct dkbad upbad[NUP];
+
+struct upsoftc {
+ u_char sc_recal; /* recalibrate state */
+ u_char sc_doseeks; /* perform explicit seeks */
+#ifdef COMPAT_42
+ u_char sc_hdr; /* next i/o includes header */
+#endif
+ short sc_state; /* open fsm */
+ short sc_wlabel; /* label sector is currently writeable */
+ u_long sc_openpart; /* bit mask of open subunits */
+ u_long sc_copenpart; /* bit mask of open character subunits */
+ u_long sc_bopenpart; /* bit mask of open block subunits */
+ int sc_blkdone; /* amount sucessfully transfered */
+ daddr_t sc_badbn; /* replacement block number */
+ short sc_softas; /* bitmask of drives needing attention */
+ short sc_ndrive; /* count of drives */
+ int sc_wticks; /* watchdog timer */
+ int sc_status; /* copy of drive status reg after format */
+ int sc_upds; /* copy of upds reg after format */
+ int sc_er1; /* copy of error reg 1 after format */
+ int sc_er2; /* copy of error reg 2 after format */
+} upsoftc[NSC];
+
+/*
+ * Drive states. Used during steps of open/initialization.
+ * States < OPEN (> 0) are transient, during an open operation.
+ * OPENRAW is used for unlabeled disks,
+ * to inhibit bad-sector forwarding or allow format operations.
+ */
+#define CLOSED 0 /* disk is closed. */
+#define WANTOPEN 1 /* open requested, not started */
+#define WANTOPENRAW 2 /* open requested, no label */
+#define RDLABEL 3 /* reading pack label */
+#define RDBADTBL 4 /* reading bad-sector table */
+#define OPEN 5 /* initialized and ready */
+#define OPENRAW 6 /* open, no label or badsect */
+
+#define b_cylin b_resid /* cyl number, for disksort */
+#define b_bufsiz b_bdone /* amount done, in ui_tab only */
+
+int upwstart, upwatch(); /* Have started guardian */
+int upseek;
+int upwaitdry;
+
+upprobe(reg)
+ caddr_t reg;
+{
+ register int br, cvec;
+
+#ifdef lint
+ br = 0; cvec = br; br = cvec; upintr(0);
+#endif
+ ((struct updevice *)reg)->upcs1 = UP_IE|UP_RDY;
+ DELAY(10);
+ ((struct updevice *)reg)->upcs1 = 0;
+ return (sizeof (struct updevice));
+}
+
+upslave(ui, reg)
+ struct uba_device *ui;
+ caddr_t reg;
+{
+ 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 int unit = ui->ui_unit;
+
+ if (upwstart == 0) {
+ timeout(upwatch, (caddr_t)0, hz);
+ upwstart++;
+ }
+ upip[ui->ui_ctlr][ui->ui_slave] = ui;
+ up_softc[ui->ui_ctlr].sc_ndrive++;
+
+ /*
+ * Try to initialize device and read pack label.
+ */
+ if (upinit(upminor(unit, 0), 0) == 0) {
+ printf(": %s", uplabel[unit].d_typename);
+#ifdef notyet
+ addswap(makedev(UPMAJOR, upminor(unit, 0)), &uplabel[unit]);
+#endif
+ } else
+ printf(": offline");
+}
+
+upopen(dev, flags, fmt)
+ dev_t dev;
+ int flags, fmt;
+{
+ register int unit = upunit(dev);
+ register struct upsoftc *sc;
+ register struct disklabel *lp;
+ register struct partition *pp;
+ struct uba_device *ui;
+ int s, error, part = uppart(dev), mask = 1 << part;
+ daddr_t start, end;
+
+ if (unit >= NUP || (ui = updinfo[unit]) == 0 || ui->ui_alive == 0)
+ return (ENXIO);
+
+ sc = &upsoftc[unit];
+ lp = &uplabel[unit];
+
+ s = spl5();
+ while (sc->sc_state != OPEN && sc->sc_state != OPENRAW &&
+ sc->sc_state != CLOSED)
+ sleep ((caddr_t)sc, PZERO+1);
+ splx(s);
+ if (sc->sc_state != OPEN && sc->sc_state != OPENRAW)
+ if (error = upinit(dev, flags))
+ return (error);
+ if (part >= lp->d_npartitions)
+ return (ENXIO);
+ /*
+ * Warn if a partion is opened
+ * that overlaps another partition which is open
+ * unless one is the "raw" partition (whole disk).
+ */
+#define RAWPART 2 /* 'c' partition */ /* XXX */
+ if ((sc->sc_openpart & mask) == 0 && part != RAWPART) {
+ pp = &lp->d_partitions[part];
+ start = pp->p_offset;
+ end = pp->p_offset + pp->p_size;
+ for (pp = lp->d_partitions;
+ pp < &lp->d_partitions[lp->d_npartitions]; pp++) {
+ if (pp->p_offset + pp->p_size <= start ||
+ pp->p_offset >= end)
+ continue;
+ if ((s = pp - lp->d_partitions) == RAWPART)
+ continue;
+ if (sc->sc_openpart & (1 << s))
+ log(LOG_WARNING,
+ "up%d%c: overlaps open partition (%c)\n",
+ unit, part + 'a', s + 'a');
+ }
+ }
+ switch (fmt) {
+ case S_IFCHR:
+ sc->sc_copenpart |= mask;
+ break;
+ case S_IFBLK:
+ sc->sc_bopenpart |= mask;
+ break;
+ }
+ sc->sc_openpart |= mask;
+ return (0);
+}
+
+/* ARGSUSED */
+upclose(dev, flags, fmt)
+ dev_t dev;
+ int flags, fmt;
+{
+ register int unit = upunit(dev);
+ register struct upsoftc *sc = &upsoftc[unit];
+ struct uba_device *ui = updinfo[unit];
+ int s;
+
+ switch (fmt) {
+ case S_IFCHR:
+ sc->sc_copenpart &= ~(1 << uppart(dev));
+ break;
+ case S_IFBLK:
+ sc->sc_bopenpart &= ~(1 << uppart(dev));
+ break;
+ }
+ if (sc->sc_openpart)
+ sc->sc_openpart = sc->sc_copenpart | sc->sc_bopenpart;
+
+ /*
+ * If this was the last open partition on the drive, then
+ * we must wait for the i/o to complete (so caller knows its
+ * ok to turn the drive off). If not the last, no problem,
+ * as some other partition must still be active. (Potential
+ * problem is some part of the "drive" can be spun down
+ * while leaving other part of same drive running ..)
+ *
+ * (this will also happen if we have disabled the drive because
+ * of an unanticipated pack change, but no harm there)
+ */
+ if (sc->sc_openpart == 0) {
+ s = spl5();
+ while (ui->ui_tab.b_actf)
+ sleep((caddr_t)sc, PZERO - 1);
+ splx(s);
+ sc->sc_state = CLOSED;
+ }
+ return (0);
+}
+
+upinit(dev, flags)
+ dev_t dev;
+ int flags;
+{
+ register struct upsoftc *sc;
+ register struct buf *bp;
+ register struct disklabel *lp;
+ struct uba_device *ui;
+ struct dkbad *db;
+ char *msg, *readdisklabel();
+ int unit, i, error = 0;
+ extern int cold;
+
+ unit = upunit(dev);
+ sc = &upsoftc[unit];
+ lp = &uplabel[unit];
+ ui = updinfo[unit];
+
+ sc->sc_state = WANTOPEN;
+
+ /*
+ * Obtain the physical drive characteristics from the
+ * controller, for use until the label overrides them.
+ */
+ upphysical(ui->ui_addr, lp);
+
+ if (flags & O_NDELAY)
+ goto raw;
+
+ /*
+ * Preset, pack acknowledge will be done in upstart
+ * during first read operation.
+ */
+ if (msg = readdisklabel(dev, upstrategy, lp)) {
+ if (cold)
+ printf(": %s", msg);
+ else
+ log(LOG_ERR, "up%d: %s\n", unit, msg);
+#ifdef COMPAT_42
+ if (upmaptype(ui, lp) == 0)
+#endif
+ goto raw;
+ }
+
+ /*
+ * Seconds per word = (60 / rpm) / (nsectors * secsize/2)
+ */
+ if (ui->ui_dk >= 0 && lp->d_rpm)
+ dk_mspw[ui->ui_dk] = 120.0 /
+ (lp->d_rpm * lp->d_nsectors * lp->d_secsize);
+ /*
+ * Read bad sector table into memory.
+ */
+ bp = geteblk(DEV_BSIZE); /* max sector size */
+ bp->b_dev = dev;
+ sc->sc_state = RDBADTBL;
+ i = 0;
+ do {
+ u.u_error = 0; /* XXX */
+ bp->b_flags = B_BUSY | B_READ;
+ bp->b_blkno = lp->d_secperunit - lp->d_nsectors + i;
+ bp->b_bcount = lp->d_secsize;
+ bp->b_cylin = lp->d_ncylinders - 1;
+ upstrategy(bp);
+ biowait(bp);
+ } while ((bp->b_flags & B_ERROR) && (i += 2) < 10 &&
+ i < lp->d_nsectors);
+ db = (struct dkbad *)(bp->b_un.b_addr);
+ if ((bp->b_flags & B_ERROR) == 0 && db->bt_mbz == 0 &&
+ db->bt_flag == 0) {
+ upbad[unit] = *db;
+ sc->sc_state = OPEN;
+ } else {
+ log(LOG_ERR, "up%d: %s bad-sector file\n", unit,
+ (bp->b_flags & B_ERROR) ? "can't read" : "format error in");
+ u.u_error = 0; /* XXX */
+ sc->sc_state = OPENRAW;
+ }
+ bp->b_flags = B_INVAL | B_AGE;
+ brelse(bp);
+ done:
+ wakeup((caddr_t)sc);
+ return (error);
+
+ raw:
+ sc->sc_state = OPENRAW;
+ wakeup((caddr_t)sc);
+ return (error);
+}
+
+/*
+ * obtain the physical drive layout from the controller prom
+ * (we know this isn't a dec controller, so we can assume sanity)
+ */
+upphysical(upaddr, lp)
+ register struct updevice *upaddr;
+ register struct disklabel *lp;
+{
+ upaddr->upcs1 = 0;
+ upaddr->upcs2 = ui->ui_slave;
+
+ upaddr->uphr = UPHR_MAXTRAK;
+ lp->d_ntracks = upaddr->uphr + 1;
+
+ upaddr->uphr = UPHR_MAXSECT;
+ lp->d_nsectors = upaddr->uphr + 1;
+
+ upaddr->uphr = UPHR_MAXCYL;
+ lp->d_ncylinders = upaddr->uphr + 1;
+
+ lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
+ lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
+
+ lp->d_npartitions = 1; /* ??? (allow label write) */
+ lp->d_partitions[0].p_offset = 0;
+ lp->d_partitions[0].p_size = lp->d_secperunit;
+
+ lp->d_secsize = DEV_BSIZE; /* assume this */
+
+ upaddr->upcs2 = UPCS2_CLR;
+}
+
+upstrategy(bp)
+ register struct buf *bp;
+{
+ register struct uba_device *ui;
+ register struct disklabel *lp;
+ register struct upsoftc *sc;
+ register int unit;
+ register struct buf *dp;
+ long bn, sz;
+ daddr_t maxsz;
+ int xunit = uppart(bp->b_dev);
+ int s;
+
+ sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT;
+ unit = hpunit(bp->b_dev);
+ if (unit >= NUP) {
+ bp->b_error = ENXIO;
+ goto bad;
+ }
+ ui = updinfo[unit];
+ if (ui == 0 || ui->ui_alive == 0) {
+ bp->b_error = ENXIO;
+ goto bad;
+ }
+ sc = &upsoftc[unit];
+ lp = &uplabel[unit];
+ if (sc->sc_state < OPEN)
+ goto q;
+ if ((sc->sc_openpart & (1 << xunit)) == 0) {
+ bp->b_error = ENODEV;
+ goto bad;
+ }
+ maxsz = lp->d_partitions[xunit].p_size;
+ if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) {
+ if (bp->b_blkno == maxsz) {
+ bp->b_resid = bp->b_bcount;
+ goto done;
+ }
+ sz = maxsz - bp->b_blkno;
+ if (sz <= 0) {
+ bp->b_error = EINVAL;
+ goto bad;
+ }
+ bp->b_bcount = sz << DEV_BSHIFT;
+ }
+ bp->b_cylin = (bp->b_blkno + lp->d_partitions[xunit].p_offset) /
+ lp->d_secpercyl;
+ q:
+ s = spl5();
+ dp = &uputab[ui->ui_unit];
+ disksort(dp, bp);
+ if (dp->b_active == 0) {
+ (void) upustart(ui);
+ bp = &ui->ui_mi->um_tab;
+ if (bp->b_actf && bp->b_active == 0)
+ (void) upstart(ui->ui_mi);
+ }
+ splx(s);
+ return;
+
+ bad:
+ bp->b_flags |= B_ERROR;
+ done:
+ biodone(bp);
+ return;
+}
+
+/*
+ * 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(ui)
+ register struct uba_device *ui;
+{
+ register struct buf *bp, *dp;
+ register struct uba_ctlr *um;
+ register struct updevice *upaddr;
+ register struct disklabel *lp;
+ struct upsoftc *sc = &upsoftc[ui->ui_unit];
+ daddr_t bn;
+ int sn, tn, dist;
+ /*
+ * 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().
+ */
+ int didie = 0;
+
+ if (ui == 0)
+ return (0);
+ um = ui->ui_mi;
+ dk_busy &= ~(1<<ui->ui_dk);
+ dp = &uputab[ui->ui_unit];
+ retry:
+ if ((bp = dp->b_actf) == NULL)
+ return (0);
+ /*
+ * 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 (um->um_tab.b_active) {
+ up_softc[um->um_ctlr].sc_softas |= 1<<ui->ui_slave;
+ 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;
+
+ lp = &uplabel[ui->ui_unit];
+ upaddr = (struct updevice *)um->um_addr;
+ upaddr->upcs2 = ui->ui_slave;
+
+ switch (sc->sc_recal) {
+
+ case 1:
+ (void)HPWAIT(mi, hpaddr);
+ upaddr->updc = bp->b_cylin;
+ upaddr->upcs1 = UP_IE|UP_SEEK|UP_GO;
+ sc->sc_recal++;
+ return (1);
+ case 2:
+ break;
+ }
+ sc->sc_recal = 0;
+ /*
+ * If drive has just come up,
+ * setup the pack.
+ */
+ if ((upaddr->upds & UPDS_VV) == 0) {
+ struct buf *bbp = &bupbuf[ui->ui_unit];
+
+ if (sc->sc_state == OPEN && lp->d_flags & D_REMOVABLE) {
+ if (sc->sc_openpart)
+ log(LOG_ERR, "up%d: volume changed\n",
+ ui->ui_unit);
+ sc->sc_openpart = 0;
+ do {
+ dp->b_actf = bp->b_forw;
+ bp->b_flags |= B_ERROR;
+ bp->b_errno = ENXIO;
+ iodone(bp);
+ } while (bp = dp->b_actf);
+ return (0);
+ }
+ upaddr->upcs1 = UP_IE|UP_DCLR|UP_GO;
+ upaddr->upcs1 = UP_IE|UP_PRESET|UP_GO;
+ upaddr->upof = UPOF_FMT22;
+ if (sc->sc_state == WANTOPENRAW) {
+ sc->sc_state = OPENRAW;
+ return (1);
+ }
+ if (sc->sc_state == WANTOPEN)
+ sc->sc_state = RDLABEL;
+ didie = 1;
+ }
+ /*
+ * If drive is offline, forget about positioning.
+ */
+ if ((upaddr->upds & (UPDS_DPR|UPDS_MOL)) != (UPDS_DPR|UPDS_MOL))
+ goto done;
+ /*
+ * 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 = bp->b_blkno;
+ sn = bn % lp->d_secpercyl;
+ tn = sn / lp->d_nsectors;
+ sn = sn % lp->d_nsectors;
+ if (bp->b_cylin == upaddr->updc) {
+ if (sc->sc_doseeks)
+ goto done; /* Ok just to be on-cylinder */
+ dist = sn - (upaddr->upla>>6) - 1;
+ if (dist < 0)
+ dist += lp->d_nsectors;
+ if (dist <= lp->d_maxdist && dist >= lp->d_mindist)
+ goto done;
+ } else
+ upaddr->updc = bp->b_cylin;
+ /*
+ * Not on cylinder at correct position,
+ * seek/search.
+ */
+ if (sc->sc_doseeks)
+ upaddr->upcs1 = UP_IE|UP_SEEK|UP_GO;
+ else {
+ sn = (sn + lp->d_nsectors - lp->d_sdist) % lp->d_nsectors;
+ upaddr->upda = sn;
+ upaddr->upcs1 = UP_IE|UP_SEARCH|UP_GO;
+ }
+ didie = 1;
+ /*
+ * Mark unit busy for iostat.
+ */
+ if (ui->ui_dk >= 0) {
+ dk_busy |= 1<<ui->ui_dk;
+ dk_seek[ui->ui_dk]++;
+ }
+ goto out;
+ done:
+ /*
+ * Device is ready to go.
+ * Put it on the ready queue for the controller
+ * (unless its already there.)
+ */
+ 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);
+}
+
+/*
+ * Start up a transfer on a drive.
+ */
+upstart(um)
+ register struct uba_ctlr *um;
+{
+ register struct buf *bp, *dp;
+ register struct uba_device *ui;
+ register struct updevice *upaddr;
+ register struct disklabel *lp = &uplabel[ui->ui_unit];
+ struct upsoftc *sc = &upsoftc[ui->ui_unit];
+ daddr_t bn;
+ int cn, dn, sn, tn, cmd, waitdry;
+
+ loop:
+ /*
+ * Pull a request off the controller queue
+ */
+ if ((dp = um->um_tab.b_actf) == NULL)
+ return (0);
+ if ((bp = dp->b_actf) == NULL) {
+ um->um_tab.b_actf = dp->b_forw;
+ goto loop;
+ }
+ /*
+ * Mark controller busy, and
+ * determine destination of this request.
+ */
+ um->um_tab.b_active++;
+ ui = updinfo[upunit(bp->b_dev)];
+ bn = bp->b_blkno;
+ 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;
+ /*
+ * Select drive if not selected already.
+ */
+ if ((upaddr->upcs2&07) != dn)
+ upaddr->upcs2 = dn;
+ /*
+ * Check that it is ready and online
+ */
+ waitdry = 0;
+ while ((upaddr->upds&UPDS_DRY) == 0) {
+ log(LOG_ERR, "up%d: ds wait ds=%o\n", upunit(bp->b_dev),
+ upaddr->upds);
+ if (++waitdry > 512)
+ break;
+ upwaitdry++;
+ }
+ if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) {
+ /*
+ * this should be "log()", but log with split
+ * messages is very messy, and we also depend here
+ * on there being a real (measureable) time taken
+ * doing the printf, to give the drive a chance to
+ * recover .. printf polls the terminal, so takes ages
+ */
+ printf("up%d: not ready", upunit(bp->b_dev));
+ 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");
+ }
+ /*
+ * Setup for the transfer, and get in the
+ * UNIBUS adaptor queue.
+ */
+ if (bp->b_flags & B_BAD) {
+ bn = sc->sc_badbn;
+ cn = bn / lp->d_secpercyl;
+ } else {
+ bn = bp->b_blkno;
+ cn = bp->b_cylin;
+ }
+ sn = bn % lp->d_secpercyl;
+ if ((bp->b_flags & B_BAD) == 0)
+ sn += sc->sc_blkdone;
+ tn = sn / lp->d_nsectors;
+ sn %= lp->d_nsectors;
+ cn += tn / lp->d_ntracks;
+ tn %= lp->d_ntracks;
+ upaddr->updc = cn;
+ upaddr->upda = (tn << 8) + sn;
+
+ if (bp->b_bcount >= 0)
+ upaddr->upwc = (-bp->b_bcount + ui->ui_tab.b_bdone) /
+ sizeof (short);
+ else
+ upaddr->upwc = (bp->b_wcount + ui->ui_tab.b_bdone) /
+ sizeof (short);
+
+ switch (bp->b_flags & (B_READ|B_WRITE|B_FORMAT)) {
+ case B_READ:
+ cmd = UP_IE|UP_RCOM|UP_GO;
+ break;
+ case B_WRITE:
+ cmd = UP_IE|UP_WCOM|UP_GO;
+ break;
+ case B_READ|B_FORMAT:
+ cmd = UP_IE|UP_RHDR|UP_GO;
+ break;
+ case B_WRITE|B_FORMAT:
+ cmd = UP_IE|UP_WHDR|UP_GO;
+ break;
+ }
+ um->um_cmd = cmd;
+ ui->ui_tab.b_bdone = dbtob(sc->sc_blkdone);
+ (void) ubago(ui);
+ return (1);
+}
+
+/*
+ * 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(sc21)
+ register sc21;
+{
+ 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;
+ struct up_softc *sc = &up_softc[um->um_ctlr];
+ int as = (upaddr->upas & 0377) | sc->sc_softas;
+ int needie = 1, waitdry;
+
+ 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[upunit(bp->b_dev)];
+ 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)) {
+ er1 = upaddr->uper1;
+ er2 = upaddr->uper2;
+ if (bp->b_flags & B_BAD) {
+ npf = bp->b_error;
+ bn = sc->sc_badbn;
+ } else {
+ npf = btop(bp->b_bcount + upaddr->upwc * sizeof(short));
+ if (er1 & (UPER_DCK | UPER_ECH))
+ npf--;
+ bn = bp->b_blkno + npf;
+ }
+ if (UPWAIT(upaddr, upunit(bp->b_dev)) == 0)
+ goto hard;
+#ifdef UPDEBUG
+ if (updebug) {
+ log(LOG_DEBUG,
+ "uperr: bp %x cyl %d blk %d blkdone %d as %o dc %x da %x\n",
+ bp, bp->b_cylin, bn, sc->sc_blkdone,
+ upaddr->upas, upaddr->updc, upaddr->upda);
+ log(LOG_DEBUG,
+ "errcnt %d er1 %b er2 %b wc -%d (bc %d)\n",
+ um->um_tab.b_errcnt, er1, UPER1_BITS,
+ er2, UPER2_BITS, -up->upwc,-up->upwc*sizeof(short));
+ }
+#endif
+ if (er1 & UPER1_HCRC) {
+ er1 &= ~(UPER1_HCE|UPER1_FER);
+ er2 &= ~UPER2_BSE;
+ }
+ if (er1 & UPER1_WLE) {
+ /*
+ * Give up on write locked devices
+ * immediately.
+ */
+ log(LOG_WARNING, "up%d: write locked\n",
+ upunit(bp->b_dev));
+ bp->b_flags |= B_ERROR;
+ } else if (bp->b_flags & B_FORMAT) {
+ goto hard;
+ } else if (er2 & UPER2_BSE) {
+ if (upecc(ui, BSW))
+ return;
+ goto hard;
+ } else if ((er1 & (UPER1_DCK|UPER1_ECH)) == UPER1_DCK &&
+ um->um_tab.b_errcnt >= 3) {
+ if (upecc(ui, ECC))
+ return;
+ /* see comment in hp.c */
+ if (ui->ui_tab.b_errcnt == 3)
+ ui->ui_tab.b_errcnt = 0;
+ } else if (er1 & UPER1_HCRC && upecc(ui, BSE)) {
+ return;
+ } else if (++um->um_tab.b_errcnt > 27 || er1 & HPER1_HARD) {
+ hard:
+ bp->b_blkno = bn; /* XXX */
+ harderr(bp, "up");
+ printf("cn=%d tn=%d sn=%d cs2=%b er1=%b er2=%b",
+ upaddr->updc, ((upaddr->upda)>>8)&077,
+ (upaddr->upda)&037,
+ upaddr->upcs2, UPCS2_BITS,
+ upaddr->uper1, UPER1_BITS,
+ upaddr->uper2, UPER2_BITS);
+ if (bp->b_flags & B_FORMAT)
+ printf(" (hdr i/o)");
+ printf("\n");
+ bp->b_flags |= B_ERROR;
+ bp->b_flags &= ~B_BAD;
+ } else
+ um->um_tab.b_active = 0; /* force retry */
+ /*
+ * 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;
+ }
+ }
+#ifdef UPDEBUG
+ else if (updebug && sc->sc_recal) {
+ log(LOG_DEBUG, "up: recal %d errcnt %d er1=%b er2=%b\n",
+ sc->sc_recal, um->um_tab.b_errcnt,
+ upaddr->uper1, UPER1_BITS,
+ upaddr->uper2, UPER2_BITS);
+ }
+#endif
+ /*
+ * 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;
+ }
+ 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));
+ sc->sc_blkdone = 0;
+ if (sc->sc_openpart == 0)
+ wakeup((caddr_t)sc);
+ iodone(bp);
+ /*
+ * If this unit has more work to do,
+ * then start it up right away.
+ */
+ if (dp->b_actf)
+ if (upustart(ui))
+ 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((long)as)) {
+ unit--; /* was 1 origin */
+ as &= ~(1<<unit);
+ upaddr->upas = 1<<unit;
+ if (unit < UPIPUNITS && upustart(upip[sc21][unit]))
+ needie = 0;
+ }
+ /*
+ * If the controller is not transferring, but
+ * there are devices ready to transfer, start
+ * the controller.
+ */
+ if (um->um_tab.b_actf && um->um_tab.b_active == 0)
+ if (upstart(um))
+ needie = 0;
+ if (needie)
+ upaddr->upcs1 = UP_IE;
+}
+
+upioctl(dev, cmd, data, flag)
+ dev_t dev;
+ int cmd;
+ caddr_t data;
+ int flag;
+{
+ int unit = upunit(dev);
+ register struct disklabel *lp;
+ register struct format_op *fop;
+ int error = 0;
+ int upformat();
+
+ lp = &uplabel[unit];
+
+ switch (cmd) {
+
+ case DIOCGDINFO:
+ *(struct disklabel *)data = *lp;
+ break;
+
+ case DIOCGPART:
+ ((struct partinfo *)data)->disklab = lp;
+ ((struct partinfo *)data)->part =
+ &lp->d_partitions[uppart(dev)];
+ break;
+
+ case DIOCSDINFO:
+ if ((flag & FWRITE) == 0)
+ error = EBADF;
+ else
+ *lp = *(struct disklabel *)data;
+ break;
+
+ case DIOCWDINFO:
+ if ((flag & FWRITE) == 0) {
+ error = EBADF;
+ break;
+ }
+ {
+ struct buf *bp;
+ struct disklabel *dlp;
+
+ *lp = *(struct disklabel *)data;
+ bp = geteblk(lp->d_secsize);
+ bp->b_dev = makedev(major(dev), upminor(upunit(dev), 0));
+ bp->b_blkno = LABELSECTOR;
+ bp->b_bcount = lp->d_secsize;
+ bp->b_flags = B_READ;
+ dlp = (struct disklabel *)(bp->b_un.b_addr + LABELOFFSET);
+ upstrategy(bp);
+ biowait(bp);
+ if (bp->b_flags & B_ERROR) {
+ error = u.u_error; /* XXX */
+ u.u_error = 0;
+ goto bad;
+ }
+ *dlp = *lp;
+ bp->b_flags = B_WRITE;
+ upstrategy(bp);
+ biowait(bp);
+ if (bp->b_flags & B_ERROR) {
+ error = u.u_error; /* XXX */
+ u.u_error = 0;
+ }
+ bad:
+ brelse(bp);
+ break;
+ }
+
+#ifdef notyet
+ case DIOCWFORMAT:
+ if ((flag & FWRITE) == 0) {
+ error = EBADF;
+ break;
+ }
+ {
+ struct uio auio;
+ struct iovec aiov;
+
+ fop = (struct format_op *)data;
+ aiov.iov_base = fop->df_buf;
+ aiov.iov_len = fop->df_count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = fop->df_count;
+ auio.uio_segflg = 0;
+ auio.uio_offset = fop->df_startblk * lp->d_secsize;
+ error = physio(upformat, &rupbuf[unit], dev, B_WRITE,
+ minphys, &auio);
+ fop->df_count -= auio.uio_resid;
+ fop->df_reg[0] = sc->sc_status;
+ fop->df_reg[1] = sc->sc_error;
+ }
+ break;
+#endif
+
+ default:
+ error = ENOTTY;
+ break;
+ }
+ return (error);
+}
+
+upformat(bp)
+ struct buf *bp;
+{
+
+ bp->b_flags |= B_FORMAT;
+ return (upstrategy(bp));
+}
+
+/*
+ * Correct an ECC error, and restart the i/o to complete
+ * the transfer if necessary. This is quite complicated because
+ * the transfer may be going to an odd memory address base and/or
+ * across a page boundary.
+ */
+upecc(ui, flag)
+ register struct uba_device *ui;
+ int flag;
+{
+ 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 disklabel *lp = &hplabel[mi->mi_unit];
+ struct uba_regs *ubp = ui->ui_hd->uh_uba;
+ struct hpsoftc *sc = &hpsoftc[mi->mi_unit];
+ register int i;
+ caddr_t addr;
+ int reg, bit, byte, npf, mask, o, cmd, ubaddr;
+ int bn, cn, tn, sn;
+ int bcr;
+
+ /*
+ * Npf is the number of sectors transferred before the sector
+ * containing the ECC error, and reg is the UBA register
+ * mapping (the first part of) the transfer.
+ * O is offset within a memory page of the first byte transferred.
+ */
+ bcr = -up->upwc * sizeof(short);
+ if (flag == CONT)
+ npf = bp->b_error;
+ else {
+ npf = bp->b_bcount - bcr;
+ /*
+ * Watch out for fractional sector at end of transfer;
+ * want to round up if finished, otherwise round down.
+ */
+ if (bcr == 0)
+ npf += 511;
+ npf = btodb(npf);
+ }
+ reg = btop(um->um_ubinfo&0x3ffff) + npf;
+ o = (int)bp->b_un.b_addr & PGOFSET;
+ mask = up->upec2;
+#ifdef UPECCDEBUG
+ if (upeccdebug)
+ log(LOG_DEBUG, "npf %d reg %x o %d mask %o pos %d\n",
+ npf, reg, o, mask, up->upec1);
+#endif
+ bn = bp->b_blkno;
+ cn = bp->b_cylin;
+ sn = bn % lp->d_secpercyl + npf;
+ tn = sn / lp->d_nsectors;
+ sn %= lp->d_nsectors;
+ cn += tn / lp->d_ntracks;
+ tn %= lp->d_ntracks;
+ bn += npf;
+ ubapurge(um);
+ um->um_tab.b_active = 2;
+ /*
+ * action taken depends on the flag
+ */
+ switch (flag) {
+ case ECC: {
+ register int i;
+ caddr_t addr;
+ struct pte mpte;
+ int bit, byte, mask;
+
+ npf--; /* because block in error is previous block */
+ bn--;
+ reg--;
+ if (bp->b_flags & B_BAD)
+ bn = sc->sc_badbn;
+ log(LOG_WARNING, "up%d%c: soft ecc sn%d\n", upunit(bp->b_dev),
+ 'a' + uppart(bp->b_dev), bp->b_blkno + npf);
+ /*
+ * 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.
+ */
+ i = up->upec1 - 1; /* -1 makes 0 origin */
+ mask = up->upec2;
+ bit = i&07;
+ i = (i&~07)>>3;
+ byte = i + o;
+ /*
+ * Correct while possible bits remain of mask. Since mask
+ * contains 11 bits, we continue while the bit offset is > -11.
+ * Also watch out for end of this block and the end of the whole
+ * transfer.
+ */
+ while (i < 512 && (int)dbtob(npf)+i < bp->b_bcount && bit > -11) {
+ struct pte pte;
+
+ pte = ubp->uba_map[reg + btop(byte)];
+ addr = ptob(pte.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));
+#ifdef UPECCDEBUG
+ printf("new: %x\n", getmemc(addr));
+#endif
+ byte++;
+ i++;
+ bit -= 8;
+ }
+ if (bcr == 0)
+ return (0);
+ npf++;
+ reg++;
+ break;
+ }
+
+ case BSE:
+ if (sc->sc_state == OPENRAW)
+ return (0);
+#ifdef UPBDEBUG
+ if (upbdebug)
+ log(LOG_DEBUG, "upecc, BSE: bn %d cn %d tn %d sn %d\n",
+ bn, cn, tn, sn);
+#endif
+ if (bp->b_flags & B_BAD)
+ return (0);
+ /*
+ * if not in bad sector table, return 0
+ */
+ if ((bn = isbad(&upbad[ui->ui_unit], cn, tn, sn)) < 0)
+ return(0);
+ /*
+ * flag this one as bad
+ */
+ bp->b_flags |= B_BAD;
+ bp->b_error = npf + 1;
+#ifdef UPBDEBUG
+ if (upbdebug)
+ log(LOG_DEBUG "BSE: restart at %d\n",npf+1);
+#endif
+ bn = lp->d_ncylinders * lp->d_secpercyl -
+ lp->d_nsectors - 1 - bn;
+ sc->sc_badbn = bn;
+ fixregs:
+ cn = bn / lp->d_secpercyl;
+ sn = bn % lp->d_secpercyl;
+ tn = sn / lp->d_nsectors;
+ sn %= lp->d_nsectors;
+ bcr = bp->b_bcount - (int)ptob(npf);
+ bcr = MIN(bcr, 512);
+ up->upwc = -bcr;
+#ifdef UPBDEBUG
+ if (upbdebug)
+ log(LOG_DEBUG, "revector to cn %d tn %d sn %d\n",
+ cn, tn, sn);
+#endif
+ break;
+
+ case CONT:
+#ifdef UPBDEBUG
+ if (upbdebug)
+ log(LOG_DEBUG, "upecc, CONT: bn %d cn %d tn %d sn %d\n",
+ bn, cn, tn, sn);
+#endif
+ bp->b_flags &= ~B_BAD;
+ if ((int)ptob(npf) >= bp->b_bcount)
+ return (0);
+ up->upwc = -((bp->b_bcount - (int)ptob(npf)) / sizeof(short));
+ break;
+ }
+ if (up->upwc == 0) {
+ um->um_tab.b_active = 0; /* retry */
+ return (0);
+ }
+ /*
+ * Have to continue the transfer... clear the drive,
+ * and compute the position where the transfer is to continue.
+ * We have completed npf+1 sectors of the transfer already;
+ * restart at offset o of next sector (i.e. in UBA register reg+1).
+ */
+#ifdef notdef
+ up->uper1 = 0;
+ up->upcs1 |= UP_GO;
+#else
+ up->upcs1 = UP_TRE|UP_IE|UP_DCLR|UP_GO;
+ up->updc = cn;
+ up->upda = (tn << 8) | sn;
+ ubaddr = (int)ptob(reg) + o;
+ up->upba = ubaddr;
+ cmd = (ubaddr >> 8) & 0x300;
+ cmd |= ((bp->b_flags&B_READ)?UP_RCOM:UP_WCOM)|UP_IE|UP_GO;
+ um->um_tab.b_errcnt = 0;
+ up->upcs1 = cmd;
+#endif
+ sc->sc_blkdone = npf;
+ return (1);
+}
+
+/*
+ * Reset driver after UBA init.
+ * Cancel software state of all pending transfers
+ * and restart all units and the controller.
+ */
+upreset(uban)
+ int uban;
+{
+ 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);
+ um->um_ubinfo = 0;
+ }
+ ((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);
+ }
+}
+
+/*
+ * 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 UNIBUS
+ * and begin anew.
+ */
+upwatch()
+{
+ 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:
+ 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;
+ register struct disklabel *lp;
+
+ unit = upunit(dev);
+ if (unit >= NUP)
+ return (ENXIO);
+#define phys(cast, addr) ((cast)((int)addr & 0x7fffffff))
+ ui = phys(struct uba_device *, updinfo[unit]);
+ if (ui == 0 || 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;
+ if (dumplo < 0)
+ return (EINVAL);
+ lp = &uplabel[unit];
+ /* should check uupart(dev) < lp->p_npartitions ? */
+ if (dumplo + num >= lp->d_partitions[uppart(dev)].p_size)
+ num = lp->d_partitions[uppart(dev)].p_size - dumplo;
+ while (num > 0) {
+ register struct pte *io;
+ register int i;
+ int blk, 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 + lp->d_partitions[hppart(dev)].p_offset) /
+ lp->d_secpercyl;
+ sn = bn % lp->d_secpercyl;
+ tn = sn / lp->d_nsectors;
+ sn = sn % lp->d_nsectors;
+ 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);
+}
+
+upsize(dev)
+ dev_t dev;
+{
+ register int unit = upunit(dev);
+ struct uba_device *ui;
+
+ if (unit >= NUP || (ui = updinfo[unit]) == 0 || ui->ui_alive == 0 ||
+ upsoftc[unit].sc_state != OPEN)
+ return (-1);
+ return ((int)uplabel[unit].d_partitions[uppart(dev)].p_size);
+}
+
+#ifdef COMPAT_42
+/*
+ * Compatibility code to fake up pack label
+ * for unlabeled disks.
+ */
+struct size {
+ daddr_t nblocks;
+ int cyloff;
+} up9300_sizes[8] = {
+ 15884, 0, /* A=cyl 0 thru 26 */
+ 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 */
+ 81376, 681, /* F=cyl 681 thru 814 */
+ 153728, 562, /* G=cyl 562 thru 814 */
+ 291346, 82, /* H=cyl 82 thru 561 */
+}, up9766_sizes[8] = {
+ 15884, 0, /* A=cyl 0 thru 26 */
+ 33440, 27, /* B=cyl 27 thru 81 */
+ 500384, 0, /* C=cyl 0 thru 822 */
+ 15884, 562, /* D=cyl 562 thru 588 */
+ 55936, 589, /* E=cyl 589 thru 680 */
+ 86240, 681, /* F=cyl 681 thru 822 */
+ 158592, 562, /* G=cyl 562 thru 822 */
+ 291346, 82, /* H=cyl 82 thru 561 */
+}, up160_sizes[8] = {
+ 15884, 0, /* A=cyl 0 thru 49 */
+ 33440, 50, /* B=cyl 50 thru 154 */
+ 263360, 0, /* C=cyl 0 thru 822 */
+ 15884, 155, /* D=cyl 155 thru 204 */
+ 55936, 205, /* E=cyl 205 thru 379 */
+ 141664, 380, /* F=cyl 380 thru 822 */
+ 213664, 155, /* G=cyl 155 thru 822 */
+ 0, 0,
+}, upam_sizes[8] = {
+ 15884, 0, /* A=cyl 0 thru 31 */
+ 33440, 32, /* B=cyl 32 thru 97 */
+ 524288, 0, /* C=cyl 0 thru 1023 */
+ 15884, 668, /* D=cyl 668 thru 699 */
+ 55936, 700, /* E=cyl 700 thru 809 */
+ 109472, 810, /* F=cyl 810 thru 1023 */
+ 182176, 668, /* G=cyl 668 thru 1023 */
+ 291346, 98, /* H=cyl 98 thru 667 */
+}, up980_sizes[8] = {
+ 15884, 0, /* A=cyl 0 thru 99 */
+ 33440, 100, /* B=cyl 100 thru 308 */
+ 131680, 0, /* C=cyl 0 thru 822 */
+ 15884, 309, /* D=cyl 309 thru 408 */
+ 55936, 409, /* E=cyl 409 thru 758 */
+ 10080, 759, /* F=cyl 759 thru 822 */
+ 82080, 309, /* G=cyl 309 thru 822 */
+ 0, 0,
+}, upeagle_sizes[8] = {
+ 15884, 0, /* A=cyl 0 thru 16 */
+ 66880, 17, /* B=cyl 17 thru 86 */
+ 808320, 0, /* C=cyl 0 thru 841 */
+ 15884, 391, /* D=cyl 391 thru 407 */
+ 307200, 408, /* E=cyl 408 thru 727 */
+ 109296, 728, /* F=cyl 728 thru 841 */
+ 432816, 391, /* G=cyl 391 thru 841 */
+ 291346, 87, /* H=cyl 87 thru 390 */
+};
+
+struct upst {
+ short nsect; /* # sectors/track */
+ short ntrak; /* # tracks/cylinder */
+ short nspc; /* # sectors/cylinder */
+ short ncyl; /* # cylinders */
+ struct size *sizes; /* partition tables */
+ short sdist; /* seek distance metric */
+ short rdist; /* rotational distance metric */
+ short mdist; /* min rotation (all guesswork) */
+} upst[] = {
+ { 32, 19, 32*19, 815, up9300_sizes, 3, 4, 1 }, /*9300*/
+ { 32, 19, 32*19, 823, up9766_sizes, 3, 4, 1 }, /*9766*/
+ { 32, 10, 32*10, 823, up160_sizes, 3, 4, 1 }, /*fuji160m*/
+ { 32, 16, 32*16, 1024, upam_sizes, 7, 8, 2 }, /*Capricorn*/
+ { 32, 5, 32*5, 823, up980_sizes, 3, 4, 1 }, /*DM980*/
+ { 48, 20, 48*20, 842, upeagle_sizes, 15, 8, 3 }, /*EAGLE*/
+ { 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+
+/*
+ * These variable are all measured in sectors.
+ * Sdist is how much to "lead" in the search for a desired sector
+ * (i.e. if want N, search for N-sdist.)
+ * Maxdist and mindist define the region right before our desired sector within
+ * which we don't bother searching. We don't search when we are already less
+ * then maxdist and more than mindist sectors "before" our desired sector.
+ * Maxdist should be >= sdist.
+ *
+ * Beware, sdist, mindist and maxdist are not well tuned
+ * for many of the drives listed in this table.
+ * Try patching things with something i/o intensive
+ * running and watch iostat.
+ */
+
+/*
+ * Map apparent SC[23]1 drive type into manufacturer
+ * specific configuration.
+ */
+upmaptype(ui, lp)
+ register struct uba_device *ui;
+ register struct disklabel *lp;
+{
+ register int type;
+ register struct upst *st;
+
+ upphysical(ui->ui_addr, lp); /* get the real drive numbers */
+ for (st = upst, type = 0; st->nsect != 0; st++, type++)
+ if (lp->d_ntracks == st->ntrak &&
+ lp->d_nsectors == st->nsect &&
+ lp->d_ncylinders == st->ncyl)
+ break;
+
+ if (st->nsect == 0) {
+ printf(": %dx%dx%d (s,t,c)?", lp->d_nsectors,
+ lp->d_ntracks, lp->d_ncylinders);
+ return (0);
+ }
+
+ ui->ui_type = type;
+
+ /*
+ * set up minimal disk label.
+ */
+ st = &hpst[type];
+ lp->d_secsize = 512;
+ lp->d_nsectors = st->nsect;
+ lp->d_ntracks = st->ntrak;
+ lp->d_secpercyl = st->nspc;
+ lp->d_ncylinders = st->ncyl;
+ lp->d_secperunit = st->nspc * st->ncyl;
+ lp->d_sdist = st->sdist;
+ lp->d_mindist = st->rdist;
+ lp->d_maxdist = st->mdist;
+ bcopy(hpst[type].name, lp->d_typename, sizeof(lp->d_typename));
+ lp->d_npartitions = 8;
+ for (i = 0; i < 8; i++) {
+ lp->d_partitions[i].p_offset = st->sizes[i].cyloff *
+ lp->d_secpercyl;
+ lp->d_partitions[i].p_size = st->sizes[i].nblocks;
+ }
+ return (1);
+}
+#endif /* COMPAT_42 */
+
+#endif /* NSC > 0 */