summaryrefslogtreecommitdiff
path: root/sys/arch/amiga/dev/fd.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /sys/arch/amiga/dev/fd.c
initial import of NetBSD tree
Diffstat (limited to 'sys/arch/amiga/dev/fd.c')
-rw-r--r--sys/arch/amiga/dev/fd.c1852
1 files changed, 1852 insertions, 0 deletions
diff --git a/sys/arch/amiga/dev/fd.c b/sys/arch/amiga/dev/fd.c
new file mode 100644
index 00000000000..800131ef243
--- /dev/null
+++ b/sys/arch/amiga/dev/fd.c
@@ -0,0 +1,1852 @@
+/* $NetBSD: fd.c,v 1.21 1995/08/12 20:30:45 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1994 Christian E. Hopps
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christian E. Hopps.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/device.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/conf.h>
+#include <sys/disklabel.h>
+#include <sys/disk.h>
+#include <sys/dkbad.h>
+#include <amiga/amiga/device.h>
+#include <amiga/amiga/custom.h>
+#include <amiga/amiga/cia.h>
+#include <amiga/amiga/cc.h>
+
+enum fdc_bits { FDB_CHANGED = 2, FDB_PROTECT, FDB_CYLZERO, FDB_READY };
+/*
+ * partitions in fd represent different format floppies
+ * partition a is 0 etc..
+ */
+enum fd_parttypes {
+ FDAMIGAPART = 0,
+#ifdef not_yet
+ FDMSDOSPART,
+#endif
+ FDMAXPARTS
+};
+
+#define FDBBSIZE (8192)
+#define FDSBSIZE (8192)
+
+#define b_cylin b_resid
+#define FDUNIT(dev) DISKUNIT(dev)
+#define FDPART(dev) DISKPART(dev)
+#define FDMAKEDEV(m, u, p) MAKEDISKDEV((m), (u), (p))
+
+#define FDNHEADS (2) /* amiga drives always have 2 heads */
+#define FDSECSIZE (512) /* amiga drives always have 512 byte sectors */
+#define FDSECLWORDS (128)
+
+#define FDSETTLEDELAY (18000) /* usec delay after seeking after switch dir */
+#define FDSTEPDELAY (3500) /* usec delay after steping */
+#define FDPRESIDEDELAY (1000) /* usec delay before writing can occur */
+#define FDWRITEDELAY (1300) /* usec delay after write */
+
+#define FDSTEPOUT (1) /* decrease track step */
+#define FDSTEPIN (0) /* increase track step */
+
+#define FDCUNITMASK (0x78) /* mask for all units (bits 6-3) */
+
+#define FDRETRIES (2) /* default number of retries */
+#define FDMAXUNITS (4) /* maximum number of supported units */
+
+#define DISKLEN_READ (0) /* fake mask for reading */
+#define DISKLEN_WRITE (1 << 14) /* bit for writing */
+#define DISKLEN_DMAEN (1 << 15) /* dma go */
+#define DMABUFSZ ((DISKLEN_WRITE - 1) * 2) /* largest dma possible */
+
+#define FDMFMSYNC (0x4489)
+
+/*
+ * floppy device type
+ */
+struct fdtype {
+ u_int driveid; /* drive identification (from drive) */
+ u_int ncylinders; /* number of cylinders on drive */
+ u_int amiga_nsectors; /* number of sectors per amiga track */
+ u_int msdos_nsectors; /* number of sectors per msdos track */
+ u_int nreadw; /* number of words (short) read per track */
+ u_int nwritew; /* number of words (short) written per track */
+ u_int gap; /* track gap size in long words */
+ u_int precomp[2]; /* 1st and 2nd precomp values */
+ char *desc; /* description of drive type (useq) */
+};
+
+/*
+ * floppy disk device data
+ */
+struct fd_softc {
+ struct dkdevice dkdev;
+ struct buf bufq; /* queue of buf's */
+ struct fdtype *type;
+ void *cachep; /* cached track data (write through) */
+ int cachetrk; /* cahced track -1 for none */
+ int hwunit; /* unit for amiga controlling hw */
+ int unitmask; /* mask for cia select deslect */
+ int pstepdir; /* previous step direction */
+ int curcyl; /* current curcyl head positioned on */
+ int flags; /* misc flags */
+ int wlabel;
+ int stepdelay; /* useq to delay after seek user setable */
+ int nsectors; /* number of sectors per track */
+ int openpart; /* which partition [ab] == [12] is open */
+ short retries; /* number of times to retry failed io */
+ short retried; /* number of times current io retried */
+};
+
+/* fd_softc->flags */
+#define FDF_MOTORON (0x01) /* motor is running */
+#define FDF_MOTOROFF (0x02) /* motor is waiting to be turned off */
+#define FDF_WMOTOROFF (0x04) /* unit wants a wakeup after off */
+#define FDF_DIRTY (0x08) /* track cache needs write */
+#define FDF_WRITEWAIT (0x10) /* need to head select delay on next setpos */
+#define FDF_HAVELABEL (0x20) /* label is valid */
+#define FDF_JUSTFLUSH (0x40) /* don't bother caching track. */
+#define FDF_NOTRACK0 (0x80) /* was not able to recalibrate drive */
+
+int fdc_wantwakeup;
+int fdc_side;
+void *fdc_dmap;
+struct fd_softc *fdc_indma;
+
+struct fdcargs {
+ struct fdtype *type;
+ int unit;
+};
+
+int fdmatch __P((struct device *, struct cfdata *, void *));
+int fdcmatch __P((struct device *, struct cfdata *, void *));
+int fdcprint __P((void *, char *));
+void fdcattach __P((struct device *, struct device *, void *));
+void fdattach __P((struct device *, struct device *, void *));
+
+void fdstart __P((struct fd_softc *));
+void fddone __P((struct fd_softc *));
+void fdfindwork __P((int));
+void fddmastart __P((struct fd_softc *, int));
+void fddmadone __P((struct fd_softc *, int));
+void fdsetpos __P((struct fd_softc *, int, int));
+void fdmotoroff __P((void *));
+void fdmotorwait __P((void *));
+void fdminphys __P((struct buf *));
+void fdcachetoraw __P((struct fd_softc *));
+int fdrawtocache __P((struct fd_softc *));
+int fdloaddisk __P((struct fd_softc *));
+u_long *mfmblkencode __P((u_long *, u_long *, u_long *, int));
+u_long *mfmblkdecode __P((u_long *, u_long *, u_long *, int));
+struct fdtype * fdcgetfdtype __P((int));
+
+void fdstrategy __P((struct buf *));
+
+struct dkdriver fddkdriver = { fdstrategy };
+
+/*
+ * read size is (nsectors + 1) * mfm secsize + gap bytes + 2 shorts
+ * write size is nsectors * mfm secsize + gap bytes + 3 shorts
+ * the extra shorts are to deal with a dma hw bug in the controller
+ * they are probably too much (I belive the bug is 1 short on write and
+ * 3 bits on read) but there is no need to be cheap here.
+ */
+#define MAXTRKSZ (22 * FDSECSIZE)
+struct fdtype fdtype[] = {
+ { 0x00000000, 80, 11, 9, 7358, 6815, 414, { 80, 161 }, "3.5dd" },
+ { 0x55555555, 40, 11, 9, 7358, 6815, 414, { 80, 161 }, "5.25dd" },
+ { 0xAAAAAAAA, 80, 22, 18, 14716, 13630, 828, { 80, 161 }, "3.5hd" }
+};
+int nfdtype = sizeof(fdtype) / sizeof(*fdtype);
+
+struct cfdriver fdcd = {
+ NULL, "fd", (cfmatch_t)fdmatch, fdattach, DV_DISK,
+ sizeof(struct fd_softc), NULL, 0 };
+
+struct cfdriver fdccd = {
+ NULL, "fdc", (cfmatch_t)fdcmatch, fdcattach, DV_DULL,
+ sizeof(struct device), NULL, 0 };
+
+/*
+ * all hw access through macros, this helps to hide the active low
+ * properties
+ */
+
+#define FDUNITMASK(unit) (1 << (3 + (unit)))
+
+/*
+ * select units using mask
+ */
+#define FDSELECT(um) do { ciab.prb &= ~(um); } while (0)
+
+/*
+ * deselect units using mask
+ */
+#define FDDESELECT(um) do { ciab.prb |= (um); delay(1); } while (0)
+
+/*
+ * test hw condition bits
+ */
+#define FDTESTC(bit) ((ciaa.pra & (1 << (bit))) == 0)
+
+/*
+ * set motor for select units, true motor on else off
+ */
+#define FDSETMOTOR(on) do { \
+ if (on) ciab.prb &= ~CIAB_PRB_MTR; else ciab.prb |= CIAB_PRB_MTR; \
+ } while (0)
+
+/*
+ * set head for select units
+ */
+#define FDSETHEAD(head) do { \
+ if (head) ciab.prb &= ~CIAB_PRB_SIDE; else ciab.prb |= CIAB_PRB_SIDE; \
+ delay(1); } while (0)
+
+/*
+ * select direction, true towards spindle else outwards
+ */
+#define FDSETDIR(in) do { \
+ if (in) ciab.prb &= ~CIAB_PRB_DIR; else ciab.prb |= CIAB_PRB_DIR; \
+ delay(1); } while (0)
+
+/*
+ * step the selected units
+ */
+#define FDSTEP do { \
+ ciab.prb &= ~CIAB_PRB_STEP; ciab.prb |= CIAB_PRB_STEP; \
+ } while (0)
+
+#define FDDMASTART(len, towrite) do { \
+ int dmasz = (len) | ((towrite) ? DISKLEN_WRITE : 0) | DISKLEN_DMAEN; \
+ custom.dsklen = dmasz; custom.dsklen = dmasz; } while (0)
+
+#define FDDMASTOP do { custom.dsklen = 0; } while (0)
+
+
+int
+fdcmatch(pdp, cfp, auxp)
+ struct device *pdp;
+ struct cfdata *cfp;
+ void *auxp;
+{
+ if (matchname("fdc", auxp) == 0 || cfp->cf_unit != 0)
+ return(0);
+ if ((fdc_dmap = alloc_chipmem(DMABUFSZ)) == NULL) {
+ printf("fdc: unable to allocate dma buffer\n");
+ return(0);
+ }
+ return(1);
+}
+
+void
+fdcattach(pdp, dp, auxp)
+ struct device *pdp, *dp;
+ void *auxp;
+{
+ struct fdcargs args;
+
+ printf(": dmabuf pa 0x%x\n", kvtop(fdc_dmap));
+ args.unit = 0;
+ args.type = fdcgetfdtype(args.unit);
+
+ fdc_side = -1;
+ config_found(dp, &args, fdcprint);
+ for (args.unit++; args.unit < FDMAXUNITS; args.unit++) {
+ if ((args.type = fdcgetfdtype(args.unit)) == NULL)
+ continue;
+ config_found(dp, &args, fdcprint);
+ }
+}
+
+int
+fdcprint(auxp, pnp)
+ void *auxp;
+ char *pnp;
+{
+ struct fdcargs *fcp;
+
+ fcp = auxp;
+ if (pnp)
+ printf("fd%d at %s:", fcp->unit, pnp);
+ return(UNCONF);
+}
+
+/*ARGSUSED*/
+int
+fdmatch(pdp, cfp, auxp)
+ struct device *pdp;
+ struct cfdata *cfp;
+ void *auxp;
+{
+#define cf_unit cf_loc[0]
+ struct fdcargs *fdap;
+
+ fdap = auxp;
+ if (cfp->cf_unit == fdap->unit || cfp->cf_unit == -1)
+ return(1);
+ return(0);
+#undef cf_unit
+}
+
+void
+fdattach(pdp, dp, auxp)
+ struct device *pdp, *dp;
+ void *auxp;
+{
+ struct fdcargs *ap;
+ struct fd_softc *sc;
+
+ ap = auxp;
+ sc = (struct fd_softc *)dp;
+
+ sc->curcyl = sc->cachetrk = -1;
+ sc->openpart = -1;
+ sc->type = ap->type;
+ sc->hwunit = ap->unit;
+ sc->unitmask = 1 << (3 + ap->unit);
+ sc->retries = FDRETRIES;
+ sc->dkdev.dk_driver = &fddkdriver;
+ sc->stepdelay = FDSTEPDELAY;
+ printf(": %s %d cyl, %d head, %d sec [%d sec], 512 bytes/sec\n",
+ sc->type->desc, sc->type->ncylinders, FDNHEADS,
+ sc->type->amiga_nsectors, sc->type->msdos_nsectors);
+
+ /*
+ * calibrate the drive
+ */
+ fdsetpos(sc, 0, 0);
+ fdsetpos(sc, sc->type->ncylinders, 0);
+ fdsetpos(sc, 0, 0);
+ fdmotoroff(sc);
+
+ /*
+ * enable disk related interrupts
+ */
+ custom.dmacon = DMAF_SETCLR | DMAF_DISK;
+ /* XXX why softint */
+ custom.intena = INTF_SETCLR |INTF_SOFTINT | INTF_DSKBLK;
+ ciaa.icr = CIA_ICR_IR_SC | CIA_ICR_FLG;
+}
+
+/*ARGSUSED*/
+int
+Fdopen(dev, flags, devtype, p)
+ dev_t dev;
+ int flags, devtype;
+ struct proc *p;
+{
+ struct fd_softc *sc;
+ int wasopen, fwork, error, s;
+
+ error = 0;
+
+ if (FDPART(dev) >= FDMAXPARTS)
+ return(ENXIO);
+
+ if ((sc = getsoftc(fdcd, FDUNIT(dev))) == NULL)
+ return(ENXIO);
+ if (sc->flags & FDF_NOTRACK0)
+ return(ENXIO);
+ if (sc->cachep == NULL)
+ sc->cachep = malloc(MAXTRKSZ, M_DEVBUF, M_WAITOK);
+
+ s = splbio();
+ /*
+ * if we are sleeping in fdclose(); waiting for a chance to
+ * shut the motor off, do a sleep here also.
+ */
+ while (sc->flags & FDF_WMOTOROFF)
+ tsleep(fdmotoroff, PRIBIO, "Fdopen", 0);
+
+ fwork = 0;
+ /*
+ * if not open let user open request type, otherwise
+ * ensure they are trying to open same type.
+ */
+ if (sc->openpart == FDPART(dev))
+ wasopen = 1;
+ else if (sc->openpart == -1) {
+ sc->openpart = FDPART(dev);
+ wasopen = 0;
+ } else {
+ wasopen = 1;
+ error = EPERM;
+ goto done;
+ }
+
+ /*
+ * wait for current io to complete if any
+ */
+ if (fdc_indma) {
+ fwork = 1;
+ fdc_wantwakeup++;
+ tsleep(Fdopen, PRIBIO, "Fdopen", 0);
+ }
+ if (error = fdloaddisk(sc))
+ goto done;
+ if (error = fdgetdisklabel(sc, dev))
+ goto done;
+#ifdef FDDEBUG
+ printf(" open successful\n");
+#endif
+done:
+ /*
+ * if we requested that fddone()->fdfindwork() wake us, allow it to
+ * complete its job now
+ */
+ if (fwork)
+ fdfindwork(FDUNIT(dev));
+ splx(s);
+
+ /*
+ * if we were not open and we marked us so reverse that.
+ */
+ if (error && wasopen == 0)
+ sc->openpart = 0;
+ return(error);
+}
+
+/*ARGSUSED*/
+int
+fdclose(dev, flags, devtype, p)
+ dev_t dev;
+ int flags, devtype;
+ struct proc *p;
+{
+ struct fd_softc *sc;
+ int s;
+
+#ifdef FDDEBUG
+ printf("fdclose()\n");
+#endif
+ sc = getsoftc(fdcd, FDUNIT(dev));
+ s = splbio();
+ if (sc->flags & FDF_MOTORON) {
+ sc->flags |= FDF_WMOTOROFF;
+ tsleep(fdmotoroff, PRIBIO, "fdclose", 0);
+ sc->flags &= ~FDF_WMOTOROFF;
+ wakeup(fdmotoroff);
+ }
+ sc->openpart = -1;
+ splx(s);
+ return(0);
+}
+
+int
+fdioctl(dev, cmd, addr, flag, p)
+ dev_t dev;
+ u_long cmd;
+ caddr_t addr;
+ int flag;
+ struct proc *p;
+{
+ struct fd_softc *sc;
+ void *data;
+ int error, wlab;
+
+ sc = getsoftc(fdcd, FDUNIT(dev));
+
+ if ((sc->flags & FDF_HAVELABEL) == 0)
+ return(EBADF);
+
+ switch (cmd) {
+ case DIOCSBAD:
+ return(EINVAL);
+ case DIOCSRETRIES:
+ if (*(int *)addr < 0)
+ return(EINVAL);
+ sc->retries = *(int *)addr;
+ return(0);
+ case DIOCSSTEP:
+ if (*(int *)addr < FDSTEPDELAY)
+ return(EINVAL);
+ sc->dkdev.dk_label.d_trkseek = sc->stepdelay = *(int *)addr;
+ return(0);
+ case DIOCGDINFO:
+ *(struct disklabel *)addr = sc->dkdev.dk_label;
+ return(0);
+ case DIOCGPART:
+ ((struct partinfo *)addr)->disklab = &sc->dkdev.dk_label;
+ ((struct partinfo *)addr)->part =
+ &sc->dkdev.dk_label.d_partitions[FDPART(dev)];
+ return(0);
+ case DIOCSDINFO:
+ if ((flag & FWRITE) == 0)
+ return(EBADF);
+ return(fdsetdisklabel(sc, (struct disklabel *)addr));
+ case DIOCWDINFO:
+ if ((flag & FWRITE) == 0)
+ return(EBADF);
+ if (error = fdsetdisklabel(sc, (struct disklabel *)addr))
+ return(error);
+ wlab = sc->wlabel;
+ sc->wlabel = 1;
+ error = fdputdisklabel(sc, dev);
+ sc->wlabel = wlab;
+ return(error);
+ case DIOCWLABEL:
+ if ((flag & FWRITE) == 0)
+ return(EBADF);
+ sc->wlabel = *(int *)addr;
+ return(0);
+ default:
+ return(ENOTTY);
+ }
+}
+
+/*
+ * no dumps to floppy disks thank you.
+ */
+int
+fdsize(dev)
+ dev_t dev;
+{
+ return(-1);
+}
+
+int
+fdread(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ return (physio(fdstrategy, NULL, dev, B_READ, fdminphys, uio));
+}
+
+int
+fdwrite(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ return (physio(fdstrategy, NULL, dev, B_WRITE, fdminphys, uio));
+}
+
+
+int
+fdintr()
+{
+ int s;
+
+ s = splbio();
+ if (fdc_indma)
+ fddmadone(fdc_indma, 0);
+ splx(s);
+}
+
+void
+fdstrategy(bp)
+ struct buf *bp;
+{
+ struct disklabel *lp;
+ struct fd_softc *sc;
+ struct buf *dp;
+ int unit, part, s;
+
+ unit = FDUNIT(bp->b_dev);
+ part = FDPART(bp->b_dev);
+ sc = getsoftc(fdcd, unit);
+
+#ifdef FDDEBUG
+ printf("fdstrategy: 0x%x\n", bp);
+#endif
+ /*
+ * check for valid partition and bounds
+ */
+ lp = &sc->dkdev.dk_label;
+ if ((sc->flags & FDF_HAVELABEL) == 0) {
+ bp->b_error = EIO;
+ goto bad;
+ }
+ if (bounds_check_with_label(bp, lp, sc->wlabel) <= 0)
+ goto done;
+
+ /*
+ * trans count of zero or bounds check indicates io is done
+ * we are done.
+ */
+ if (bp->b_bcount == 0)
+ goto done;
+
+ /*
+ * queue the buf and kick the low level code
+ */
+ s = splbio();
+ dp = &sc->bufq;
+ disksort(dp, bp);
+ fdstart(sc);
+ splx(s);
+ return;
+bad:
+ bp->b_flags |= B_ERROR;
+done:
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+}
+
+/*
+ * make sure disk is loaded and label is up-to-date.
+ */
+int
+fdloaddisk(sc)
+ struct fd_softc *sc;
+{
+ /*
+ * if diskchange is low step drive to 0 then up one then to zero.
+ */
+ fdsetpos(sc, 0, 0);
+ if (FDTESTC(FDB_CHANGED)) {
+ sc->cachetrk = -1; /* invalidate the cache */
+ sc->flags &= ~FDF_HAVELABEL;
+ fdsetpos(sc, FDNHEADS, 0);
+ fdsetpos(sc, 0, 0);
+ if (FDTESTC(FDB_CHANGED)) {
+ fdmotoroff(sc);
+ return(ENXIO);
+ }
+ }
+ fdmotoroff(sc);
+ sc->type = fdcgetfdtype(sc->hwunit);
+ if (sc->type == NULL)
+ return(ENXIO);
+#ifdef not_yet
+ if (sc->openpart == FDMSDOSPART)
+ sc->nsectors = sc->type->msdos_nsectors;
+ else
+#endif
+ sc->nsectors = sc->type->amiga_nsectors;
+ return(0);
+}
+
+/*
+ * read disk label, if present otherwise create one
+ * return a new label if raw part and none found, otherwise err.
+ */
+int
+fdgetdisklabel(sc, dev)
+ struct fd_softc *sc;
+ dev_t dev;
+{
+ struct disklabel *lp, *dlp;
+ struct cpu_disklabel *clp;
+ struct buf *bp;
+ int error, part;
+
+ if (sc->flags & FDF_HAVELABEL)
+ return(0);
+#ifdef FDDEBUG
+ printf("fdgetdisklabel()\n");
+#endif
+ part = FDPART(dev);
+ lp = &sc->dkdev.dk_label;
+ clp = &sc->dkdev.dk_cpulabel;
+ bzero(lp, sizeof(struct disklabel));
+ bzero(clp, sizeof(struct cpu_disklabel));
+
+ lp->d_secsize = FDSECSIZE;
+ lp->d_ntracks = FDNHEADS;
+ lp->d_ncylinders = sc->type->ncylinders;
+ lp->d_nsectors = sc->nsectors;
+ lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
+ lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
+ lp->d_npartitions = part + 1;
+ lp->d_partitions[part].p_size = lp->d_secperunit;
+ lp->d_partitions[part].p_fstype = FS_UNUSED;
+ lp->d_partitions[part].p_fsize = 1024;
+ lp->d_partitions[part].p_frag = 8;
+
+ sc->flags |= FDF_HAVELABEL;
+
+ bp = (void *)geteblk((int)lp->d_secsize);
+ bp->b_dev = dev;
+ bp->b_blkno = 0;
+ bp->b_cylin = 0;
+ bp->b_bcount = FDSECSIZE;
+ bp->b_flags = B_BUSY | B_READ;
+ fdstrategy(bp);
+ if (error = biowait(bp))
+ goto nolabel;
+ dlp = (struct disklabel *)(bp->b_data + LABELOFFSET);
+ if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC ||
+ dkcksum(dlp)) {
+ error = EINVAL;
+ goto nolabel;
+ }
+ bcopy(dlp, lp, sizeof(struct disklabel));
+ if (lp->d_trkseek > FDSTEPDELAY)
+ sc->stepdelay = lp->d_trkseek;
+ brelse(bp);
+ return(0);
+nolabel:
+ bzero(lp, sizeof(struct disklabel));
+ lp->d_secsize = FDSECSIZE;
+ lp->d_ntracks = FDNHEADS;
+ lp->d_ncylinders = sc->type->ncylinders;
+ lp->d_nsectors = sc->nsectors;
+ lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
+ lp->d_type = DTYPE_FLOPPY;
+ lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
+ lp->d_rpm = 300; /* good guess I suppose. */
+ lp->d_interleave = 1; /* should change when adding msdos */
+ sc->stepdelay = lp->d_trkseek = FDSTEPDELAY;
+ lp->d_bbsize = 0;
+ lp->d_sbsize = 0;
+ lp->d_partitions[part].p_size = lp->d_secperunit;
+ lp->d_partitions[part].p_fstype = FS_UNUSED;
+ lp->d_partitions[part].p_fsize = 1024;
+ lp->d_partitions[part].p_frag = 8;
+ lp->d_npartitions = part + 1;
+ lp->d_magic = lp->d_magic2 = DISKMAGIC;
+ lp->d_checksum = dkcksum(lp);
+ brelse(bp);
+ return(0);
+}
+
+/*
+ * set the incore copy of this units disklabel
+ */
+int
+fdsetdisklabel(sc, lp)
+ struct fd_softc *sc;
+ struct disklabel *lp;
+{
+ struct disklabel *clp;
+ struct partition *pp;
+
+ /*
+ * must have at least opened raw unit to fetch the
+ * raw_part stuff.
+ */
+ if ((sc->flags & FDF_HAVELABEL) == 0)
+ return(EINVAL);
+ clp = &sc->dkdev.dk_label;
+ /*
+ * make sure things check out and we only have one valid
+ * partition
+ */
+#ifdef FDDEBUG
+ printf("fdsetdisklabel\n");
+#endif
+ if (lp->d_secsize != FDSECSIZE ||
+ lp->d_nsectors != clp->d_nsectors ||
+ lp->d_ntracks != FDNHEADS ||
+ lp->d_ncylinders != clp->d_ncylinders ||
+ lp->d_secpercyl != clp->d_secpercyl ||
+ lp->d_secperunit != clp->d_secperunit ||
+ lp->d_magic != DISKMAGIC ||
+ lp->d_magic2 != DISKMAGIC ||
+ lp->d_npartitions == 0 ||
+ lp->d_npartitions > FDMAXPARTS ||
+ (lp->d_partitions[0].p_offset && lp->d_partitions[1].p_offset) ||
+ dkcksum(lp))
+ return(EINVAL);
+ /*
+ * if any partitions are present make sure they
+ * represent the currently open type
+ */
+ if ((pp = &lp->d_partitions[0])->p_size) {
+ if ((pp = &lp->d_partitions[1])->p_size == 0)
+ goto done;
+ else if (sc->openpart != 1)
+ return(EINVAL);
+ } else if (sc->openpart != 0)
+ return(EINVAL);
+ /*
+ * make sure selected partition is within bounds
+ * XXX on the second check, its to handle a bug in
+ * XXX the cluster routines as they require mutliples
+ * XXX of CLBYTES currently
+ */
+ if ((pp->p_offset + pp->p_size >= lp->d_secperunit) ||
+ (pp->p_frag * pp->p_fsize % CLBYTES))
+ return(EINVAL);
+done:
+ bcopy(lp, clp, sizeof(struct disklabel));
+ return(0);
+}
+
+/*
+ * write out the incore copy of this units disklabel
+ */
+int
+fdputdisklabel(sc, dev)
+ struct fd_softc *sc;
+ dev_t dev;
+{
+ struct disklabel *lp, *dlp;
+ struct buf *bp;
+ int error;
+
+ if ((sc->flags & FDF_HAVELABEL) == 0)
+ return(EBADF);
+#ifdef FDDEBUG
+ printf("fdputdisklabel\n");
+#endif
+ /*
+ * get buf and read in sector 0
+ */
+ lp = &sc->dkdev.dk_label;
+ bp = (void *)geteblk((int)lp->d_secsize);
+ bp->b_dev = FDMAKEDEV(major(dev), FDUNIT(dev), RAW_PART);
+ bp->b_blkno = 0;
+ bp->b_cylin = 0;
+ bp->b_bcount = FDSECSIZE;
+ bp->b_flags = B_BUSY | B_READ;
+ fdstrategy(bp);
+ if (error = biowait(bp))
+ goto done;
+ /*
+ * copy disklabel to buf and write it out syncronous
+ */
+ dlp = (struct disklabel *)(bp->b_data + LABELOFFSET);
+ bcopy(lp, dlp, sizeof(struct disklabel));
+ bp->b_blkno = 0;
+ bp->b_cylin = 0;
+ bp->b_flags = B_WRITE;
+ fdstrategy(bp);
+ error = biowait(bp);
+done:
+ brelse(bp);
+ return(error);
+}
+
+/*
+ * figure out drive type or NULL if none.
+ */
+struct fdtype *
+fdcgetfdtype(unit)
+ int unit;
+{
+ struct fdtype *ftp;
+ u_long id, idb;
+ int cnt, umask;
+
+ id = 0;
+ umask = 1 << (3 + unit);
+
+ FDDESELECT(FDCUNITMASK);
+
+ FDSETMOTOR(1);
+ delay(1);
+ FDSELECT(umask);
+ delay(1);
+ FDDESELECT(umask);
+
+ FDSETMOTOR(0);
+ delay(1);
+ FDSELECT(umask);
+ delay(1);
+ FDDESELECT(umask);
+
+ for (idb = 0x80000000; idb; idb >>= 1) {
+ FDSELECT(umask);
+ delay(1);
+ if (FDTESTC(FDB_READY) == 0)
+ id |= idb;
+ FDDESELECT(umask);
+ delay(1);
+ }
+#ifdef FDDEBUG
+ printf("fdcgettype unit %d id 0x%x\n", unit, id);
+#endif
+
+ for (cnt = 0, ftp = fdtype; cnt < nfdtype; ftp++, cnt++)
+ if (ftp->driveid == id)
+ return(ftp);
+ /*
+ * 3.5dd's at unit 0 do not always return id.
+ */
+ if (unit == 0)
+ return(fdtype);
+ return(NULL);
+}
+
+/*
+ * turn motor off if possible otherwise mark as needed and will be done
+ * later.
+ */
+void
+fdmotoroff(arg)
+ void *arg;
+{
+ struct fd_softc *sc;
+ int unitmask, s;
+
+ sc = arg;
+ s = splbio();
+
+#ifdef FDDEBUG
+ printf("fdmotoroff: unit %d\n", sc->hwunit);
+#endif
+ if ((sc->flags & FDF_MOTORON) == 0)
+ goto done;
+ /*
+ * if we have a timeout on a dma operation let fddmadone()
+ * deal with it.
+ */
+ if (fdc_indma == sc) {
+ fddmadone(sc, 1);
+ goto done;
+ }
+#ifdef FDDEBUG
+ printf(" motor was on, turning off\n");
+#endif
+
+ /*
+ * flush cache if needed
+ */
+ if (sc->flags & FDF_DIRTY) {
+ sc->flags |= FDF_JUSTFLUSH | FDF_MOTOROFF;
+#ifdef FDDEBUG
+ printf(" flushing dirty buffer first\n");
+#endif
+ /*
+ * if dma'ing done for now, fddone() will call us again
+ */
+ if (fdc_indma)
+ goto done;
+ fddmastart(sc, sc->cachetrk);
+ goto done;
+ }
+
+ /*
+ * if controller is busy just schedule us to be called back
+ */
+ if (fdc_indma) {
+ /*
+ * someone else has the controller now
+ * just set flag and let fddone() call us again.
+ */
+ sc->flags |= FDF_MOTOROFF;
+ goto done;
+ }
+
+#ifdef FDDEBUG
+ printf(" hw turing unit off\n");
+#endif
+
+ sc->flags &= ~(FDF_MOTORON | FDF_MOTOROFF);
+ FDDESELECT(FDCUNITMASK);
+ FDSETMOTOR(0);
+ delay(1);
+ FDSELECT(sc->unitmask);
+ delay(4);
+ FDDESELECT(sc->unitmask);
+ delay(1);
+ if (sc->flags & FDF_WMOTOROFF)
+ wakeup(fdmotoroff);
+done:
+ splx(s);
+}
+
+/*
+ * select drive seek to track exit with motor on.
+ * fdsetpos(x, 0, 0) does calibrates the drive.
+ */
+void
+fdsetpos(sc, trk, towrite)
+ struct fd_softc *sc;
+ int trk, towrite;
+{
+ int nstep, sdir, ondly, ncyl, nside;
+
+ FDDESELECT(FDCUNITMASK);
+ FDSETMOTOR(1);
+ delay(1);
+ FDSELECT(sc->unitmask);
+ delay(1);
+ if ((sc->flags & FDF_MOTORON) == 0) {
+ ondly = 0;
+ while (FDTESTC(FDB_READY) == 0) {
+ delay(1000);
+ if (++ondly >= 1000)
+ break;
+ }
+ }
+ sc->flags |= FDF_MOTORON;
+
+ ncyl = trk / FDNHEADS;
+ nside = trk % FDNHEADS;
+
+ if (sc->curcyl == ncyl && fdc_side == nside)
+ return;
+
+ if (towrite)
+ sc->flags |= FDF_WRITEWAIT;
+
+#ifdef FDDEBUG
+ printf("fdsetpos: cyl %d head %d towrite %d\n", trk / FDNHEADS,
+ trk % FDNHEADS, towrite);
+#endif
+ nstep = ncyl - sc->curcyl;
+ if (nstep) {
+ /*
+ * figure direction
+ */
+ if (nstep > 0 && ncyl != 0) {
+ sdir = FDSTEPIN;
+ FDSETDIR(1);
+ } else {
+ nstep = -nstep;
+ sdir = FDSTEPOUT;
+ FDSETDIR(0);
+ }
+ if (ncyl == 0) {
+ /*
+ * either just want cylinder 0 or doing
+ * a calibrate.
+ */
+ nstep = 256;
+ while (FDTESTC(FDB_CYLZERO) == 0 && nstep--) {
+ FDSTEP;
+ delay(sc->stepdelay);
+ }
+ if (nstep < 0)
+ sc->flags |= FDF_NOTRACK0;
+ } else {
+ /*
+ * step the needed amount amount.
+ */
+ while (nstep--) {
+ FDSTEP;
+ delay(sc->stepdelay);
+ }
+ }
+ /*
+ * if switched directions
+ * allow drive to settle.
+ */
+ if (sc->pstepdir != sdir)
+ delay(FDSETTLEDELAY);
+ sc->pstepdir = sdir;
+ sc->curcyl = ncyl;
+ }
+ if (nside == fdc_side)
+ return;
+ /*
+ * select side
+ */
+ fdc_side = nside;
+ FDSETHEAD(nside);
+ delay(FDPRESIDEDELAY);
+}
+
+void
+fdselunit(sc)
+ struct fd_softc *sc;
+{
+ FDDESELECT(FDCUNITMASK); /* deselect all */
+ FDSETMOTOR(sc->flags & FDF_MOTORON); /* set motor to unit's state */
+ delay(1);
+ FDSELECT(sc->unitmask); /* select unit */
+ delay(1);
+}
+
+/*
+ * process next buf on device queue.
+ * normall sequence of events:
+ * fdstart() -> fddmastart();
+ * fdintr() -> fddmadone() -> fddone();
+ * if the track is in the cache then fdstart() will short-circuit
+ * to fddone() else if the track cache is dirty it will flush. If
+ * the buf is not an entire track it will cache the requested track.
+ */
+void
+fdstart(sc)
+ struct fd_softc *sc;
+{
+ int trk, error, write;
+ struct buf *bp, *dp;
+
+#ifdef FDDEBUG
+ printf("fdstart: unit %d\n", sc->hwunit);
+#endif
+
+ /*
+ * if dma'ing just return. we must have been called from fdstartegy.
+ */
+ if (fdc_indma)
+ return;
+
+ /*
+ * get next buf if there.
+ */
+ dp = &sc->bufq;
+ if ((bp = dp->b_actf) == NULL) {
+#ifdef FDDEBUG
+ printf(" nothing to do\n");
+#endif
+ return;
+ }
+
+ /*
+ * make sure same disk is loaded
+ */
+ fdselunit(sc);
+ if (FDTESTC(FDB_CHANGED)) {
+ /*
+ * disk missing, invalidate all future io on
+ * this unit until re-open()'ed also invalidate
+ * all current io
+ */
+#ifdef FDDEBUG
+ printf(" disk was removed invalidating all io\n");
+#endif
+ sc->flags &= ~FDF_HAVELABEL;
+ for (;;) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ if (bp->b_actf == NULL)
+ break;
+ biodone(bp);
+ bp = bp->b_actf;
+ }
+ /*
+ * do fddone() on last buf to allow other units to start.
+ */
+ dp->b_actf = bp;
+ fddone(sc);
+ return;
+ }
+
+ /*
+ * we have a valid buf, setup our local version
+ * we use this count to allow reading over multiple tracks.
+ * into a single buffer
+ */
+ dp->b_bcount = bp->b_bcount;
+ dp->b_blkno = bp->b_blkno;
+ dp->b_data = bp->b_data;
+ dp->b_flags = bp->b_flags;
+ dp->b_resid = 0;
+
+ if (bp->b_flags & B_READ)
+ write = 0;
+ else if (FDTESTC(FDB_PROTECT) == 0)
+ write = 1;
+ else {
+ error = EPERM;
+ goto bad;
+ }
+
+ /*
+ * figure trk given blkno
+ */
+ trk = bp->b_blkno / sc->nsectors;
+
+ /*
+ * check to see if same as currently cached track
+ * if so we need to do no dma read.
+ */
+ if (trk == sc->cachetrk) {
+ fddone(sc);
+ return;
+ }
+
+ /*
+ * if we will be overwriting the entire cache, don't bother to
+ * fetch it.
+ */
+ if (bp->b_bcount == (sc->nsectors * FDSECSIZE) && write &&
+ bp->b_blkno % sc->nsectors == 0) {
+ if (sc->flags & FDF_DIRTY)
+ sc->flags |= FDF_JUSTFLUSH;
+ else {
+ sc->cachetrk = trk;
+ fddone(sc);
+ return;
+ }
+ }
+
+ /*
+ * start dma read of `trk'
+ */
+ fddmastart(sc, trk);
+ return;
+bad:
+ bp->b_flags |= B_ERROR;
+ bp->b_error = error;
+ fddone(sc);
+}
+
+/*
+ * continue a started operation on next track. always begin at
+ * sector 0 on the next track.
+ */
+void
+fdcont(sc)
+ struct fd_softc *sc;
+{
+ struct buf *dp, *bp;
+ int trk, write;
+
+ dp = &sc->bufq;
+ bp = dp->b_actf;
+ dp->b_data += (dp->b_bcount - bp->b_resid);
+ dp->b_blkno += (dp->b_bcount - bp->b_resid) / FDSECSIZE;
+ dp->b_bcount = bp->b_resid;
+
+ /*
+ * figure trk given blkno
+ */
+ trk = dp->b_blkno / sc->nsectors;
+#ifdef DEBUG
+ if (trk != sc->cachetrk + 1 || dp->b_blkno % sc->nsectors != 0)
+ panic("fdcont: confused");
+#endif
+ if (dp->b_flags & B_READ)
+ write = 0;
+ else
+ write = 1;
+ /*
+ * if we will be overwriting the entire cache, don't bother to
+ * fetch it.
+ */
+ if (dp->b_bcount == (sc->nsectors * FDSECSIZE) && write) {
+ if (sc->flags & FDF_DIRTY)
+ sc->flags |= FDF_JUSTFLUSH;
+ else {
+ sc->cachetrk = trk;
+ fddone(sc);
+ return;
+ }
+ }
+ /*
+ * start dma read of `trk'
+ */
+ fddmastart(sc, trk);
+ return;
+}
+
+void
+fddmastart(sc, trk)
+ struct fd_softc *sc;
+ int trk;
+{
+ int adkmask, ndmaw, write, dmatrk;
+
+#ifdef FDDEBUG
+ printf("fddmastart: unit %d cyl %d head %d", sc->hwunit,
+ trk / FDNHEADS, trk % FDNHEADS);
+#endif
+ /*
+ * flush the cached track if dirty else read requested track.
+ */
+ if (sc->flags & FDF_DIRTY) {
+ fdcachetoraw(sc);
+ ndmaw = sc->type->nwritew;
+ dmatrk = sc->cachetrk;
+ write = 1;
+ } else {
+ ndmaw = sc->type->nreadw;
+ dmatrk = trk;
+ write = 0;
+ }
+
+#ifdef FDDEBUG
+ printf(" %s", write ? " flushing cache\n" : " loading cache\n");
+#endif
+ sc->cachetrk = trk;
+ fdc_indma = sc;
+ fdsetpos(sc, dmatrk, write);
+
+ /*
+ * setup dma stuff
+ */
+ if (write == 0) {
+ custom.adkcon = ADKF_MSBSYNC;
+ custom.adkcon = ADKF_SETCLR | ADKF_WORDSYNC | ADKF_FAST;
+ custom.dsksync = FDMFMSYNC;
+ } else {
+ custom.adkcon = ADKF_PRECOMP1 | ADKF_PRECOMP0 | ADKF_WORDSYNC |
+ ADKF_MSBSYNC;
+ adkmask = ADKF_SETCLR | ADKF_FAST | ADKF_MFMPREC;
+ if (dmatrk >= sc->type->precomp[0])
+ adkmask |= ADKF_PRECOMP0;
+ if (dmatrk >= sc->type->precomp[1])
+ adkmask |= ADKF_PRECOMP1;
+ custom.adkcon = adkmask;
+ }
+ custom.dskpt = (u_char *)kvtop(fdc_dmap);
+ FDDMASTART(ndmaw, write);
+
+#ifdef FDDEBUG
+ printf(" dma started\n");
+#endif
+}
+
+/*
+ * recalibrate the drive
+ */
+void
+fdcalibrate(arg)
+ void *arg;
+{
+ struct fd_softc *sc;
+ static int loopcnt;
+
+ sc = arg;
+
+ if (loopcnt == 0) {
+ /*
+ * seek cyl 0
+ */
+ fdc_indma = sc;
+ sc->stepdelay += 900;
+ if (sc->cachetrk > 1)
+ fdsetpos(sc, sc->cachetrk % FDNHEADS, 0);
+ sc->stepdelay -= 900;
+ }
+ if (loopcnt++ & 1)
+ fdsetpos(sc, sc->cachetrk, 0);
+ else
+ fdsetpos(sc, sc->cachetrk + FDNHEADS, 0);
+ /*
+ * trk++, trk, trk++, trk, trk++, trk, trk++, trk and dma
+ */
+ if (loopcnt < 8)
+ timeout(fdcalibrate, sc, hz / 8);
+ else {
+ loopcnt = 0;
+ fdc_indma = NULL;
+ timeout(fdmotoroff, sc, 3 * hz / 2);
+ fddmastart(sc, sc->cachetrk);
+ }
+}
+
+void
+fddmadone(sc, timeo)
+ struct fd_softc *sc;
+ int timeo;
+{
+#ifdef FDDEBUG
+ printf("fddmadone: unit %d, timeo %d\n", sc->hwunit, timeo);
+#endif
+ fdc_indma = NULL;
+ untimeout(fdmotoroff, sc);
+ FDDMASTOP;
+
+ /*
+ * guarantee the drive has been at current head and cyl
+ * for at least FDWRITEDELAY after a write.
+ */
+ if (sc->flags & FDF_WRITEWAIT) {
+ delay(FDWRITEDELAY);
+ sc->flags &= ~FDF_WRITEWAIT;
+ }
+
+ if ((sc->flags & FDF_MOTOROFF) == 0) {
+ /*
+ * motor runs for 1.5 seconds after last dma
+ */
+ timeout(fdmotoroff, sc, 3 * hz / 2);
+ }
+ if (sc->flags & FDF_DIRTY) {
+ /*
+ * if buffer dirty, the last dma cleaned it
+ */
+ sc->flags &= ~FDF_DIRTY;
+ if (timeo)
+ printf("%s: write of track cache timed out.\n",
+ sc->dkdev.dk_dev.dv_xname);
+ if (sc->flags & FDF_JUSTFLUSH) {
+ sc->flags &= ~FDF_JUSTFLUSH;
+ /*
+ * we are done dma'ing
+ */
+ fddone(sc);
+ return;
+ }
+ /*
+ * load the cache
+ */
+ fddmastart(sc, sc->cachetrk);
+ return;
+ }
+#ifdef FDDEBUG
+ else if (sc->flags & FDF_MOTOROFF)
+ panic("fddmadone: FDF_MOTOROFF with no FDF_DIRTY");
+#endif
+
+ /*
+ * cache loaded decode it into cache buffer
+ */
+ if (timeo == 0 && fdrawtocache(sc) == 0)
+ sc->retried = 0;
+ else {
+#ifdef FDDEBUG
+ if (timeo)
+ printf("%s: fddmadone: cache load timed out.\n",
+ sc->dkdev.dk_dev.dv_xname);
+#endif
+ if (sc->retried >= sc->retries) {
+ sc->retried = 0;
+ sc->cachetrk = -1;
+ } else {
+ sc->retried++;
+ /*
+ * this will be restarted at end of calibrate loop.
+ */
+ untimeout(fdmotoroff, sc);
+ fdcalibrate(sc);
+ return;
+ }
+ }
+ fddone(sc);
+}
+
+void
+fddone(sc)
+ struct fd_softc *sc;
+{
+ struct buf *dp, *bp;
+ char *data;
+ int sz, blk;
+
+#ifdef FDDEBUG
+ printf("fddone: unit %d\n", sc->hwunit);
+#endif
+ /*
+ * check to see if unit is just flushing the cache,
+ * that is we have no io queued.
+ */
+ if (sc->flags & FDF_MOTOROFF)
+ goto nobuf;
+
+ dp = &sc->bufq;
+ if ((bp = dp->b_actf) == NULL)
+ panic ("fddone");
+ /*
+ * check for an error that may have occured
+ * while getting the track.
+ */
+ if (sc->cachetrk == -1) {
+ sc->retried = 0;
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ } else if ((bp->b_flags & B_ERROR) == 0) {
+ data = sc->cachep;
+ /*
+ * get offset of data in track cache and limit
+ * the copy size to not exceed the cache's end.
+ */
+ data += (dp->b_blkno % sc->nsectors) * FDSECSIZE;
+ sz = sc->nsectors - dp->b_blkno % sc->nsectors;
+ sz *= FDSECSIZE;
+ sz = min(dp->b_bcount, sz);
+ if (bp->b_flags & B_READ)
+ bcopy(data, dp->b_data, sz);
+ else {
+ bcopy(dp->b_data, data, sz);
+ sc->flags |= FDF_DIRTY;
+ }
+ bp->b_resid = dp->b_bcount - sz;
+ if (bp->b_resid == 0) {
+ bp->b_error = 0;
+ } else {
+ /*
+ * not done yet need to read next track
+ */
+ fdcont(sc);
+ return;
+ }
+ }
+ /*
+ * remove from queue.
+ */
+ dp->b_actf = bp->b_actf;
+ biodone(bp);
+nobuf:
+ fdfindwork(sc->dkdev.dk_dev.dv_unit);
+}
+
+void
+fdfindwork(unit)
+ int unit;
+{
+ struct fd_softc *ssc, *sc;
+ int i, last;
+
+ /*
+ * first see if we have any Fdopen()'s waiting
+ */
+ if (fdc_wantwakeup) {
+ wakeup(Fdopen);
+ fdc_wantwakeup--;
+ return;
+ }
+
+ /*
+ * start next available unit, linear search from the next unit
+ * wrapping and finally this unit.
+ */
+ last = 0;
+ ssc = NULL;
+ for (i = unit + 1; last == 0; i++) {
+ if (i == unit)
+ last = 1;
+ if (i >= fdcd.cd_ndevs) {
+ i = -1;
+ continue;
+ }
+ if ((sc = fdcd.cd_devs[i]) == NULL)
+ continue;
+
+ /*
+ * if unit has requested to be turned off
+ * and it has no buf's queued do it now
+ */
+ if (sc->flags & FDF_MOTOROFF) {
+ if (sc->bufq.b_actf == NULL)
+ fdmotoroff(sc);
+ else {
+ /*
+ * we gained a buf request while
+ * we waited, forget the motoroff
+ */
+ sc->flags &= ~FDF_MOTOROFF;
+ }
+ /*
+ * if we now have dma unit must have needed
+ * flushing, quit
+ */
+ if (fdc_indma)
+ return;
+ }
+ /*
+ * if we have no start unit and the current unit has
+ * io waiting choose this unit to start.
+ */
+ if (ssc == NULL && sc->bufq.b_actf)
+ ssc = sc;
+ }
+ if (ssc)
+ fdstart(ssc);
+}
+
+/*
+ * min byte count to whats left of the track in question
+ */
+void
+fdminphys(bp)
+ struct buf *bp;
+{
+ struct fd_softc *sc;
+ int trk, sec, toff, tsz;
+
+ if ((sc = getsoftc(fdcd, FDUNIT(bp->b_dev))) == NULL)
+ panic("fdminphys: couldn't get softc");
+
+ trk = bp->b_blkno / sc->nsectors;
+ sec = bp->b_blkno % sc->nsectors;
+
+ toff = sec * FDSECSIZE;
+ tsz = sc->nsectors * FDSECSIZE;
+#ifdef FDDEBUG
+ printf("fdminphys: before %d", bp->b_bcount);
+#endif
+ bp->b_bcount = min(bp->b_bcount, tsz - toff);
+#ifdef FDDEBUG
+ printf(" after %d\n", bp->b_bcount);
+#endif
+ minphys(bp);
+}
+
+/*
+ * encode the track cache into raw MFM ready for dma
+ * when we go to multiple disk formats, this will call type dependent
+ * functions
+ */
+void
+fdcachetoraw(sc)
+ struct fd_softc *sc;
+{
+ static u_long mfmnull[4];
+ u_long *rp, *crp, *dp, hcksum, dcksum, info, zero;
+ int sec, i;
+
+ rp = fdc_dmap;
+
+ /*
+ * not yet one sector (- 1 long) gap.
+ * for now use previous drivers values
+ */
+ for (i = 0; i < sc->type->gap; i++)
+ *rp++ = 0xaaaaaaaa;
+ /*
+ * process sectors
+ */
+ dp = sc->cachep;
+ zero = 0;
+ info = 0xff000000 | (sc->cachetrk << 16) | sc->nsectors;
+ for (sec = 0; sec < sc->nsectors; sec++, info += (1 << 8) - 1) {
+ hcksum = dcksum = 0;
+ /*
+ * sector format
+ * offset description
+ *-----------------------------------
+ * 0 null
+ * 1 sync
+ * oddbits evenbits
+ *----------------------
+ * 2 3 [0xff]b [trk]b [sec]b [togap]b
+ * 4-7 8-11 null
+ * 12 13 header cksum [2-11]
+ * 14 15 data cksum [16-271]
+ * 16-143 144-271 data
+ */
+ *rp = 0xaaaaaaaa;
+ if (*(rp - 1) & 0x1)
+ *rp &= 0x7fffffff; /* clock bit correction */
+ rp++;
+ *rp++ = (FDMFMSYNC << 16) | FDMFMSYNC;
+ rp = mfmblkencode(&info, rp, &hcksum, 1);
+ rp = mfmblkencode(mfmnull, rp, &hcksum, 4);
+ rp = mfmblkencode(&hcksum, rp, NULL, 1);
+
+ crp = rp;
+ rp = mfmblkencode(dp, rp + 2, &dcksum, FDSECLWORDS);
+ dp += FDSECLWORDS;
+ crp = mfmblkencode(&dcksum, crp, NULL, 1);
+ if (*(crp - 1) & 0x1)
+ *crp &= 0x7fffffff; /* clock bit correction */
+ else if ((*crp & 0x40000000) == 0)
+ *crp |= 0x80000000;
+ }
+ *rp = 0xaaa80000;
+ if (*(rp - 1) & 0x1)
+ *rp &= 0x7fffffff;
+}
+
+u_long *
+fdfindsync(rp, ep)
+ u_long *rp, *ep;
+{
+ u_short *sp;
+
+ sp = (u_short *)rp;
+ while ((u_long *)sp < ep && *sp != FDMFMSYNC)
+ sp++;
+ while ((u_long *)sp < ep && *sp == FDMFMSYNC)
+ sp++;
+ if ((u_long *)sp < ep)
+ return((u_long *)sp);
+ return(NULL);
+}
+
+/*
+ * decode raw MFM from dma into units track cache.
+ * when we go to multiple disk formats, this will call type dependent
+ * functions
+ */
+int
+fdrawtocache(sc)
+ struct fd_softc *sc;
+{
+ u_long mfmnull[4];
+ u_long *dp, *rp, *erp, *crp, *srp, hcksum, dcksum, info, cktmp;
+ int cnt, doagain;
+
+ doagain = 1;
+ srp = rp = fdc_dmap;
+ erp = (u_long *)((u_short *)rp + sc->type->nreadw);
+ cnt = 0;
+again:
+ if (doagain == 0 || (rp = srp = fdfindsync(srp, erp)) == NULL) {
+#ifdef DIAGNOSTIC
+ printf("%s: corrupted track (%d) data.\n",
+ sc->dkdev.dk_dev.dv_xname, sc->cachetrk);
+#endif
+ return(-1);
+ }
+
+ /*
+ * process sectors
+ */
+ for (; cnt < sc->nsectors; cnt++) {
+ hcksum = dcksum = 0;
+ rp = mfmblkdecode(rp, &info, &hcksum, 1);
+ rp = mfmblkdecode(rp, mfmnull, &hcksum, 4);
+ rp = mfmblkdecode(rp, &cktmp, NULL, 1);
+ if (cktmp != hcksum) {
+#ifdef FDDEBUG
+ printf(" info 0x%x hchksum 0x%x trkhcksum 0x%x\n",
+ info, hcksum, cktmp);
+#endif
+ goto again;
+ }
+ if (((info >> 16) & 0xff) != sc->cachetrk) {
+#ifdef DEBUG
+ printf("%s: incorrect track found: 0x%0x %d\n",
+ sc->dkdev.dk_dev.dv_xname, info, sc->cachetrk);
+#endif
+ goto again;
+ }
+#ifdef FDDEBUG
+ printf(" info 0x%x\n", info);
+#endif
+
+ rp = mfmblkdecode(rp, &cktmp, NULL, 1);
+ dp = sc->cachep;
+ dp += FDSECLWORDS * ((info >> 8) & 0xff);
+ crp = mfmblkdecode(rp, dp, &dcksum, FDSECLWORDS);
+ if (cktmp != dcksum) {
+#ifdef FDDEBUG
+ printf(" info 0x%x dchksum 0x%x trkdcksum 0x%x\n",
+ info, dcksum, cktmp);
+#endif
+ goto again;
+ }
+
+ /*
+ * if we are at gap then we can no longer be sure
+ * of correct sync marks
+ */
+ if ((info && 0xff) == 1)
+ doagain = 1;
+ else
+ doagain = 0;
+ srp = rp = fdfindsync(crp, erp);
+ }
+ return(0);
+}
+
+/*
+ * encode len longwords of `dp' data in amiga mfm block format (`rp')
+ * this format specified that the odd bits are at current pos and even
+ * bits at len + current pos
+ */
+u_long *
+mfmblkencode(dp, rp, cp, len)
+ u_long *dp, *rp, *cp;
+ int len;
+{
+ u_long *sdp, *edp, d, dtmp, correct;
+ int i;
+
+ sdp = dp;
+ edp = dp + len;
+
+ if (*(rp - 1) & 0x1)
+ correct = 1;
+ else
+ correct = 0;
+ /*
+ * do odd bits
+ */
+ while (dp < edp) {
+ d = (*dp >> 1) & 0x55555555; /* remove clock bits */
+ dtmp = d ^ 0x55555555;
+ d |= ((dtmp >> 1) | 0x80000000) & (dtmp << 1);
+ /*
+ * correct upper clock bit if needed
+ */
+ if (correct)
+ d &= 0x7fffffff;
+ if (d & 0x1)
+ correct = 1;
+ else
+ correct = 0;
+ /*
+ * do checksums and store in raw buffer
+ */
+ if (cp)
+ *cp ^= d;
+ *rp++ = d;
+ dp++;
+ }
+ /*
+ * do even bits
+ */
+ dp = sdp;
+ while (dp < edp) {
+ d = *dp & 0x55555555; /* remove clock bits */
+ dtmp = d ^ 0x55555555;
+ d |= ((dtmp >> 1) | 0x80000000) & (dtmp << 1);
+ /*
+ * correct upper clock bit if needed
+ */
+ if (correct)
+ d &= 0x7fffffff;
+ if (d & 0x1)
+ correct = 1;
+ else
+ correct = 0;
+ /*
+ * do checksums and store in raw buffer
+ */
+ if (cp)
+ *cp ^= d;
+ *rp++ = d;
+ dp++;
+ }
+ if (cp)
+ *cp &= 0x55555555;
+ return(rp);
+}
+
+/*
+ * decode len longwords of `dp' data in amiga mfm block format (`rp')
+ * this format specified that the odd bits are at current pos and even
+ * bits at len + current pos
+ */
+u_long *
+mfmblkdecode(rp, dp, cp, len)
+ u_long *rp, *dp, *cp;
+ int len;
+{
+ u_long o, e;
+ int cnt;
+
+ cnt = len;
+ while (cnt--) {
+ o = *rp;
+ e = *(rp + len);
+ if (cp) {
+ *cp ^= o;
+ *cp ^= e;
+ }
+ o &= 0x55555555;
+ e &= 0x55555555;
+ *dp++ = (o << 1) | e;
+ rp++;
+ }
+ if (cp)
+ *cp &= 0x55555555;
+ return(rp + len);
+}
+
+int
+fddump()
+{
+ return (EINVAL);
+}